Skip to contentSkip to conversation

mtd: spi-nor: add a new framework for SPI NOR

PATCH Superseded Huang Shijie 7 participants 35 messages Feb 24, 2014 – Apr 11, 2014

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

1.) Why add a new framework for SPI NOR?
The SPI-NOR controller such as Freescale's Quadspi controller is working in a different way from the SPI bus. It should knows the NOR commands to find the right LUT sequence. Unfortunately, the current code can not meet this requirement.

2.) How does this patch set do?
This patch set adds a new spi-nor layer.
Before this patch, the layer is like:

                   MTD
         ------------------------
                  m25p80
         ------------------------
	       spi bus driver
         ------------------------
	        SPI NOR chip

After this patch, the layer is like:

                   MTD
         ------------------------
                  spi-nor
         ------------------------
                  m25p80
         ------------------------
	       spi bus driver
         ------------------------
	       SPI NOR chip

With the spi-nor controller driver(Freescale Quadspi), it looks like:

                   MTD
         ------------------------
                  spi-nor
         ------------------------
                fsl-quadspi
         ------------------------
	       SPI NOR chip

3.) more details
This patch set adds a new data structrue spi_nor{}, clones most the common
code to spi-nor.c. Add spi_nor_xfer_cfg {} for the fundamental primitives:
read_xfer/write_xfer.

Make the m25p80.c/fsl_quaspi.c use the new APIs.

4.) Change log:
v4 --> v5:
[1] rebase the patch set on the l2-mtd tree.
[2] fixed bugs in the fsl-quadspi driver.
[3] tested the fsl-quadspi with imx6sx board.

v3 --> v4:
[1] move the wait_till_ready to the @read hook.
[2] convert to linux-doc style comment.
[3] remove the some DT property for QuadSPI driver, such as "fsl,nor-size".
[4] others

v2 --> v3:
[1] add prepare/unprepare hooks for spi_nor{}.
[2] add a new "priv" field for spi_nor{}.
[3] add the Freescale Quadspi driver which supports the Quad read by default.

v1 --> v2:

	[1] follow Angus's advice, add more hooks and data structrures.
	[2] others.

*** BLURB HERE ***

Huang Shijie (8):
mtd: spi-nor: copy the SPI NOR commands to a new header file
mtd: spi-nor: add the basic data structures
mtd: spi-nor: add the framework for SPI NOR
Documentation: add the document for the SPI NOR framework
mtd: m25p80: use the SPI nor framework
mtd: spi-nor: add a helper to find the spi_device_id
Documentation: add the binding file for Freescale QuadSPI driver
mtd: spi-nor: Add Freescale QuadSPI driver

 .../devicetree/bindings/mtd/fsl-quadspi.txt        |   35 +
 Documentation/mtd/spi-nor.txt                      |   59 +
 drivers/mtd/Kconfig                                |    2 +
 drivers/mtd/Makefile                               |    1 +
 drivers/mtd/devices/m25p80.c                       | 1302 ++------------------
 drivers/mtd/spi-nor/Kconfig                        |   12 +
 drivers/mtd/spi-nor/Makefile                       |    2 +
 drivers/mtd/spi-nor/fsl-quadspi.c                  | 1009 +++++++++++++++
 drivers/mtd/spi-nor/spi-nor.c                      | 1099 +++++++++++++++++
 include/linux/mtd/spi-nor.h                        |  197 +++
 10 files changed, 2521 insertions(+), 1197 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
 create mode 100644 Documentation/mtd/spi-nor.txt
 create mode 100644 drivers/mtd/spi-nor/Kconfig
 create mode 100644 drivers/mtd/spi-nor/Makefile
 create mode 100644 drivers/mtd/spi-nor/fsl-quadspi.c
 create mode 100644 drivers/mtd/spi-nor/spi-nor.c
 create mode 100644 include/linux/mtd/spi-nor.h
Signature · 1 line
1.7.2.rc3
#1 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

This patch adds a new header :spi-nor.h,
and copies all the SPI NOR commands and relative macros into this new header.

This hearder can be used by the m25p80.c and other spi-nor controller, such as Freescale's Quadspi.

---
 include/linux/mtd/spi-nor.h |   55 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 55 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/mtd/spi-nor.h
include/linux/mtd/spi-nor.h new file +55 -0
new file mode 100644
index 0000000..483fc2a
@@ -0,0 +1,55 @@
+#ifndef __LINUX_MTD_SPI_NOR_H
+#define __LINUX_MTD_SPI_NOR_H
+
+/* Flash opcodes. */
+#define	OPCODE_WREN		0x06	/* Write enable */
+#define	OPCODE_RDSR		0x05	/* Read status register */
+#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
+#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
+#define	OPCODE_DUAL_READ        0x3b    /* Read data bytes (Dual SPI) */
+#define	OPCODE_QUAD_READ        0x6b    /* Read data bytes (Quad SPI) */
+#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
+#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
+#define	OPCODE_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
+#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
+#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
+#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
+#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
+#define	OPCODE_RDCR             0x35    /* Read configuration register */
+
+/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
+#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */
+#define	OPCODE_DUAL_READ_4B	0x3c    /* Read data bytes (Dual SPI) */
+#define	OPCODE_QUAD_READ_4B	0x6c    /* Read data bytes (Quad SPI) */
+#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */
+#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
+
+/* Used for SST flashes only. */
+#define	OPCODE_BP		0x02	/* Byte program */
+#define	OPCODE_WRDI		0x04	/* Write disable */
+#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
+
+/* Used for Macronix and Winbond flashes. */
+#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
+#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
+
+/* Used for Spansion flashes only. */
+#define	OPCODE_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 */
+
+#endif
Signature · 1 line
1.7.2.rc3
#2 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

Quoted 8 lines · Huang Shijie

This patch adds a new header :spi-nor.h,
and copies all the SPI NOR commands and relative macros into this new header.

This hearder can be used by the m25p80.c and other spi-nor controller, such as Freescale's Quadspi.

Please keep me on CC ;-)

I honestly have to wonder ... why do you not 'move' these definitions instead of copying them ?

Best regards,
Marek Vasut

#3 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

? 2014?03?05? 06:13, Marek Vasut ??:

Quoted 1 line

Please keep me on CC;-)

sorry, My fault :(

Quoted 3 lines

I honestly have to wonder ... why do you not 'move' these definitions instead of copying them ?

In the first version , i did so:
http://marc.info/?l=linux-arm-kernel&m=138544933522564&w=2

if I need to resend the patch set again, i can change to move the commands to the new header.

thanks
Huang Shijie

#4 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

Quoted 5 lines · Huang Shijie

? 2014?03?05? 06:13, Marek Vasut ??:

Quoted 1 line

Please keep me on CC;-)

sorry, My fault :(

Quoted 8 lines
Quoted 2 lines

I honestly have to wonder ... why do you not 'move' these definitions instead of copying them ?

In the first version , i did so:
http://marc.info/?l=linux-arm-kernel&m=138544933522564&w=2

if I need to resend the patch set again, i can change to move the commands to the new header.

Why didn't you keep it like that? Was there some reason for that ?

Best regards,
Marek Vasut

#5 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

? 2014?03?05? 11:43, Marek Vasut ??:

Quoted 1 line

Why didn't you keep it like that? Was there some reason for that ?

http://marc.info/?l=linux-arm-kernel&m=138545182232220&w=4

Pekon suggestted do not touch the m25p80.c. So i just copy these commands to a new header. Anyway, it's really not an important issue for me. :)

thanks
Huang Shijie

#6 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

Hi Marek, Shijie,

Quoted 10 lines

From: Huang Shijie [mailto:b32955 at freescale.com]

? 2014?03?05? 11:43, Marek Vasut ??:

Quoted 1 line

Why didn't you keep it like that? Was there some reason for that ?

http://marc.info/?l=linux-arm-kernel&m=138545182232220&w=4

Pekon suggestted do not touch the m25p80.c. So i just copy these commands to a new header. Anyway, it's really not an important issue for me. :)

My opinion was that we should not touch m25p80.c because:
(1) As m25p80 is already working for various vendors. So unless multiple platforms start using SPI NOR framework, m25p80.c should remain untouched.
(2) we should not clutter header file with vendor specific op-codes as used in m25p80. I was of the opinion that instead of hard-coding the vendor specific info as MACROs, such information should be taken from DT or platform-data.

However, this was my thought during initial versions of SPI NOR patches. But now if you think that SPI NOR is stable enough that same header info can be reused without conflicts, then no problems.

(Apologies to Huang Shijie for re-work :-) ).

with regards, pekon

#7 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

Quoted 21 lines · Pekon

Hi Marek, Shijie,

Quoted 10 lines

From: Huang Shijie [mailto:b32955 at freescale.com]

? 2014?03?05? 11:43, Marek Vasut ??:

Quoted 1 line

Why didn't you keep it like that? Was there some reason for that ?

http://marc.info/?l=linux-arm-kernel&m=138545182232220&w=4

Pekon suggestted do not touch the m25p80.c. So i just copy these commands to a new header. Anyway, it's really not an important issue for me. :)

My opinion was that we should not touch m25p80.c because:
(1) As m25p80 is already working for various vendors. So unless multiple platforms start using SPI NOR framework, m25p80.c should remain
untouched. (2) we should not clutter header file with vendor specific op-codes as used in m25p80. I was of the opinion that instead of hard-coding the vendor specific info as MACROs, such information should be taken from DT or platform-data.

Quoted 5 lines

However, this was my thought during initial versions of SPI NOR patches. But now if you think that SPI NOR is stable enough that same header info can be reused without conflicts, then no problems.

(Apologies to Huang Shijie for re-work :-) ).

Ah, thanks for explaining, understood. Given that the m25p80.c is reworked later in the series anyway, I'm mostly OK with this patch ... even though I'd prefer to see no duplication throughout the series ;-)

Best regards,
Marek Vasut

#8 lore raw

PATCH v5 1/8 mtd: spi-nor: copy the SPI NOR commands to a new header file

In reply to Huang Shijie
Quoted 2 lines · Huang Shijie

This patch adds a new header :spi-nor.h,
and copies all the SPI NOR commands and relative macros into this new header.

...

include/linux/mtd/spi-nor.h line 3

Quoted 9 lines
include/linux/mtd/spi-nor.h new file +3 -0
new file mode 100644
index 0000000..483fc2a
@@ -0,0 +1,55 @@
+#ifndef __LINUX_MTD_SPI_NOR_H
+#define __LINUX_MTD_SPI_NOR_H
+

You're missing a copyright and license header. Please submit one against l2-mtd.git/spinor, preferably after the patch series I just sent out that renames a few things here.

Regarding copyright assignments (also an issue with spi-nor.c): please carry the original copyright from m25p80.c, and add your own or your employer's wherever your work was a significant addition.

Quoted 42 lines
include/linux/mtd/spi-nor.h new file +42 -0
+/* Flash opcodes. */
+#define	OPCODE_WREN		0x06	/* Write enable */
+#define	OPCODE_RDSR		0x05	/* Read status register */
+#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
+#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
+#define	OPCODE_DUAL_READ        0x3b    /* Read data bytes (Dual SPI) */
+#define	OPCODE_QUAD_READ        0x6b    /* Read data bytes (Quad SPI) */
+#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
+#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
+#define	OPCODE_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
+#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
+#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
+#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
+#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
+#define	OPCODE_RDCR             0x35    /* Read configuration register */
+
+/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
+#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */
+#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */
+#define	OPCODE_DUAL_READ_4B	0x3c    /* Read data bytes (Dual SPI) */
+#define	OPCODE_QUAD_READ_4B	0x6c    /* Read data bytes (Quad SPI) */
+#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */
+#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
+
+/* Used for SST flashes only. */
+#define	OPCODE_BP		0x02	/* Byte program */
+#define	OPCODE_WRDI		0x04	/* Write disable */
+#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
+
+/* Used for Macronix and Winbond flashes. */
+#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
+#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
+
+/* Used for Spansion flashes only. */
+#define	OPCODE_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 */

include/linux/mtd/spi-nor.h line 55

Quoted 10 lines
include/linux/mtd/spi-nor.h new file +10 -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 */
+
+#endif

Brian

#9 lore raw

PATCH v5 2/8 mtd: spi-nor: add the basic data structures

In reply to Huang Shijie

The spi_nor{} is cloned from the m25p{}.
The spi_nor{} can be used by both the m25p80 and spi-nor controller.

We also add the spi_nor_xfer_cfg{} which can be used by the two fundamental primitives: read_xfer/write_xfer.

1) the hooks for spi_nor{}:
@prepare/unpreare: used to do some work before or after the
read/write/erase/lock/unlock.
@read_xfer/write_xfer: We can use these two hooks to code all
the following hooks if the driver tries to implement them by itself.
@read_reg: used to read the registers, such as read status register,
read configure register.
@write_reg: used to write the registers, such as write enable,
erase sector.
@read_id: read out the ID info.
@wait_till_ready: wait till the NOR becomes ready. @read: read out the data from the NOR.
@write: write data to the NOR.
@erase: erase a sector of the NOR.

2) Add a new field sst_write_second for the SST NOR write.

---
 include/linux/mtd/spi-nor.h |  110 +++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 110 insertions(+), 0 deletions(-)
include/linux/mtd/spi-nor.h +110 -0
index 483fc2a..3a3c387 100644
@@ -52,4 +52,114 @@
 /* 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;
+};
 #endif
Signature · 1 line
1.7.2.rc3
#10 lore raw

PATCH v5 3/8 mtd: spi-nor: add the framework for SPI NOR

In reply to Huang Shijie

This patch cloned most of the m25p80.c. In theory, it adds a new spi-nor layer.

Before this patch, the layer is like:

                   MTD
         ------------------------
                  m25p80
         ------------------------
	       spi bus driver
         ------------------------
	        SPI NOR chip

After this patch, the layer is like:

                   MTD
         ------------------------
                  spi-nor
         ------------------------
                  m25p80
         ------------------------
	       spi bus driver
         ------------------------
	       SPI NOR chip

With the spi-nor controller driver(Freescale Quadspi), it looks like:

                   MTD
         ------------------------
                  spi-nor
         ------------------------
                fsl-quadspi
         ------------------------
	       SPI NOR chip

New APIs:
spi_nor_scan: used to scan a spi-nor flash.

---
 drivers/mtd/Kconfig           |    2 +
 drivers/mtd/Makefile          |    1 +
 drivers/mtd/spi-nor/Kconfig   |    6 +
 drivers/mtd/spi-nor/Makefile  |    1 +
 drivers/mtd/spi-nor/spi-nor.c | 1087 +++++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/spi-nor.h   |   20 +
 6 files changed, 1117 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/spi-nor/Kconfig
 create mode 100644 drivers/mtd/spi-nor/Makefile
 create mode 100644 drivers/mtd/spi-nor/spi-nor.c
drivers/mtd/Kconfig +2 -0
index 5ebcda3..5fd85ca 100644
@@ -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
drivers/mtd/Makefile +1 -0
index 4cfb31e..40fd153 100644
@@ -32,4 +32,5 @@ inftl-objs		:= inftlcore.o inftlmount.o
 
 obj-y		+= chips/ lpddr/ maps/ devices/ nand/ onenand/ tests/
 
+obj-$(CONFIG_MTD_SPI_NOR_BASE)	+= spi-nor/
 obj-$(CONFIG_MTD_UBI)		+= ubi/
drivers/mtd/spi-nor/Kconfig new file +6 -0
new file mode 100644
index 0000000..41591af
@@ -0,0 +1,6 @@
+config MTD_SPI_NOR_BASE
+	bool "the framework for SPI-NOR 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.
drivers/mtd/spi-nor/Makefile new file +1 -0
new file mode 100644
index 0000000..7dfe1f9
@@ -0,0 +1 @@
+obj-$(CONFIG_MTD_SPI_NOR_BASE)	+= spi-nor.o
drivers/mtd/spi-nor/spi-nor.c new file +1087 -0

1,087 lines not on this page · raw message

include/linux/mtd/spi-nor.h +20 -0
index 3a3c387..16d8409 100644
@@ -162,4 +162,24 @@ struct spi_nor {
 
 	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[];
+
 #endif
Signature · 1 line
1.7.2.rc3
#11 lore raw

PATCH v5 3/8 mtd: spi-nor: add the framework for SPI NOR

Quoted 1 line · Huang Shijie

This patch cloned most of the m25p80.c. In theory, it adds a new spi-nor layer.

...

drivers/mtd/spi-nor/spi-nor.c line 2

Quoted 8 lines
drivers/mtd/spi-nor/spi-nor.c new file +2 -0
new file mode 100644
index 0000000..a840e88
@@ -0,0 +1,1087 @@
+/*
+ * Cloned most of the code from the m25p80.c

Please include a copyright notice here. This should carry both the original m25p80.c copyright notice, as well as your own additional copyright if you choose. For instance:

* Based on m25p80.c, by Mike Lavender (mike at steroidmicros.com), with
* influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c *
* Copyright ? 2005, Intec Automation Inc.
* Copyright ? 2014, Freescale Semiconductor, Inc.

drivers/mtd/spi-nor/spi-nor.c line 14

Quoted 12 lines
drivers/mtd/spi-nor/spi-nor.c new file +12 -0
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/math64.h>

...

Brian

#12 lore raw

PATCH v5 3/8 mtd: spi-nor: add the framework for SPI NOR

Quoted 16 lines · Brian Norris
Quoted 1 line · Huang Shijie

This patch cloned most of the m25p80.c. In theory, it adds a new spi-nor layer.

...

drivers/mtd/spi-nor/spi-nor.c line 2

Quoted 8 lines
drivers/mtd/spi-nor/spi-nor.c new file +2 -0
new file mode 100644
index 0000000..a840e88
@@ -0,0 +1,1087 @@
+/*
+ * Cloned most of the code from the m25p80.c

Please include a copyright notice here. This should carry both the original m25p80.c copyright notice, as well as your own additional copyright if you choose. For instance:

Quoted 6 lines

* Based on m25p80.c, by Mike Lavender (mike at steroidmicros.com), with
* influence from lart.c (Abraham Van Der Merwe) and mtd_dataflash.c *
* Copyright ? 2005, Intec Automation Inc.
* Copyright ? 2014, Freescale Semiconductor, Inc.

I will send a patch to add the copyright info which will based on your patch set.

thanks
Huang Shijie

#13 lore raw

PATCH v5 4/8 Documentation: add the document for the SPI NOR framework

In reply to Huang Shijie

This patch adds the document for the SPI NOR framework.

---
 Documentation/mtd/spi-nor.txt |   59 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 59 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/mtd/spi-nor.txt
Documentation/mtd/spi-nor.txt new file +59 -0
new file mode 100644
index 0000000..b9585f8
@@ -0,0 +1,59 @@
+                          SPI NOR framework
+               ============================================
+
+Part I - why we need this framework?
+-------------------------------------
+
+The SPI bus controller only deals with the byte stream.
+Some controller does not works like a SPI bus controller, it works
+like a SPI NOR controller instead, such as the Freescale's QuadSPI controller.
+
+The Freescale's QuadSPI controller should knows the NOR commands to
+find the right LUT sequence. Unfortunately, the old code can not meet
+this requirement.
+
+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 the drivers use the framework
+-------------------------------------
+
+The main API is the spi_nor_scan(). Before you call the hook, you should
+initialize the necessary fields for spi_nor{}.
+Please see the drivers/mtd/spi-nor/spi-nor.c for detail.
+Please also reference to the fsl-quadspi.c when you want to write a new driver
+for a SPI NOR controller.
Signature · 1 line
1.7.2.rc3
#14 lore raw

PATCH v5 4/8 Documentation: add the document for the SPI NOR framework

24.02.2014, 15:26, "Huang Shijie" <[email protected]>:

Quoted 17 lines

This patch adds the document for the SPI NOR framework.

---
?Documentation/mtd/spi-nor.txt | ??59 +++++++++++++++++++++++++++++++++++++++++ ?1 files changed, 59 insertions(+), 0 deletions(-)
?create mode 100644 Documentation/mtd/spi-nor.txt

Documentation/mtd/spi-nor.txt new file +3 -0
new file mode 100644
index 0000000..b9585f8
@@ -0,0 +1,59 @@
+ ?????????????????????????SPI NOR framework
+ ??????????????============================================
+

Documentation/mtd/spi-nor.txt line 13

Quoted 10 lines
Documentation/mtd/spi-nor.txt new file +10 -0
+Part I - why we need this framework?
+-------------------------------------
+
+The SPI bus controller only deals with the byte stream.
+Some controller does not works like a SPI bus controller, it works
+like a SPI NOR controller instead, such as the Freescale's QuadSPI controller.
+
+The Freescale's QuadSPI controller should knows the NOR commands to
+find the right LUT sequence. Unfortunately, the old code can not meet
+this requirement.

should know

Quoted 49 lines
Documentation/mtd/spi-nor.txt new file +46 -1
+
+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 the drivers use the framework
+-------------------------------------
+
+The main API is the spi_nor_scan(). Before you call the hook, you should
+initialize the necessary fields for spi_nor{}.
+Please see the drivers/mtd/spi-nor/spi-nor.c for detail.
+Please also reference to the fsl-quadspi.c when you want to write a new driver
+for a SPI NOR controller.
--

1.7.2.rc3

Quoted 3 lines

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

Regards,
Konstantin

#15 lore raw

PATCH v5 4/8 Documentation: add the document for the SPI NOR framework

? 2014?02?24? 23:17, Konstantin Tokarev ??:

Quoted 1 line

should know

sorry for my poor english. thanks a lot.

Huang Shijie

#16 lore raw

PATCH v5 4/8 Documentation: add the document for the SPI NOR framework

In reply to Huang Shijie

This patch adds the document for the SPI NOR framework.

---
fix a typo

---
 Documentation/mtd/spi-nor.txt |   59 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 59 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/mtd/spi-nor.txt
Documentation/mtd/spi-nor.txt new file +59 -0
new file mode 100644
index 0000000..294d5b0
@@ -0,0 +1,59 @@
+                          SPI NOR framework
+               ============================================
+
+Part I - why we need this framework?
+-------------------------------------
+
+The SPI bus controller only deals with the byte stream.
+Some controller does not works like a SPI bus controller, it works
+like a SPI NOR controller instead, such as the Freescale's QuadSPI controller.
+
+The Freescale's QuadSPI controller should know the NOR commands to
+find the right LUT sequence. Unfortunately, the old code can not meet
+this requirement.
+
+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 the drivers use the framework
+-------------------------------------
+
+The main API is the spi_nor_scan(). Before you call the hook, you should
+initialize the necessary fields for spi_nor{}.
+Please see the drivers/mtd/spi-nor/spi-nor.c for detail.
+Please also reference to the fsl-quadspi.c when you want to write a new driver
+for a SPI NOR controller.
Signature · 1 line
1.7.2.rc3
#17 lore raw

PATCH v5 5/8 mtd: m25p80: use the SPI nor framework

In reply to Huang Shijie

Use the new SPI nor framework, and rewrite the m25p80:
(0) remove all the NOR comands.
(1) change the m25p->command to an array.
(2) implement the necessary hooks, such as m25p80_read/m25p80_write.

Tested with the m25p32.

---
 drivers/mtd/devices/m25p80.c | 1302 ++++--------------------------------------
 1 files changed, 105 insertions(+), 1197 deletions(-)
drivers/mtd/devices/m25p80.c +105 -1197
index 882b720..4af6400 100644
@@ -19,485 +19,98 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/math64.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/mod_devicetable.h>
 
-#include <linux/mtd/cfi.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
-#include <linux/of_platform.h>
 
 #include <linux/spi/spi.h>
 #include <linux/spi/flash.h>
+#include <linux/mtd/spi-nor.h>
 
-/* Flash opcodes. */
-#define	OPCODE_WREN		0x06	/* Write enable */
-#define	OPCODE_RDSR		0x05	/* Read status register */
-#define	OPCODE_WRSR		0x01	/* Write status register 1 byte */
-#define	OPCODE_NORM_READ	0x03	/* Read data bytes (low frequency) */
-#define	OPCODE_FAST_READ	0x0b	/* Read data bytes (high frequency) */
-#define	OPCODE_DUAL_READ        0x3b    /* Read data bytes (Dual SPI) */
-#define	OPCODE_QUAD_READ        0x6b    /* Read data bytes (Quad SPI) */
-#define	OPCODE_PP		0x02	/* Page program (up to 256 bytes) */
-#define	OPCODE_BE_4K		0x20	/* Erase 4KiB block */
-#define	OPCODE_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
-#define	OPCODE_BE_32K		0x52	/* Erase 32KiB block */
-#define	OPCODE_CHIP_ERASE	0xc7	/* Erase whole flash chip */
-#define	OPCODE_SE		0xd8	/* Sector erase (usually 64KiB) */
-#define	OPCODE_RDID		0x9f	/* Read JEDEC ID */
-#define	OPCODE_RDCR             0x35    /* Read configuration register */
-
-/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define	OPCODE_NORM_READ_4B	0x13	/* Read data bytes (low frequency) */
-#define	OPCODE_FAST_READ_4B	0x0c	/* Read data bytes (high frequency) */
-#define	OPCODE_DUAL_READ_4B	0x3c    /* Read data bytes (Dual SPI) */
-#define	OPCODE_QUAD_READ_4B	0x6c    /* Read data bytes (Quad SPI) */
-#define	OPCODE_PP_4B		0x12	/* Page program (up to 256 bytes) */
-#define	OPCODE_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
-
-/* Used for SST flashes only. */
-#define	OPCODE_BP		0x02	/* Byte program */
-#define	OPCODE_WRDI		0x04	/* Write disable */
-#define	OPCODE_AAI_WP		0xad	/* Auto address increment word program */
-
-/* Used for Macronix and Winbond flashes. */
-#define	OPCODE_EN4B		0xb7	/* Enter 4-byte mode */
-#define	OPCODE_EX4B		0xe9	/* Exit 4-byte mode */
-
-/* Used for Spansion flashes only. */
-#define	OPCODE_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 */
-
-/* Define max times to check status register before we give up. */
-#define	MAX_READY_WAIT_JIFFIES	(40 * HZ)	/* M25P16 specs 40s max chip erase */
 #define	MAX_CMD_SIZE		6
-
-#define JEDEC_MFR(_jedec_id)	((_jedec_id) >> 16)
-
-/****************************************************************************/
-
-enum read_type {
-	M25P80_NORMAL = 0,
-	M25P80_FAST,
-	M25P80_DUAL,
-	M25P80_QUAD,
-};
-
 struct m25p {
 	struct spi_device	*spi;
-	struct mutex		lock;
+	struct spi_nor		spi_nor;
 	struct mtd_info		mtd;
-	u16			page_size;
-	u16			addr_width;
-	u8			erase_opcode;
-	u8			read_opcode;
-	u8			program_opcode;
-	u8			*command;
-	enum read_type		flash_read;
+	u8			command[MAX_CMD_SIZE];
 };
 
-static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
-{
-	return container_of(mtd, struct m25p, mtd);
-}
-
-/****************************************************************************/
-
-/*
- * Internal helper functions
- */
-
-/*
- * Read the status register, returning its value in the location
- * Return the status register value.
- * Returns negative if error occurred.
- */
-static int read_sr(struct m25p *flash)
-{
-	ssize_t retval;
-	u8 code = OPCODE_RDSR;
-	u8 val;
-
-	retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
-
-	if (retval < 0) {
-		dev_err(&flash->spi->dev, "error %d reading SR\n",
-				(int) retval);
-		return retval;
-	}
-
-	return val;
-}
-
-/*
- * Read configuration register, returning its value in the
- * location. Return the configuration register value.
- * Returns negative if error occured.
- */
-static int read_cr(struct m25p *flash)
-{
-	u8 code = OPCODE_RDCR;
-	int ret;
-	u8 val;
-
-	ret = spi_write_then_read(flash->spi, &code, 1, &val, 1);
-	if (ret < 0) {
-		dev_err(&flash->spi->dev, "error %d reading CR\n", ret);
-		return ret;
-	}
-
-	return val;
-}
-
-/*
- * Write status register 1 byte
- * Returns negative if error occurred.
- */
-static int write_sr(struct m25p *flash, u8 val)
-{
-	flash->command[0] = OPCODE_WRSR;
-	flash->command[1] = val;
-
-	return spi_write(flash->spi, flash->command, 2);
-}
-
-/*
- * Set write enable latch with Write Enable command.
- * Returns negative if error occurred.
- */
-static inline int write_enable(struct m25p *flash)
-{
-	u8	code = OPCODE_WREN;
-
-	return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
-}
-
-/*
- * Send write disble instruction to the chip.
- */
-static inline int write_disable(struct m25p *flash)
-{
-	u8	code = OPCODE_WRDI;
-
-	return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
-}
-
-/*
- * Enable/disable 4-byte addressing mode.
- */
-static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
-{
-	int status;
-	bool need_wren = false;
-
-	switch (JEDEC_MFR(jedec_id)) {
-	case CFI_MFR_ST: /* Micron, actually */
-		/* Some Micron need WREN command; all will accept it */
-		need_wren = true;
-	case CFI_MFR_MACRONIX:
-	case 0xEF /* winbond */:
-		if (need_wren)
-			write_enable(flash);
-
-		flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B;
-		status = spi_write(flash->spi, flash->command, 1);
-
-		if (need_wren)
-			write_disable(flash);
-
-		return status;
-	default:
-		/* Spansion style */
-		flash->command[0] = OPCODE_BRWR;
-		flash->command[1] = enable << 7;
-		return spi_write(flash->spi, flash->command, 2);
-	}
-}
-
-/*
- * Service routine to read status register until ready, or timeout occurs.
- * Returns non-zero if error.
- */
-static int wait_till_ready(struct m25p *flash)
-{
-	unsigned long deadline;
-	int sr;
-
-	deadline = jiffies + MAX_READY_WAIT_JIFFIES;
-
-	do {
-		if ((sr = read_sr(flash)) < 0)
-			break;
-		else if (!(sr & SR_WIP))
-			return 0;
-
-		cond_resched();
-
-	} while (!time_after_eq(jiffies, deadline));
-
-	return 1;
-}
-
-/*
- * Write status Register and configuration register with 2 bytes
- * The first byte will be written to the status register, while the
- * second byte will be written to the configuration register.
- * Return negative if error occured.
- */
-static int write_sr_cr(struct m25p *flash, u16 val)
-{
-	flash->command[0] = OPCODE_WRSR;
-	flash->command[1] = val & 0xff;
-	flash->command[2] = (val >> 8);
-
-	return spi_write(flash->spi, flash->command, 3);
-}
-
-static int macronix_quad_enable(struct m25p *flash)
-{
-	int ret, val;
-	u8 cmd[2];
-	cmd[0] = OPCODE_WRSR;
-
-	val = read_sr(flash);
-	cmd[1] = val | SR_QUAD_EN_MX;
-	write_enable(flash);
-
-	spi_write(flash->spi, &cmd, 2);
-
-	if (wait_till_ready(flash))
-		return 1;
-
-	ret = read_sr(flash);
-	if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
-		dev_err(&flash->spi->dev, "Macronix Quad bit not set\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int spansion_quad_enable(struct m25p *flash)
+static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
 {
+	struct m25p *flash = nor->priv;
+	struct spi_device *spi = flash->spi;
 	int ret;
-	int quad_en = CR_QUAD_EN_SPAN << 8;
-
-	write_enable(flash);
 
-	ret = write_sr_cr(flash, quad_en);
-	if (ret < 0) {
-		dev_err(&flash->spi->dev,
-			"error while writing configuration register\n");
-		return -EINVAL;
-	}
-
-	/* read back and check it */
-	ret = read_cr(flash);
-	if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
-		dev_err(&flash->spi->dev, "Spansion Quad bit not set\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int set_quad_mode(struct m25p *flash, u32 jedec_id)
-{
-	int status;
-
-	switch (JEDEC_MFR(jedec_id)) {
-	case CFI_MFR_MACRONIX:
-		status = macronix_quad_enable(flash);
-		if (status) {
-			dev_err(&flash->spi->dev,
-				"Macronix quad-read not enabled\n");
-			return -EINVAL;
-		}
-		return status;
-	default:
-		status = spansion_quad_enable(flash);
-		if (status) {
-			dev_err(&flash->spi->dev,
-				"Spansion quad-read not enabled\n");
-			return -EINVAL;
-		}
-		return status;
-	}
-}
-
-/*
- * Erase the whole flash memory
- *
- * Returns 0 if successful, non-zero otherwise.
- */
-static int erase_chip(struct m25p *flash)
-{
-	pr_debug("%s: %s %lldKiB\n", dev_name(&flash->spi->dev), __func__,
-			(long long)(flash->mtd.size >> 10));
+	ret = spi_write_then_read(spi, &code, 1, val, len);
+	if (ret < 0)
+		dev_err(&spi->dev, "error %d reading %x\n", ret, code);
 
-	/* Wait until finished previous write command. */
-	if (wait_till_ready(flash))
-		return 1;
-
-	/* Send write enable, then erase commands. */
-	write_enable(flash);
-
-	/* Set up command buffer. */
-	flash->command[0] = OPCODE_CHIP_ERASE;
-
-	spi_write(flash->spi, flash->command, 1);
-
-	return 0;
+	return ret;
 }
 
-static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd)
+static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
 {
 	/* opcode is in cmd[0] */
-	cmd[1] = addr >> (flash->addr_width * 8 -  8);
-	cmd[2] = addr >> (flash->addr_width * 8 - 16);
-	cmd[3] = addr >> (flash->addr_width * 8 - 24);
-	cmd[4] = addr >> (flash->addr_width * 8 - 32);
+	cmd[1] = addr >> (nor->addr_width * 8 -  8);
+	cmd[2] = addr >> (nor->addr_width * 8 - 16);
+	cmd[3] = addr >> (nor->addr_width * 8 - 24);
+	cmd[4] = addr >> (nor->addr_width * 8 - 32);
 }
 
-static int m25p_cmdsz(struct m25p *flash)
+static int m25p_cmdsz(struct spi_nor *nor)
 {
-	return 1 + flash->addr_width;
+	return 1 + nor->addr_width;
 }
 
-/*
- * Erase one sector of flash memory at offset ``offset'' which is any
- * address within the sector which should be erased.
- *
- * Returns 0 if successful, non-zero otherwise.
- */
-static int erase_sector(struct m25p *flash, u32 offset)
+static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
+			int wr_en)
 {
-	pr_debug("%s: %s %dKiB at 0x%08x\n", dev_name(&flash->spi->dev),
-			__func__, flash->mtd.erasesize / 1024, offset);
-
-	/* Wait until finished previous write command. */
-	if (wait_till_ready(flash))
-		return 1;
+	struct m25p *flash = nor->priv;
+	struct spi_device *spi = flash->spi;
 
-	/* Send write enable, then erase commands. */
-	write_enable(flash);
-
-	/* Set up command buffer. */
-	flash->command[0] = flash->erase_opcode;
-	m25p_addr2cmd(flash, offset, flash->command);
-
-	spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
+	flash->command[0] = opcode;
+	if (buf)
+		memcpy(&flash->command[1], buf, len);
 
-	return 0;
+	return spi_write(spi, flash->command, len + 1);
 }
 
-/****************************************************************************/
-
-/*
- * MTD implementation
- */
-
-/*
- * Erase an address range on the flash chip.  The address range may extend
- * one or more erase sectors.  Return an error is there is a problem erasing.
- */
-static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
+static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
+			size_t *retlen, const u_char *buf)
 {
-	struct m25p *flash = mtd_to_m25p(mtd);
-	u32 addr,len;
-	uint32_t rem;
-
-	pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev),
-			__func__, (long long)instr->addr,
-			(long long)instr->len);
-
-	div_u64_rem(instr->len, mtd->erasesize, &rem);
-	if (rem)
-		return -EINVAL;
-
-	addr = instr->addr;
-	len = instr->len;
-
-	mutex_lock(&flash->lock);
-
-	/* whole-chip erase? */
-	if (len == flash->mtd.size) {
-		if (erase_chip(flash)) {
-			instr->state = MTD_ERASE_FAILED;
-			mutex_unlock(&flash->lock);
-			return -EIO;
-		}
+	struct m25p *flash = nor->priv;
+	struct spi_device *spi = flash->spi;
+	struct spi_transfer t[2] = {};
+	struct spi_message m;
+	int cmd_sz = m25p_cmdsz(nor);
 
-	/* REVISIT in some cases we could speed up erasing large regions
-	 * by using OPCODE_SE instead of OPCODE_BE_4K.  We may have set up
-	 * to use "small sector erase", but that's not always optimal.
-	 */
+	spi_message_init(&m);
 
-	/* "sector"-at-a-time erase */
-	} else {
-		while (len) {
-			if (erase_sector(flash, addr)) {
-				instr->state = MTD_ERASE_FAILED;
-				mutex_unlock(&flash->lock);
-				return -EIO;
-			}
+	if (nor->program_opcode == OPCODE_AAI_WP && nor->sst_write_second)
+		cmd_sz = 1;
 
-			addr += mtd->erasesize;
-			len -= mtd->erasesize;
-		}
-	}
+	flash->command[0] = nor->program_opcode;
+	m25p_addr2cmd(nor, to, flash->command);
 
-	mutex_unlock(&flash->lock);
+	t[0].tx_buf = flash->command;
+	t[0].len = cmd_sz;
+	spi_message_add_tail(&t[0], &m);
 
-	instr->state = MTD_ERASE_DONE;
-	mtd_erase_callback(instr);
+	t[1].tx_buf = buf;
+	t[1].len = len;
+	spi_message_add_tail(&t[1], &m);
 
-	return 0;
-}
+	spi_sync(spi, &m);
 
-/*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int m25p80_dummy_cycles_read(struct m25p *flash)
-{
-	switch (flash->flash_read) {
-	case M25P80_FAST:
-	case M25P80_DUAL:
-	case M25P80_QUAD:
-		return 1;
-	case M25P80_NORMAL:
-		return 0;
-	default:
-		dev_err(&flash->spi->dev, "No valid read type supported\n");
-		return -1;
-	}
+	*retlen += m.actual_length - cmd_sz;
 }
 
-static inline unsigned int m25p80_rx_nbits(const struct m25p *flash)
+static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
 {
-	switch (flash->flash_read) {
-	case M25P80_DUAL:
+	switch (nor->flash_read) {
+	case SPI_NOR_DUAL:
 		return 2;
-	case M25P80_QUAD:
+	case SPI_NOR_QUAD:
 		return 4;
 	default:
 		return 0;
@@ -505,589 +118,72 @@ static inline unsigned int m25p80_rx_nbits(const struct m25p *flash)
 }
 
 /*
- * Read an address range from the flash chip.  The address range
+ * Read an address range from the nor chip.  The address range
  * may be any size provided it is within the physical boundaries.
  */
-static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
-	size_t *retlen, u_char *buf)
+static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
+			size_t *retlen, u_char *buf)
 {
-	struct m25p *flash = mtd_to_m25p(mtd);
+	struct m25p *flash = nor->priv;
+	struct spi_device *spi = flash->spi;
 	struct spi_transfer t[2];
 	struct spi_message m;
-	uint8_t opcode;
-	int dummy;
+	int dummy = nor->read_dummy;
+	int ret;
 
-	pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
-			__func__, (u32)from, len);
+	/* Wait till previous write/erase is done. */
+	ret = nor->wait_till_ready(nor);
+	if (ret)
+		return ret;
 
 	spi_message_init(&m);
 	memset(t, 0, (sizeof t));
 
-	dummy =  m25p80_dummy_cycles_read(flash);
-	if (dummy < 0) {
-		dev_err(&flash->spi->dev, "No valid read command supported\n");
-		return -EINVAL;
-	}
+	flash->command[0] = nor->read_opcode;
+	m25p_addr2cmd(nor, from, flash->command);
 
 	t[0].tx_buf = flash->command;
-	t[0].len = m25p_cmdsz(flash) + dummy;
+	t[0].len = m25p_cmdsz(nor) + dummy;
 	spi_message_add_tail(&t[0], &m);
 
 	t[1].rx_buf = buf;
-	t[1].rx_nbits = m25p80_rx_nbits(flash);
+	t[1].rx_nbits = m25p80_rx_nbits(nor);
 	t[1].len = len;
 	spi_message_add_tail(&t[1], &m);
 
-	mutex_lock(&flash->lock);
-
-	/* Wait till previous write/erase is done. */
-	if (wait_till_ready(flash)) {
-		/* REVISIT status return?? */
-		mutex_unlock(&flash->lock);
-		return 1;
-	}
-
-	/* Set up the write data buffer. */
-	opcode = flash->read_opcode;
-	flash->command[0] = opcode;
-	m25p_addr2cmd(flash, from, flash->command);
-
-	spi_sync(flash->spi, &m);
-
-	*retlen = m.actual_length - m25p_cmdsz(flash) - dummy;
-
-	mutex_unlock(&flash->lock);
+	spi_sync(spi, &m);
 
+	*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
 	return 0;
 }
 
-/*
- * Write an address range to the flash chip.  Data must be written in
- * FLASH_PAGESIZE chunks.  The address range may be any size provided
- * it is within the physical boundaries.
- */
-static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
-	size_t *retlen, const u_char *buf)
+static int m25p80_erase(struct spi_nor *nor, loff_t offset)
 {
-	struct m25p *flash = mtd_to_m25p(mtd);
-	u32 page_offset, page_size;
-	struct spi_transfer t[2];
-	struct spi_message m;
-
-	pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
-			__func__, (u32)to, len);
-
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
-
-	t[0].tx_buf = flash->command;
-	t[0].len = m25p_cmdsz(flash);
-	spi_message_add_tail(&t[0], &m);
-
-	t[1].tx_buf = buf;
-	spi_message_add_tail(&t[1], &m);
-
-	mutex_lock(&flash->lock);
-
-	/* Wait until finished previous write command. */
-	if (wait_till_ready(flash)) {
-		mutex_unlock(&flash->lock);
-		return 1;
-	}
-
-	write_enable(flash);
-
-	/* Set up the opcode in the write buffer. */
-	flash->command[0] = flash->program_opcode;
-	m25p_addr2cmd(flash, to, flash->command);
-
-	page_offset = to & (flash->page_size - 1);
-
-	/* do all the bytes fit onto one page? */
-	if (page_offset + len <= flash->page_size) {
-		t[1].len = len;
-
-		spi_sync(flash->spi, &m);
-
-		*retlen = m.actual_length - m25p_cmdsz(flash);
-	} else {
-		u32 i;
-
-		/* the size of data remaining on the first page */
-		page_size = flash->page_size - page_offset;
-
-		t[1].len = page_size;
-		spi_sync(flash->spi, &m);
-
-		*retlen = m.actual_length - m25p_cmdsz(flash);
-
-		/* write everything in flash->page_size chunks */
-		for (i = page_size; i < len; i += page_size) {
-			page_size = len - i;
-			if (page_size > flash->page_size)
-				page_size = flash->page_size;
-
-			/* write the next page to flash */
-			m25p_addr2cmd(flash, to + i, flash->command);
-
-			t[1].tx_buf = buf + i;
-			t[1].len = page_size;
-
-			wait_till_ready(flash);
-
-			write_enable(flash);
-
-			spi_sync(flash->spi, &m);
-
-			*retlen += m.actual_length - m25p_cmdsz(flash);
-		}
-	}
-
-	mutex_unlock(&flash->lock);
-
-	return 0;
-}
-
-static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
-		size_t *retlen, const u_char *buf)
-{
-	struct m25p *flash = mtd_to_m25p(mtd);
-	struct spi_transfer t[2];
-	struct spi_message m;
-	size_t actual;
-	int cmd_sz, ret;
-
-	pr_debug("%s: %s to 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
-			__func__, (u32)to, len);
-
-	spi_message_init(&m);
-	memset(t, 0, (sizeof t));
-
-	t[0].tx_buf = flash->command;
-	t[0].len = m25p_cmdsz(flash);
-	spi_message_add_tail(&t[0], &m);
-
-	t[1].tx_buf = buf;
-	spi_message_add_tail(&t[1], &m);
+	struct m25p *flash = nor->priv;
+	int ret;
 
-	mutex_lock(&flash->lock);
+	dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
+		flash->mtd.erasesize / 1024, (u32)offset);
 
 	/* Wait until finished previous write command. */
-	ret = wait_till_ready(flash);
+	ret = nor->wait_till_ready(nor);
 	if (ret)
-		goto time_out;
-
-	write_enable(flash);
-
-	actual = to % 2;
-	/* Start write from odd address. */
-	if (actual) {
-		flash->command[0] = OPCODE_BP;
-		m25p_addr2cmd(flash, to, flash->command);
-
-		/* write one byte. */
-		t[1].len = 1;
-		spi_sync(flash->spi, &m);
-		ret = wait_till_ready(flash);
-		if (ret)
-			goto time_out;
-		*retlen += m.actual_length - m25p_cmdsz(flash);
-	}
-	to += actual;
-
-	flash->command[0] = OPCODE_AAI_WP;
-	m25p_addr2cmd(flash, to, flash->command);
-
-	/* Write out most of the data here. */
-	cmd_sz = m25p_cmdsz(flash);
-	for (; actual < len - 1; actual += 2) {
-		t[0].len = cmd_sz;
-		/* write two bytes. */
-		t[1].len = 2;
-		t[1].tx_buf = buf + actual;
+		return ret;
 
-		spi_sync(flash->spi, &m);
-		ret = wait_till_ready(flash);
-		if (ret)
-			goto time_out;
-		*retlen += m.actual_length - cmd_sz;
-		cmd_sz = 1;
-		to += 2;
-	}
-	write_disable(flash);
-	ret = wait_till_ready(flash);
+	/* Send write enable, then erase commands. */
+	ret = nor->write_reg(nor, OPCODE_WREN, NULL, 0, 0);
 	if (ret)
-		goto time_out;
-
-	/* Write out trailing byte if it exists. */
-	if (actual != len) {
-		write_enable(flash);
-		flash->command[0] = OPCODE_BP;
-		m25p_addr2cmd(flash, to, flash->command);
-		t[0].len = m25p_cmdsz(flash);
-		t[1].len = 1;
-		t[1].tx_buf = buf + actual;
-
-		spi_sync(flash->spi, &m);
-		ret = wait_till_ready(flash);
-		if (ret)
-			goto time_out;
-		*retlen += m.actual_length - m25p_cmdsz(flash);
-		write_disable(flash);
-	}
-
-time_out:
-	mutex_unlock(&flash->lock);
-	return ret;
-}
-
-static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
-	struct m25p *flash = mtd_to_m25p(mtd);
-	uint32_t offset = ofs;
-	uint8_t status_old, status_new;
-	int res = 0;
-
-	mutex_lock(&flash->lock);
-	/* Wait until finished previous command */
-	if (wait_till_ready(flash)) {
-		res = 1;
-		goto err;
-	}
-
-	status_old = read_sr(flash);
-
-	if (offset < flash->mtd.size-(flash->mtd.size/2))
-		status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
-	else if (offset < flash->mtd.size-(flash->mtd.size/4))
-		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
-	else if (offset < flash->mtd.size-(flash->mtd.size/8))
-		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
-	else if (offset < flash->mtd.size-(flash->mtd.size/16))
-		status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
-	else if (offset < flash->mtd.size-(flash->mtd.size/32))
-		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
-	else if (offset < flash->mtd.size-(flash->mtd.size/64))
-		status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1;
-	else
-		status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0;
-
-	/* Only modify protection if it will not unlock other areas */
-	if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) >
-					(status_old&(SR_BP2|SR_BP1|SR_BP0))) {
-		write_enable(flash);
-		if (write_sr(flash, status_new) < 0) {
-			res = 1;
-			goto err;
-		}
-	}
-
-err:	mutex_unlock(&flash->lock);
-	return res;
-}
-
-static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
-{
-	struct m25p *flash = mtd_to_m25p(mtd);
-	uint32_t offset = ofs;
-	uint8_t status_old, status_new;
-	int res = 0;
-
-	mutex_lock(&flash->lock);
-	/* Wait until finished previous command */
-	if (wait_till_ready(flash)) {
-		res = 1;
-		goto err;
-	}
-
-	status_old = read_sr(flash);
-
-	if (offset+len > flash->mtd.size-(flash->mtd.size/64))
-		status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0);
-	else if (offset+len > flash->mtd.size-(flash->mtd.size/32))
-		status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0;
-	else if (offset+len > flash->mtd.size-(flash->mtd.size/16))
-		status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1;
-	else if (offset+len > flash->mtd.size-(flash->mtd.size/8))
-		status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
-	else if (offset+len > flash->mtd.size-(flash->mtd.size/4))
-		status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
-	else if (offset+len > flash->mtd.size-(flash->mtd.size/2))
-		status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
-	else
-		status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
-
-	/* Only modify protection if it will not lock other areas */
-	if ((status_new&(SR_BP2|SR_BP1|SR_BP0)) <
-					(status_old&(SR_BP2|SR_BP1|SR_BP0))) {
-		write_enable(flash);
-		if (write_sr(flash, status_new) < 0) {
-			res = 1;
-			goto err;
-		}
-	}
-
-err:	mutex_unlock(&flash->lock);
-	return res;
-}
-
-/****************************************************************************/
-
-/*
- * SPI device driver setup and teardown
- */
-
-struct flash_info {
-	/* JEDEC id zero means "no ID" (most older chips); otherwise it has
-	 * a high byte of zero plus three data bytes: the manufacturer id,
-	 * then a two byte device id.
-	 */
-	u32		jedec_id;
-	u16             ext_id;
-
-	/* The size listed here is what works with OPCODE_SE, which isn't
-	 * necessarily called a "sector" by the vendor.
-	 */
-	unsigned	sector_size;
-	u16		n_sectors;
-
-	u16		page_size;
-	u16		addr_width;
-
-	u16		flags;
-#define	SECT_4K		0x01		/* OPCODE_BE_4K works uniformly */
-#define	M25P_NO_ERASE	0x02		/* No erase command needed */
-#define	SST_WRITE	0x04		/* use SST byte programming */
-#define	M25P_NO_FR	0x08		/* Can't do fastread */
-#define	SECT_4K_PMC	0x10		/* OPCODE_BE_4K_PMC works uniformly */
-#define	M25P80_DUAL_READ	0x20    /* Flash supports Dual Read */
-#define	M25P80_QUAD_READ	0x40    /* Flash supports Quad Read */
-};
-
-#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)	\
-	((kernel_ulong_t)&(struct flash_info) {				\
-		.jedec_id = (_jedec_id),				\
-		.ext_id = (_ext_id),					\
-		.sector_size = (_sector_size),				\
-		.n_sectors = (_n_sectors),				\
-		.page_size = 256,					\
-		.flags = (_flags),					\
-	})
-
-#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags)	\
-	((kernel_ulong_t)&(struct flash_info) {				\
-		.sector_size = (_sector_size),				\
-		.n_sectors = (_n_sectors),				\
-		.page_size = (_page_size),				\
-		.addr_width = (_addr_width),				\
-		.flags = (_flags),					\
-	})
-
-/* NOTE: double check command sets and memory organization when you add
- * more flash chips.  This current list focusses on newer chips, which
- * have been converging on command sets which including JEDEC ID.
- */
-static const struct spi_device_id m25p_ids[] = {
-	/* Atmel -- some are (confusingly) marketed as "DataFlash" */
-	{ "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
-	{ "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },
-
-	{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) },
-	{ "at25df321a", INFO(0x1f4701, 0, 64 * 1024,  64, SECT_4K) },
-	{ "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
-
-	{ "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },
-	{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
-	{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
-	{ "at26df321",  INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
-
-	{ "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) },
-
-	/* EON -- en25xxx */
-	{ "en25f32",    INFO(0x1c3116, 0, 64 * 1024,   64, SECT_4K) },
-	{ "en25p32",    INFO(0x1c2016, 0, 64 * 1024,   64, 0) },
-	{ "en25q32b",   INFO(0x1c3016, 0, 64 * 1024,   64, 0) },
-	{ "en25p64",    INFO(0x1c2017, 0, 64 * 1024,  128, 0) },
-	{ "en25q64",    INFO(0x1c3017, 0, 64 * 1024,  128, SECT_4K) },
-	{ "en25qh256",  INFO(0x1c7019, 0, 64 * 1024,  512, 0) },
-
-	/* ESMT */
-	{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
-
-	/* Everspin */
-	{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) },
-	{ "mr25h10",  CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) },
-
-	/* GigaDevice */
-	{ "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SECT_4K) },
-	{ "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
-
-	/* Intel/Numonyx -- xxxs33b */
-	{ "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) },
-	{ "320s33b",  INFO(0x898912, 0, 64 * 1024,  64, 0) },
-	{ "640s33b",  INFO(0x898913, 0, 64 * 1024, 128, 0) },
-
-	/* Macronix */
-	{ "mx25l2005a",  INFO(0xc22012, 0, 64 * 1024,   4, SECT_4K) },
-	{ "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) },
-	{ "mx25l8005",   INFO(0xc22014, 0, 64 * 1024,  16, 0) },
-	{ "mx25l1606e",  INFO(0xc22015, 0, 64 * 1024,  32, SECT_4K) },
-	{ "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, 0) },
-	{ "mx25l3255e",  INFO(0xc29e16, 0, 64 * 1024,  64, SECT_4K) },
-	{ "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, 0) },
-	{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
-	{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
-	{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
-	{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
-	{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) },
-
-	/* Micron */
-	{ "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, 0) },
-	{ "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, 0) },
-	{ "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, 0) },
-	{ "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K) },
-	{ "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) },
-
-	/* PMC */
-	{ "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },
-	{ "pm25lv010",   INFO(0,        0, 32 * 1024,    4, SECT_4K_PMC) },
-	{ "pm25lq032",   INFO(0x7f9d46, 0, 64 * 1024,   64, SECT_4K) },
-
-	/* Spansion -- single (large) sector size only, at least
-	 * for the chips listed here (without boot sectors).
-	 */
-	{ "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, 0) },
-	{ "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, 0) },
-	{ "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
-	{ "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) },
-	{ "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) },
-	{ "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
-	{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
-	{ "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
-	{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
-	{ "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
-	{ "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
-	{ "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) },
-	{ "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },
-	{ "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },
-	{ "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },
-	{ "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SECT_4K) },
-	{ "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) },
-	{ "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },
-
-	/* SST -- large erase sizes are "overlays", "sectors" are 4K */
-	{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
-	{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
-	{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
-	{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
-	{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
-	{ "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K | SST_WRITE) },
-	{ "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) },
-	{ "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) },
-	{ "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
-
-	/* ST Microelectronics -- newer production may have feature updates */
-	{ "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
-	{ "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) },
-	{ "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) },
-	{ "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },
-	{ "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },
-	{ "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) },
-	{ "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
-	{ "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
-	{ "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
-	{ "n25q032", INFO(0x20ba16,  0,  64 * 1024,  64, 0) },
-
-	{ "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) },
-	{ "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) },
-	{ "m25p20-nonjedec",  INFO(0, 0,  64 * 1024,   4, 0) },
-	{ "m25p40-nonjedec",  INFO(0, 0,  64 * 1024,   8, 0) },
-	{ "m25p80-nonjedec",  INFO(0, 0,  64 * 1024,  16, 0) },
-	{ "m25p16-nonjedec",  INFO(0, 0,  64 * 1024,  32, 0) },
-	{ "m25p32-nonjedec",  INFO(0, 0,  64 * 1024,  64, 0) },
-	{ "m25p64-nonjedec",  INFO(0, 0,  64 * 1024, 128, 0) },
-	{ "m25p128-nonjedec", INFO(0, 0, 256 * 1024,  64, 0) },
-
-	{ "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, 0) },
-	{ "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, 0) },
-	{ "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, 0) },
-
-	{ "m25pe20", INFO(0x208012,  0, 64 * 1024,  4,       0) },
-	{ "m25pe80", INFO(0x208014,  0, 64 * 1024, 16,       0) },
-	{ "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) },
-
-	{ "m25px16",    INFO(0x207115,  0, 64 * 1024, 32, SECT_4K) },
-	{ "m25px32",    INFO(0x207116,  0, 64 * 1024, 64, SECT_4K) },
-	{ "m25px32-s0", INFO(0x207316,  0, 64 * 1024, 64, SECT_4K) },
-	{ "m25px32-s1", INFO(0x206316,  0, 64 * 1024, 64, SECT_4K) },
-	{ "m25px64",    INFO(0x207117,  0, 64 * 1024, 128, 0) },
-
-	/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
-	{ "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) },
-	{ "w25x20", INFO(0xef3012, 0, 64 * 1024,  4,  SECT_4K) },
-	{ "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) },
-	{ "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) },
-	{ "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) },
-	{ "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },
-	{ "w25q32", INFO(0xef4016, 0, 64 * 1024,  64, SECT_4K) },
-	{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024,  64, SECT_4K) },
-	{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
-	{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
-	{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
-	{ "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },
-	{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
-	{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
-	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
-
-	/* Catalyst / On Semiconductor -- non-JEDEC */
-	{ "cat25c11", CAT25_INFO(  16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) },
-	{ "cat25c03", CAT25_INFO(  32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) },
-	{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
-	{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) },
-	{ "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) },
-	{ },
-};
-MODULE_DEVICE_TABLE(spi, m25p_ids);
-
-static const struct spi_device_id *jedec_probe(struct spi_device *spi)
-{
-	int			tmp;
-	u8			code = OPCODE_RDID;
-	u8			id[5];
-	u32			jedec;
-	u16                     ext_jedec;
-	struct flash_info	*info;
+		return ret;
 
-	/* JEDEC also defines an optional "extended device information"
-	 * string for after vendor-specific data, after the three bytes
-	 * we use here.  Supporting some chips might require using it.
-	 */
-	tmp = spi_write_then_read(spi, &code, 1, id, 5);
-	if (tmp < 0) {
-		pr_debug("%s: error %d reading JEDEC ID\n",
-				dev_name(&spi->dev), tmp);
-		return ERR_PTR(tmp);
-	}
-	jedec = id[0];
-	jedec = jedec << 8;
-	jedec |= id[1];
-	jedec = jedec << 8;
-	jedec |= id[2];
+	/* Set up command buffer. */
+	flash->command[0] = nor->erase_opcode;
+	m25p_addr2cmd(nor, offset, flash->command);
 
-	ext_jedec = id[3] << 8 | id[4];
+	spi_write(flash->spi, flash->command, m25p_cmdsz(nor));
 
-	for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
-		info = (void *)m25p_ids[tmp].driver_data;
-		if (info->jedec_id == jedec) {
-			if (info->ext_id == 0 || info->ext_id == ext_jedec)
-				return &m25p_ids[tmp];
-		}
-	}
-	dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
-	return ERR_PTR(-ENODEV);
+	return 0;
 }
 
-
 /*
  * board specific setup should have ensured the SPI clock used here
  * matches what the READ command supports, at least until this driver
@@ -1095,231 +191,43 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)
  */
 static int m25p_probe(struct spi_device *spi)
 {
-	const struct spi_device_id	*id = spi_get_device_id(spi);
-	struct flash_platform_data	*data;
-	struct m25p			*flash;
-	struct flash_info		*info;
-	unsigned			i;
 	struct mtd_part_parser_data	ppdata;
-	struct device_node *np = spi->dev.of_node;
+	struct flash_platform_data	*data;
+	struct m25p *flash;
+	struct spi_nor *nor;
+	enum read_mode mode = SPI_NOR_NORMAL;
 	int ret;
 
-	/* Platform data helps sort out which chip type we have, as
-	 * well as how this board partitions it.  If we don't have
-	 * a chip ID, try the JEDEC id commands; they'll work for most
-	 * newer chips, even if we don't recognize the particular chip.
-	 */
-	data = dev_get_platdata(&spi->dev);
-	if (data && data->type) {
-		const struct spi_device_id *plat_id;
-
-		for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
-			plat_id = &m25p_ids[i];
-			if (strcmp(data->type, plat_id->name))
-				continue;
-			break;
-		}
-
-		if (i < ARRAY_SIZE(m25p_ids) - 1)
-			id = plat_id;
-		else
-			dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
-	}
-
-	info = (void *)id->driver_data;
-
-	if (info->jedec_id) {
-		const struct spi_device_id *jid;
-
-		jid = jedec_probe(spi);
-		if (IS_ERR(jid)) {
-			return PTR_ERR(jid);
-		} else if (jid != id) {
-			/*
-			 * JEDEC knows better, so overwrite platform ID. We
-			 * can't trust partitions any longer, but we'll let
-			 * mtd apply them anyway, since some partitions may be
-			 * marked read-only, and we don't want to lose that
-			 * information, even if it's not 100% accurate.
-			 */
-			dev_warn(&spi->dev, "found %s, expected %s\n",
-				 jid->name, id->name);
-			id = jid;
-			info = (void *)jid->driver_data;
-		}
-	}
-
 	flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
 	if (!flash)
 		return -ENOMEM;
 
-	flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE, GFP_KERNEL);
-	if (!flash->command)
-		return -ENOMEM;
-
-	flash->spi = spi;
-	mutex_init(&flash->lock);
-	spi_set_drvdata(spi, flash);
-
-	/*
-	 * Atmel, SST and Intel/Numonyx serial flash tend to power
-	 * up with the software protection bits set
-	 */
+	nor = &flash->spi_nor;
 
-	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
-	    JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
-	    JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
-		write_enable(flash);
-		write_sr(flash, 0);
-	}
-
-	if (data && data->name)
-		flash->mtd.name = data->name;
-	else
-		flash->mtd.name = dev_name(&spi->dev);
-
-	flash->mtd.type = MTD_NORFLASH;
-	flash->mtd.writesize = 1;
-	flash->mtd.flags = MTD_CAP_NORFLASH;
-	flash->mtd.size = info->sector_size * info->n_sectors;
-	flash->mtd._erase = m25p80_erase;
-	flash->mtd._read = m25p80_read;
-
-	/* flash protection support for STmicro chips */
-	if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
-		flash->mtd._lock = m25p80_lock;
-		flash->mtd._unlock = m25p80_unlock;
-	}
+	/* install the hooks */
+	nor->read = m25p80_read;
+	nor->write = m25p80_write;
+	nor->erase = m25p80_erase;
+	nor->write_reg = m25p80_write_reg;
+	nor->read_reg = m25p80_read_reg;
 
-	/* sst flash chips use AAI word program */
-	if (info->flags & SST_WRITE)
-		flash->mtd._write = sst_write;
-	else
-		flash->mtd._write = m25p80_write;
+	nor->dev = &spi->dev;
+	nor->mtd = &flash->mtd;
+	nor->priv = flash;
 
-	/* prefer "small sector" erase if possible */
-	if (info->flags & SECT_4K) {
-		flash->erase_opcode = OPCODE_BE_4K;
-		flash->mtd.erasesize = 4096;
-	} else if (info->flags & SECT_4K_PMC) {
-		flash->erase_opcode = OPCODE_BE_4K_PMC;
-		flash->mtd.erasesize = 4096;
-	} else {
-		flash->erase_opcode = OPCODE_SE;
-		flash->mtd.erasesize = info->sector_size;
-	}
+	spi_set_drvdata(spi, flash);
+	flash->mtd.priv = nor;
+	flash->spi = spi;
 
-	if (info->flags & M25P_NO_ERASE)
-		flash->mtd.flags |= MTD_NO_ERASE;
+	if (spi->mode & SPI_RX_QUAD)
+		mode = SPI_NOR_QUAD;
+	ret = spi_nor_scan(nor, spi_get_device_id(spi), mode);
+	if (ret)
+		return ret;
 
+	data = dev_get_platdata(&spi->dev);
 	ppdata.of_node = spi->dev.of_node;
-	flash->mtd.dev.parent = &spi->dev;
-	flash->page_size = info->page_size;
-	flash->mtd.writebufsize = flash->page_size;
-
-	if (np) {
-		/* If we were instantiated by DT, use it */
-		if (of_property_read_bool(np, "m25p,fast-read"))
-			flash->flash_read = M25P80_FAST;
-		else
-			flash->flash_read = M25P80_NORMAL;
-	} else {
-		/* If we weren't instantiated by DT, default to fast-read */
-		flash->flash_read = M25P80_FAST;
-	}
-
-	/* Some devices cannot do fast-read, no matter what DT tells us */
-	if (info->flags & M25P_NO_FR)
-		flash->flash_read = M25P80_NORMAL;
-
-	/* Quad/Dual-read mode takes precedence over fast/normal */
-	if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) {
-		ret = set_quad_mode(flash, info->jedec_id);
-		if (ret) {
-			dev_err(&flash->spi->dev, "quad mode not supported\n");
-			return ret;
-		}
-		flash->flash_read = M25P80_QUAD;
-	} else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) {
-		flash->flash_read = M25P80_DUAL;
-	}
 
-	/* Default commands */
-	switch (flash->flash_read) {
-	case M25P80_QUAD:
-		flash->read_opcode = OPCODE_QUAD_READ;
-		break;
-	case M25P80_DUAL:
-		flash->read_opcode = OPCODE_DUAL_READ;
-		break;
-	case M25P80_FAST:
-		flash->read_opcode = OPCODE_FAST_READ;
-		break;
-	case M25P80_NORMAL:
-		flash->read_opcode = OPCODE_NORM_READ;
-		break;
-	default:
-		dev_err(&flash->spi->dev, "No Read opcode defined\n");
-		return -EINVAL;
-	}
-
-	flash->program_opcode = OPCODE_PP;
-
-	if (info->addr_width)
-		flash->addr_width = info->addr_width;
-	else if (flash->mtd.size > 0x1000000) {
-		/* enable 4-byte addressing if the device exceeds 16MiB */
-		flash->addr_width = 4;
-		if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
-			/* Dedicated 4-byte command set */
-			switch (flash->flash_read) {
-			case M25P80_QUAD:
-				flash->read_opcode = OPCODE_QUAD_READ_4B;
-				break;
-			case M25P80_DUAL:
-				flash->read_opcode = OPCODE_DUAL_READ_4B;
-				break;
-			case M25P80_FAST:
-				flash->read_opcode = OPCODE_FAST_READ_4B;
-				break;
-			case M25P80_NORMAL:
-				flash->read_opcode = OPCODE_NORM_READ_4B;
-				break;
-			}
-			flash->program_opcode = OPCODE_PP_4B;
-			/* No small sector erase for 4-byte command set */
-			flash->erase_opcode = OPCODE_SE_4B;
-			flash->mtd.erasesize = info->sector_size;
-		} else
-			set_4byte(flash, info->jedec_id, 1);
-	} else {
-		flash->addr_width = 3;
-	}
-
-	dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
-			(long long)flash->mtd.size >> 10);
-
-	pr_debug("mtd .name = %s, .size = 0x%llx (%lldMiB) "
-			".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
-		flash->mtd.name,
-		(long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
-		flash->mtd.erasesize, flash->mtd.erasesize / 1024,
-		flash->mtd.numeraseregions);
-
-	if (flash->mtd.numeraseregions)
-		for (i = 0; i < flash->mtd.numeraseregions; i++)
-			pr_debug("mtd.eraseregions[%d] = { .offset = 0x%llx, "
-				".erasesize = 0x%.8x (%uKiB), "
-				".numblocks = %d }\n",
-				i, (long long)flash->mtd.eraseregions[i].offset,
-				flash->mtd.eraseregions[i].erasesize,
-				flash->mtd.eraseregions[i].erasesize / 1024,
-				flash->mtd.eraseregions[i].numblocks);
-
-
-	/* partitions should match sector boundaries; and it may be good to
-	 * use readonly partitions for writeprotected sectors (BP2..BP0).
-	 */
 	return mtd_device_parse_register(&flash->mtd, NULL, &ppdata,
 			data ? data->parts : NULL,
 			data ? data->nr_parts : 0);
@@ -1340,7 +248,7 @@ static struct spi_driver m25p80_driver = {
 		.name	= "m25p80",
 		.owner	= THIS_MODULE,
 	},
-	.id_table	= m25p_ids,
+	.id_table	= spi_nor_ids,
 	.probe	= m25p_probe,
 	.remove	= m25p_remove,
 
Signature · 1 line
1.7.2.rc3
#18 lore raw

PATCH v5 5/8 mtd: m25p80: use the SPI nor framework

Use the new SPI nor framework, and rewrite the m25p80:
(0) remove all the NOR comands.
(1) change the m25p->command to an array.
(2) implement the necessary hooks, such as m25p80_read/m25p80_write.

Tested with the m25p32.

---
add back the dependency information in the Kconfig.

---
 drivers/mtd/devices/Kconfig  |    2 +-
 drivers/mtd/devices/m25p80.c | 1302 ++++--------------------------------------
 2 files changed, 106 insertions(+), 1198 deletions(-)
drivers/mtd/devices/Kconfig +1 -1
index 0128138..004b17b 100644
@@ -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_BASE
 	help
 	  This enables access to most modern SPI flash chips, used for
 	  program and data storage.   Series supported include Atmel AT26DF,
drivers/mtd/devices/m25p80.c +105 -1197

1,417 lines not on this page · raw message

Signature · 1 line
1.7.2.rc3
#19 lore raw

PATCH v5 5/8 mtd: m25p80: use the SPI nor framework

+ Russell

Quoted 12 lines · Huang Shijie

add back the dependency information in the Kconfig.

---
 drivers/mtd/devices/Kconfig  |    2 +-
 drivers/mtd/devices/m25p80.c | 1302 ++++--------------------------------------
 2 files changed, 106 insertions(+), 1198 deletions(-)
drivers/mtd/devices/Kconfig +0 -0
index 0128138..004b17b 100644

drivers/mtd/devices/Kconfig config MTD_DATAFLASH_OTP · line 86

Quoted 8 lines
drivers/mtd/devices/Kconfig +1 -1
@@ -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_BASE
 	help
 	  This enables access to most modern SPI flash chips, used for
 	  program and data storage.   Series supported include Atmel AT26DF,

Now that M25P80 depends on MTD_SPI_NOR_BASE (which I proposed to rename to MTD_SPI_NOR a few hours ago), shouldn't we be updating everybody's defconfigs? As it stands, the defconfigs that use M25P80 will just drop it silently when built.

And if so, I'm not sure how this should be handled. Should defconfig patches be sent to their respective arch maintainers?

Also, I think m25p80.c is no longer a "self-contained MTD device driver" and should be moved under drivers/mtd/spi-nor/.

(Once these questions are settled, did you want to handle cooking the patches, Huang, or should I?)

Brian

#20 lore raw

PATCH v5 5/8 mtd: m25p80: use the SPI nor framework

Quoted 13 lines · Brian Norris

drivers/mtd/devices/Kconfig config MTD_DATAFLASH_OTP · line 86

Quoted 7 lines

config MTD_M25P80

drivers/mtd/devices/Kconfig +1 -1
 	tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
-	depends on SPI_MASTER
+	depends on SPI_MASTER && MTD_SPI_NOR_BASE
 	help
 	  This enables access to most modern SPI flash chips, used for
 	  program and data storage.   Series supported include Atmel AT26DF,

Now that M25P80 depends on MTD_SPI_NOR_BASE (which I proposed to rename to MTD_SPI_NOR a few hours ago), shouldn't we be updating everybody's defconfigs? As it stands, the defconfigs that use M25P80 will just drop it silently when built.

Quoted 2 lines

And if so, I'm not sure how this should be handled. Should defconfig patches be sent to their respective arch maintainers?

IMHO, we should create a patch or several patches to add the MTD_SPI_NOR to the defconfigs.

Another method is to select the MTD_SPI_NOR which we enable the MTD.

Quoted 3 lines

Also, I think m25p80.c is no longer a "self-contained MTD device driver" and should be moved under drivers/mtd/spi-nor/.

If we move the m25p80.c to the drivers/mtd/spi-nor/, we also need to move other drivers too.

thanks
Huang Shijie

#21 lore raw

PATCH v5 5/8 mtd: m25p80: use the SPI nor framework

Quoted 14 lines · Huang Shijie
Quoted 13 lines · Brian Norris

drivers/mtd/devices/Kconfig config MTD_DATAFLASH_OTP · line 86

Quoted 7 lines

config MTD_M25P80

drivers/mtd/devices/Kconfig +1 -1
 	tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
-	depends on SPI_MASTER
+	depends on SPI_MASTER && MTD_SPI_NOR_BASE
 	help
 	  This enables access to most modern SPI flash chips, used for
 	  program and data storage.   Series supported include Atmel AT26DF,

Now that M25P80 depends on MTD_SPI_NOR_BASE (which I proposed to rename to MTD_SPI_NOR a few hours ago), shouldn't we be updating everybody's defconfigs? As it stands, the defconfigs that use M25P80 will just drop it silently when built.

Quoted 5 lines
Quoted 2 lines

And if so, I'm not sure how this should be handled. Should defconfig patches be sent to their respective arch maintainers?

IMHO, we should create a patch or several patches to add the MTD_SPI_NOR to the defconfigs.

Yeah, I think that's best.

Quoted 1 line

Another method is to select the MTD_SPI_NOR which we enable the MTD.

I'm pretty sure it's a rule that drivers should not 'select' subsystems.

Quoted 4 lines
Quoted 2 lines

Also, I think m25p80.c is no longer a "self-contained MTD device driver" and should be moved under drivers/mtd/spi-nor/.

If we move the m25p80.c to the drivers/mtd/spi-nor/, we also need to move other drivers too.

Which ones? I think it only makes sense to move those that depend on the same core code (i.e., spi-nor.c), or have a close relation to it (so Lee's st_spi_fsm.c is a candidate).

Brian

#22 lore raw

PATCH v5 5/8 mtd: m25p80: use the SPI nor framework

Quoted 4 lines · Brian Norris
Quoted 2 lines

IMHO, we should create a patch or several patches to add the MTD_SPI_NOR to the defconfigs.

Yeah, I think that's best.

I will create a patch when i back to office.
Or you can create it yourself.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines

Also, I think m25p80.c is no longer a "self-contained MTD device driver" and should be moved under drivers/mtd/spi-nor/.

If we move the m25p80.c to the drivers/mtd/spi-nor/, we also need to move other drivers too.

Which ones? I think it only makes sense to move those that depend on the same core code (i.e., spi-nor.c), or have a close relation to it (so Lee's st_spi_fsm.c is a candidate).

yes, i mean the st_spi_fsm.c and other drivers which will use the spi-nor core code in the future.

thanks
Huang Shijie

#23 lore raw

PATCH v5 5/8 mtd: m25p80: use the SPI nor framework

Quoted 14 lines · Huang Shijie
Quoted 4 lines · Brian Norris
Quoted 2 lines

IMHO, we should create a patch or several patches to add the MTD_SPI_NOR to the defconfigs.

Yeah, I think that's best.

I will create a patch when i back to office.
Or you can create it yourself.

Quoted 6 lines
Quoted 5 lines
Quoted 3 lines

Also, I think m25p80.c is no longer a "self-contained MTD device driver" and should be moved under drivers/mtd/spi-nor/.

If we move the m25p80.c to the drivers/mtd/spi-nor/, we also need to move other drivers too.

Quoted 6 lines
Quoted 3 lines

Which ones? I think it only makes sense to move those that depend on the same core code (i.e., spi-nor.c), or have a close relation to it (so Lee's st_spi_fsm.c is a candidate).

yes, i mean the st_spi_fsm.c and other drivers which will use the spi-nor core code in the future.

+1 - happy with this.

Signature · 4 lines
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
#24 lore raw

PATCH v5 6/8 mtd: spi-nor: add a helper to find the spi_device_id

In reply to Huang Shijie

Add the spi_nor_match_id() to find the proper spi_device_id with the NOR flash's name in the spi_nor_ids table.

---
 drivers/mtd/spi-nor/spi-nor.c |   12 ++++++++++++
 include/linux/mtd/spi-nor.h   |   12 ++++++++++++
 2 files changed, 24 insertions(+), 0 deletions(-)
drivers/mtd/spi-nor/spi-nor.c +12 -0
index a840e88..6c8fd01 100644
@@ -1081,6 +1081,18 @@ int spi_nor_scan(struct spi_nor *nor, const struct spi_device_id *id,
 	return 0;
 }
 
+const struct spi_device_id *spi_nor_match_id(char *name)
+{
+	const struct spi_device_id *id = spi_nor_ids;
+
+	while (id->name[0]) {
+		if (!strcmp(name, id->name))
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Huang Shijie <[email protected]>");
 MODULE_AUTHOR("Mike Lavender");
include/linux/mtd/spi-nor.h +12 -0
index 16d8409..41dae78 100644
@@ -182,4 +182,16 @@ 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
Signature · 1 line
1.7.2.rc3
#25 lore raw

PATCH v5 7/8 Documentation: add the binding file for Freescale QuadSPI driver

In reply to Huang Shijie

This patch adds the binding file for Freescale QuadSPI driver.

---
 .../devicetree/bindings/mtd/fsl-quadspi.txt        |   35 ++++++++++++++++++++
 1 files changed, 35 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
Documentation/devicetree/bindings/mtd/fsl-quadspi.txt new file +35 -0
new file mode 100644
index 0000000..823d134
@@ -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 at 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 at 0 {
+		....
+	};
+};
Signature · 1 line
1.7.2.rc3
#26 lore raw

PATCH v5 8/8 mtd: spi-nor: Add Freescale QuadSPI driver

In reply to Huang Shijie

(0) What is the QuadSPI controller?

The QuadSPI(Quad Serial Peripheral Interface) acts as an interface to one single or two external serial flash devices, each with up to 4 bidirectional data lines.

(1) The QuadSPI controller is driven by the LUT(Look-up Table) registers. The LUT registers are a look-up-table for sequences of instructions. A valid sequence consists of four LUT registers.

(2) The definition of the LUT register shows below:

---------------------------------------------------
| INSTR1 | PAD1 | OPRND1 | INSTR0 | PAD0 | OPRND0 |
---------------------------------------------------

There are several types of INSTRx, such as:
CMD : the SPI NOR command.
ADDR : the address for the SPI NOR command.
DUMMY : the dummy cycles needed by the SPI NOR command.
....

There are several types of PADx, such as:
PAD1 : use a singe I/O line.
PAD2 : use two I/O lines.
PAD4 : use quad I/O lines.
....

(3) Test this driver with the JFFS2 and UBIFS:

For jffs2:
-------------
#flash_eraseall /dev/mtd0
#mount -t jffs2 /dev/mtdblock0 tmp
#bonnie++ -d tmp -u 0 -s 10 -r 5

For ubifs:
-------------
#flash_eraseall /dev/mtd0
#ubiattach /dev/ubi_ctrl -m 0
#ubimkvol /dev/ubi0 -N test -m
#mount -t ubifs ubi0:test tmp
#bonnie++ -d tmp -u 0 -s 10 -r 5

---
 drivers/mtd/spi-nor/Kconfig       |    6 +
 drivers/mtd/spi-nor/Makefile      |    1 +
 drivers/mtd/spi-nor/fsl-quadspi.c | 1009 +++++++++++++++++++++++++++++++++++++
 3 files changed, 1016 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/spi-nor/fsl-quadspi.c
drivers/mtd/spi-nor/Kconfig +6 -0
index 41591af..64cfc39 100644
@@ -4,3 +4,9 @@ config MTD_SPI_NOR_BASE
 	help
 	  This is the framework for the SPI NOR which can be used by the SPI
 	  device drivers and the SPI-NOR device driver.
+config SPI_FSL_QUADSPI
+	tristate "Freescale Quad SPI controller"
+	depends on ARCH_MXC && MTD_SPI_NOR_BASE
+	help
+	  This enables support for the Quad SPI controller in master mode.
+	  We only connect the NOR to this controller now.
drivers/mtd/spi-nor/Makefile +1 -0
index 7dfe1f9..51f9d8b 100644
@@ -1 +1,2 @@
 obj-$(CONFIG_MTD_SPI_NOR_BASE)	+= spi-nor.o
+obj-$(CONFIG_SPI_FSL_QUADSPI)	+= fsl-quadspi.o
drivers/mtd/spi-nor/fsl-quadspi.c new file +1009 -0

1,009 lines not on this page · raw message

Signature · 1 line
1.7.2.rc3
#27 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

In reply to Huang Shijie
Quoted 43 lines · Huang Shijie

1.) Why add a new framework for SPI NOR?
The SPI-NOR controller such as Freescale's Quadspi controller is working in a different way from the SPI bus. It should knows the NOR commands to find the right LUT sequence. Unfortunately, the current code can not meet this requirement.

2.) How does this patch set do?
This patch set adds a new spi-nor layer.
Before this patch, the layer is like:

                   MTD
         ------------------------
                  m25p80
         ------------------------
	       spi bus driver
         ------------------------
	        SPI NOR chip
   After this patch, the layer is like:
                   MTD
         ------------------------
                  spi-nor
         ------------------------
                  m25p80
         ------------------------
	       spi bus driver
         ------------------------
	       SPI NOR chip

With the spi-nor controller driver(Freescale Quadspi), it looks like:

                   MTD
         ------------------------
                  spi-nor
         ------------------------
                fsl-quadspi
         ------------------------
	       SPI NOR chip

3.) more details
This patch set adds a new data structrue spi_nor{}, clones most the
common code to spi-nor.c. Add spi_nor_xfer_cfg {} for the fundamental
primitives: read_xfer/write_xfer.

Quoted 7 lines

Make the m25p80.c/fsl_quaspi.c use the new APIs.

4.) Change log:
v4 --> v5:
[1] rebase the patch set on the l2-mtd tree.
[2] fixed bugs in the fsl-quadspi driver.
[3] tested the fsl-quadspi with imx6sx board.

It's starting to take a really nice shape, I really like it :)

#28 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

In reply to Huang Shijie

On Mon, Feb 24, 2014 at 06:37:34PM +0800, Huang Shijie wrote:
Hi Brian:

Could you please review this patch set? I really hope it can be merged as soon as possibel. But now, the patch set has stalled for a long time.

Since you have pushed the patches for m25p80.c, i have to rebase this patch set again and again...

thanks
Huang Shijie

#29 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

Quoted 3 lines · Huang Shijie

On Mon, Feb 24, 2014 at 06:37:34PM +0800, Huang Shijie wrote:
Could you please review this patch set? I really hope it can be merged as soon as possibel. But now, the patch set has stalled for a long time.

I'm sorry for the delay. Others are in the same position of delay (e.g., Lee's SPI-NOR driver), because I simply have not had the time to really devote this the attention it deserves, especially considering how it rearchitects such an important, commonly-used driver.

Nonetheless, I will try to give your patches some more attention soon.

Quoted 2 lines

Since you have pushed the patches for m25p80.c, i have to rebase this patch set again and again...

You mean locally? I promise you, I can handle rebasing on my reviewing end for small one-line conflicts like new chip IDs, so don't rebase and resend simply for that. Unless I find substantive things that you should change, I'd only expect at most one more rebase to be necessary.

Brian

#30 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

+ Lee, Marek

Hi Huang,

Quoted 12 lines · Brian Norris
Quoted 3 lines · Huang Shijie

On Mon, Feb 24, 2014 at 06:37:34PM +0800, Huang Shijie wrote:
Could you please review this patch set? I really hope it can be merged as soon as possibel. But now, the patch set has stalled for a long time.

I'm sorry for the delay. Others are in the same position of delay (e.g., Lee's SPI-NOR driver), because I simply have not had the time to really devote this the attention it deserves, especially considering how it rearchitects such an important, commonly-used driver.

Nonetheless, I will try to give your patches some more attention soon.

Quoted 7 lines
Quoted 2 lines

Since you have pushed the patches for m25p80.c, i have to rebase this patch set again and again...

You mean locally? I promise you, I can handle rebasing on my reviewing end for small one-line conflicts like new chip IDs, so don't rebase and resend simply for that. Unless I find substantive things that you should change, I'd only expect at most one more rebase to be necessary.

Unfortunately, I did not get the time to review and test this sufficiently for the 3.15 merge window, but to avoid further delay, I've queued it up in l2-mtd.git [1] under its own branch for now. I rebased it myself, to accommodate for changes to m25p80.c since your submission. Please take a look at the branch.

If all goes well, I should merge the spinor branch into master some time after the 3.15 merge window closes, so it will get linux-next testing. For any additional comments/corrections (I have a few), please just submit patches on top, rather than resending the whole series.

Marek, what was your opinion on this series? Last response I saw was "looking good"; is that an Ack? Or did you have any more gating requests?

Personally, I think it looks ready enough, and it's better to merge this than not. I was able to test with my platforms, with no regression.

Lee, I have a few patches that do some trivial unification of your ST SPI FSM driver and the SPI-NOR framework. I plan to send these out soon, and I will begin applying any ST-FSM patches to the same spinor branch along with Huang's code. I hope that you can spend some effort on improving integration within the next few months in preparation for 3.16, when I expect this to all go upstream.

Thanks,
Brian

[1] branch spinor, at http://git.infradead.org/l2-mtd.git

#31 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

? 2014?04?09? 11:36, Brian Norris ??:

Quoted 2 lines

For any additional comments/corrections (I have a few), please just submit patches on top, rather than resending the whole series.

thanks a lot!

i am happy that we can add more features on base of the spi nor framework.

Huang Shijie

#32 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

In reply to Brian Norris
Quoted 37 lines · Brian Norris

+ Lee, Marek

Hi Huang,

Quoted 13 lines · Brian Norris
Quoted 4 lines · Huang Shijie

On Mon, Feb 24, 2014 at 06:37:34PM +0800, Huang Shijie wrote:
Could you please review this patch set? I really hope it can be merged as soon as possibel. But now, the patch set has stalled for a long time.

I'm sorry for the delay. Others are in the same position of delay (e.g., Lee's SPI-NOR driver), because I simply have not had the time to really devote this the attention it deserves, especially considering how it rearchitects such an important, commonly-used driver.

Nonetheless, I will try to give your patches some more attention soon.

Quoted 7 lines
Quoted 2 lines

Since you have pushed the patches for m25p80.c, i have to rebase this patch set again and again...

You mean locally? I promise you, I can handle rebasing on my reviewing end for small one-line conflicts like new chip IDs, so don't rebase and resend simply for that. Unless I find substantive things that you should change, I'd only expect at most one more rebase to be necessary.

Unfortunately, I did not get the time to review and test this sufficiently for the 3.15 merge window, but to avoid further delay, I've queued it up in l2-mtd.git [1] under its own branch for now. I rebased it myself, to accommodate for changes to m25p80.c since your submission. Please take a look at the branch.

If all goes well, I should merge the spinor branch into master some time after the 3.15 merge window closes, so it will get linux-next testing. For any additional comments/corrections (I have a few), please just submit patches on top, rather than resending the whole series.

Quoted 6 lines

Marek, what was your opinion on this series? Last response I saw was "looking good"; is that an Ack? Or did you have any more gating requests?

Personally, I think it looks ready enough, and it's better to merge this than not. I was able to test with my platforms, with no regression.

It looked OK to me, so that's an:

Thanks for watching over this!

Best regards,
Marek Vasut

#33 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

In reply to Brian Norris
Quoted 43 lines

+ Lee, Marek

Hi Huang,

Quoted 12 lines · Brian Norris
Quoted 3 lines · Huang Shijie

On Mon, Feb 24, 2014 at 06:37:34PM +0800, Huang Shijie wrote:
Could you please review this patch set? I really hope it can be merged as soon as possibel. But now, the patch set has stalled for a long time.

I'm sorry for the delay. Others are in the same position of delay (e.g., Lee's SPI-NOR driver), because I simply have not had the time to really devote this the attention it deserves, especially considering how it rearchitects such an important, commonly-used driver.

Nonetheless, I will try to give your patches some more attention soon.

Quoted 7 lines
Quoted 2 lines

Since you have pushed the patches for m25p80.c, i have to rebase this patch set again and again...

You mean locally? I promise you, I can handle rebasing on my reviewing end for small one-line conflicts like new chip IDs, so don't rebase and resend simply for that. Unless I find substantive things that you should change, I'd only expect at most one more rebase to be necessary.

Unfortunately, I did not get the time to review and test this sufficiently for the 3.15 merge window, but to avoid further delay, I've queued it up in l2-mtd.git [1] under its own branch for now. I rebased it myself, to accommodate for changes to m25p80.c since your submission. Please take a look at the branch.

If all goes well, I should merge the spinor branch into master some time after the 3.15 merge window closes, so it will get linux-next testing. For any additional comments/corrections (I have a few), please just submit patches on top, rather than resending the whole series.

Marek, what was your opinion on this series? Last response I saw was "looking good"; is that an Ack? Or did you have any more gating requests?

Personally, I think it looks ready enough, and it's better to merge this than not. I was able to test with my platforms, with no regression.

Quoted 6 lines

Lee, I have a few patches that do some trivial unification of your ST SPI FSM driver and the SPI-NOR framework. I plan to send these out soon, and I will begin applying any ST-FSM patches to the same spinor branch along with Huang's code. I hope that you can spend some effort on improving integration within the next few months in preparation for 3.16, when I expect this to all go upstream.

I certainly plan to. Although, I'm up to my neck in the ST BCH NAND driver at the moment. Trying to re-spin something that'll be accepted i.e. more intensive use of the framework.

Quick question, is there a chat medium for MTD? Somewhere for quick-fire questions i.e. an IRC channel or similar?

Quoted 1 line

[1] branch spinor, at http://git.infradead.org/l2-mtd.git

Signature · 4 lines
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
#34 lore raw

v5 cover mtd: spi-nor: add a new framework for SPI NOR

Quoted 2 lines · Lee Jones

Quick question, is there a chat medium for MTD? Somewhere for quick-fire questions i.e. an IRC channel or similar?

OFTC #mtd channel. It's noted at the bottom here:

http://www.linux-mtd.infradead.org/mail.html

Many of us lurk there, but quickness varies :)

Brian