Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1526702 > unrolled thread
| Started by | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| First post | 2016-11-21 15:20 +0100 |
| Last post | 2016-11-21 15:20 +0100 |
| Articles | 7 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH v4 0/8] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-11-21 15:20 +0100
[PATCH v4 3/8] mtd: spi-nor: add an alternative method to support memory >16MiB Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-11-21 15:20 +0100
[PATCH v4 5/8] mtd: spi-nor: remove unused set_quad_mode() function Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-11-21 15:20 +0100
[PATCH v4 8/8] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-11-21 15:20 +0100
[PATCH v4 7/8] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-11-21 15:20 +0100
[PATCH v4 1/8] mtd: spi-nor: improve macronix_quad_enable() Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-11-21 15:20 +0100
[PATCH v4 4/8] mtd: spi-nor: add support of SPI protocols like SPI 1-2-2 and SPI 1-4-4 Cyrille Pitchen <cyrille.pitchen@atmel.com> - 2016-11-21 15:20 +0100
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-11-21 15:20 +0100 |
| Subject | [PATCH v4 0/8] mtd: spi-nor: parse SFDP tables to setup (Q)SPI memories |
| Message-ID | <sFXBT-2h6-3@gated-at.bofh.it> |
Hi all,
This series extends support of SPI protocols to new protocols such as
SPI x-2-2 and SPI x-4-4. Also spi_nor_scan() tries now to select the right
op codes, timing parameters (number of mode and dummy cycles) and erase
sector size by parsing the Serial Flash Discoverable Parameter (SFDP)
tables, when available, as defined in the JEDEC JESD216 specifications.
When SFDP tables are not available, legacy settings are still used for
backward compatibility (SPI and earlier QSPI memories).
Support of SPI memories >128Mbits is also improved by using the 4byte
address instruction set, when available. Using those dedicated op codes
is stateless as opposed to enter the 4byte address mode, hence a better
compatibility with some boot loaders which expect to use 3byte address
op codes.
This series was tested on a Atmel sama5d2 xplained board with the
atmel-qspi.c driver. Except for SST memories, the SPI bus clock was set
to 83MHz. The config MTD_SPI_NOR_USE_4K_SECTORS was selected.
For my tests, I used some shell scripts using mtd_debug and sha1sum to
check the data integrity.
e.g:
#!/bin/sh
mtd_debug erase /dev/mtd5 0 0x780000
mtd_debug write /dev/mtd5 0 7674703 sama5d4_doc11238.pdf
mtd_debug read /dev/mtd5 0 7674703 sama5d4_tmp.pdf
sha1sum sama5d4_doc11238.pdf sama5d4_tmp.pdf
Depending on the actual memory size, I may have used other partitions
(/dev/mtd4) and input file size (2880044 and 320044 bytes).
The series was tested with the following QSPI memories:
Spansion/Cypress:
- s25fl127s OK
- s25fl512s OK
- s25fl164k OK
Micron:
- n25q128a OK
- n25q512 OK
- n25q01g OK
Macronix:
- mx25v1635f OK
- mx25l3235f OK
- mx25l3273f OK
- mx25l6433f OK
- mx25l6473f OK
- mx25l12835f OK
- mx25l12845g OK
- mx25l12873g OK
- mx25l25645g OK
- mx25l25673g OK
- mx25l51245g OK
- mx66l1g45g OK
SST:
- sst26vf016b OK
- sst26vf032b OK
- sst26vf064b OK
Best regards,
Cyrille
ChangeLog:
v3 -> v4
- replace dev_info() by dev_dbg() in patch 1.
- split former patch 2 into 2 patches:
+ new patch 2 deals with the rename of SPINOR_OP_READ4_* macros
+ new patch 3 deals with the alternative methode to support memory >16MiB
- add comment in patch 3 to describe the dichotomic search performed by
spi_nor_convert_opcode().
- change return type from int to void for m25p80_proto2nbits() in patch 6.
- remove former patches 8 & 9 from the v2 series: the support of the
Macronix mx66l1g45g memory will be sent in a separated patch.
v2 -> v3
- tested with new samples: Micron n25q512, n25q01g and Macronix
mx25v1635f, mx25l3235f, mx25l3273f.
- add "Reviewed-by: Jagan Teki <jagan@openedev.com>" on patch 1.
- add "Tested-by: Vignesh R <vigneshr@ti.com>" on patch 2.
- fix some checkpatch warnings.
- add call of spi_nor_wait_till_ready() in spansion_new_quad_enable()
and sr2_bit7_quad_enable(), as suggested by Joel Esponde on patch 6.
- test JESD216 rev A (minor 5) instead of rev B (minor 6) with the return
code of spi_nor_parse_sfdp() from spi_nor_init_params() on patch 6.
The seven additional DWORDs of the Basic Flash Parameter Table were
introduced in rev A, not rev B, so the 15th DWORD was already available
in rev A. The 15th DWORD provides us with the Quad Enable Requirements
(QER) bits.
Basic Flash Parameter Table size:
+ JESD216 : 9 DWORDS
+ JESD216A: 16 DWORDS
+ JESD216B: 16 DWORDS
v1 -> v2
- fix patch 3 to resolve compiler errors on hisi-sfc.c and cadence-quadspi.c
drivers
Cyrille Pitchen (8):
mtd: spi-nor: improve macronix_quad_enable()
mtd: spi-nor: rename SPINOR_OP_* macros of the 4-byte address op codes
mtd: spi-nor: add an alternative method to support memory >16MiB
mtd: spi-nor: add support of SPI protocols like SPI 1-2-2 and SPI
1-4-4
mtd: spi-nor: remove unused set_quad_mode() function
mtd: m25p80: add support of dual and quad spi protocols to all
commands
mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables
mtd: spi-nor: parse SFDP 4-byte Address Instruction Table
drivers/mtd/devices/m25p80.c | 191 ++++--
drivers/mtd/devices/serial_flash_cmds.h | 7 -
drivers/mtd/devices/st_spi_fsm.c | 28 +-
drivers/mtd/spi-nor/atmel-quadspi.c | 83 ++-
drivers/mtd/spi-nor/cadence-quadspi.c | 18 +-
drivers/mtd/spi-nor/fsl-quadspi.c | 8 +-
drivers/mtd/spi-nor/hisi-sfc.c | 32 +-
drivers/mtd/spi-nor/mtk-quadspi.c | 16 +-
drivers/mtd/spi-nor/nxp-spifi.c | 21 +-
drivers/mtd/spi-nor/spi-nor.c | 1017 ++++++++++++++++++++++++++++---
drivers/spi/spi-bcm-qspi.c | 6 +-
include/linux/mtd/spi-nor.h | 164 ++++-
12 files changed, 1351 insertions(+), 240 deletions(-)
--
2.7.4
[toc] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-11-21 15:20 +0100 |
| Subject | [PATCH v4 3/8] mtd: spi-nor: add an alternative method to support memory >16MiB |
| Message-ID | <sFXBT-2h6-31@gated-at.bofh.it> |
| In reply to | #1526702 |
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>
Tested-by: Vignesh R <vigneshr@ti.com>
---
drivers/mtd/spi-nor/spi-nor.c | 114 ++++++++++++++++++++++++++++++++++--------
1 file changed, 93 insertions(+), 21 deletions(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 8a190122b13f..fd39516fef35 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,91 @@ 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;
+
+ /*
+ * This function implements a dichotomic search in the entries[]
+ * array indexed by src_opcode. Hence we assume that the entries[]
+ * array is sorted by src_opcode.
+ * The dichotomic search has a logarithmic complexity as opposed
+ * to a simple loop on all entires, which has a linear complexity:
+ * it means that when n is the number of entries in the input array,
+ * the dichotomic search performs O(log2(n)) comparisons whereas
+ * a simple loop performs O(n) comparisons.
+ */
+ 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 (cf spi_nor_convert_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_BE_32K), /* 0x52 */
+ 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)
@@ -1476,27 +1565,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_READ_1_1_4_4B;
- break;
- case SPI_NOR_DUAL:
- nor->read_opcode = SPINOR_OP_READ_1_1_2_4B;
- break;
- case SPI_NOR_FAST:
- nor->read_opcode = SPINOR_OP_READ_FAST_4B;
- break;
- case SPI_NOR_NORMAL:
- nor->read_opcode = SPINOR_OP_READ_4B;
- 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;
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-11-21 15:20 +0100 |
| Subject | [PATCH v4 5/8] mtd: spi-nor: remove unused set_quad_mode() function |
| Message-ID | <sFXBU-2h6-33@gated-at.bofh.it> |
| In reply to | #1526702 |
The set_quad_mode() function is no longer used, so we remove it.
This patch is not squashed into the previous patch on purpose for
readiness issue and to ease the review process of the whole series.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/spi-nor/spi-nor.c | 24 ------------------------
1 file changed, 24 deletions(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 01e9b40c825f..9438b4a937e9 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1341,30 +1341,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
-static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
-{
- int status;
-
- switch (JEDEC_MFR(info)) {
- case SNOR_MFR_MACRONIX:
- status = macronix_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Macronix quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
- case SNOR_MFR_MICRON:
- return 0;
- default:
- status = spansion_quad_enable(nor);
- if (status) {
- dev_err(nor->dev, "Spansion quad-read not enabled\n");
- return -EINVAL;
- }
- return status;
- }
-}
-
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-11-21 15:20 +0100 |
| Subject | [PATCH v4 8/8] mtd: spi-nor: parse SFDP 4-byte Address Instruction Table |
| Message-ID | <sFXBU-2h6-39@gated-at.bofh.it> |
| In reply to | #1526702 |
This patch adds supports for SFDP (JESD216B) 4-byte Address Instruction
Table. This table is optional but when available, we parse it to get the
4-byte address op codes supported by the memory.
Using these op codes is stateless as opposed to entering the 4-byte
address mode or setting the Base Address Register (BAR).
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/spi-nor/spi-nor.c | 140 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 139 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index e5f97bc0a370..c1522a9a6838 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1504,6 +1504,7 @@ struct sfdp_parameter_header {
#define SFDP_BFPT_ID 0xff00u /* Basic Flash Parameter Table */
+#define SFDP_4BAIT_ID 0xff84u /* 4-byte Address Instruction Table */
#define SFDP_SIGNATURE 0x50444653u
#define SFDP_JESD216_MAJOR 1
@@ -1751,6 +1752,124 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
return 0;
}
+struct sfdp_4bait {
+ enum spi_nor_protocol_index pindex;
+ int wbit;
+};
+
+static int spi_nor_parse_4bait(struct spi_nor *nor,
+ const struct sfdp_parameter_header *param_header,
+ struct spi_nor_basic_flash_parameter *params)
+{
+ static const struct sfdp_4bait reads[] = {
+ {SNOR_PINDEX_SLOW, 0}, /* 0x13 */
+ {SNOR_PINDEX_1_1_1, 1}, /* 0x0c */
+ {SNOR_PINDEX_1_1_2, 2}, /* 0x3c */
+ {SNOR_PINDEX_1_2_2, 3}, /* 0xbc */
+ {SNOR_PINDEX_1_1_4, 4}, /* 0x6c */
+ {SNOR_PINDEX_1_4_4, 5}, /* 0xec */
+ };
+ static const struct sfdp_4bait programs[] = {
+ {SNOR_PINDEX_1_1_1, 6}, /* 0x12 */
+ {SNOR_PINDEX_1_1_4, 7}, /* 0x34 */
+ {SNOR_PINDEX_1_4_4, 8}, /* 0x3e */
+ };
+ static const struct sfdp_4bait erases[SNOR_MAX_ERASE_TYPES] = {
+ {SNOR_PINDEX_1_1_1, 9},
+ {SNOR_PINDEX_1_1_1, 10},
+ {SNOR_PINDEX_1_1_1, 11},
+ {SNOR_PINDEX_1_1_1, 12},
+ };
+ u32 word[2], addr, rd_modes, wr_modes, erase_modes;
+ int i, err;
+
+ if (param_header->major != SFDP_JESD216_MAJOR ||
+ param_header->length < 2)
+ return -EINVAL;
+
+ /* Read the 4-byte Address Instruction Table. */
+ addr = SFDP_PARAM_HEADER_PTP(param_header);
+ err = spi_nor_read_sfdp(nor, addr, sizeof(word), word);
+ if (err)
+ return err;
+
+ for (i = 0; i < 2; ++i)
+ word[i] = le32_to_cpu(word[i]);
+
+ /*
+ * Compute the subset of (Fast) Read commands for which the 4-byte
+ * version is supported.
+ */
+ rd_modes = 0;
+ for (i = 0; i < ARRAY_SIZE(reads); ++i) {
+ const struct sfdp_4bait *read = &reads[i];
+
+ if ((params->rd_modes & BIT(read->pindex)) &&
+ (word[0] & BIT(read->wbit)))
+ rd_modes |= BIT(read->pindex);
+ }
+
+ /*
+ * Compute the subset of Page Program commands for which the 4-byte
+ * version is supported.
+ */
+ wr_modes = 0;
+ for (i = 0; i < ARRAY_SIZE(programs); ++i) {
+ const struct sfdp_4bait *program = &programs[i];
+
+ if ((params->wr_modes & BIT(program->pindex)) &&
+ (word[0] & BIT(program->wbit)))
+ wr_modes |= BIT(program->pindex);
+ }
+
+ /*
+ * Compute the subet of Sector Erase commands for which the 4-byte
+ * version is supported.
+ */
+ erase_modes = 0;
+ for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i) {
+ const struct sfdp_4bait *erase = &erases[i];
+
+ if ((params->erase_types[i].size > 0) &&
+ (word[0] & BIT(erase->wbit)))
+ erase_modes |= BIT(i);
+ }
+
+ /*
+ * We need at least one 4-byte op code per read, program and erase
+ * operation; the .read(), .write() and .erase() hooks share the
+ * nor->addr_width value.
+ */
+ if (!rd_modes || !wr_modes || !erase_modes)
+ return 0;
+
+ /*
+ * Discard all operations from the 4-byte instruction set which are
+ * not supported by this memory.
+ */
+ params->rd_modes = rd_modes;
+ params->wr_modes = wr_modes;
+ for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i)
+ if (!(erase_modes & BIT(i)))
+ params->erase_types[i].size = 0;
+
+ /* Use the 4-byte address instruction set. */
+ params->reads[SNOR_PINDEX_SLOW].opcode = SPINOR_OP_READ_4B;
+ params->reads[SNOR_PINDEX_1_1_1].opcode = SPINOR_OP_READ_FAST_4B;
+ params->reads[SNOR_PINDEX_1_1_2].opcode = SPINOR_OP_READ_1_1_2_4B;
+ params->reads[SNOR_PINDEX_1_2_2].opcode = SPINOR_OP_READ_1_2_2_4B;
+ params->reads[SNOR_PINDEX_1_1_4].opcode = SPINOR_OP_READ_1_1_4_4B;
+ params->reads[SNOR_PINDEX_1_4_4].opcode = SPINOR_OP_READ_1_4_4_4B;
+ params->page_programs[SNOR_PINDEX_1_1_1] = SPINOR_OP_PP_4B;
+ params->page_programs[SNOR_PINDEX_1_1_4] = SPINOR_OP_PP_1_1_4_4B;
+ params->page_programs[SNOR_PINDEX_1_4_4] = SPINOR_OP_PP_1_4_4_4B;
+ for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i)
+ params->erase_types[i].opcode = (word[1] >> (i * 8)) & 0xff;
+
+ nor->addr_width = 4;
+ return 0;
+}
+
static int spi_nor_parse_sfdp(struct spi_nor *nor,
struct spi_nor_basic_flash_parameter *params)
{
@@ -1818,6 +1937,23 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor,
if (err)
goto exit;
+ /* Parse other parameter headers. */
+ for (i = 0; i < header.nph; ++i) {
+ param_header = ¶m_headers[i];
+
+ switch (SFDP_PARAM_HEADER_ID(param_header)) {
+ case SFDP_4BAIT_ID:
+ err = spi_nor_parse_4bait(nor, param_header, params);
+ break;
+
+ default:
+ break;
+ }
+
+ if (err)
+ goto exit;
+ }
+
exit:
kfree(param_headers);
return (err) ? err : bfpt_header->minor;
@@ -2172,7 +2308,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
if (ret)
return ret;
- if (info->addr_width)
+ if (nor->addr_width)
+ ; /* already configured by spi_nor_setup(). */
+ else if (info->addr_width)
nor->addr_width = info->addr_width;
else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-11-21 15:20 +0100 |
| Subject | [PATCH v4 7/8] mtd: spi-nor: parse Serial Flash Discoverable Parameters (SFDP) tables |
| Message-ID | <sFXBT-2h6-23@gated-at.bofh.it> |
| In reply to | #1526702 |
This patch adds support the the JESD216B standard and parse the SFDP
tables to dynamically initialize the spi_nor_basic_flash_parameter
structure.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/spi-nor/spi-nor.c | 468 +++++++++++++++++++++++++++++++++++++++++-
include/linux/mtd/spi-nor.h | 6 +
2 files changed, 473 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 9438b4a937e9..e5f97bc0a370 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -17,6 +17,7 @@
#include <linux/mutex.h>
#include <linux/math64.h>
#include <linux/sizes.h>
+#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/of_platform.h>
@@ -79,6 +80,7 @@ struct flash_info {
* Use dedicated 4byte address op codes
* to support memory size above 128Mib.
*/
+#define SPI_NOR_SKIP_SFDP BIT(11) /* Skip read of SFDP tables */
};
#define JEDEC_MFR(info) ((info)->id[0])
@@ -1341,6 +1343,99 @@ static int spansion_quad_enable(struct spi_nor *nor)
return 0;
}
+static int spansion_new_quad_enable(struct spi_nor *nor)
+{
+ u8 sr_cr[2];
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = read_cr(nor);
+ if (ret < 0) {
+ dev_err(nor->dev,
+ "error while reading configuration register\n");
+ return -EINVAL;
+ }
+ sr_cr[1] = ret;
+ if (sr_cr[1] & CR_QUAD_EN_SPAN)
+ return 0;
+
+ dev_info(nor->dev, "setting Spansion Quad Enable (non-volatile) bit\n");
+
+ /* Keep the current value of the Status Register. */
+ ret = read_sr(nor);
+ if (ret < 0) {
+ dev_err(nor->dev,
+ "error while reading status register\n");
+ return -EINVAL;
+ }
+ sr_cr[0] = ret;
+ sr_cr[1] |= CR_QUAD_EN_SPAN;
+
+ write_enable(nor);
+
+ ret = nor->write_reg(nor, SPINOR_OP_WRSR, sr_cr, 2);
+ if (ret < 0) {
+ dev_err(nor->dev,
+ "error while writing configuration register\n");
+ return -EINVAL;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while waiting for WRSR completion\n");
+ return ret;
+ }
+
+ /* read back and check it */
+ ret = read_cr(nor);
+ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
+ dev_err(nor->dev, "Spansion Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sr2_bit7_quad_enable(struct spi_nor *nor)
+{
+ u8 sr2;
+ int ret;
+
+ /* Check current Quad Enable bit value. */
+ ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
+ if (ret)
+ return ret;
+ if (sr2 & SR2_QUAD_EN_BIT7)
+ return 0;
+
+ /* Update the Quad Enable bit. */
+ sr2 |= SR2_QUAD_EN_BIT7;
+
+ write_enable(nor);
+
+ ret = nor->write_reg(nor, SPINOR_OP_WRSR2, &sr2, 1);
+ if (ret < 0) {
+ dev_err(nor->dev,
+ "error while writing status register 2\n");
+ return -EINVAL;
+ }
+
+ ret = spi_nor_wait_till_ready(nor);
+ if (ret < 0) {
+ dev_err(nor->dev, "error while waiting for WRSR2 completion\n");
+ return ret;
+ }
+
+ /* Read back and check it. */
+ ret = nor->read_reg(nor, SPINOR_OP_RDSR2, &sr2, 1);
+ if (ret || !(sr2 & SR2_QUAD_EN_BIT7)) {
+ dev_err(nor->dev, "SR2 Quad bit not set\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int spi_nor_check(struct spi_nor *nor)
{
if (!nor->dev || !nor->read || !nor->write ||
@@ -1369,11 +1464,381 @@ static inline void spi_nor_set_erase_settings(struct spi_nor_erase_type *erase,
erase->opcode = opcode;
}
+static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr,
+ size_t len, void *buf)
+{
+ u8 addr_width, read_opcode, read_dummy;
+ int ret;
+
+ read_opcode = nor->read_opcode;
+ addr_width = nor->addr_width;
+ read_dummy = nor->read_dummy;
+
+ nor->read_opcode = SPINOR_OP_RDSFDP;
+ nor->addr_width = 3;
+ nor->read_dummy = 8;
+
+ ret = nor->read(nor, addr, len, (u8 *)buf);
+
+ nor->read_opcode = read_opcode;
+ nor->addr_width = addr_width;
+ nor->read_dummy = read_dummy;
+
+ return (ret < 0) ? ret : 0;
+}
+
+struct sfdp_parameter_header {
+ u8 id_lsb;
+ u8 minor;
+ u8 major;
+ u8 length; /* in double words */
+ u8 parameter_table_pointer[3]; /* byte address */
+ u8 id_msb;
+};
+
+#define SFDP_PARAM_HEADER_ID(p) ((u16)(((p)->id_msb << 8) | (p)->id_lsb))
+#define SFDP_PARAM_HEADER_PTP(p) \
+ ((u32)(((p)->parameter_table_pointer[2] << 16) | \
+ ((p)->parameter_table_pointer[1] << 8) | \
+ ((p)->parameter_table_pointer[0] << 0)))
+
+
+#define SFDP_BFPT_ID 0xff00u /* Basic Flash Parameter Table */
+
+#define SFDP_SIGNATURE 0x50444653u
+#define SFDP_JESD216_MAJOR 1
+#define SFDP_JESD216_MINOR 0
+#define SFDP_JESD216A_MINOR 5
+#define SFDP_JESD216B_MINOR 6
+
+struct sfdp_header {
+ u32 signature; /* Ox50444653 <=> "SFDP" */
+ u8 minor;
+ u8 major;
+ u8 nph; /* 0-base number of parameter headers */
+ u8 unused;
+
+ /* Basic Flash Parameter Table. */
+ struct sfdp_parameter_header bfpt_header;
+};
+
+/* Basic Flash Parameter Table */
+
+/* 1st DWORD. */
+#define BFPT_WORD0_FAST_READ_1_1_2 BIT(16)
+#define BFPT_WORD0_ADDRESS_BYTES_MASK GENMASK(18, 17)
+#define BFPT_WORD0_ADDRESS_BYTES_3_ONLY (0u << 17)
+#define BFPT_WORD0_ADDRESS_BYTES_3_OR_4 (1u << 17)
+#define BFPT_WORD0_ADDRESS_BYTES_4_ONLY (2u << 17)
+#define BFPT_WORD0_DTR BIT(19)
+#define BFPT_WORD0_FAST_READ_1_2_2 BIT(20)
+#define BFPT_WORD0_FAST_READ_1_4_4 BIT(21)
+#define BFPT_WORD0_FAST_READ_1_1_4 BIT(22)
+
+/* 15th DWORD. */
+
+/*
+ * (from JESD216B)
+ * Quad Enable Requirements (QER):
+ * - 000b: Device does not have a QE bit. Device detects 1-1-4 and 1-4-4
+ * reads based on instruction. DQ3/HOLD# functions are hold during
+ * instruction pahse.
+ * - 001b: QE is bit 1 of status register 2. It is set via Write Status with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ * Writing only one byte to the status register has the side-effect of
+ * clearing status register 2, including the QE bit. The 100b code is
+ * used if writing one byte to the status register does not modify
+ * status register 2.
+ * - 010b: QE is bit 6 of status register 1. It is set via Write Status with
+ * one data byte where bit 6 is one.
+ * [...]
+ * - 011b: QE is bit 7 of status register 2. It is set via Write status
+ * register 2 instruction 3Eh with one data byte where bit 7 is one.
+ * [...]
+ * The status register 2 is read using instruction 3Fh.
+ * - 100b: QE is bit 1 of status register 2. It is set via Write Status with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ * In contrast to the 001b code, writing one byte to the status
+ * register does not modify status register 2.
+ * - 101b: QE is bit 1 of status register 2. Status register 1 is read using
+ * Read Status instruction 05h. Status register2 is read using
+ * instruction 35h. QE is set via Writ Status instruction 01h with
+ * two data bytes where bit 1 of the second byte is one.
+ * [...]
+ */
+#define BFPT_WORD14_QER_MASK GENMASK(22, 20)
+#define BFPT_WORD14_QER_NONE (0 << 20) /* Micron */
+#define BFPT_WORD14_QER_SR2_BIT1_BUGGY (1 << 20)
+#define BFPT_WORD14_QER_SR1_BIT6 (2 << 20) /* Macronix */
+#define BFPT_WORD14_QER_SR2_BIT7 (3 << 20)
+#define BFPT_WORD14_QER_SR2_BIT1_NO_RD (4 << 20)
+#define BFPT_WORD14_QER_SR2_BIT1 (5 << 20) /* Spansion */
+
+/* JESD216B defines a Basic Flash Parameter Table of 16 words. */
+#define SFDP_BFPT_MAX_WORDS 16
+
+struct sfdp_bfpt {
+ u32 word[SFDP_BFPT_MAX_WORDS];
+};
+
+/* Fast Read settings. */
+#define BFPT_FAST_READ_WAIT_STATES(half) ((((u16)(half)) >> 0) & 0x1f)
+#define BFPT_FAST_READ_MODE_CLOCKS(half) ((((u16)(half)) >> 5) & 0x07)
+#define BFPT_FAST_READ_OP_CODE(half) ((((u16)(half)) >> 8) & 0xff)
+
+struct sfdp_read {
+ /* The protocol index of SPI x-y-z. */
+ enum spi_nor_protocol_index pindex;
+
+ /*
+ * The bit <wbit> in DWORD<windex> tells us whether the
+ * Fast Read x-y-z command is supported.
+ */
+ int windex;
+ int wbit;
+
+ /*
+ * The half-word at offset <hshift> in DWORD<hindex> encodes the
+ * op code, the number of mode clocks and the number of wait states
+ * to be used by Fast Read x-y-z commands.
+ */
+ int hindex;
+ int hshift;
+};
+
+static const struct sfdp_read sfdp_reads[] = {
+ /* Supported: DWORD0 bit 16, Settings: DWORD3 bit[15:0] */
+ {SNOR_PINDEX_1_1_2, 0, 16, 3, 0},
+
+ /* Supported: DWORD0 bit 20, Settings: DWORD3 bit[31:16] */
+ {SNOR_PINDEX_1_2_2, 0, 20, 3, 16},
+
+ /* Supported: DWORD4 bit 0, Settings: DWORD5 bit[31:16] */
+ {SNOR_PINDEX_2_2_2, 4, 0, 5, 16},
+
+ /* Supported: DWORD0 bit 22, Settings: DWORD2 bit[31:16] */
+ {SNOR_PINDEX_1_1_4, 0, 22, 2, 16},
+
+ /* Supported: DWORD0 bit 21, Settings: DWORD2 bit[15:0] */
+ {SNOR_PINDEX_1_4_4, 0, 21, 2, 0},
+
+ /* Supported: DWORD4 bit 4, Settings: DWORD6 bit[31:16] */
+ {SNOR_PINDEX_4_4_4, 4, 4, 6, 16},
+};
+
+
+/* Sector Erase settings. */
+#define BFPT_ERASE_SIZE(half) ((((u16)(half)) >> 0) & 0xff)
+#define BFPT_ERASE_OP_CODE(half) ((((u16)(half)) >> 8) & 0xff)
+
+struct sfdp_erase {
+ /*
+ * The half-word at offset <hshift> in DWORD<hindex> encodes the
+ * op code and erase sector size to be used by Sector Erase commands.
+ */
+ int hindex;
+ int hshift;
+};
+
+static const struct sfdp_erase sfdp_erases[SNOR_MAX_ERASE_TYPES] = {
+ /* Erase Type 1 in DWORD7 bits[15:0] */
+ {7, 0},
+
+ /* Erase Type 2 in DWORD7 bits[31:16] */
+ {7, 16},
+
+ /* Erase Type 3 in DWORD8 bits[15:0] */
+ {8, 0},
+
+ /* Erase Type 4: in DWORD8 bits[31:16] */
+ {8, 16},
+};
+
+
+static int spi_nor_parse_bfpt(struct spi_nor *nor,
+ const struct sfdp_parameter_header *bfpt_header,
+ struct spi_nor_basic_flash_parameter *params)
+{
+ struct sfdp_bfpt bfpt;
+ size_t len;
+ int i, err;
+ u32 addr;
+ u16 half;
+
+ /* JESD216 Basic Flash Parameter Table length is at least 9 words. */
+ if (bfpt_header->length < 9)
+ return -EINVAL;
+
+ /* Read the Basic Flash Parameter Table. */
+ len = min_t(size_t, sizeof(bfpt),
+ bfpt_header->length * sizeof(uint32_t));
+ addr = SFDP_PARAM_HEADER_PTP(bfpt_header);
+ memset(&bfpt, 0, sizeof(bfpt));
+ err = spi_nor_read_sfdp(nor, addr, len, &bfpt);
+ if (err)
+ return err;
+
+ for (i = 0; i < SFDP_BFPT_MAX_WORDS; ++i)
+ bfpt.word[i] = le32_to_cpu(bfpt.word[i]);
+
+ memset(params, 0, sizeof(*params));
+
+ /* Fast Read settings. */
+ params->rd_modes = (SNOR_MODE_SLOW | SNOR_MODE_1_1_1);
+ spi_nor_set_read_settings(¶ms->reads[SNOR_PINDEX_SLOW],
+ 0, 0, SPINOR_OP_READ);
+ spi_nor_set_read_settings(¶ms->reads[SNOR_PINDEX_1_1_1],
+ 0, 8, SPINOR_OP_READ_FAST);
+
+ for (i = 0; i < ARRAY_SIZE(sfdp_reads); ++i) {
+ const struct sfdp_read *rd_info = &sfdp_reads[i];
+ struct spi_nor_read *read = ¶ms->reads[rd_info->pindex];
+
+ if (!(bfpt.word[rd_info->windex] & BIT(rd_info->wbit)))
+ continue;
+
+ params->rd_modes |= BIT(rd_info->pindex);
+ half = bfpt.word[rd_info->hindex] >> rd_info->hshift;
+ read->num_mode_clocks = BFPT_FAST_READ_MODE_CLOCKS(half);
+ read->num_wait_states = BFPT_FAST_READ_WAIT_STATES(half);
+ read->opcode = BFPT_FAST_READ_OP_CODE(half);
+ }
+
+ /* Page Program settings. */
+ params->wr_modes = SNOR_MODE_1_1_1;
+ params->page_programs[SNOR_PINDEX_1_1_1] = SPINOR_OP_PP;
+
+ /* Sector Erase settings. */
+ for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i) {
+ const struct sfdp_erase *er_info = &sfdp_erases[i];
+ struct spi_nor_erase_type *erase = ¶ms->erase_types[i];
+
+ half = bfpt.word[er_info->hindex] >> er_info->hshift;
+ erase->size = BFPT_ERASE_SIZE(half);
+ erase->opcode = BFPT_ERASE_OP_CODE(half);
+ }
+
+ /* Stop here if not JESD216 rev A or later. */
+ if (bfpt_header->length < 15)
+ return 0;
+
+ /* Enable Quad I/O. */
+ switch (bfpt.word[14] & BFPT_WORD14_QER_MASK) {
+ default:
+ case BFPT_WORD14_QER_NONE:
+ break;
+
+ case BFPT_WORD14_QER_SR2_BIT1_BUGGY:
+ case BFPT_WORD14_QER_SR2_BIT1_NO_RD:
+ params->enable_quad_io = spansion_quad_enable;
+ break;
+
+ case BFPT_WORD14_QER_SR1_BIT6:
+ params->enable_quad_io = macronix_quad_enable;
+ break;
+
+ case BFPT_WORD14_QER_SR2_BIT7:
+ params->enable_quad_io = sr2_bit7_quad_enable;
+ break;
+
+ case BFPT_WORD14_QER_SR2_BIT1:
+ params->enable_quad_io = spansion_new_quad_enable;
+ break;
+ }
+
+ return 0;
+}
+
+static int spi_nor_parse_sfdp(struct spi_nor *nor,
+ struct spi_nor_basic_flash_parameter *params)
+{
+ const struct sfdp_parameter_header *param_header, *bfpt_header;
+ struct sfdp_parameter_header *param_headers = NULL;
+ struct sfdp_header header;
+ size_t psize;
+ int i, err;
+
+ /* Get the SFDP header. */
+ err = spi_nor_read_sfdp(nor, 0, sizeof(header), &header);
+ if (err)
+ return err;
+
+ /* Check the SFDP header version. */
+ if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
+ header.major != SFDP_JESD216_MAJOR ||
+ header.minor < SFDP_JESD216_MINOR)
+ return -EINVAL;
+
+ /*
+ * Verify that the first and only mandatory parameter header is a
+ * Basic Flash Parameter Table header as specified in JESD216.
+ */
+ bfpt_header = &header.bfpt_header;
+ if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||
+ bfpt_header->major != SFDP_JESD216_MAJOR)
+ return -EINVAL;
+
+ /* Allocate memory for parameter headers. */
+ if (header.nph) {
+ psize = header.nph * sizeof(*param_headers);
+
+ param_headers = kmalloc(psize, GFP_KERNEL);
+ if (!param_headers) {
+ dev_err(nor->dev,
+ "failed to allocate memory for SFDP parameter headers\n");
+ return -ENOMEM;
+ }
+
+ err = spi_nor_read_sfdp(nor, sizeof(header),
+ psize, param_headers);
+ if (err) {
+ dev_err(nor->dev,
+ "failed to read SFDP parameter headers\n");
+ goto exit;
+ }
+ }
+
+ /*
+ * Check other parameter headers to get the latest revision of
+ * the basic flash parameter table.
+ */
+ for (i = 0; i < header.nph; ++i) {
+ param_header = ¶m_headers[i];
+
+ if (SFDP_PARAM_HEADER_ID(param_header) == SFDP_BFPT_ID &&
+ param_header->major == SFDP_JESD216_MAJOR &&
+ (param_header->minor > bfpt_header->minor ||
+ (param_header->minor == bfpt_header->minor &&
+ param_header->length > bfpt_header->length)))
+ bfpt_header = param_header;
+ }
+ err = spi_nor_parse_bfpt(nor, bfpt_header, params);
+ if (err)
+ goto exit;
+
+exit:
+ kfree(param_headers);
+ return (err) ? err : bfpt_header->minor;
+}
+
static int spi_nor_init_params(struct spi_nor *nor,
const struct flash_info *info,
struct spi_nor_basic_flash_parameter *params)
{
- // TODO: parse SFDP table
+ int jesd216_minor = -EINVAL;
+
+ /* First trying to parse SFDP tables. */
+ if (!(info->flags & SPI_NOR_SKIP_SFDP)) {
+ jesd216_minor = spi_nor_parse_sfdp(nor, params);
+
+ if (jesd216_minor >= SFDP_JESD216A_MINOR)
+ return 0;
+
+ if (jesd216_minor >= SFDP_JESD216_MINOR)
+ goto set_enable_quad_io;
+ }
/* If SFDP tables are not available, use legacy settings. */
memset(params, 0, sizeof(*params));
@@ -1412,6 +1877,7 @@ static int spi_nor_init_params(struct spi_nor *nor,
spi_nor_set_erase_settings(¶ms->erase_types[1],
SNOR_ERASE_4K, SPINOR_OP_BE_4K_PMC);
+set_enable_quad_io:
/* Select the procedure to set the Quad Enable bit. */
if (params->rd_modes & (SNOR_MODE_1_1_4 |
SNOR_MODE_1_4_4 |
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 88ac446b1230..8fd9619dabff 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -41,6 +41,8 @@
#define SPINOR_OP_WREN 0x06 /* Write enable */
#define SPINOR_OP_RDSR 0x05 /* Read status register */
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
+#define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */
+#define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */
#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 Output SPI) */
@@ -56,6 +58,7 @@
#define SPINOR_OP_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define SPINOR_OP_SE 0xd8 /* Sector erase (usually 64KiB) */
#define SPINOR_OP_RDID 0x9f /* Read JEDEC ID */
+#define SPINOR_OP_RDSFDP 0x5a /* Read SFDP */
#define SPINOR_OP_RDCR 0x35 /* Read configuration register */
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
@@ -110,6 +113,9 @@
/* Configuration Register bits. */
#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */
+/* Status Register 2 bits. */
+#define SR2_QUAD_EN_BIT7 BIT(7)
+
/* Supported SPI protocols */
enum spi_nor_protocol_class {
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-11-21 15:20 +0100 |
| Subject | [PATCH v4 1/8] mtd: spi-nor: improve macronix_quad_enable() |
| Message-ID | <sFXBU-2h6-41@gated-at.bofh.it> |
| In reply to | #1526702 |
The patch checks whether the Quad Enable bit is already set in the Status Register. If so, the function exits immediately with a successful return code. Otherwise, a message is now printed telling we're setting the non-volatile bit. Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> Reviewed-by: Jagan Teki <jagan@openedev.com> --- drivers/mtd/spi-nor/spi-nor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index d0fc165d7d66..7ff807211e1e 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1211,6 +1211,11 @@ static int macronix_quad_enable(struct spi_nor *nor) val = read_sr(nor); if (val < 0) return val; + if (val & SR_QUAD_EN_MX) + return 0; + + /* Update the Quad Enable bit. */ + dev_dbg(nor->dev, "setting Macronix Quad Enable (non-volatile) bit\n"); write_enable(nor); write_sr(nor, val | SR_QUAD_EN_MX); -- 2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Cyrille Pitchen <cyrille.pitchen@atmel.com> |
|---|---|
| Date | 2016-11-21 15:20 +0100 |
| Subject | [PATCH v4 4/8] mtd: spi-nor: add support of SPI protocols like SPI 1-2-2 and SPI 1-4-4 |
| Message-ID | <sFXBU-2h6-45@gated-at.bofh.it> |
| In reply to | #1526702 |
This patch changes the prototype of spi_nor_scan(): its 3rd parameter
is replaced by a const struct spi_nor_modes pointer, which tells the
spi-nor framework about which SPI protocols are supported by the SPI
controller.
Besides, this patch also introduces a new spi_nor_basic_flash_parameter
structure telling the spi-nor framework about the SPI protocols supported
by the SPI memory and the needed op codes to use these SPI protocols.
Currently the struct spi_nor_basic_flash_parameter is filled with legacy
values but a later patch will allow to fill it dynamically by reading the
JESD216 Serial Flash Discoverable Parameter (SFDP) tables from the SPI
memory.
With both structures, the spi-nor framework can now compute the best
match between SPI protocols supported by both the (Q)SPI memory and
controller hence selecting the relevant op codes for (Fast) Read, Page
Program and Sector Erase operations.
The spi_nor_basic_flash_parameter structure also provides the spi-nor
framework with the number of dummy cycles to be used with each Fast Read
commands and the erase block size associated to the erase block op codes.
Finally the spi_nor_basic_flash_parameter structure, through the optional
.enable_quad_io() hook, tells the spi-nor framework how to set the Quad
Enable (QE) bit of the QSPI memory to enable its Quad SPI features.
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
drivers/mtd/devices/m25p80.c | 17 ++-
drivers/mtd/spi-nor/atmel-quadspi.c | 83 ++++++----
drivers/mtd/spi-nor/cadence-quadspi.c | 18 ++-
drivers/mtd/spi-nor/fsl-quadspi.c | 8 +-
drivers/mtd/spi-nor/hisi-sfc.c | 32 +++-
drivers/mtd/spi-nor/mtk-quadspi.c | 16 +-
drivers/mtd/spi-nor/nxp-spifi.c | 21 +--
drivers/mtd/spi-nor/spi-nor.c | 280 +++++++++++++++++++++++++++-------
include/linux/mtd/spi-nor.h | 136 +++++++++++++++--
9 files changed, 482 insertions(+), 129 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 9cf7fcd28034..f0a55c01406b 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -111,10 +111,10 @@ static ssize_t 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;
@@ -195,7 +195,10 @@ 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 = {
+ .rd_modes = SNOR_MODE_SLOW,
+ .wr_modes = SNOR_MODE_1_1_1,
+ };
char *flash_name;
int ret;
@@ -221,9 +224,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;
@@ -240,7 +243,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/atmel-quadspi.c b/drivers/mtd/spi-nor/atmel-quadspi.c
index 47937d9beec6..9f7e3124e8b8 100644
--- a/drivers/mtd/spi-nor/atmel-quadspi.c
+++ b/drivers/mtd/spi-nor/atmel-quadspi.c
@@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
static int atmel_qspi_run_command(struct atmel_qspi *aq,
const struct atmel_qspi_command *cmd,
- u32 ifr_tfrtyp, u32 ifr_width)
+ u32 ifr_tfrtyp, enum spi_nor_protocol proto)
{
u32 iar, icr, ifr, sr;
int err = 0;
iar = 0;
icr = 0;
- ifr = ifr_tfrtyp | ifr_width;
+ ifr = ifr_tfrtyp;
+
+ /* Set the SPI protocol */
+ switch (proto) {
+ case SNOR_PROTO_1_1_1:
+ ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
+ break;
+
+ case SNOR_PROTO_1_1_2:
+ ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
+ break;
+
+ case SNOR_PROTO_1_1_4:
+ ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
+ break;
+
+ case SNOR_PROTO_1_2_2:
+ ifr |= QSPI_IFR_WIDTH_DUAL_IO;
+ break;
+
+ case SNOR_PROTO_1_4_4:
+ ifr |= QSPI_IFR_WIDTH_QUAD_IO;
+ break;
+
+ case SNOR_PROTO_2_2_2:
+ ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
+ break;
+
+ case SNOR_PROTO_4_4_4:
+ ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
+ break;
+
+ default:
+ return -EINVAL;
+ }
/* Compute instruction parameters */
if (cmd->enable.bits.instruction) {
@@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
cmd.rx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->reg_proto);
}
static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
@@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
cmd.tx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->reg_proto);
}
static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
@@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
cmd.tx_buf = write_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->write_proto);
return (ret < 0) ? ret : len;
}
@@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
cmd.instruction = nor->erase_opcode;
cmd.address = (u32)offs;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
- QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
+ nor->reg_proto);
}
static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
@@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
u8 num_mode_cycles, num_dummy_cycles;
- u32 ifr_width;
ssize_t ret;
- switch (nor->flash_read) {
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
- ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
- break;
-
- case SPI_NOR_DUAL:
- ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
- break;
-
- case SPI_NOR_QUAD:
- ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
- break;
-
- default:
- return -EINVAL;
- }
-
if (nor->read_dummy >= 2) {
num_mode_cycles = 2;
num_dummy_cycles = nor->read_dummy - 2;
@@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
cmd.rx_buf = read_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
- ifr_width);
+ nor->read_proto);
return (ret < 0) ? ret : len;
}
@@ -596,6 +611,20 @@ static int atmel_qspi_probe(struct platform_device *pdev)
struct spi_nor *nor;
struct mtd_info *mtd;
int irq, err = 0;
+ struct spi_nor_modes modes = {
+ .rd_modes = (SNOR_MODE_SLOW |
+ SNOR_MODE_1_1_1 |
+ SNOR_MODE_1_1_2 |
+ SNOR_MODE_1_1_4 |
+ SNOR_MODE_1_2_2 |
+ SNOR_MODE_1_4_4),
+ .wr_modes = (SNOR_MODE_SLOW |
+ SNOR_MODE_1_1_1 |
+ SNOR_MODE_1_1_2 |
+ SNOR_MODE_1_1_4 |
+ SNOR_MODE_1_2_2 |
+ SNOR_MODE_1_4_4),
+ };
if (of_get_child_count(np) != 1)
return -ENODEV;
@@ -679,7 +708,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
if (err)
goto disable_clk;
- err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ err = spi_nor_scan(nor, NULL, &modes);
if (err)
goto disable_clk;
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index d403ba7b8f43..87e49231b4ee 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -853,15 +853,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
if (read) {
- switch (nor->flash_read) {
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
+ switch (nor->read_proto) {
+ case SNOR_PROTO_1_1_1:
f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
break;
- case SPI_NOR_DUAL:
+ case SNOR_PROTO_1_1_2:
f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
break;
- case SPI_NOR_QUAD:
+ case SNOR_PROTO_1_1_4:
f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
break;
default:
@@ -1074,6 +1073,13 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
struct mtd_info *mtd;
unsigned int cs;
int i, ret;
+ static const struct spi_nor_modes modes = {
+ .rd_modes = (SNOR_MODE_SLOW |
+ SNOR_MODE_1_1_1 |
+ SNOR_MODE_1_1_2 |
+ SNOR_MODE_1_1_4),
+ .wr_modes = SNOR_MODE_1_1_1,
+ };
/* Get flash device data */
for_each_available_child_of_node(dev->of_node, np) {
@@ -1119,7 +1125,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
goto err;
}
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, &modes);
if (ret)
goto err;
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c
index 5c82e4ef1904..01e7356d6623 100644
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
@@ -980,6 +980,12 @@ static int fsl_qspi_probe(struct platform_device *pdev)
struct spi_nor *nor;
struct mtd_info *mtd;
int ret, i = 0;
+ static const struct spi_nor_modes modes = {
+ .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)
@@ -1081,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/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
index 20378b0d55e9..e4d7625b4397 100644
--- a/drivers/mtd/spi-nor/hisi-sfc.c
+++ b/drivers/mtd/spi-nor/hisi-sfc.c
@@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
}
-static int get_if_type(enum read_mode flash_read)
+static int get_if_type(enum spi_nor_protocol proto)
{
enum hifmc_iftype if_type;
- switch (flash_read) {
- case SPI_NOR_DUAL:
+ switch (proto) {
+ case SNOR_PROTO_1_1_2:
if_type = IF_TYPE_DUAL;
break;
- case SPI_NOR_QUAD:
+ case SNOR_PROTO_1_2_2:
+ if_type = IF_TYPE_DIO;
+ break;
+ case SNOR_PROTO_1_1_4:
if_type = IF_TYPE_QUAD;
break;
- case SPI_NOR_NORMAL:
- case SPI_NOR_FAST:
+ case SNOR_PROTO_1_4_4:
+ if_type = IF_TYPE_QIO;
+ break;
+ case SNOR_PROTO_1_1_1:
default:
if_type = IF_TYPE_STD;
break;
@@ -238,6 +243,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
{
struct hifmc_priv *priv = nor->priv;
struct hifmc_host *host = priv->host;
+ enum spi_nor_protocol proto;
u8 if_type = 0;
u32 reg;
@@ -253,7 +259,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
reg = OP_CFG_FM_CS(priv->chipselect);
- if_type = get_if_type(nor->flash_read);
+ proto = (op_type == FMC_OP_READ)
+ ? nor->read_proto
+ : nor->write_proto;
+ if_type = get_if_type(proto);
reg |= OP_CFG_MEM_IF_TYPE(if_type);
if (op_type == FMC_OP_READ)
reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
@@ -326,6 +335,13 @@ static int hisi_spi_nor_register(struct device_node *np,
struct hifmc_priv *priv;
struct mtd_info *mtd;
int ret;
+ static const struct spi_nor_modes modes = {
+ .rd_modes = (SNOR_MODE_SLOW |
+ SNOR_MODE_1_1_1 |
+ SNOR_MODE_1_1_2 |
+ SNOR_MODE_1_1_4),
+ .wr_modes = SNOR_MODE_1_1_1,
+ };
nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL);
if (!nor)
@@ -362,7 +378,7 @@ static int hisi_spi_nor_register(struct device_node *np,
nor->read = hisi_spi_nor_read;
nor->write = hisi_spi_nor_write;
nor->erase = NULL;
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, &modes);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
index e661877c23de..4dc2bb8eb488 100644
--- a/drivers/mtd/spi-nor/mtk-quadspi.c
+++ b/drivers/mtd/spi-nor/mtk-quadspi.c
@@ -121,20 +121,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 +
@@ -383,6 +383,12 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
{
int ret;
struct spi_nor *nor;
+ static const struct spi_nor_modes modes = {
+ .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);
@@ -399,7 +405,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 73a14f40928b..327c5b5fe9da 100644
--- a/drivers/mtd/spi-nor/nxp-spifi.c
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
@@ -240,13 +240,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:
@@ -274,7 +273,10 @@ 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 = {
+ .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;
@@ -308,13 +310,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)) {
@@ -351,7 +352,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 fd39516fef35..01e9b40c825f 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -143,24 +143,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.
*/
@@ -1394,8 +1376,206 @@ 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 inline void spi_nor_set_read_settings(struct spi_nor_read *read,
+ u8 num_mode_clocks,
+ u8 num_wait_states,
+ u8 opcode)
+{
+ read->num_mode_clocks = num_mode_clocks;
+ read->num_wait_states = num_wait_states;
+ read->opcode = opcode;
+}
+
+static inline void spi_nor_set_erase_settings(struct spi_nor_erase_type *erase,
+ u8 size, u8 opcode)
+{
+ erase->size = size;
+ erase->opcode = opcode;
+}
+
+static int spi_nor_init_params(struct spi_nor *nor,
+ const struct flash_info *info,
+ struct spi_nor_basic_flash_parameter *params)
+{
+ // TODO: parse SFDP table
+
+ /* If SFDP tables are not available, use legacy settings. */
+ memset(params, 0, sizeof(*params));
+
+ /* (Fast) Read settings. */
+ params->rd_modes = SNOR_MODE_SLOW;
+ spi_nor_set_read_settings(¶ms->reads[SNOR_PINDEX_SLOW],
+ 0, 0, SPINOR_OP_READ);
+ if (!(info->flags & SPI_NOR_NO_FR)) {
+ params->rd_modes |= SNOR_MODE_1_1_1;
+ spi_nor_set_read_settings(¶ms->reads[SNOR_PINDEX_1_1_1],
+ 0, 8, SPINOR_OP_READ_FAST);
+ }
+ if (info->flags & SPI_NOR_DUAL_READ) {
+ params->rd_modes |= SNOR_MODE_1_1_2;
+ spi_nor_set_read_settings(¶ms->reads[SNOR_PINDEX_1_1_2],
+ 0, 8, SPINOR_OP_READ_1_1_2);
+ }
+ if (info->flags & SPI_NOR_QUAD_READ) {
+ params->rd_modes |= SNOR_MODE_1_1_4;
+ spi_nor_set_read_settings(¶ms->reads[SNOR_PINDEX_1_1_4],
+ 0, 8, SPINOR_OP_READ_1_1_4);
+ }
+
+ /* Page Program settings. */
+ params->wr_modes = SNOR_MODE_1_1_1;
+ params->page_programs[SNOR_PINDEX_1_1_1] = SPINOR_OP_PP;
+
+ /* Sector Erase settings. */
+ spi_nor_set_erase_settings(¶ms->erase_types[0],
+ SNOR_ERASE_64K, SPINOR_OP_SE);
+ if (info->flags & SECT_4K)
+ spi_nor_set_erase_settings(¶ms->erase_types[1],
+ SNOR_ERASE_4K, SPINOR_OP_BE_4K);
+ else if (info->flags & SECT_4K_PMC)
+ spi_nor_set_erase_settings(¶ms->erase_types[1],
+ SNOR_ERASE_4K, SPINOR_OP_BE_4K_PMC);
+
+ /* Select the procedure to set the Quad Enable bit. */
+ if (params->rd_modes & (SNOR_MODE_1_1_4 |
+ SNOR_MODE_1_4_4 |
+ SNOR_MODE_4_4_4)) {
+ switch (JEDEC_MFR(info)) {
+ case SNOR_MFR_MACRONIX:
+ params->enable_quad_io = macronix_quad_enable;
+ break;
+
+ case SNOR_MFR_MICRON:
+ break;
+
+ default:
+ params->enable_quad_io = spansion_quad_enable;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int spi_nor_pindex2proto(int pindex, enum spi_nor_protocol *proto)
+{
+ enum spi_nor_protocol_width pwidth;
+ enum spi_nor_protocol_class pclass;
+ uint8_t width;
+
+ if (pindex < 0)
+ return -EINVAL;
+
+ pwidth = (enum spi_nor_protocol_width)(pindex / SNOR_PCLASS_MAX);
+ pclass = (enum spi_nor_protocol_class)(pindex % SNOR_PCLASS_MAX);
+
+ width = (1 << pwidth) & 0xf;
+ if (!width)
+ return -EINVAL;
+
+ switch (pclass) {
+ case SNOR_PCLASS_1_1_N:
+ *proto = SNOR_PROTO(1, 1, width);
+ break;
+
+ case SNOR_PCLASS_1_N_N:
+ *proto = SNOR_PROTO(1, width, width);
+ break;
+
+ case SNOR_PCLASS_N_N_N:
+ *proto = SNOR_PROTO(width, width, width);
+ 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;
+ u32 rd_modes, wr_modes, mask;
+ const struct spi_nor_erase_type *erase_type = NULL;
+ const struct spi_nor_read *read;
+ int rd_pindex, wr_pindex, i, err = 0;
+ u8 erase_size = SNOR_ERASE_64K;
+
+ /* 2-2-2 or 4-4-4 modes are not supported yet. */
+ mask = (SNOR_MODE_2_2_2 | SNOR_MODE_4_4_4);
+ rd_modes = modes->rd_modes & ~mask;
+ wr_modes = modes->wr_modes & ~mask;
+
+ /* Setup read operation. */
+ rd_pindex = fls(params->rd_modes & rd_modes) - 1;
+ if (spi_nor_pindex2proto(rd_pindex, &nor->read_proto)) {
+ dev_err(nor->dev, "invalid (fast) read\n");
+ return -EINVAL;
+ }
+ read = ¶ms->reads[rd_pindex];
+ nor->read_opcode = read->opcode;
+ nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
+
+ /* Set page program op code and protocol. */
+ wr_pindex = fls(params->wr_modes & wr_modes) - 1;
+ if (spi_nor_pindex2proto(wr_pindex, &nor->write_proto)) {
+ dev_err(nor->dev, "invalid page program\n");
+ return -EINVAL;
+ }
+ nor->program_opcode = params->page_programs[wr_pindex];
+
+ /* Set sector erase op code and size. */
+ erase_type = ¶ms->erase_types[0];
+#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
+ erase_size = SNOR_ERASE_4K;
+#endif
+ for (i = 0; i < SNOR_MAX_ERASE_TYPES; ++i) {
+ if (params->erase_types[i].size == erase_size) {
+ erase_type = ¶ms->erase_types[i];
+ break;
+ } else if (!erase_type->size && params->erase_types[i].size) {
+ erase_type = ¶ms->erase_types[i];
+ }
+ }
+ 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 Quad I/O if needed. */
+ if (enable_quad_io && params->enable_quad_io) {
+ err = params->enable_quad_io(nor);
+ if (err) {
+ dev_err(nor->dev,
+ "failed to enable the Quad I/O mode\n");
+ return err;
+ }
+ }
+
+ 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=%u\n",
+ nor->erase_opcode, nor->reg_proto, (u32)nor->mtd.erasesize);
+
+ return 0;
+}
+
+int spi_nor_scan(struct spi_nor *nor, const char *name,
+ const struct spi_nor_modes *modes)
+{
+ struct spi_nor_basic_flash_parameter params;
+ struct spi_nor_modes fixed_modes = *modes;
const struct flash_info *info = NULL;
struct device *dev = nor->dev;
struct mtd_info *mtd = &nor->mtd;
@@ -1407,6 +1587,11 @@ 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->reg_proto = SNOR_PROTO_1_1_1;
+ nor->read_proto = SNOR_PROTO_1_1_1;
+ nor->write_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 */
@@ -1439,6 +1624,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
}
}
+ /* Parse the Serial Flash Discoverable Parameters table */
+ ret = spi_nor_init_params(nor, info, ¶ms);
+ if (ret)
+ return ret;
+
mutex_init(&nor->lock);
/*
@@ -1515,51 +1705,31 @@ 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;
+ fixed_modes.rd_modes |= SNOR_MODE_1_1_1;
else
- nor->flash_read = SPI_NOR_NORMAL;
+ fixed_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;
+ fixed_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;
-
- /* 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");
- return ret;
- }
- 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;
- }
+ fixed_modes.rd_modes &= ~SNOR_MODE_1_1_1;
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).
+ */
+ ret = spi_nor_setup(nor, info, ¶ms, &fixed_modes);
+ if (ret)
+ return ret;
+
if (info->addr_width)
nor->addr_width = info->addr_width;
else if (mtd->size > 0x1000000) {
@@ -1580,8 +1750,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 8b02fd7864d0..88ac446b1230 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -110,11 +110,124 @@
/* 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 SPI protocols */
+enum spi_nor_protocol_class {
+ SNOR_PCLASS_1_1_N,
+ SNOR_PCLASS_1_N_N,
+ SNOR_PCLASS_N_N_N,
+
+ SNOR_PCLASS_MAX
+};
+
+enum spi_nor_protocol_width {
+ SNOR_PWIDTH_1,
+ SNOR_PWIDTH_2,
+ SNOR_PWIDTH_4,
+ SNOR_PWIDTH_8,
+};
+
+/* The encoding is chosen so the higher index the higher priority */
+#define SNOR_PINDEX(pwidth, pclass) \
+ ((pwidth) * SNOR_PCLASS_MAX + (pclass))
+enum spi_nor_protocol_index {
+ SNOR_PINDEX_SLOW = SNOR_PINDEX(SNOR_PWIDTH_1, SNOR_PCLASS_1_1_N),
+ /* Little trick to make the difference between Read and Fast Read. */
+ SNOR_PINDEX_1_1_1 = SNOR_PINDEX(SNOR_PWIDTH_1, SNOR_PCLASS_1_N_N),
+ SNOR_PINDEX_1_1_2 = SNOR_PINDEX(SNOR_PWIDTH_2, SNOR_PCLASS_1_1_N),
+ SNOR_PINDEX_1_2_2 = SNOR_PINDEX(SNOR_PWIDTH_2, SNOR_PCLASS_1_N_N),
+ SNOR_PINDEX_2_2_2 = SNOR_PINDEX(SNOR_PWIDTH_2, SNOR_PCLASS_N_N_N),
+ SNOR_PINDEX_1_1_4 = SNOR_PINDEX(SNOR_PWIDTH_4, SNOR_PCLASS_1_1_N),
+ SNOR_PINDEX_1_4_4 = SNOR_PINDEX(SNOR_PWIDTH_4, SNOR_PCLASS_1_N_N),
+ SNOR_PINDEX_4_4_4 = SNOR_PINDEX(SNOR_PWIDTH_4, SNOR_PCLASS_N_N_N),
+
+ SNOR_PINDEX_MAX
+};
+
+#define SNOR_MODE_SLOW BIT(SNOR_PINDEX_SLOW)
+#define SNOR_MODE_1_1_1 BIT(SNOR_PINDEX_1_1_1)
+#define SNOR_MODE_1_1_2 BIT(SNOR_PINDEX_1_1_2)
+#define SNOR_MODE_1_2_2 BIT(SNOR_PINDEX_1_2_2)
+#define SNOR_MODE_2_2_2 BIT(SNOR_PINDEX_2_2_2)
+#define SNOR_MODE_1_1_4 BIT(SNOR_PINDEX_1_1_4)
+#define SNOR_MODE_1_4_4 BIT(SNOR_PINDEX_1_4_4)
+#define SNOR_MODE_4_4_4 BIT(SNOR_PINDEX_4_4_4)
+
+struct spi_nor_modes {
+ 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;
+};
+
+struct spi_nor_erase_type {
+ u8 size; /* specifies 'N' so erase size = 2^N */
+ u8 opcode;
+};
+
+#define SNOR_ERASE_64K 0x10
+#define SNOR_ERASE_32K 0x0f
+#define SNOR_ERASE_4K 0x0c
+
+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_PINDEX_MAX];
+
+ /* Page Program settings */
+ u32 wr_modes;
+ u8 page_programs[SNOR_PINDEX_MAX];
+
+ /* Sector Erase settings */
+ struct spi_nor_erase_type erase_types[SNOR_MAX_ERASE_TYPES];
+
+ int (*enable_quad_io)(struct spi_nor *nor);
+};
+
+
+#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
@@ -142,9 +255,11 @@ 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_*)
+ * @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
@@ -173,7 +288,9 @@ struct spi_nor {
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
- enum read_mode flash_read;
+ 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];
@@ -211,7 +328,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
@@ -221,6 +338,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,
+ const struct spi_nor_modes *modes);
#endif
--
2.7.4
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web