Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1378115 > unrolled thread
| Started by | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| First post | 2016-04-13 19:30 +0200 |
| Last post | 2016-04-13 19:30 +0200 |
| Articles | 8 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH RFC 0/8] mtd: spi-nor: fix support of Quad SPI memories Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
[PATCH RFC 2/8] mtd: spi-nor: allow different flash_info entries to share the same JEDEC ID Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
[PATCH RFC 7/8] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
[PATCH RFC 5/8] mtd: m25p80: add support of dual and quad spi protocols to all commands Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
[PATCH RFC 1/8] mtd: spi-nor: add an alternative method to support memory >16MiB Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
[PATCH RFC 3/8] mtd: spi-nor: add entry for Macronix mx25l25673g Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
[PATCH RFC 6/8] mtd: spi-nor: add support for Micron Dual and Quad SPI memories Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
[PATCH RFC 4/8] mtd: spi-nor: fix support of Dual (x-y-2) and Quad (x-y-4) SPI protocols Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-04-13 19:30 +0200
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 0/8] mtd: spi-nor: fix support of Quad SPI memories |
| Message-ID | <rnwM2-4Jb-9@gated-at.bofh.it> |
Hi all,
this series is RFC but has already been tested on a sama5d2x xplained
board with the Atmel QSPI controller + Micron n25q128a13:
compatible = "micron,n25q128a13", "jedec,spi-nor";
This first 3 patches of the series are stable and have already been
submitted to linux-mtd. They are required as a base for the later
patches.
Support of Micron memories has been implemented as an example, other
memory entries should be updated as needed in the spi_nor_ids[] table.
This new way to support Quad SPI memories is inspired by the JEDEC
SFDP standard. However SFDP tables are not provided by all memories and
some of them badly implement the standard. Also the standard itself
can tell whether the memory supports the 2-2-2 mode but doesn't provide
the procedure to enter/leave this mode as provided for the 4-4-4 mode.
Please note that some commit messages are missing but a review might be
really helpfull! :)
Almost all the actual rework is done in patch 4.
Best regards,
Cyrille
Cyrille Pitchen (8):
mtd: spi-nor: add an alternative method to support memory >16MiB
mtd: spi-nor: allow different flash_info entries to share the same
JEDEC ID
mtd: spi-nor: add entry for Macronix mx25l25673g
mtd: spi-nor: fix support of Dual (x-y-2) and Quad (x-y-4) SPI
protocols
mtd: m25p80: add support of dual and quad spi protocols to all
commands
mtd: spi-nor: add support for Micron Dual and Quad SPI memories
Documentation: atmel-quadspi: add binding file for Atmel QSPI driver
mtd: atmel-quadspi: add driver for Atmel QSPI controller
.../devicetree/bindings/mtd/atmel-quadspi.txt | 32 +
drivers/mtd/devices/m25p80.c | 212 ++++--
drivers/mtd/devices/serial_flash_cmds.h | 7 -
drivers/mtd/devices/st_spi_fsm.c | 28 +-
drivers/mtd/spi-nor/Kconfig | 9 +
drivers/mtd/spi-nor/Makefile | 1 +
drivers/mtd/spi-nor/atmel-quadspi.c | 812 +++++++++++++++++++++
drivers/mtd/spi-nor/fsl-quadspi.c | 9 +-
drivers/mtd/spi-nor/mtk-quadspi.c | 17 +-
drivers/mtd/spi-nor/nxp-spifi.c | 22 +-
drivers/mtd/spi-nor/spi-nor.c | 635 +++++++++++++---
include/linux/mtd/spi-nor.h | 160 +++-
12 files changed, 1750 insertions(+), 194 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
create mode 100644 drivers/mtd/spi-nor/atmel-quadspi.c
--
1.8.2.2
[toc] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 2/8] mtd: spi-nor: allow different flash_info entries to share the same JEDEC ID |
| Message-ID | <rnwM2-4Jb-11@gated-at.bofh.it> |
| In reply to | #1378115 |
Some SPI memories like Macronix MX25L25635E and MX25L25673G share the very
same JEDEC ID with no ext ID but provide different hardware capabilities.
For instance, the 35E revision doesn't support the dedicated 4byte address
opcodes for (Fast) Read, Page Program and Sector Erase operations whereas
the 73G does.
The 'name' argument of spi_nor_scan() is used by spi_nor_match_id() to look
the right entry up. Later, spi_nor_read_id() is called to check whether the
actual JEDEC ID read from the hardware matches the one associated with the
struct flash_info pointer returned by spi_nor_match_id().
However this check was done by comparing the jinfo and info struct
flash_info pointers. Since these pointer values might be different now,
the updated code checks the values of the id_len and id fields, which
should be the same for all entries associated to the same JEDEC ID.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/spi-nor/spi-nor.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 4606eac237fe..aac291a590e1 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1359,7 +1359,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
jinfo = spi_nor_read_id(nor);
if (IS_ERR(jinfo)) {
return PTR_ERR(jinfo);
- } else if (jinfo != info) {
+ } else if (jinfo->id_len != info->id_len ||
+ memcmp(jinfo->id, info->id, info->id_len)) {
/*
* JEDEC knows better, so overwrite platform ID. We
* can't trust partitions any longer, but we'll let
--
1.8.2.2
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 7/8] Documentation: atmel-quadspi: add binding file for Atmel QSPI driver |
| Message-ID | <rnwM2-4Jb-13@gated-at.bofh.it> |
| In reply to | #1378115 |
This patch documents the DT bindings for the driver of the Atmel QSPI
controller embedded inside sama5d2x SoCs.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
.../devicetree/bindings/mtd/atmel-quadspi.txt | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
diff --git a/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt b/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
new file mode 100644
index 000000000000..489807005eda
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt
@@ -0,0 +1,32 @@
+* Atmel Quad Serial Peripheral Interface (QSPI)
+
+Required properties:
+- compatible: Should be "atmel,sama5d2-qspi".
+- reg: Should contain the locations and lengths of the base registers
+ and the mapped memory.
+- reg-names: Should contain the resource reg names:
+ - qspi_base: configuration register address space
+ - qspi_mmap: memory mapped address space
+- interrupts: Should contain the interrupt for the device.
+- clocks: The phandle of the clock needed by the QSPI controller.
+- #address-cells: Should be <1>.
+- #size-cells: Should be <0>.
+
+Example:
+
+spi@f0020000 {
+ compatible = "atmel,sama5d2-qspi";
+ reg = <0xf0020000 0x100>, <0xd0000000 0x8000000>;
+ reg-names = "qspi_base", "qspi_mmap";
+ interrupts = <52 IRQ_TYPE_LEVEL_HIGH 7>;
+ clocks = <&spi0_clk>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi0_default>;
+ status = "okay";
+
+ m25p80@0 {
+ ...
+ };
+};
--
1.8.2.2
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 5/8] mtd: m25p80: add support of dual and quad spi protocols to all commands |
| Message-ID | <rnwM3-4Jb-25@gated-at.bofh.it> |
| In reply to | #1378115 |
Before this patch, m25p80_read() supported few SPI protocols:
- regular SPI 1-1-1
- SPI Dual Output 1-1-2
- SPI Quad Output 1-1-4
On the other hand, all other m25p80_*() hooks only supported SPI 1-1-1.
However once their Quad mode enabled, Micron and Macronix spi-nor memories
expect all commands to use the SPI 4-4-4 protocol.
Also, once their Dual mode enabled, Micron spi-nor memories expect all
commands to use the SPI-2-2-2 protocol.
So this patch adds support to all currently existing SPI protocols to
cover as many protocols as possible.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/devices/m25p80.c | 200 +++++++++++++++++++++++++++++++++----------
1 file changed, 155 insertions(+), 45 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 12112f7ae1a4..d4a3bc0e4c3f 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -27,22 +27,64 @@
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
-#define MAX_CMD_SIZE 6
+#define MAX_CMD_SIZE 16
struct m25p {
struct spi_device *spi;
struct spi_nor spi_nor;
u8 command[MAX_CMD_SIZE];
};
+static inline int m25p80_proto2nbits(enum spi_nor_protocol proto,
+ unsigned *code_nbits,
+ unsigned *addr_nbits,
+ unsigned *data_nbits)
+{
+ if (code_nbits)
+ *code_nbits = SNOR_PROTO_CODE_FROM_PROTO(proto);
+ if (addr_nbits)
+ *addr_nbits = SNOR_PROTO_ADDR_FROM_PROTO(proto);
+ if (data_nbits)
+ *data_nbits = SNOR_PROTO_DATA_FROM_PROTO(proto);
+
+ return 0;
+}
+
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;
+ unsigned code_nbits, data_nbits;
+ struct spi_transfer xfers[2];
int ret;
- ret = spi_write_then_read(spi, &code, 1, val, len);
+ /* Check the total length of command op code and data. */
+ if (len + 1 > MAX_CMD_SIZE)
+ return -EINVAL;
+
+ /* Get transfer protocols (addr_nbits is not relevant here). */
+ ret = m25p80_proto2nbits(nor->reg_proto,
+ &code_nbits, NULL, &data_nbits);
+ if (ret < 0)
+ return ret;
+
+ /* Set up transfers. */
+ memset(xfers, 0, sizeof(xfers));
+
+ flash->command[0] = code;
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
+
+ xfers[1].len = len;
+ xfers[1].rx_buf = &flash->command[1];
+ xfers[1].rx_nbits = data_nbits;
+
+ /* Process command. */
+ ret = spi_sync_transfer(spi, xfers, 2);
if (ret < 0)
dev_err(&spi->dev, "error %d reading %x\n", ret, code);
+ else
+ memcpy(val, &flash->command[1], len);
return ret;
}
@@ -65,12 +107,42 @@ static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
+ unsigned code_nbits, data_nbits, num_xfers = 1;
+ struct spi_transfer xfers[2];
+ int ret;
+
+ /* Check the total length of command op code and data. */
+ if (buf && (len + 1 > MAX_CMD_SIZE))
+ return -EINVAL;
+
+ /* Get transfer protocols (addr_nbits is not relevant here). */
+ ret = m25p80_proto2nbits(nor->reg_proto,
+ &code_nbits, NULL, &data_nbits);
+ if (ret < 0)
+ return ret;
+
+ /* Set up transfer(s). */
+ memset(xfers, 0, sizeof(xfers));
flash->command[0] = opcode;
- if (buf)
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
+
+ if (buf) {
memcpy(&flash->command[1], buf, len);
+ if (data_nbits == code_nbits) {
+ xfers[0].len += len;
+ } else {
+ xfers[1].len = len;
+ xfers[1].tx_buf = &flash->command[1];
+ xfers[1].tx_nbits = data_nbits;
+ num_xfers++;
+ }
+ }
- return spi_write(spi, flash->command, len + 1);
+ /* Process command. */
+ return spi_sync_transfer(spi, xfers, num_xfers);
}
static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -78,43 +150,54 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
- struct spi_transfer t[2] = {};
+ unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1;
+ struct spi_transfer xfers[3];
struct spi_message m;
- int cmd_sz = m25p_cmdsz(nor);
-
- spi_message_init(&m);
+ int ret, cmd_sz = m25p_cmdsz(nor);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
cmd_sz = 1;
- flash->command[0] = nor->program_opcode;
- m25p_addr2cmd(nor, to, flash->command);
+ /* Get transfer protocols. */
+ ret = m25p80_proto2nbits(nor->write_proto,
+ &code_nbits, &addr_nbits, &data_nbits);
+ if (ret < 0) {
+ *retlen = 0;
+ return;
+ }
- t[0].tx_buf = flash->command;
- t[0].len = cmd_sz;
- spi_message_add_tail(&t[0], &m);
+ /* Set up transfers. */
+ memset(xfers, 0, sizeof(xfers));
+
+ flash->command[0] = nor->program_opcode;
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
+
+ if (cmd_sz > 1) {
+ m25p_addr2cmd(nor, to, flash->command);
+ if (addr_nbits == code_nbits) {
+ xfers[0].len += nor->addr_width;
+ } else {
+ xfers[1].len = nor->addr_width;
+ xfers[1].tx_buf = &flash->command[1];
+ xfers[1].tx_nbits = addr_nbits;
+ num_xfers++;
+ }
+ }
- t[1].tx_buf = buf;
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ xfers[num_xfers].len = len;
+ xfers[num_xfers].tx_buf = buf;
+ xfers[num_xfers].tx_nbits = data_nbits;
+ num_xfers++;
+ /* Process command. */
+ spi_message_init_with_transfers(&m, xfers, num_xfers);
spi_sync(spi, &m);
*retlen += m.actual_length - cmd_sz;
}
-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
-{
- switch (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto)) {
- case 2:
- return 2;
- case 4:
- return 4;
- default:
- return 0;
- }
-}
-
/*
* Read an address range from the nor chip. The address range
* may be any size provided it is within the physical boundaries.
@@ -124,13 +207,24 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
- struct spi_transfer t[2];
- struct spi_message m;
+ unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1;
unsigned int dummy = nor->read_dummy;
+ struct spi_transfer xfers[3];
+ struct spi_message m;
+ int ret;
+
+ /* Get transfer protocols. */
+ ret = m25p80_proto2nbits(nor->read_proto,
+ &code_nbits, &addr_nbits, &data_nbits);
+ if (ret < 0) {
+ *retlen = 0;
+ return ret;
+ }
/* convert the dummy cycles to the number of bytes */
- dummy /= 8;
+ dummy = (dummy * addr_nbits) / 8;
+ /* Use the SPI flash API if supported. */
if (spi_flash_read_supported(spi)) {
struct spi_flash_read_message msg;
int ret;
@@ -143,31 +237,47 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
msg.read_opcode = nor->read_opcode;
msg.addr_width = nor->addr_width;
msg.dummy_bytes = dummy;
- /* TODO: Support other combinations */
- msg.opcode_nbits = SPI_NBITS_SINGLE;
- msg.addr_nbits = SPI_NBITS_SINGLE;
- msg.data_nbits = m25p80_rx_nbits(nor);
+ msg.opcode_nbits = code_nbits;
+ msg.addr_nbits = addr_nbits;
+ msg.data_nbits = data_nbits;
ret = spi_flash_read(spi, &msg);
*retlen = msg.retlen;
return ret;
}
- spi_message_init(&m);
- memset(t, 0, (sizeof t));
+ /* Set up transfers. */
+ memset(xfers, 0, sizeof(xfers));
flash->command[0] = nor->read_opcode;
- m25p_addr2cmd(nor, from, flash->command);
+ xfers[0].len = 1;
+ xfers[0].tx_buf = flash->command;
+ xfers[0].tx_nbits = code_nbits;
- t[0].tx_buf = flash->command;
- t[0].len = m25p_cmdsz(nor) + dummy;
- spi_message_add_tail(&t[0], &m);
+ m25p_addr2cmd(nor, from, flash->command);
+ /*
+ * Clear all dummy/mode cycle bits to avoid sending some manufacturer
+ * specific pattern, which might make the memory enter its Continuous
+ * Read mode by mistake.
+ */
+ memset(flash->command + 1 + nor->addr_width, 0, dummy);
+
+ if (addr_nbits == code_nbits) {
+ xfers[0].len += nor->addr_width + dummy;
+ } else {
+ xfers[1].len = nor->addr_width + dummy;
+ xfers[1].tx_buf = &flash->command[1];
+ xfers[1].tx_nbits = addr_nbits;
+ num_xfers++;
+ }
- t[1].rx_buf = buf;
- t[1].rx_nbits = m25p80_rx_nbits(nor);
- t[1].len = len;
- spi_message_add_tail(&t[1], &m);
+ xfers[num_xfers].len = len;
+ xfers[num_xfers].rx_buf = buf;
+ xfers[num_xfers].rx_nbits = data_nbits;
+ num_xfers++;
+ /* Process command. */
+ spi_message_init_with_transfers(&m, xfers, num_xfers);
spi_sync(spi, &m);
*retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
--
1.8.2.2
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 1/8] mtd: spi-nor: add an alternative method to support memory >16MiB |
| Message-ID | <rnwM3-4Jb-29@gated-at.bofh.it> |
| In reply to | #1378115 |
This patch provides an alternative mean to support memory above 16MiB
(128Mib) by replacing 3byte address op codes by their associated 4byte
address versions.
Using the dedicated 4byte address op codes doesn't change the internal
state of the SPI NOR memory as opposed to using other means such as
updating a Base Address Register (BAR) and sending command to enter/leave
the 4byte mode.
Hence when a CPU reset occurs, early bootloaders don't need to be aware
of BAR value or 4byte mode being enabled: they can still access the first
16MiB of the SPI NOR memory using the regular 3byte address op codes.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/devices/serial_flash_cmds.h | 7 ---
drivers/mtd/devices/st_spi_fsm.c | 28 ++++-----
drivers/mtd/spi-nor/spi-nor.c | 103 +++++++++++++++++++++++++-------
include/linux/mtd/spi-nor.h | 21 +++++--
4 files changed, 111 insertions(+), 48 deletions(-)
diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h
index f59a125295d0..8b81e15105dd 100644
--- a/drivers/mtd/devices/serial_flash_cmds.h
+++ b/drivers/mtd/devices/serial_flash_cmds.h
@@ -18,19 +18,12 @@
#define SPINOR_OP_RDVCR 0x85
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
-#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
-#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */
-
#define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
#define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
#define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
#define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
#define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
-/* READ commands with 32-bit addressing */
-#define SPINOR_OP_READ4_1_2_2 0xbc
-#define SPINOR_OP_READ4_1_4_4 0xec
-
/* Configuration flags */
#define FLASH_FLAG_SINGLE 0x000000ff
#define FLASH_FLAG_READ_WRITE 0x00000001
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
index 5454b4113589..804313a33f2b 100644
--- a/drivers/mtd/devices/st_spi_fsm.c
+++ b/drivers/mtd/devices/st_spi_fsm.c
@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {
* - 'FAST' variants configured for 8 dummy cycles (see note above.)
*/
static struct seq_rw_config n25q_read4_configs[] = {
- {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
- {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
- {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
- {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+ {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
};
/*
@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
* entering a state that is incompatible with the SPIBoot Controller.
*/
static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
- {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
- {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
- {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
- {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
- {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
- {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
- {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+ {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 2, 4},
+ {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+ {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 4, 0},
+ {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+ {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
+ {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
+ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
};
static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 157841dc3e99..4606eac237fe 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -75,6 +75,10 @@ struct flash_info {
* bit. Must be used with
* SPI_NOR_HAS_LOCK.
*/
+#define SPI_NOR_4B_OPCODES BIT(10) /*
+ * Use dedicated 4byte address op codes
+ * to support memory size above 128Mib.
+ */
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -188,6 +192,80 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
return mtd->priv;
}
+
+struct spi_nor_address_entry {
+ u8 src_opcode;
+ u8 dst_opcode;
+};
+
+static u8 spi_nor_convert_opcode(u8 opcode,
+ const struct spi_nor_address_entry *entries,
+ size_t num_entries)
+{
+ int min, max;
+
+ min = 0;
+ max = num_entries - 1;
+ while (min <= max) {
+ int mid = (min + max) >> 1;
+ const struct spi_nor_address_entry *entry = &entries[mid];
+
+ if (opcode == entry->src_opcode)
+ return entry->dst_opcode;
+
+ if (opcode < entry->src_opcode)
+ max = mid - 1;
+ else
+ min = mid + 1;
+ }
+
+ /* No conversion found */
+ return opcode;
+}
+
+static u8 spi_nor_3to4_opcode(u8 opcode)
+{
+ /* MUST be sorted by 3byte opcode */
+#define ENTRY_3TO4(_opcode) { _opcode, _opcode##_4B }
+ static const struct spi_nor_address_entry spi_nor_3to4_table[] = {
+ ENTRY_3TO4(SPINOR_OP_PP), /* 0x02 */
+ ENTRY_3TO4(SPINOR_OP_READ), /* 0x03 */
+ ENTRY_3TO4(SPINOR_OP_READ_FAST), /* 0x0b */
+ ENTRY_3TO4(SPINOR_OP_BE_4K), /* 0x20 */
+ ENTRY_3TO4(SPINOR_OP_PP_1_1_4), /* 0x32 */
+ ENTRY_3TO4(SPINOR_OP_PP_1_4_4), /* 0x38 */
+ ENTRY_3TO4(SPINOR_OP_READ_1_1_2), /* 0x3b */
+ ENTRY_3TO4(SPINOR_OP_READ_1_1_4), /* 0x6b */
+ ENTRY_3TO4(SPINOR_OP_READ_1_2_2), /* 0xbb */
+ ENTRY_3TO4(SPINOR_OP_SE), /* 0xd8 */
+ ENTRY_3TO4(SPINOR_OP_READ_1_4_4), /* 0xeb */
+ };
+#undef ENTRY_3TO4
+
+ return spi_nor_convert_opcode(opcode, spi_nor_3to4_table,
+ ARRAY_SIZE(spi_nor_3to4_table));
+}
+
+static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
+ const struct flash_info *info)
+{
+ /* Do some manufacturer fixups first */
+ switch (JEDEC_MFR(info)) {
+ case SNOR_MFR_SPANSION:
+ /* No small sector erase for 4-byte command set */
+ nor->erase_opcode = SPINOR_OP_SE;
+ nor->mtd.erasesize = info->sector_size;
+ break;
+
+ default:
+ break;
+ }
+
+ nor->read_opcode = spi_nor_3to4_opcode(nor->read_opcode);
+ nor->program_opcode = spi_nor_3to4_opcode(nor->program_opcode);
+ nor->erase_opcode = spi_nor_3to4_opcode(nor->erase_opcode);
+}
+
/* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
int enable)
@@ -1421,27 +1499,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4;
- if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
- /* Dedicated 4-byte command set */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ4_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ4_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ4_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ4;
- break;
- }
- nor->program_opcode = SPINOR_OP_PP_4B;
- /* No small sector erase for 4-byte command set */
- nor->erase_opcode = SPINOR_OP_SE_4B;
- mtd->erasesize = info->sector_size;
- } else
+ if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+ info->flags & SPI_NOR_4B_OPCODES)
+ spi_nor_set_4byte_opcodes(nor, info);
+ else
set_4byte(nor, info, 1);
} else {
nor->addr_width = 3;
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 3c36113a88e1..a91e95756a48 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -42,9 +42,13 @@
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
-#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */
+#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
@@ -55,11 +59,16 @@
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
-#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */
-#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
-#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
-#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
+#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
+#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
+#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
+#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
+#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
+#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
+#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */
+#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */
+#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
/* Used for SST flashes only. */
--
1.8.2.2
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 3/8] mtd: spi-nor: add entry for Macronix mx25l25673g |
| Message-ID | <rnwM3-4Jb-31@gated-at.bofh.it> |
| In reply to | #1378115 |
The Macronix MX25L25635E and MX25L25673G share the same JEDEC ID, C22019,
with no extended ID to differenciate them at runtime.
However, the 73G supports dedicated 4byte address op code for (Fast) Read,
Page Program and Sectore Erase Operation whereas the 35E doesn't.
So this patch adds a specific entry to support the 73G revision.
For backward compatibility purpose, this new entry is inserted AFTER the
legacy "mx25l25635e" entry so this later entry is still the first one
found hence returned by spi_nor_read_id().
Then using the new entry requires the "mx25l25673g" string to be given
as the 'name' argument of spi_nor_scan().
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/spi-nor/spi-nor.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index aac291a590e1..54115aface9f 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -933,6 +933,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
+ { "mx25l25673g", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
--
1.8.2.2
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 6/8] mtd: spi-nor: add support for Micron Dual and Quad SPI memories |
| Message-ID | <rnwM3-4Jb-35@gated-at.bofh.it> |
| In reply to | #1378115 |
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/spi-nor/spi-nor.c | 275 ++++++++++++++++++++++++++++++++++++++++--
include/linux/mtd/spi-nor.h | 1 +
2 files changed, 265 insertions(+), 11 deletions(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 3573cc708b16..260ba5b9d010 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -807,6 +807,214 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
return ret;
}
+struct spi_nor_read_id {
+ u32 id_mode;
+ u8 opcode;
+};
+
+static int spi_nor_read_id_multi_io(struct spi_nor *nor,
+ u8 *id, size_t id_len,
+ u32 id_modes,
+ const struct spi_nor_read_id *configs,
+ size_t num_configs,
+ u8 mfr_id);
+
+static int spi_nor_micron_read_id(struct spi_nor *nor, u8 *id, size_t id_len,
+ u32 id_modes)
+{
+ static const struct spi_nor_read_id configs[] = {
+ {SNOR_MODE_1_1_1, SPINOR_OP_RDID},
+ {SNOR_MODE_4_4_4, SPINOR_OP_MIO_RDID},
+ {SNOR_MODE_2_2_2, SPINOR_OP_MIO_RDID},
+ };
+
+ return spi_nor_read_id_multi_io(nor, id, id_len, id_modes,
+ configs, ARRAY_SIZE(configs),
+ SNOR_MFR_MICRON);
+}
+
+static int micron_set_protocol(struct spi_nor *nor, u8 mask, u8 val,
+ enum spi_nor_protocol proto)
+{
+ u8 evcr;
+ int ret;
+
+ /* Read the Enhanced Volatile Configuration Register (EVCR). */
+ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while reading EVCR register\n");
+ return ret;
+ }
+
+ /* Check whether we need to update the protocol bits. */
+ if ((evcr & mask) == val)
+ return 0;
+
+ /* Set EVCR protocol bits. */
+ write_enable(nor);
+ evcr = (evcr & ~mask) | val;
+ ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, &evcr, 1);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while writing EVCR register\n");
+ return ret;
+ }
+
+ /* Switch reg protocol now before accessing any other registers. */
+ nor->reg_proto = proto;
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret)
+ return ret;
+
+ /* Read EVCR and check it. */
+ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1);
+ if (ret < 0 || (evcr & mask) != val) {
+ dev_err(nor->dev, "Micron EVCR protocol bits not updated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int micron_set_quad_mode(struct spi_nor *nor)
+{
+ int ret;
+
+ /* Clear Quad bit to select the Quad SPI mode */
+ ret = micron_set_protocol(nor,
+ EVCR_QUAD_EN_MICRON, 0,
+ SNOR_PROTO_4_4_4);
+ if (ret) {
+ dev_err(nor->dev, "Failed to set Micron Quad SPI mode\n");
+ return ret;
+ }
+
+ nor->read_proto = SNOR_PROTO_4_4_4;
+ nor->write_proto = SNOR_PROTO_4_4_4;
+ nor->erase_proto = SNOR_PROTO_4_4_4;
+ return 0;
+}
+
+static int micron_set_dual_mode(struct spi_nor *nor)
+{
+ int ret;
+
+ /* Set Quad/Dual bits to 10 to select the Dual SPI mode */
+ ret = micron_set_protocol(nor,
+ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+ EVCR_QUAD_EN_MICRON,
+ SNOR_PROTO_2_2_2);
+ if (ret) {
+ dev_err(nor->dev, "Failed to set Micron Dual SPI mode\n");
+ return ret;
+ }
+
+ nor->read_proto = SNOR_PROTO_2_2_2;
+ nor->write_proto = SNOR_PROTO_2_2_2;
+ nor->erase_proto = SNOR_PROTO_2_2_2;
+ return 0;
+}
+
+static int micron_set_extended_spi_mode(struct spi_nor *nor)
+{
+ int ret;
+
+ /* Set Quad/Dual bits to 11 to select the Extended SPI mode */
+ ret = micron_set_protocol(nor,
+ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON,
+ SNOR_PROTO_1_1_1);
+ if (ret) {
+ dev_err(nor->dev, "Failed to set Micron Extended SPI mode\n");
+ return ret;
+ }
+
+ nor->write_proto = SNOR_PROTO_1_1_1;
+ nor->erase_proto = SNOR_PROTO_1_1_1;
+ return 0;
+}
+
+static int spi_nor_micron_enable_4_4_4(struct spi_nor *nor, bool enable)
+{
+ if (enable)
+ return micron_set_quad_mode(nor);
+ return micron_set_extended_spi_mode(nor);
+}
+
+static int spi_nor_micron_enable_2_2_2(struct spi_nor *nor, bool enable)
+{
+ if (enable)
+ return micron_set_dual_mode(nor);
+ return micron_set_extended_spi_mode(nor);
+}
+
+#define SNOR_MICRON_RD_MODES \
+ (SNOR_MODE_SLOW | \
+ SNOR_MODE_1_1_1 | \
+ SNOR_MODE_1_1_2 | \
+ SNOR_MODE_1_2_2 | \
+ SNOR_MODE_2_2_2 | \
+ SNOR_MODE_1_1_4 | \
+ SNOR_MODE_1_4_4 | \
+ SNOR_MODE_4_4_4)
+
+#define SNOR_MICRON_WR_MODES \
+ (SNOR_MODE_1_1_1 | \
+ SNOR_MODE_2_2_2 | \
+ SNOR_MODE_1_1_4 | \
+ SNOR_MODE_4_4_4)
+
+static const struct spi_nor_basic_flash_parameter micron_params = {
+ .rd_modes = SNOR_MICRON_RD_MODES,
+ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ),
+ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST),
+ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2),
+ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_2_2),
+ .reads[SNOR_MIDX_2_2_2] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_2_2),
+ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_1_4),
+ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(1, 9, SPINOR_OP_READ_1_4_4),
+ .reads[SNOR_MIDX_4_4_4] = SNOR_OP_READ(1, 9, SPINOR_OP_READ_1_4_4),
+
+ .wr_modes = SNOR_MICRON_WR_MODES,
+ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP,
+ .page_programs[SNOR_MIDX_2_2_2] = SPINOR_OP_PP,
+ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4,
+ .page_programs[SNOR_MIDX_4_4_4] = SPINOR_OP_PP_1_1_4,
+
+ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE),
+
+ .read_id = spi_nor_micron_read_id,
+ .enable_4_4_4 = spi_nor_micron_enable_4_4_4,
+ .enable_2_2_2 = spi_nor_micron_enable_2_2_2,
+};
+
+static const struct spi_nor_basic_flash_parameter micron_4k_params = {
+ .rd_modes = SNOR_MICRON_RD_MODES,
+ .reads[SNOR_MIDX_SLOW] = SNOR_OP_READ(0, 0, SPINOR_OP_READ),
+ .reads[SNOR_MIDX_1_1_1] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_FAST),
+ .reads[SNOR_MIDX_1_1_2] = SNOR_OP_READ(0, 8, SPINOR_OP_READ_1_1_2),
+ .reads[SNOR_MIDX_1_2_2] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_2_2),
+ .reads[SNOR_MIDX_2_2_2] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_2_2),
+ .reads[SNOR_MIDX_1_1_4] = SNOR_OP_READ(1, 7, SPINOR_OP_READ_1_1_4),
+ .reads[SNOR_MIDX_1_4_4] = SNOR_OP_READ(1, 9, SPINOR_OP_READ_1_4_4),
+ .reads[SNOR_MIDX_4_4_4] = SNOR_OP_READ(1, 9, SPINOR_OP_READ_1_4_4),
+
+ .wr_modes = SNOR_MICRON_WR_MODES,
+ .page_programs[SNOR_MIDX_1_1_1] = SPINOR_OP_PP,
+ .page_programs[SNOR_MIDX_2_2_2] = SPINOR_OP_PP,
+ .page_programs[SNOR_MIDX_1_1_4] = SPINOR_OP_PP_1_1_4,
+ .page_programs[SNOR_MIDX_4_4_4] = SPINOR_OP_PP_1_1_4,
+
+ .erase_types[0] = SNOR_OP_ERASE_64K(SPINOR_OP_SE),
+ .erase_types[1] = SNOR_OP_ERASE_4K(SPINOR_OP_BE_4K),
+
+ .read_id = spi_nor_micron_read_id,
+ .enable_4_4_4 = spi_nor_micron_enable_4_4_4,
+ .enable_2_2_2 = spi_nor_micron_enable_2_2_2,
+};
+
+#define PARAMS(_name) .params = &_name##_params
+
/* Used when the "_ext_id" is two bytes at most */
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \
@@ -820,7 +1028,7 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
.sector_size = (_sector_size), \
.n_sectors = (_n_sectors), \
.page_size = 256, \
- .flags = (_flags),
+ .flags = (_flags)
#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
.id = { \
@@ -923,16 +1131,16 @@ static const struct flash_info spi_nor_ids[] = {
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
/* Micron */
- { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
- { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
- { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
- { "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
- { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
- { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
- { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
- { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
- { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
- { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, 0), PARAMS(micron) },
+ { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, 0), PARAMS(micron) },
+ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0), PARAMS(micron_4k) },
+ { "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, 0), PARAMS(micron_4k) },
+ { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0), PARAMS(micron_4k) },
+ { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0), PARAMS(micron_4k) },
+ { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, 0), PARAMS(micron_4k) },
+ { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, USE_FSR), PARAMS(micron_4k) },
+ { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR), PARAMS(micron_4k) },
+ { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR), PARAMS(micron_4k) },
/* PMC */
{ "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) },
@@ -1358,6 +1566,51 @@ static int spi_nor_midx2proto(int midx, enum spi_nor_protocol *proto)
return 0;
}
+static int spi_nor_read_id_multi_io(struct spi_nor *nor,
+ u8 *id, size_t id_len,
+ u32 id_modes,
+ const struct spi_nor_read_id *configs,
+ size_t num_configs,
+ u8 mfr_id)
+{
+ size_t i;
+ int err;
+
+ memset(id, 0, id_len);
+ for (i = 0; i < num_configs; ++i) {
+ const struct spi_nor_read_id *config = &configs[i];
+ enum spi_nor_protocol proto;
+ int id_midx;
+
+ /* Skip unsupported modes. */
+ if (!(config->id_mode & id_modes))
+ continue;
+
+ /* Set SPI protocols */
+ id_midx = fls(config->id_mode) - 1;
+ if (spi_nor_midx2proto(id_midx, &proto)) {
+ dev_err(nor->dev,
+ "Invalid spi-nor mode to read the JEDEC ID\n");
+ return -EINVAL;
+ }
+ nor->reg_proto = proto;
+ nor->read_proto = proto;
+ nor->write_proto = proto;
+ nor->erase_proto = proto;
+
+ err = nor->read_reg(nor, config->opcode, id, id_len);
+ if (err)
+ return err;
+
+ if (id[0] == mfr_id)
+ break;
+
+ memset(id, 0, id_len);
+ }
+
+ return 0;
+}
+
static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
const struct spi_nor_basic_flash_parameter *params,
const struct spi_nor_modes *modes)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 770ea84370d0..d857f628b5ed 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -101,6 +101,7 @@
#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */
/* Enhanced Volatile Configuration Register bits */
+#define EVCR_DUAL_EN_MICRON BIT(6) /* Micron Dual I/O */
#define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */
/* Flag Status Register bits */
--
1.8.2.2
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-04-13 19:30 +0200 |
| Subject | [PATCH RFC 4/8] mtd: spi-nor: fix support of Dual (x-y-2) and Quad (x-y-4) SPI protocols |
| Message-ID | <rnwM3-4Jb-37@gated-at.bofh.it> |
| In reply to | #1378115 |
The quad (or dual) mode of a spi-nor memory may be enabled at boot time by
non-volatile bits in some setting register. Also such a mode may have
already been enabled at early stage by some boot loader.
Hence, we should not guess the spi-nor memory is always configured for the
regular SPI 1-1-1 protocol.
When other SPI modes such as SPI 2-2-2 or SPI 4-4-4 are already enabled,
some manufacturers (Micron, Macronix, ...) require to use a new dedicated
op code AFh to read the JEDEC ID.
Moreover, the procedures to enter/leave the 4-4-4 or 2-2-2 modes or to
enable the Quad Enable bits also depend on the memory's manufacturer.
Here again, some manufucturers have updated/fixed their procedure, hence
some procedures might not always be the same for a given manufacturer.
Also, depending on the SPI protocols for (Fast) Read, Page Program or
Sector Erase operations, the op codes might not be the same between
manufacturers and in some cases between memory parts all sharing the
same JEDEC ID (Micron n25q256*).
So to deal with all those issues, this patch extends the spi-nor framework
so basic flash parameters can be provided for each entry in the
spi_nor_ids[] table.
Finally, the spi_nor_scan() prototype has been changed: the enum read_mode
has been replaced by a struct spi_nor_modes so each SPI controller driver
can precisely declare the lists of SPI modes it supports for (Fast) Read
and Page Program operations. Equivalent lists are also available at the
memory side so the best match is selected.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/devices/m25p80.c | 18 +--
drivers/mtd/spi-nor/fsl-quadspi.c | 9 +-
drivers/mtd/spi-nor/mtk-quadspi.c | 17 ++-
drivers/mtd/spi-nor/nxp-spifi.c | 22 ++--
drivers/mtd/spi-nor/spi-nor.c | 253 +++++++++++++++++++++++++++++---------
include/linux/mtd/spi-nor.h | 138 +++++++++++++++++++--
6 files changed, 365 insertions(+), 92 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 9d6854467651..12112f7ae1a4 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -105,10 +105,10 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
{
- switch (nor->flash_read) {
- case SPI_NOR_DUAL:
+ switch (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto)) {
+ case 2:
return 2;
- case SPI_NOR_QUAD:
+ case 4:
return 4;
default:
return 0;
@@ -184,7 +184,11 @@ static int m25p_probe(struct spi_device *spi)
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
- enum read_mode mode = SPI_NOR_NORMAL;
+ struct spi_nor_modes modes = {
+ .id_modes = SNOR_MODE_1_1_1,
+ .rd_modes = SNOR_MODE_SLOW,
+ .wr_modes = SNOR_MODE_1_1_1,
+ };
char *flash_name;
int ret;
@@ -210,9 +214,9 @@ static int m25p_probe(struct spi_device *spi)
flash->spi = spi;
if (spi->mode & SPI_RX_QUAD)
- mode = SPI_NOR_QUAD;
+ modes.rd_modes |= SNOR_MODE_1_1_4;
else if (spi->mode & SPI_RX_DUAL)
- mode = SPI_NOR_DUAL;
+ modes.rd_modes |= SNOR_MODE_1_1_2;
if (data && data->name)
nor->mtd.name = data->name;
@@ -229,7 +233,7 @@ static int m25p_probe(struct spi_device *spi)
else
flash_name = spi->modalias;
- ret = spi_nor_scan(nor, flash_name, mode);
+ ret = spi_nor_scan(nor, flash_name, &modes);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 9ab2b51d54b8..2eaf60711013 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -979,6 +979,13 @@ static int fsl_qspi_probe(struct platform_device *pdev)
struct spi_nor *nor;
struct mtd_info *mtd;
int ret, i = 0;
+ struct spi_nor_modes modes = {
+ .id_modes = SNOR_MODE_1_1_1,
+ .rd_modes = (SNOR_MODE_SLOW |
+ SNOR_MODE_1_1_1 |
+ SNOR_MODE_1_1_4),
+ .wr_modes = SNOR_MODE_1_1_1,
+ };
q = devm_kzalloc(dev, sizeof(*q), GFP_KERNEL);
if (!q)
@@ -1080,7 +1087,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
/* set the chip address for READID */
fsl_qspi_set_base_addr(q, nor);
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, &modes);
if (ret)
goto mutex_failed;
diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
index 8bed1a4cb79c..928031520d73 100644
--- a/drivers/mtd/spi-nor/mtk-quadspi.c
+++ b/drivers/mtd/spi-nor/mtk-quadspi.c
@@ -122,20 +122,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
{
struct spi_nor *nor = &mt8173_nor->nor;
- switch (nor->flash_read) {
- case SPI_NOR_FAST:
+ switch (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto)) {
+ case 1:
writeb(nor->read_opcode, mt8173_nor->base +
MTK_NOR_PRGDATA3_REG);
writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
MTK_NOR_CFG1_REG);
break;
- case SPI_NOR_DUAL:
+ case 2:
writeb(nor->read_opcode, mt8173_nor->base +
MTK_NOR_PRGDATA3_REG);
writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
MTK_NOR_DUAL_REG);
break;
- case SPI_NOR_QUAD:
+ case 4:
writeb(nor->read_opcode, mt8173_nor->base +
MTK_NOR_PRGDATA4_REG);
writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
@@ -376,6 +376,13 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
{
int ret;
struct spi_nor *nor;
+ struct spi_nor_modes modes = {
+ .id_modes = SNOR_MODE_1_1_1,
+ .rd_modes = (SNOR_MODE_SLOW |
+ SNOR_MODE_1_1_1 |
+ SNOR_MODE_1_1_2),
+ .wr_modes = SNOR_MODE_1_1_1,
+ };
/* initialize controller to accept commands */
writel(MTK_NOR_ENABLE_SF_CMD, mt8173_nor->base + MTK_NOR_WRPROT_REG);
@@ -392,7 +399,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
nor->write_reg = mt8173_nor_write_reg;
nor->mtd.name = "mtk_nor";
/* initialized with NULL */
- ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
+ ret = spi_nor_scan(nor, NULL, &modes);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c
index ae428cb0e04b..c0254c37f198 100644
--- a/drivers/mtd/spi-nor/nxp-spifi.c
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
@@ -237,13 +237,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
{
- switch (spifi->nor.flash_read) {
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
+ switch (SNOR_PROTO_DATA_FROM_PROTO(spifi->nor.read_proto)) {
+ case 1:
spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
break;
- case SPI_NOR_DUAL:
- case SPI_NOR_QUAD:
+ case 2:
+ case 4:
spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
break;
default:
@@ -271,7 +270,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
struct device_node *np)
{
- enum read_mode flash_read;
+ struct spi_nor_modes modes = {
+ .id_modes = SNOR_MODE_1_1_1,
+ .rd_modes = (SNOR_MODE_SLOW | SNOR_MODE_1_1_1),
+ .wr_modes = SNOR_MODE_1_1_1,
+ };
u32 ctrl, property;
u16 mode = 0;
int ret;
@@ -305,13 +308,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
if (mode & SPI_RX_DUAL) {
ctrl |= SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_DUAL;
+ modes.rd_modes |= SNOR_MODE_1_1_2;
} else if (mode & SPI_RX_QUAD) {
ctrl &= ~SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_QUAD;
+ modes.rd_modes |= SNOR_MODE_1_1_4;
} else {
ctrl |= SPIFI_CTRL_DUAL;
- flash_read = SPI_NOR_NORMAL;
}
switch (mode & (SPI_CPHA | SPI_CPOL)) {
@@ -348,7 +350,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
*/
nxp_spifi_dummy_id_read(&spifi->nor);
- ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
+ ret = spi_nor_scan(&spifi->nor, NULL, &modes);
if (ret) {
dev_err(spifi->dev, "device scan failed\n");
return ret;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 54115aface9f..3573cc708b16 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -79,6 +79,8 @@ struct flash_info {
* Use dedicated 4byte address op codes
* to support memory size above 128Mib.
*/
+
+ const struct spi_nor_basic_flash_parameter *params;
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -143,24 +145,6 @@ static int read_cr(struct spi_nor *nor)
}
/*
- * Dummy Cycle calculation for different type of read.
- * It can be used to support more commands with
- * different dummy cycle requirements.
- */
-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
-{
- switch (nor->flash_read) {
- case SPI_NOR_FAST:
- case SPI_NOR_DUAL:
- case SPI_NOR_QUAD:
- return 8;
- case SPI_NOR_NORMAL:
- return 0;
- }
- return 0;
-}
-
-/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
@@ -1073,13 +1057,19 @@ static const struct flash_info spi_nor_ids[] = {
{ },
};
-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *
+spi_nor_read_id(struct spi_nor *nor,
+ const struct spi_nor_basic_flash_parameter *params,
+ u32 id_modes)
{
int tmp;
u8 id[SPI_NOR_MAX_ID_LEN];
const struct flash_info *info;
- tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
+ if (params && params->read_id)
+ tmp = params->read_id(nor, id, sizeof(id), id_modes);
+ else
+ tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, sizeof(id));
if (tmp < 0) {
dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp);
return ERR_PTR(tmp);
@@ -1329,8 +1319,159 @@ static int spi_nor_check(struct spi_nor *nor)
return 0;
}
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+static int spi_nor_midx2proto(int midx, enum spi_nor_protocol *proto)
{
+ switch (midx) {
+ case SNOR_MIDX_SLOW:
+ case SNOR_MIDX_1_1_1:
+ *proto = SNOR_PROTO_1_1_1;
+ break;
+
+ case SNOR_MIDX_1_1_2:
+ *proto = SNOR_PROTO_1_1_2;
+ break;
+
+ case SNOR_MIDX_1_2_2:
+ *proto = SNOR_PROTO_1_2_2;
+ break;
+
+ case SNOR_MIDX_2_2_2:
+ *proto = SNOR_PROTO_2_2_2;
+ break;
+
+ case SNOR_MIDX_1_1_4:
+ *proto = SNOR_PROTO_1_1_4;
+ break;
+
+ case SNOR_MIDX_1_4_4:
+ *proto = SNOR_PROTO_1_4_4;
+ break;
+
+ case SNOR_MIDX_4_4_4:
+ *proto = SNOR_PROTO_4_4_4;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
+ const struct spi_nor_basic_flash_parameter *params,
+ const struct spi_nor_modes *modes)
+{
+ bool enable_quad_io, enable_4_4_4, enable_2_2_2;
+ u32 rd_modes, wr_modes, cmd_modes, mask;
+ const struct spi_nor_erase_type *erase_type;
+ const struct spi_nor_read *read;
+ int rd_midx, wr_midx, err = 0;
+
+ /* 2-2-2 or 4-4-4 modes must be supported by BOTH read and write */
+ mask = (SNOR_MODE_2_2_2 | SNOR_MODE_4_4_4);
+ cmd_modes = (modes->rd_modes & modes->wr_modes) & mask;
+ rd_modes = (modes->rd_modes & ~mask) | cmd_modes;
+ wr_modes = (modes->wr_modes & ~mask) | cmd_modes;
+
+ /* Setup read operation. */
+ rd_midx = fls(params->rd_modes & rd_modes) - 1;
+ if (spi_nor_midx2proto(rd_midx, &nor->read_proto)) {
+ dev_err(nor->dev, "invalid (fast) read\n");
+ return -EINVAL;
+ }
+ read = ¶ms->reads[rd_midx];
+ nor->read_opcode = read->opcode;
+ nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+
+ /* Set page program op code and protocol. */
+ wr_midx = fls(params->wr_modes & wr_modes) - 1;
+ if (spi_nor_midx2proto(wr_midx, &nor->write_proto)) {
+ dev_err(nor->dev, "invalid page program\n");
+ return -EINVAL;
+ }
+ nor->program_opcode = params->page_programs[wr_midx];
+
+ /* Set sector erase op code and size. */
+ erase_type = ¶ms->erase_types[0];
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ for (i = 1; i < SNOR_MAX_ERASE_TYPES; ++i)
+ if (params->erase_types[i].size == 0x0c)
+ erase_type = ¶ms->erase_types[i];
+#endif
+ nor->erase_opcode = erase_type->opcode;
+ nor->mtd.erasesize = (1 << erase_type->size);
+
+
+ enable_quad_io = (SNOR_PROTO_DATA_FROM_PROTO(nor->read_proto) == 4 ||
+ SNOR_PROTO_DATA_FROM_PROTO(nor->write_proto) == 4);
+ enable_4_4_4 = (nor->read_proto == SNOR_PROTO_4_4_4);
+ enable_2_2_2 = (nor->read_proto == SNOR_PROTO_2_2_2);
+
+ /* Enable Quad I/O if needed. */
+ if ((enable_quad_io || enable_4_4_4) &&
+ params->enable_quad_io &&
+ nor->reg_proto != SNOR_PROTO_4_4_4) {
+ err = params->enable_quad_io(nor, true);
+ if (err) {
+ dev_err(nor->dev,
+ "failed to enable the Quad I/O mode\n");
+ return err;
+ }
+ }
+
+ /* Enter/Leave 2-2-2 or 4-4-4 if needed. */
+ if (enable_2_2_2 && params->enable_2_2_2 &&
+ nor->reg_proto != SNOR_PROTO_2_2_2)
+ err = params->enable_2_2_2(nor, true);
+ else if (enable_4_4_4 && params->enable_4_4_4 &&
+ nor->reg_proto != SNOR_PROTO_4_4_4)
+ err = params->enable_4_4_4(nor, true);
+ else if (!enable_2_2_2 && params->enable_2_2_2 &&
+ nor->reg_proto == SNOR_PROTO_2_2_2)
+ err = params->enable_2_2_2(nor, false);
+ else if (!enable_4_4_4 && params->enable_4_4_4 &&
+ nor->reg_proto == SNOR_PROTO_4_4_4)
+ err = params->enable_4_4_4(nor, false);
+ if (err)
+ return err;
+
+ /*
+ * Fix erase protocol if needed, read and write protocols should
+ * already be valid.
+ */
+ switch (nor->reg_proto) {
+ case SNOR_PROTO_4_4_4:
+ nor->erase_proto = SNOR_PROTO_4_4_4;
+ break;
+
+ case SNOR_PROTO_2_2_2:
+ nor->erase_proto = SNOR_PROTO_2_2_2;
+ break;
+
+ default:
+ nor->erase_proto = SNOR_PROTO_1_1_1;
+ break;
+ }
+
+ dev_dbg(nor->dev,
+ "(Fast) Read: opcode=%02Xh, protocol=%03x, mode=%u, wait=%u\n",
+ nor->read_opcode, nor->read_proto,
+ read->num_mode_clocks, read->num_wait_states);
+ dev_dbg(nor->dev,
+ "Page Program: opcode=%02Xh, protocol=%03x\n",
+ nor->program_opcode, nor->write_proto);
+ dev_dbg(nor->dev,
+ "Sector Erase: opcode=%02Xh, protocol=%03x, sector size=%zu\n",
+ nor->erase_opcode, nor->erase_proto, nor->mtd.erasesize);
+
+ return 0;
+}
+
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ struct spi_nor_modes *modes)
+{
+ const struct spi_nor_basic_flash_parameter *params = NULL;
const struct flash_info *info = NULL;
struct device *dev = nor->dev;
struct mtd_info *mtd = &nor->mtd;
@@ -1342,11 +1483,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (ret)
return ret;
+ /* Reset SPI protocol for all commands */
+ nor->erase_proto = SNOR_PROTO_1_1_1;
+ nor->read_proto = SNOR_PROTO_1_1_1;
+ nor->write_proto = SNOR_PROTO_1_1_1;
+ nor->reg_proto = SNOR_PROTO_1_1_1;
+
if (name)
info = spi_nor_match_id(name);
/* Try to auto-detect if chip name wasn't specified or not found */
if (!info)
- info = spi_nor_read_id(nor);
+ info = spi_nor_read_id(nor, NULL, 0);
if (IS_ERR_OR_NULL(info))
return -ENOENT;
@@ -1357,7 +1504,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (name && info->id_len) {
const struct flash_info *jinfo;
- jinfo = spi_nor_read_id(nor);
+ jinfo = spi_nor_read_id(nor, info->params, modes->id_modes);
if (IS_ERR(jinfo)) {
return PTR_ERR(jinfo);
} else if (jinfo->id_len != info->id_len ||
@@ -1374,6 +1521,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
info = jinfo;
}
}
+ params = info->params;
mutex_init(&nor->lock);
@@ -1451,51 +1599,42 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
if (np) {
/* If we were instantiated by DT, use it */
if (of_property_read_bool(np, "m25p,fast-read"))
- nor->flash_read = SPI_NOR_FAST;
+ modes->rd_modes |= SNOR_MODE_1_1_1;
else
- nor->flash_read = SPI_NOR_NORMAL;
+ modes->rd_modes &= ~SNOR_MODE_1_1_1;
} else {
/* If we weren't instantiated by DT, default to fast-read */
- nor->flash_read = SPI_NOR_FAST;
+ modes->rd_modes |= SNOR_MODE_1_1_1;
}
/* Some devices cannot do fast-read, no matter what DT tells us */
if (info->flags & SPI_NOR_NO_FR)
- nor->flash_read = SPI_NOR_NORMAL;
+ modes->rd_modes &= ~SNOR_MODE_1_1_1;
- /* Quad/Dual-read mode takes precedence over fast/normal */
- if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
- ret = set_quad_mode(nor, info);
- if (ret) {
- dev_err(dev, "quad mode not supported\n");
+ nor->program_opcode = SPINOR_OP_PP;
+
+ /*
+ * Configure the SPI memory:
+ * - select op codes for (Fast) Read, Page Program and Sector Erase.
+ * - set the number of dummy cycles (mode cycles + wait states).
+ * - set the SPI protocols for register and memory accesses.
+ * - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
+ * - enter/leave the 4-4-4 or 2-2-2 modes if needed.
+ */
+ if (params) {
+ ret = spi_nor_setup(nor, info, params, modes);
+ if (ret)
return ret;
+ } else {
+ if (modes->rd_modes & SNOR_MODE_1_1_1) {
+ nor->read_opcode = SPINOR_OP_READ_FAST;
+ nor->read_dummy = 8;
+ } else {
+ nor->read_opcode = SPINOR_OP_READ;
+ nor->read_dummy = 0;
}
- nor->flash_read = SPI_NOR_QUAD;
- } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
- nor->flash_read = SPI_NOR_DUAL;
}
- /* Default commands */
- switch (nor->flash_read) {
- case SPI_NOR_QUAD:
- nor->read_opcode = SPINOR_OP_READ_1_1_4;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ_1_1_2;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ_FAST;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ;
- break;
- default:
- dev_err(dev, "No Read opcode defined\n");
- return -EINVAL;
- }
-
- nor->program_opcode = SPINOR_OP_PP;
-
if (info->addr_width)
nor->addr_width = info->addr_width;
else if (mtd->size > 0x1000000) {
@@ -1516,8 +1655,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
return -EINVAL;
}
- nor->read_dummy = spi_nor_read_dummy_cycles(nor);
-
dev_info(dev, "%s (%lld Kbytes)\n", info->name,
(long long)mtd->size >> 10);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index a91e95756a48..770ea84370d0 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -84,8 +84,9 @@
#define SPINOR_OP_BRWR 0x17 /* Bank register write */
/* Used for Micron flashes only. */
-#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
-#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
+#define SPINOR_OP_MIO_RDID 0xaf /* Multiple I/O Read JEDEC ID */
+#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
+#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
/* Status Register bits. */
#define SR_WIP BIT(0) /* Write in progress */
@@ -108,11 +109,119 @@
/* Configuration Register bits. */
#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */
-enum read_mode {
- SPI_NOR_NORMAL = 0,
- SPI_NOR_FAST,
- SPI_NOR_DUAL,
- SPI_NOR_QUAD,
+
+/* Supported modes */
+enum spi_nor_mode_index {
+ /* Sorted by ascending priority order */
+ SNOR_MIDX_SLOW = 0,
+ SNOR_MIDX_1_1_1,
+ SNOR_MIDX_1_1_2,
+ SNOR_MIDX_1_2_2,
+ SNOR_MIDX_2_2_2,
+ SNOR_MIDX_1_1_4,
+ SNOR_MIDX_1_4_4,
+ SNOR_MIDX_4_4_4,
+
+ SNOR_MIDX_MAX
+};
+
+#define SNOR_MODE_SLOW BIT(SNOR_MIDX_SLOW)
+#define SNOR_MODE_1_1_1 BIT(SNOR_MIDX_1_1_1)
+#define SNOR_MODE_1_1_2 BIT(SNOR_MIDX_1_1_2)
+#define SNOR_MODE_1_2_2 BIT(SNOR_MIDX_1_2_2)
+#define SNOR_MODE_2_2_2 BIT(SNOR_MIDX_2_2_2)
+#define SNOR_MODE_1_1_4 BIT(SNOR_MIDX_1_1_4)
+#define SNOR_MODE_1_4_4 BIT(SNOR_MIDX_1_4_4)
+#define SNOR_MODE_4_4_4 BIT(SNOR_MIDX_4_4_4)
+
+struct spi_nor_modes {
+ u32 id_modes; /* supported SPI modes for Read JEDEC ID */
+ u32 rd_modes; /* supported SPI modes for (Fast) Read */
+ u32 wr_modes; /* supported SPI modes for Page Program */
+};
+
+
+struct spi_nor_read {
+ u8 num_wait_states:5;
+ u8 num_mode_clocks:3;
+ u8 opcode;
+};
+
+#define SNOR_OP_READ(_num_mode_clocks, _num_wait_states, _opcode) \
+ { \
+ .num_wait_states = _num_wait_states, \
+ .num_mode_clocks = _num_mode_clocks, \
+ .opcode = _opcode, \
+ }
+
+struct spi_nor_erase_type {
+ u8 size; /* specifies 'N' so erase size = 2^N */
+ u8 opcode;
+};
+
+#define SNOR_OP_ERASE(_size, _opcode) { .size = _size, .opcode = _opcode }
+#define SNOR_OP_ERASE_64K(_opcode) SNOR_OP_ERASE(0x10, _opcode)
+#define SNOR_OP_ERASE_32K(_opcode) SNOR_OP_ERASE(0x0f, _opcode)
+#define SNOR_OP_ERASE_4K(_opcode) SNOR_OP_ERASE(0x0c, _opcode)
+
+struct spi_nor;
+
+#define SNOR_MAX_ERASE_TYPES 4
+
+struct spi_nor_basic_flash_parameter {
+ /* Fast Read settings */
+ u32 rd_modes;
+ struct spi_nor_read reads[SNOR_MIDX_MAX];
+
+ /* Page Program settings */
+ u32 wr_modes;
+ u8 page_programs[SNOR_MIDX_MAX];
+
+ /* Sector Erase settings */
+ struct spi_nor_erase_type erase_types[SNOR_MAX_ERASE_TYPES];
+
+ int (*read_id)(struct spi_nor *nor, u8 *id, size_t id_len,
+ u32 id_modes);
+ int (*enable_quad_io)(struct spi_nor *nor, bool enable);
+ int (*enable_4_4_4)(struct spi_nor *nor, bool enable);
+ int (*enable_2_2_2)(struct spi_nor *nor, bool enable);
+};
+
+
+#define SNOR_PROTO_CODE_OFF 8
+#define SNOR_PROTO_CODE_MASK GENMASK(11, 8)
+#define SNOR_PROTO_CODE_TO_PROTO(code) \
+ (((code) << SNOR_PROTO_CODE_OFF) & SNOR_PROTO_CODE_MASK)
+#define SNOR_PROTO_CODE_FROM_PROTO(proto) \
+ ((((u32)(proto)) & SNOR_PROTO_CODE_MASK) >> SNOR_PROTO_CODE_OFF)
+
+#define SNOR_PROTO_ADDR_OFF 4
+#define SNOR_PROTO_ADDR_MASK GENMASK(7, 4)
+#define SNOR_PROTO_ADDR_TO_PROTO(addr) \
+ (((addr) << SNOR_PROTO_ADDR_OFF) & SNOR_PROTO_ADDR_MASK)
+#define SNOR_PROTO_ADDR_FROM_PROTO(proto) \
+ ((((u32)(proto)) & SNOR_PROTO_ADDR_MASK) >> SNOR_PROTO_ADDR_OFF)
+
+#define SNOR_PROTO_DATA_OFF 0
+#define SNOR_PROTO_DATA_MASK GENMASK(3, 0)
+#define SNOR_PROTO_DATA_TO_PROTO(data) \
+ (((data) << SNOR_PROTO_DATA_OFF) & SNOR_PROTO_DATA_MASK)
+#define SNOR_PROTO_DATA_FROM_PROTO(proto) \
+ ((((u32)(proto)) & SNOR_PROTO_DATA_MASK) >> SNOR_PROTO_DATA_OFF)
+
+#define SNOR_PROTO(code, addr, data) \
+ (SNOR_PROTO_CODE_TO_PROTO(code) | \
+ SNOR_PROTO_ADDR_TO_PROTO(addr) | \
+ SNOR_PROTO_DATA_TO_PROTO(data))
+
+enum spi_nor_protocol {
+ SNOR_PROTO_1_1_1 = SNOR_PROTO(1, 1, 1), /* SPI */
+ SNOR_PROTO_1_1_2 = SNOR_PROTO(1, 1, 2), /* Dual Output */
+ SNOR_PROTO_1_1_4 = SNOR_PROTO(1, 1, 4), /* Quad Output */
+ SNOR_PROTO_1_2_2 = SNOR_PROTO(1, 2, 2), /* Dual IO */
+ SNOR_PROTO_1_4_4 = SNOR_PROTO(1, 4, 4), /* Quad IO */
+ SNOR_PROTO_2_2_2 = SNOR_PROTO(2, 2, 2), /* Dual Command */
+ SNOR_PROTO_4_4_4 = SNOR_PROTO(4, 4, 4), /* Quad Command */
};
#define SPI_NOR_MAX_CMD_SIZE 8
@@ -140,9 +249,12 @@ enum spi_nor_option_flags {
* @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
* @flags: flag options for the current SPI-NOR (SNOR_F_*)
+ * @erase_proto: the SPI protocol used by erase operations
+ * @read_proto: the SPI protocol used by read operations
+ * @write_proto: the SPI protocol used by write operations
+ * @reg_proto the SPI protocol used by read_reg/write_reg operations
* @cmd_buf: used by the write_reg
* @prepare: [OPTIONAL] do some preparations for the
* read/write/erase/lock/unlock operations
@@ -171,7 +283,10 @@ struct spi_nor {
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
- enum read_mode flash_read;
+ enum spi_nor_protocol erase_proto;
+ enum spi_nor_protocol read_proto;
+ enum spi_nor_protocol write_proto;
+ enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
@@ -209,7 +324,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
* spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure
* @name: the chip type name
- * @mode: the read mode supported by the driver
+ * @modes: the SPI modes supported by the controller 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
@@ -219,6 +334,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
*
* Return: 0 for success, others for failure.
*/
-int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ struct spi_nor_modes *modes);
#endif
--
1.8.2.2
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web