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.pitc...@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 7fc0138c1d68..db4874d4af79 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -1494,6 +1494,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
@@ -1741,6 +1742,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)
 {
@@ -1808,6 +1927,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 = &param_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;
@@ -2162,7 +2298,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

Reply via email to