forked from Minki/linux
Merge branch 'spinor'
Addition of the spi-nor framework, plus updates to the ST SPI FSM driver.
This commit is contained in:
commit
d66d3519c6
35
Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
Normal file
35
Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
Normal file
@ -0,0 +1,35 @@
|
||||
* Freescale Quad Serial Peripheral Interface(QuadSPI)
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,vf610-qspi"
|
||||
- reg : the first contains the register location and length,
|
||||
the second contains the memory mapping address and length
|
||||
- reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
|
||||
- interrupts : Should contain the interrupt for the device
|
||||
- clocks : The clocks needed by the QuadSPI controller
|
||||
- clock-names : the name of the clocks
|
||||
|
||||
Optional properties:
|
||||
- fsl,qspi-has-second-chip: The controller has two buses, bus A and bus B.
|
||||
Each bus can be connected with two NOR flashes.
|
||||
Most of the time, each bus only has one NOR flash
|
||||
connected, this is the default case.
|
||||
But if there are two NOR flashes connected to the
|
||||
bus, you should enable this property.
|
||||
(Please check the board's schematic.)
|
||||
|
||||
Example:
|
||||
|
||||
qspi0: quadspi@40044000 {
|
||||
compatible = "fsl,vf610-qspi";
|
||||
reg = <0x40044000 0x1000>, <0x20000000 0x10000000>;
|
||||
reg-names = "QuadSPI", "QuadSPI-memory";
|
||||
interrupts = <0 24 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clks VF610_CLK_QSPI0_EN>,
|
||||
<&clks VF610_CLK_QSPI0>;
|
||||
clock-names = "qspi_en", "qspi";
|
||||
|
||||
flash0: s25fl128s@0 {
|
||||
....
|
||||
};
|
||||
};
|
62
Documentation/mtd/spi-nor.txt
Normal file
62
Documentation/mtd/spi-nor.txt
Normal file
@ -0,0 +1,62 @@
|
||||
SPI NOR framework
|
||||
============================================
|
||||
|
||||
Part I - Why do we need this framework?
|
||||
---------------------------------------
|
||||
|
||||
SPI bus controllers (drivers/spi/) only deal with streams of bytes; the bus
|
||||
controller operates agnostic of the specific device attached. However, some
|
||||
controllers (such as Freescale's QuadSPI controller) cannot easily handle
|
||||
arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
|
||||
|
||||
In particular, Freescale's QuadSPI controller must know the NOR commands to
|
||||
find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of
|
||||
opcodes, addresses, or data payloads; a SPI controller simply knows to send or
|
||||
receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under
|
||||
which the controller driver is aware of the opcodes, addressing, and other
|
||||
details of the SPI NOR protocol.
|
||||
|
||||
Part II - How does the framework work?
|
||||
--------------------------------------
|
||||
|
||||
This framework just adds a new layer between the MTD and the SPI bus driver.
|
||||
With this new layer, the SPI NOR controller driver does not depend on the
|
||||
m25p80 code anymore.
|
||||
|
||||
Before this framework, the layer is like:
|
||||
|
||||
MTD
|
||||
------------------------
|
||||
m25p80
|
||||
------------------------
|
||||
SPI bus driver
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
|
||||
After this framework, the layer is like:
|
||||
MTD
|
||||
------------------------
|
||||
SPI NOR framework
|
||||
------------------------
|
||||
m25p80
|
||||
------------------------
|
||||
SPI bus driver
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
|
||||
With the SPI NOR controller driver (Freescale QuadSPI), it looks like:
|
||||
MTD
|
||||
------------------------
|
||||
SPI NOR framework
|
||||
------------------------
|
||||
fsl-quadSPI
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
|
||||
Part III - How can drivers use the framework?
|
||||
---------------------------------------------
|
||||
|
||||
The main API is spi_nor_scan(). Before you call the hook, a driver should
|
||||
initialize the necessary fields for spi_nor{}. Please see
|
||||
drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to fsl-quadspi.c
|
||||
when you want to write a new driver for a SPI NOR controller.
|
@ -321,6 +321,8 @@ source "drivers/mtd/onenand/Kconfig"
|
||||
|
||||
source "drivers/mtd/lpddr/Kconfig"
|
||||
|
||||
source "drivers/mtd/spi-nor/Kconfig"
|
||||
|
||||
source "drivers/mtd/ubi/Kconfig"
|
||||
|
||||
endif # MTD
|
||||
|
@ -32,4 +32,5 @@ inftl-objs := inftlcore.o inftlmount.o
|
||||
|
||||
obj-y += chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
|
||||
|
||||
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/
|
||||
obj-$(CONFIG_MTD_UBI) += ubi/
|
||||
|
@ -80,7 +80,7 @@ config MTD_DATAFLASH_OTP
|
||||
|
||||
config MTD_M25P80
|
||||
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
|
||||
depends on SPI_MASTER
|
||||
depends on SPI_MASTER && MTD_SPI_NOR
|
||||
help
|
||||
This enables access to most modern SPI flash chips, used for
|
||||
program and data storage. Series supported include Atmel AT26DF,
|
||||
@ -212,7 +212,7 @@ config MTD_DOCG3
|
||||
|
||||
config MTD_ST_SPI_FSM
|
||||
tristate "ST Microelectronics SPI FSM Serial Flash Controller"
|
||||
depends on ARM || SH
|
||||
depends on ARCH_STI
|
||||
help
|
||||
This provides an MTD device driver for the ST Microelectronics
|
||||
SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,43 +13,23 @@
|
||||
#define _MTD_SERIAL_FLASH_CMDS_H
|
||||
|
||||
/* Generic Flash Commands/OPCODEs */
|
||||
#define FLASH_CMD_WREN 0x06
|
||||
#define FLASH_CMD_WRDI 0x04
|
||||
#define FLASH_CMD_RDID 0x9f
|
||||
#define FLASH_CMD_RDSR 0x05
|
||||
#define FLASH_CMD_RDSR2 0x35
|
||||
#define FLASH_CMD_WRSR 0x01
|
||||
#define FLASH_CMD_SE_4K 0x20
|
||||
#define FLASH_CMD_SE_32K 0x52
|
||||
#define FLASH_CMD_SE 0xd8
|
||||
#define FLASH_CMD_CHIPERASE 0xc7
|
||||
#define FLASH_CMD_WRVCR 0x81
|
||||
#define FLASH_CMD_RDVCR 0x85
|
||||
#define SPINOR_OP_RDSR2 0x35
|
||||
#define SPINOR_OP_WRVCR 0x81
|
||||
#define SPINOR_OP_RDVCR 0x85
|
||||
|
||||
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
|
||||
#define FLASH_CMD_READ 0x03 /* READ */
|
||||
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
|
||||
#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||
#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
|
||||
#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||
#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */
|
||||
|
||||
#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
||||
|
||||
#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
|
||||
#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
|
||||
#define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||
#define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
||||
|
||||
/* READ commands with 32-bit addressing */
|
||||
#define FLASH_CMD_READ4 0x13
|
||||
#define FLASH_CMD_READ4_FAST 0x0c
|
||||
#define FLASH_CMD_READ4_1_1_2 0x3c
|
||||
#define FLASH_CMD_READ4_1_2_2 0xbc
|
||||
#define FLASH_CMD_READ4_1_1_4 0x6c
|
||||
#define FLASH_CMD_READ4_1_4_4 0xec
|
||||
#define SPINOR_OP_READ4_1_2_2 0xbc
|
||||
#define SPINOR_OP_READ4_1_4_4 0xec
|
||||
|
||||
/* Configuration flags */
|
||||
#define FLASH_FLAG_SINGLE 0x000000ff
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
@ -201,44 +202,6 @@
|
||||
|
||||
#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */
|
||||
|
||||
/* Flash Commands */
|
||||
#define FLASH_CMD_WREN 0x06
|
||||
#define FLASH_CMD_WRDI 0x04
|
||||
#define FLASH_CMD_RDID 0x9f
|
||||
#define FLASH_CMD_RDSR 0x05
|
||||
#define FLASH_CMD_RDSR2 0x35
|
||||
#define FLASH_CMD_WRSR 0x01
|
||||
#define FLASH_CMD_SE_4K 0x20
|
||||
#define FLASH_CMD_SE_32K 0x52
|
||||
#define FLASH_CMD_SE 0xd8
|
||||
#define FLASH_CMD_CHIPERASE 0xc7
|
||||
#define FLASH_CMD_WRVCR 0x81
|
||||
#define FLASH_CMD_RDVCR 0x85
|
||||
|
||||
#define FLASH_CMD_READ 0x03 /* READ */
|
||||
#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */
|
||||
#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */
|
||||
#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */
|
||||
#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */
|
||||
|
||||
#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
|
||||
#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
|
||||
|
||||
#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */
|
||||
#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */
|
||||
|
||||
/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */
|
||||
#define FLASH_CMD_READ4 0x13
|
||||
#define FLASH_CMD_READ4_FAST 0x0c
|
||||
#define FLASH_CMD_READ4_1_1_2 0x3c
|
||||
#define FLASH_CMD_READ4_1_2_2 0xbc
|
||||
#define FLASH_CMD_READ4_1_1_4 0x6c
|
||||
#define FLASH_CMD_READ4_1_4_4 0xec
|
||||
|
||||
/* S25FLxxxS commands */
|
||||
#define S25FL_CMD_WRITE4_1_1_4 0x34
|
||||
#define S25FL_CMD_SE4 0xdc
|
||||
@ -246,7 +209,7 @@
|
||||
#define S25FL_CMD_DYBWR 0xe1
|
||||
#define S25FL_CMD_DYBRD 0xe0
|
||||
#define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with
|
||||
* 'FLASH_CMD_WRITE_1_4_4'
|
||||
* 'SPINOR_OP_WRITE_1_4_4'
|
||||
* as found on N25Qxxx devices! */
|
||||
|
||||
/* Status register */
|
||||
@ -261,6 +224,12 @@
|
||||
#define S25FL_STATUS_E_ERR 0x20
|
||||
#define S25FL_STATUS_P_ERR 0x40
|
||||
|
||||
#define N25Q_CMD_WRVCR 0x81
|
||||
#define N25Q_CMD_RDVCR 0x85
|
||||
#define N25Q_CMD_RDVECR 0x65
|
||||
#define N25Q_CMD_RDNVCR 0xb5
|
||||
#define N25Q_CMD_WRNVCR 0xb1
|
||||
|
||||
#define FLASH_PAGESIZE 256 /* In Bytes */
|
||||
#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */
|
||||
#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */
|
||||
@ -270,7 +239,6 @@
|
||||
*/
|
||||
#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001
|
||||
#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002
|
||||
#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004
|
||||
#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
|
||||
#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010
|
||||
|
||||
@ -329,7 +297,7 @@ struct flash_info {
|
||||
u32 jedec_id;
|
||||
u16 ext_id;
|
||||
/*
|
||||
* The size listed here is what works with FLASH_CMD_SE, which isn't
|
||||
* The size listed here is what works with SPINOR_OP_SE, which isn't
|
||||
* necessarily called a "sector" by the vendor.
|
||||
*/
|
||||
unsigned sector_size;
|
||||
@ -369,17 +337,26 @@ static struct flash_info flash_types[] = {
|
||||
{ "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL },
|
||||
{ "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL },
|
||||
|
||||
/* Macronix MX25xxx
|
||||
* - Support for 'FLASH_FLAG_WRITE_1_4_4' is omitted for devices
|
||||
* where operating frequency must be reduced.
|
||||
*/
|
||||
#define MX25_FLAG (FLASH_FLAG_READ_WRITE | \
|
||||
FLASH_FLAG_READ_FAST | \
|
||||
FLASH_FLAG_READ_1_1_2 | \
|
||||
FLASH_FLAG_READ_1_2_2 | \
|
||||
FLASH_FLAG_READ_1_1_4 | \
|
||||
FLASH_FLAG_READ_1_4_4 | \
|
||||
FLASH_FLAG_SE_4K | \
|
||||
FLASH_FLAG_SE_32K)
|
||||
{ "mx25l3255e", 0xc29e16, 0, 64 * 1024, 64,
|
||||
(MX25_FLAG | FLASH_FLAG_WRITE_1_4_4), 86,
|
||||
stfsm_mx25_config},
|
||||
{ "mx25l25635e", 0xc22019, 0, 64*1024, 512,
|
||||
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
|
||||
stfsm_mx25_config },
|
||||
{ "mx25l25655e", 0xc22619, 0, 64*1024, 512,
|
||||
(MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
|
||||
stfsm_mx25_config},
|
||||
|
||||
#define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \
|
||||
FLASH_FLAG_READ_FAST | \
|
||||
@ -407,6 +384,8 @@ static struct flash_info flash_types[] = {
|
||||
FLASH_FLAG_READ_1_4_4 | \
|
||||
FLASH_FLAG_WRITE_1_1_4 | \
|
||||
FLASH_FLAG_READ_FAST)
|
||||
{ "s25fl032p", 0x010215, 0x4d00, 64 * 1024, 64, S25FLXXXP_FLAG, 80,
|
||||
stfsm_s25fl_config},
|
||||
{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80,
|
||||
stfsm_s25fl_config },
|
||||
{ "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80,
|
||||
@ -473,22 +452,22 @@ static struct flash_info flash_types[] = {
|
||||
|
||||
/* Default READ configurations, in order of preference */
|
||||
static struct seq_rw_config default_read_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
/* Default WRITE configurations */
|
||||
static struct seq_rw_config default_write_configs[] = {
|
||||
{FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_WRITE, 1, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_4_4, SPINOR_OP_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_4, SPINOR_OP_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_2_2, SPINOR_OP_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_WRITE_1_1_2, SPINOR_OP_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_WRITE, 1, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -511,12 +490,12 @@ static struct seq_rw_config default_write_configs[] = {
|
||||
* cycles.
|
||||
*/
|
||||
static struct seq_rw_config n25q_read3_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -526,12 +505,12 @@ static struct seq_rw_config n25q_read3_configs[] = {
|
||||
* - 'FAST' variants configured for 8 dummy cycles (see note above.)
|
||||
*/
|
||||
static struct seq_rw_config n25q_read4_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -544,7 +523,7 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
|
||||
{
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_EN4B) |
|
||||
SEQ_OPC_CSDEASSERT);
|
||||
|
||||
seq->seq[0] = STFSM_INST_CMD1;
|
||||
@ -572,12 +551,12 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
|
||||
* entering a state that is incompatible with the SPIBoot Controller.
|
||||
*/
|
||||
static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
|
||||
{FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
|
||||
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
|
||||
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
|
||||
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
|
||||
{0x00, 0, 0, 0, 0, 0x00, 0, 0},
|
||||
};
|
||||
|
||||
@ -590,13 +569,13 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
|
||||
/*
|
||||
* [W25Qxxx] Configuration
|
||||
*/
|
||||
#define W25Q_STATUS_QE (0x1 << 9)
|
||||
#define W25Q_STATUS_QE (0x1 << 1)
|
||||
|
||||
static struct stfsm_seq stfsm_seq_read_jedec = {
|
||||
.data_size = TRANSFER_SIZE(8),
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_RDID)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_RDID)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_DATA_READ,
|
||||
@ -612,7 +591,7 @@ static struct stfsm_seq stfsm_seq_read_status_fifo = {
|
||||
.data_size = TRANSFER_SIZE(4),
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_RDSR)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_DATA_READ,
|
||||
@ -628,10 +607,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
|
||||
/* 'addr_cfg' configured during initialisation */
|
||||
.seq_opc = {
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_SE)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_SE)),
|
||||
},
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
@ -649,10 +628,10 @@ static struct stfsm_seq stfsm_seq_erase_sector = {
|
||||
static struct stfsm_seq stfsm_seq_erase_chip = {
|
||||
.seq_opc = {
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
|
||||
(SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_CHIP_ERASE) | SEQ_OPC_CSDEASSERT),
|
||||
},
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
@ -669,26 +648,9 @@ static struct stfsm_seq stfsm_seq_erase_chip = {
|
||||
|
||||
static struct stfsm_seq stfsm_seq_write_status = {
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_CMD2,
|
||||
STFSM_INST_STA_WR1,
|
||||
STFSM_INST_STOP,
|
||||
},
|
||||
.seq_cfg = (SEQ_CFG_PADS_1 |
|
||||
SEQ_CFG_READNOTWRITE |
|
||||
SEQ_CFG_CSDEASSERT |
|
||||
SEQ_CFG_STARTSEQ),
|
||||
};
|
||||
|
||||
static struct stfsm_seq stfsm_seq_wrvcr = {
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)),
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WRSR)),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
STFSM_INST_CMD2,
|
||||
@ -704,9 +666,9 @@ static struct stfsm_seq stfsm_seq_wrvcr = {
|
||||
static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
|
||||
{
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR));
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_EN4B));
|
||||
seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
|
||||
SEQ_OPC_CSDEASSERT);
|
||||
|
||||
seq->seq[0] = STFSM_INST_CMD2;
|
||||
@ -793,7 +755,7 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
|
||||
|
||||
dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
|
||||
|
||||
BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
|
||||
BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
|
||||
|
||||
while (remaining) {
|
||||
for (;;) {
|
||||
@ -817,7 +779,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
|
||||
|
||||
dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
|
||||
|
||||
BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
|
||||
BUG_ON((((uintptr_t)buf) & 0x3) || (size & 0x3));
|
||||
|
||||
writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
|
||||
|
||||
@ -827,7 +789,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
|
||||
static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
|
||||
{
|
||||
struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
|
||||
uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR;
|
||||
uint32_t cmd = enter ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
|
||||
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
@ -851,7 +813,7 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
|
||||
/* Use RDRS1 */
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_RDSR));
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_RDSR));
|
||||
|
||||
/* Load read_status sequence */
|
||||
stfsm_load_seq(fsm, seq);
|
||||
@ -889,59 +851,56 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm)
|
||||
}
|
||||
|
||||
static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
|
||||
uint8_t *status)
|
||||
uint8_t *data, int bytes)
|
||||
{
|
||||
struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
|
||||
uint32_t tmp;
|
||||
uint8_t *t = (uint8_t *)&tmp;
|
||||
int i;
|
||||
|
||||
dev_dbg(fsm->dev, "reading STA[%s]\n",
|
||||
(cmd == FLASH_CMD_RDSR) ? "1" : "2");
|
||||
dev_dbg(fsm->dev, "read 'status' register [0x%02x], %d byte(s)\n",
|
||||
cmd, bytes);
|
||||
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
BUG_ON(bytes != 1 && bytes != 2);
|
||||
|
||||
seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(cmd)),
|
||||
|
||||
stfsm_load_seq(fsm, seq);
|
||||
|
||||
stfsm_read_fifo(fsm, &tmp, 4);
|
||||
|
||||
*status = (uint8_t)(tmp >> 24);
|
||||
for (i = 0; i < bytes; i++)
|
||||
data[i] = t[i];
|
||||
|
||||
stfsm_wait_seq(fsm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stfsm_write_status(struct stfsm *fsm, uint16_t status,
|
||||
int sta_bytes)
|
||||
static int stfsm_write_status(struct stfsm *fsm, uint8_t cmd,
|
||||
uint16_t data, int bytes, int wait_busy)
|
||||
{
|
||||
struct stfsm_seq *seq = &stfsm_seq_write_status;
|
||||
|
||||
dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n",
|
||||
(sta_bytes == 1) ? "1" : "1+2", status);
|
||||
dev_dbg(fsm->dev,
|
||||
"write 'status' register [0x%02x], %d byte(s), 0x%04x\n"
|
||||
" %s wait-busy\n", cmd, bytes, data, wait_busy ? "with" : "no");
|
||||
|
||||
seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT;
|
||||
seq->seq[2] = (sta_bytes == 1) ?
|
||||
STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
|
||||
BUG_ON(bytes != 1 && bytes != 2);
|
||||
|
||||
seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(cmd));
|
||||
|
||||
seq->status = (uint32_t)data | STA_PADS_1 | STA_CSDEASSERT;
|
||||
seq->seq[2] = (bytes == 1) ? STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
|
||||
|
||||
stfsm_load_seq(fsm, seq);
|
||||
|
||||
stfsm_wait_seq(fsm);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
|
||||
{
|
||||
struct stfsm_seq *seq = &stfsm_seq_wrvcr;
|
||||
|
||||
dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data);
|
||||
|
||||
seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT);
|
||||
|
||||
stfsm_load_seq(fsm, seq);
|
||||
|
||||
stfsm_wait_seq(fsm);
|
||||
if (wait_busy)
|
||||
stfsm_wait_busy(fsm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1027,7 +986,7 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm,
|
||||
if (cfg->write)
|
||||
seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
|
||||
SEQ_OPC_CSDEASSERT);
|
||||
|
||||
/* Address configuration (24 or 32-bit addresses) */
|
||||
@ -1149,31 +1108,36 @@ static int stfsm_mx25_config(struct stfsm *fsm)
|
||||
stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
|
||||
|
||||
soc_reset = stfsm_can_handle_soc_reset(fsm);
|
||||
if (soc_reset || !fsm->booted_from_spi) {
|
||||
if (soc_reset || !fsm->booted_from_spi)
|
||||
/* If we can handle SoC resets, we enable 32-bit address
|
||||
* mode pervasively */
|
||||
stfsm_enter_32bit_addr(fsm, 1);
|
||||
|
||||
} else {
|
||||
else
|
||||
/* Else, enable/disable 32-bit addressing before/after
|
||||
* each operation */
|
||||
fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
|
||||
CFG_WRITE_TOGGLE_32BIT_ADDR |
|
||||
CFG_ERASESEC_TOGGLE_32BIT_ADDR);
|
||||
/* It seems a small delay is required after exiting
|
||||
* 32-bit mode following a write operation. The issue
|
||||
* is under investigation.
|
||||
*/
|
||||
fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/* For QUAD mode, set 'QE' STATUS bit */
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sta, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
if (data_pads == 4) {
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta);
|
||||
sta |= MX25_STATUS_QE;
|
||||
stfsm_write_status(fsm, sta, 1);
|
||||
if (!(sta & MX25_STATUS_QE)) {
|
||||
/* Set 'QE' */
|
||||
sta |= MX25_STATUS_QE;
|
||||
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
|
||||
}
|
||||
} else {
|
||||
if (sta & MX25_STATUS_QE) {
|
||||
/* Clear 'QE' */
|
||||
sta &= ~MX25_STATUS_QE;
|
||||
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1239,7 +1203,7 @@ static int stfsm_n25q_config(struct stfsm *fsm)
|
||||
*/
|
||||
vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
|
||||
N25Q_VCR_WRAP_CONT);
|
||||
stfsm_wrvcr(fsm, vcr);
|
||||
stfsm_write_status(fsm, N25Q_CMD_WRVCR, vcr, 1, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1297,7 +1261,7 @@ static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby)
|
||||
{
|
||||
struct stfsm_seq seq = {
|
||||
.seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WREN) |
|
||||
SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
|
||||
@ -1337,7 +1301,7 @@ static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm)
|
||||
SEQ_OPC_CSDEASSERT),
|
||||
.seq_opc[1] = (SEQ_OPC_PADS_1 |
|
||||
SEQ_OPC_CYCLES(8) |
|
||||
SEQ_OPC_OPCODE(FLASH_CMD_WRDI) |
|
||||
SEQ_OPC_OPCODE(SPINOR_OP_WRDI) |
|
||||
SEQ_OPC_CSDEASSERT),
|
||||
.seq = {
|
||||
STFSM_INST_CMD1,
|
||||
@ -1367,6 +1331,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
||||
uint32_t offs;
|
||||
uint16_t sta_wr;
|
||||
uint8_t sr1, cr1, dyb;
|
||||
int update_sr = 0;
|
||||
int ret;
|
||||
|
||||
if (flags & FLASH_FLAG_32BIT_ADDR) {
|
||||
@ -1414,34 +1379,28 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check status of 'QE' bit */
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
|
||||
if (data_pads == 4) {
|
||||
if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
|
||||
/* Set 'QE' */
|
||||
cr1 |= STFSM_S25FL_CONFIG_QE;
|
||||
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
|
||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
||||
|
||||
stfsm_write_status(fsm, sta_wr, 2);
|
||||
|
||||
stfsm_wait_busy(fsm);
|
||||
update_sr = 1;
|
||||
}
|
||||
} else {
|
||||
if ((cr1 & STFSM_S25FL_CONFIG_QE)) {
|
||||
if (cr1 & STFSM_S25FL_CONFIG_QE) {
|
||||
/* Clear 'QE' */
|
||||
cr1 &= ~STFSM_S25FL_CONFIG_QE;
|
||||
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
|
||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
||||
|
||||
stfsm_write_status(fsm, sta_wr, 2);
|
||||
|
||||
stfsm_wait_busy(fsm);
|
||||
update_sr = 1;
|
||||
}
|
||||
|
||||
}
|
||||
if (update_sr) {
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
|
||||
sta_wr = ((uint16_t)cr1 << 8) | sr1;
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sta_wr, 2, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1456,27 +1415,36 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
||||
static int stfsm_w25q_config(struct stfsm *fsm)
|
||||
{
|
||||
uint32_t data_pads;
|
||||
uint16_t sta_wr;
|
||||
uint8_t sta1, sta2;
|
||||
uint8_t sr1, sr2;
|
||||
uint16_t sr_wr;
|
||||
int update_sr = 0;
|
||||
int ret;
|
||||
|
||||
ret = stfsm_prepare_rwe_seqs_default(fsm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If using QUAD mode, set QE STATUS bit */
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
if (data_pads == 4) {
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1);
|
||||
stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2);
|
||||
|
||||
sta_wr = ((uint16_t)sta2 << 8) | sta1;
|
||||
|
||||
sta_wr |= W25Q_STATUS_QE;
|
||||
|
||||
stfsm_write_status(fsm, sta_wr, 2);
|
||||
|
||||
stfsm_wait_busy(fsm);
|
||||
if (!(sr2 & W25Q_STATUS_QE)) {
|
||||
/* Set 'QE' */
|
||||
sr2 |= W25Q_STATUS_QE;
|
||||
update_sr = 1;
|
||||
}
|
||||
} else {
|
||||
if (sr2 & W25Q_STATUS_QE) {
|
||||
/* Clear 'QE' */
|
||||
sr2 &= ~W25Q_STATUS_QE;
|
||||
update_sr = 1;
|
||||
}
|
||||
}
|
||||
if (update_sr) {
|
||||
/* Write status register */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR, &sr1, 1);
|
||||
sr_wr = ((uint16_t)sr2 << 8) | sr1;
|
||||
stfsm_write_status(fsm, SPINOR_OP_WRSR, sr_wr, 2, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1506,7 +1474,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
|
||||
read_mask = (data_pads << 2) - 1;
|
||||
|
||||
/* Handle non-aligned buf */
|
||||
p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
|
||||
p = ((uintptr_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
|
||||
|
||||
/* Handle non-aligned size */
|
||||
size_ub = (size + read_mask) & ~read_mask;
|
||||
@ -1528,7 +1496,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
|
||||
}
|
||||
|
||||
/* Handle non-aligned buf */
|
||||
if ((uint32_t)buf & 0x3)
|
||||
if ((uintptr_t)buf & 0x3)
|
||||
memcpy(buf, page_buf, size);
|
||||
|
||||
/* Wait for sequence to finish */
|
||||
@ -1570,7 +1538,7 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
|
||||
write_mask = (data_pads << 2) - 1;
|
||||
|
||||
/* Handle non-aligned buf */
|
||||
if ((uint32_t)buf & 0x3) {
|
||||
if ((uintptr_t)buf & 0x3) {
|
||||
memcpy(page_buf, buf, size);
|
||||
p = (uint8_t *)page_buf;
|
||||
} else {
|
||||
@ -1628,11 +1596,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
|
||||
stfsm_s25fl_clear_status_reg(fsm);
|
||||
|
||||
/* Exit 32-bit address mode, if required */
|
||||
if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) {
|
||||
if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
|
||||
stfsm_enter_32bit_addr(fsm, 0);
|
||||
if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1736,7 +1701,7 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
||||
while (len) {
|
||||
/* Write up to page boundary */
|
||||
bytes = min(FLASH_PAGESIZE - page_offs, len);
|
||||
bytes = min_t(size_t, FLASH_PAGESIZE - page_offs, len);
|
||||
|
||||
ret = stfsm_write(fsm, b, bytes, to);
|
||||
if (ret)
|
||||
@ -1935,6 +1900,13 @@ static int stfsm_init(struct stfsm *fsm)
|
||||
fsm->base + SPI_CONFIGDATA);
|
||||
writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
|
||||
|
||||
/*
|
||||
* Set the FSM 'WAIT' delay to the minimum workable value. Note, for
|
||||
* our purposes, the WAIT instruction is used purely to achieve
|
||||
* "sequence validity" rather than actually implement a delay.
|
||||
*/
|
||||
writel(0x00000001, fsm->base + SPI_PROGRAM_ERASE_TIME);
|
||||
|
||||
/* Clear FIFO, just in case */
|
||||
stfsm_clear_fifo(fsm);
|
||||
|
||||
|
17
drivers/mtd/spi-nor/Kconfig
Normal file
17
drivers/mtd/spi-nor/Kconfig
Normal file
@ -0,0 +1,17 @@
|
||||
menuconfig MTD_SPI_NOR
|
||||
tristate "SPI-NOR device support"
|
||||
depends on MTD
|
||||
help
|
||||
This is the framework for the SPI NOR which can be used by the SPI
|
||||
device drivers and the SPI-NOR device driver.
|
||||
|
||||
if MTD_SPI_NOR
|
||||
|
||||
config SPI_FSL_QUADSPI
|
||||
tristate "Freescale Quad SPI controller"
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
This enables support for the Quad SPI controller in master mode.
|
||||
We only connect the NOR to this controller now.
|
||||
|
||||
endif # MTD_SPI_NOR
|
2
drivers/mtd/spi-nor/Makefile
Normal file
2
drivers/mtd/spi-nor/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
|
||||
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
|
1009
drivers/mtd/spi-nor/fsl-quadspi.c
Normal file
1009
drivers/mtd/spi-nor/fsl-quadspi.c
Normal file
File diff suppressed because it is too large
Load Diff
1107
drivers/mtd/spi-nor/spi-nor.c
Normal file
1107
drivers/mtd/spi-nor/spi-nor.c
Normal file
File diff suppressed because it is too large
Load Diff
214
include/linux/mtd/spi-nor.h
Normal file
214
include/linux/mtd/spi-nor.h
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MTD_SPI_NOR_H
|
||||
#define __LINUX_MTD_SPI_NOR_H
|
||||
|
||||
/*
|
||||
* Note on opcode nomenclature: some opcodes have a format like
|
||||
* SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number
|
||||
* of I/O lines used for the opcode, address, and data (respectively). The
|
||||
* FUNCTION has an optional suffix of '4', to represent an opcode which
|
||||
* requires a 4-byte (32-bit) address.
|
||||
*/
|
||||
|
||||
/* Flash opcodes. */
|
||||
#define SPINOR_OP_WREN 0x06 /* Write enable */
|
||||
#define SPINOR_OP_RDSR 0x05 /* Read status register */
|
||||
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
|
||||
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
|
||||
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
|
||||
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
|
||||
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
|
||||
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
|
||||
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
|
||||
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
|
||||
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
|
||||
#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */
|
||||
#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */
|
||||
#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
|
||||
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
|
||||
|
||||
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
||||
#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */
|
||||
#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
|
||||
#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
|
||||
#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
|
||||
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
|
||||
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
|
||||
|
||||
/* Used for SST flashes only. */
|
||||
#define SPINOR_OP_BP 0x02 /* Byte program */
|
||||
#define SPINOR_OP_WRDI 0x04 /* Write disable */
|
||||
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
|
||||
|
||||
/* Used for Macronix and Winbond flashes. */
|
||||
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
|
||||
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
|
||||
|
||||
/* Used for Spansion flashes only. */
|
||||
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
|
||||
|
||||
/* Status Register bits. */
|
||||
#define SR_WIP 1 /* Write in progress */
|
||||
#define SR_WEL 2 /* Write enable latch */
|
||||
/* meaning of other SR_* bits may differ between vendors */
|
||||
#define SR_BP0 4 /* Block protect 0 */
|
||||
#define SR_BP1 8 /* Block protect 1 */
|
||||
#define SR_BP2 0x10 /* Block protect 2 */
|
||||
#define SR_SRWD 0x80 /* SR write protect */
|
||||
|
||||
#define SR_QUAD_EN_MX 0x40 /* Macronix Quad I/O */
|
||||
|
||||
/* Configuration Register bits. */
|
||||
#define CR_QUAD_EN_SPAN 0x2 /* Spansion Quad I/O */
|
||||
|
||||
enum read_mode {
|
||||
SPI_NOR_NORMAL = 0,
|
||||
SPI_NOR_FAST,
|
||||
SPI_NOR_DUAL,
|
||||
SPI_NOR_QUAD,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spi_nor_xfer_cfg - Structure for defining a Serial Flash transfer
|
||||
* @wren: command for "Write Enable", or 0x00 for not required
|
||||
* @cmd: command for operation
|
||||
* @cmd_pins: number of pins to send @cmd (1, 2, 4)
|
||||
* @addr: address for operation
|
||||
* @addr_pins: number of pins to send @addr (1, 2, 4)
|
||||
* @addr_width: number of address bytes
|
||||
* (3,4, or 0 for address not required)
|
||||
* @mode: mode data
|
||||
* @mode_pins: number of pins to send @mode (1, 2, 4)
|
||||
* @mode_cycles: number of mode cycles (0 for mode not required)
|
||||
* @dummy_cycles: number of dummy cycles (0 for dummy not required)
|
||||
*/
|
||||
struct spi_nor_xfer_cfg {
|
||||
u8 wren;
|
||||
u8 cmd;
|
||||
u8 cmd_pins;
|
||||
u32 addr;
|
||||
u8 addr_pins;
|
||||
u8 addr_width;
|
||||
u8 mode;
|
||||
u8 mode_pins;
|
||||
u8 mode_cycles;
|
||||
u8 dummy_cycles;
|
||||
};
|
||||
|
||||
#define SPI_NOR_MAX_CMD_SIZE 8
|
||||
enum spi_nor_ops {
|
||||
SPI_NOR_OPS_READ = 0,
|
||||
SPI_NOR_OPS_WRITE,
|
||||
SPI_NOR_OPS_ERASE,
|
||||
SPI_NOR_OPS_LOCK,
|
||||
SPI_NOR_OPS_UNLOCK,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct spi_nor - Structure for defining a the SPI NOR layer
|
||||
* @mtd: point to a mtd_info structure
|
||||
* @lock: the lock for the read/write/erase/lock/unlock operations
|
||||
* @dev: point to a spi device, or a spi nor controller device.
|
||||
* @page_size: the page size of the SPI NOR
|
||||
* @addr_width: number of address bytes
|
||||
* @erase_opcode: the opcode for erasing a sector
|
||||
* @read_opcode: the read opcode
|
||||
* @read_dummy: the dummy needed by the read operation
|
||||
* @program_opcode: the program opcode
|
||||
* @flash_read: the mode of the read
|
||||
* @sst_write_second: used by the SST write operation
|
||||
* @cfg: used by the read_xfer/write_xfer
|
||||
* @cmd_buf: used by the write_reg
|
||||
* @prepare: [OPTIONAL] do some preparations for the
|
||||
* read/write/erase/lock/unlock operations
|
||||
* @unprepare: [OPTIONAL] do some post work after the
|
||||
* read/write/erase/lock/unlock operations
|
||||
* @read_xfer: [OPTIONAL] the read fundamental primitive
|
||||
* @write_xfer: [OPTIONAL] the writefundamental primitive
|
||||
* @read_reg: [DRIVER-SPECIFIC] read out the register
|
||||
* @write_reg: [DRIVER-SPECIFIC] write data to the register
|
||||
* @read_id: [REPLACEABLE] read out the ID data, and find
|
||||
* the proper spi_device_id
|
||||
* @wait_till_ready: [REPLACEABLE] wait till the NOR becomes ready
|
||||
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
|
||||
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
|
||||
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
|
||||
* at the offset @offs
|
||||
* @priv: the private data
|
||||
*/
|
||||
struct spi_nor {
|
||||
struct mtd_info *mtd;
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
u32 page_size;
|
||||
u8 addr_width;
|
||||
u8 erase_opcode;
|
||||
u8 read_opcode;
|
||||
u8 read_dummy;
|
||||
u8 program_opcode;
|
||||
enum read_mode flash_read;
|
||||
bool sst_write_second;
|
||||
struct spi_nor_xfer_cfg cfg;
|
||||
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
||||
|
||||
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
int (*read_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
|
||||
u8 *buf, size_t len);
|
||||
int (*write_xfer)(struct spi_nor *nor, struct spi_nor_xfer_cfg *cfg,
|
||||
u8 *buf, size_t len);
|
||||
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
|
||||
int write_enable);
|
||||
const struct spi_device_id *(*read_id)(struct spi_nor *nor);
|
||||
int (*wait_till_ready)(struct spi_nor *nor);
|
||||
|
||||
int (*read)(struct spi_nor *nor, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *read_buf);
|
||||
void (*write)(struct spi_nor *nor, loff_t to,
|
||||
size_t len, size_t *retlen, const u_char *write_buf);
|
||||
int (*erase)(struct spi_nor *nor, loff_t offs);
|
||||
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* spi_nor_scan() - scan the SPI NOR
|
||||
* @nor: the spi_nor structure
|
||||
* @id: the spi_device_id provided by the driver
|
||||
* @mode: the read mode supported by the driver
|
||||
*
|
||||
* The drivers can use this fuction to scan the SPI NOR.
|
||||
* In the scanning, it will try to get all the necessary information to
|
||||
* fill the mtd_info{} and the spi_nor{}.
|
||||
*
|
||||
* The board may assigns a spi_device_id with @id which be used to compared with
|
||||
* the spi_device_id detected by the scanning.
|
||||
*
|
||||
* Return: 0 for success, others for failure.
|
||||
*/
|
||||
int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
|
||||
enum read_mode mode);
|
||||
extern const struct spi_device_id spi_nor_ids[];
|
||||
|
||||
/**
|
||||
* spi_nor_match_id() - find the spi_device_id by the name
|
||||
* @name: the name of the spi_device_id
|
||||
*
|
||||
* The drivers use this function to find the spi_device_id
|
||||
* specified by the @name.
|
||||
*
|
||||
* Return: returns the right spi_device_id pointer on success,
|
||||
* and returns NULL on failure.
|
||||
*/
|
||||
const struct spi_device_id *spi_nor_match_id(char *name);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user