From 5b24efe7d55a4bea6992ae1ee00707d77ec22575 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 1 Feb 2020 23:55:08 +0300 Subject: [PATCH 001/103] mtd: spi-nor: use le32_to_cpu_array() The driver calls le32_to_cpu() to convert the little-endian tables to a CPU endianness, where le32_to_cpus() should have been called. Was going to use that one... and then discovered a whole array converter, le32_to_cpu_array()! :-) Signed-off-by: Sergei Shtylyov Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 4fc632ec18fe..864ed6f49e87 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -3598,8 +3598,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, return err; /* Fix endianness of the BFPT DWORDs. */ - for (i = 0; i < BFPT_DWORD_MAX; i++) - bfpt.dwords[i] = le32_to_cpu(bfpt.dwords[i]); + le32_to_cpu_array(bfpt.dwords, BFPT_DWORD_MAX); /* Number of address bytes. */ switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { @@ -4057,7 +4056,7 @@ static int spi_nor_parse_smpt(struct spi_nor *nor, u32 *smpt; size_t len; u32 addr; - int i, ret; + int ret; /* Read the Sector Map Parameter Table. */ len = smpt_header->length * sizeof(*smpt); @@ -4071,8 +4070,7 @@ static int spi_nor_parse_smpt(struct spi_nor *nor, goto out; /* Fix endianness of the SMPT DWORDs. */ - for (i = 0; i < smpt_header->length; i++) - smpt[i] = le32_to_cpu(smpt[i]); + le32_to_cpu_array(smpt, smpt_header->length); sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length); if (IS_ERR(sector_map)) { @@ -4165,8 +4163,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, goto out; /* Fix endianness of the 4BAIT DWORDs. */ - for (i = 0; i < SFDP_4BAIT_DWORD_MAX; i++) - dwords[i] = le32_to_cpu(dwords[i]); + le32_to_cpu_array(dwords, SFDP_4BAIT_DWORD_MAX); /* * Compute the subset of (Fast) Read commands for which the 4-byte From 8c79fa6c44deac8042bd747527fea06a32738158 Mon Sep 17 00:00:00 2001 From: Jungseung Lee Date: Mon, 13 Jan 2020 14:59:05 +0900 Subject: [PATCH 002/103] mtd: spi-nor: introduce SR_BP_SHIFT define The shift variable of SR_BP is conclusive because the first bit of SR_BP is fixed on all known flashes. Replace ffs operation with SR_BP_SHIFT. Signed-off-by: Jungseung Lee Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 11 +++++------ include/linux/mtd/spi-nor.h | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 864ed6f49e87..b5ef17b2897a 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1767,7 +1767,6 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, struct mtd_info *mtd = &nor->mtd; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; - int shift = ffs(mask) - 1; int pow; if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) @@ -1778,7 +1777,7 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, *ofs = 0; *len = 0; } else { - pow = ((sr & mask) ^ mask) >> shift; + pow = ((sr & mask) ^ mask) >> SR_BP_SHIFT; *len = mtd->size >> pow; if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask) *ofs = 0; @@ -1860,7 +1859,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; - u8 shift = ffs(mask) - 1, pow, val; + u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; @@ -1909,7 +1908,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) */ pow = ilog2(mtd->size) - ilog2(lock_len); - val = mask - (pow << shift); + val = mask - (pow << SR_BP_SHIFT); if (val & ~mask) return -EINVAL; /* Don't "lock" with no region! */ @@ -1946,7 +1945,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; - u8 shift = ffs(mask) - 1, pow, val; + u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; @@ -1997,7 +1996,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) if (lock_len == 0) { val = 0; /* fully unlocked */ } else { - val = mask - (pow << shift); + val = mask - (pow << SR_BP_SHIFT); /* Some power-of-two sizes are not supported */ if (val & ~mask) return -EINVAL; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 5abd91cc6dfa..61be6ed33097 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -137,6 +137,8 @@ #define SR1_QUAD_EN_BIT6 BIT(6) +#define SR_BP_SHIFT 2 + /* Enhanced Volatile Configuration Register bits */ #define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */ From b7ad6be2ee67b12cba31d30deb34beab19acac57 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 19 Feb 2020 00:21:00 +0300 Subject: [PATCH 003/103] mtd: spi-nor: split spi_nor_spimem_xfer_data() spi_nor_spimem_xfer_data() being a helper function for the data reads/ writes contains 3 fragments that depend on the data direction; and I'm going to add another one to call the SPI dirmap API... I think this function should be split so that the common fragments are put into 2 functions, spi_nor_spimem_bounce() and spi_nor_spimem_exec_op() called from spi_nor_spimem_{read|write}_data(), and the data direction dependent bits moved back into those read/write functions -- that way we would be able to avoid *goto*s otherwise needed in the next patch adding the SPI dirmap support... Signed-off-by: Sergei Shtylyov Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 87 +++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index b5ef17b2897a..1ce9784d86e8 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -246,55 +246,45 @@ struct flash_info { #define JEDEC_MFR(info) ((info)->id[0]) /** - * spi_nor_spimem_xfer_data() - helper function to read/write data to - * flash's memory region + * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data + * transfer * @nor: pointer to 'struct spi_nor' * @op: pointer to 'struct spi_mem_op' template for transfer * - * Return: number of bytes transferred on success, -errno otherwise + * If we have to use the bounce buffer, the data field in @op will be updated. + * + * Return: true if the bounce buffer is needed, false if not */ -static ssize_t spi_nor_spimem_xfer_data(struct spi_nor *nor, - struct spi_mem_op *op) +static bool spi_nor_spimem_bounce(struct spi_nor *nor, struct spi_mem_op *op) { - bool usebouncebuf = false; - void *rdbuf = NULL; - const void *buf; - int ret; - - if (op->data.dir == SPI_MEM_DATA_IN) - buf = op->data.buf.in; - else - buf = op->data.buf.out; - - if (object_is_on_stack(buf) || !virt_addr_valid(buf)) - usebouncebuf = true; - - if (usebouncebuf) { + /* op->data.buf.in occupies the same memory as op->data.buf.out */ + if (object_is_on_stack(op->data.buf.in) || + !virt_addr_valid(op->data.buf.in)) { if (op->data.nbytes > nor->bouncebuf_size) op->data.nbytes = nor->bouncebuf_size; - - if (op->data.dir == SPI_MEM_DATA_IN) { - rdbuf = op->data.buf.in; - op->data.buf.in = nor->bouncebuf; - } else { - op->data.buf.out = nor->bouncebuf; - memcpy(nor->bouncebuf, buf, - op->data.nbytes); - } + op->data.buf.in = nor->bouncebuf; + return true; } - ret = spi_mem_adjust_op_size(nor->spimem, op); - if (ret) - return ret; + return false; +} - ret = spi_mem_exec_op(nor->spimem, op); - if (ret) - return ret; +/** + * spi_nor_spimem_exec_op() - execute a memory operation + * @nor: pointer to 'struct spi_nor' + * @op: pointer to 'struct spi_mem_op' template for transfer + * + * Return: 0 on success, -error otherwise. + */ +static int spi_nor_spimem_exec_op(struct spi_nor *nor, struct spi_mem_op *op) +{ + int error; - if (usebouncebuf && op->data.dir == SPI_MEM_DATA_IN) - memcpy(rdbuf, nor->bouncebuf, op->data.nbytes); + error = spi_mem_adjust_op_size(nor->spimem, op); + if (error) + return error; - return op->data.nbytes; + return spi_mem_exec_op(nor->spimem, op); } /** @@ -315,6 +305,8 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, SPI_MEM_OP_ADDR(nor->addr_width, from, 1), SPI_MEM_OP_DUMMY(nor->read_dummy, 1), SPI_MEM_OP_DATA_IN(len, buf, 1)); + bool usebouncebuf; + int error; /* get transfer protocols. */ op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); @@ -325,7 +317,16 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, /* convert the dummy cycles to the number of bytes */ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; - return spi_nor_spimem_xfer_data(nor, &op); + usebouncebuf = spi_nor_spimem_bounce(nor, &op); + + error = spi_nor_spimem_exec_op(nor, &op); + if (error) + return error; + + if (usebouncebuf) + memcpy(buf, op.data.buf.in, op.data.nbytes); + + return op.data.nbytes; } /** @@ -364,6 +365,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, SPI_MEM_OP_ADDR(nor->addr_width, to, 1), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(len, buf, 1)); + int error; op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); @@ -372,7 +374,14 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op.addr.nbytes = 0; - return spi_nor_spimem_xfer_data(nor, &op); + if (spi_nor_spimem_bounce(nor, &op)) + memcpy(nor->bouncebuf, buf, op.data.nbytes); + + error = spi_nor_spimem_exec_op(nor, &op); + if (error) + return error; + + return op.data.nbytes; } /** From df5c21002cf4bb9c755c6330d101487c5d530c10 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 19 Feb 2020 00:24:10 +0300 Subject: [PATCH 004/103] mtd: spi-nor: use spi-mem dirmap API Make use of the spi-mem direct mapping API to let advanced controllers optimize read/write operations when they support direct mapping. Based on the original patch by Boris Brezillon . Signed-off-by: Sergei Shtylyov Reviewed-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 94 +++++++++++++++++++++++++++++++---- include/linux/mtd/spi-nor.h | 6 +++ 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1ce9784d86e8..1224247b26cc 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -306,6 +306,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, SPI_MEM_OP_DUMMY(nor->read_dummy, 1), SPI_MEM_OP_DATA_IN(len, buf, 1)); bool usebouncebuf; + ssize_t nbytes; int error; /* get transfer protocols. */ @@ -319,14 +320,20 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, usebouncebuf = spi_nor_spimem_bounce(nor, &op); - error = spi_nor_spimem_exec_op(nor, &op); - if (error) - return error; + if (nor->dirmap.rdesc) { + nbytes = spi_mem_dirmap_read(nor->dirmap.rdesc, op.addr.val, + op.data.nbytes, op.data.buf.in); + } else { + error = spi_nor_spimem_exec_op(nor, &op); + if (error) + return error; + nbytes = op.data.nbytes; + } - if (usebouncebuf) - memcpy(buf, op.data.buf.in, op.data.nbytes); + if (usebouncebuf && nbytes > 0) + memcpy(buf, op.data.buf.in, nbytes); - return op.data.nbytes; + return nbytes; } /** @@ -365,6 +372,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, SPI_MEM_OP_ADDR(nor->addr_width, to, 1), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(len, buf, 1)); + ssize_t nbytes; int error; op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); @@ -377,11 +385,17 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, if (spi_nor_spimem_bounce(nor, &op)) memcpy(nor->bouncebuf, buf, op.data.nbytes); - error = spi_nor_spimem_exec_op(nor, &op); - if (error) - return error; + if (nor->dirmap.wdesc) { + nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val, + op.data.nbytes, op.data.buf.out); + } else { + error = spi_nor_spimem_exec_op(nor, &op); + if (error) + return error; + nbytes = op.data.nbytes; + } - return op.data.nbytes; + return nbytes; } /** @@ -5265,6 +5279,58 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } EXPORT_SYMBOL_GPL(spi_nor_scan); +static int spi_nor_create_read_dirmap(struct spi_nor *nor) +{ + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + SPI_MEM_OP_DUMMY(nor->read_dummy, 1), + SPI_MEM_OP_DATA_IN(0, NULL, 1)), + .offset = 0, + .length = nor->mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); + op->dummy.buswidth = op->addr.buswidth; + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + + /* convert the dummy cycles to the number of bytes */ + op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; + + nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, + &info); + return PTR_ERR_OR_ZERO(nor->dirmap.rdesc); +} + +static int spi_nor_create_write_dirmap(struct spi_nor *nor) +{ + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 1)), + .offset = 0, + .length = nor->mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); + op->dummy.buswidth = op->addr.buswidth; + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); + + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) + op->addr.nbytes = 0; + + nor->dirmap.wdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, + &info); + return PTR_ERR_OR_ZERO(nor->dirmap.wdesc); +} + static int spi_nor_probe(struct spi_mem *spimem) { struct spi_device *spi = spimem->spi; @@ -5326,6 +5392,14 @@ static int spi_nor_probe(struct spi_mem *spimem) return -ENOMEM; } + ret = spi_nor_create_read_dirmap(nor); + if (ret) + return ret; + + ret = spi_nor_create_write_dirmap(nor); + if (ret) + return ret; + return mtd_device_register(&nor->mtd, data ? data->parts : NULL, data ? data->nr_parts : 0); } diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 61be6ed33097..de90724f62f1 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -580,6 +580,7 @@ struct flash_info; * The structure includes legacy flash parameters and * settings that can be overwritten by the spi_nor_fixups * hooks, or dynamically when parsing the SFDP tables. + * @dirmap: pointers to struct spi_mem_dirmap_desc for reads/writes. * @priv: the private data */ struct spi_nor { @@ -606,6 +607,11 @@ struct spi_nor { struct spi_nor_flash_parameter params; + struct { + struct spi_mem_dirmap_desc *rdesc; + struct spi_mem_dirmap_desc *wdesc; + } dirmap; + void *priv; }; From 2dcbfe365b130321b1b48cfa55f7bdad6baf85ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Sun, 23 Feb 2020 18:37:13 +0100 Subject: [PATCH 005/103] mtd: spi-nor: Refactor spi_nor_read_id() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Don't use `tmp` for two purposes (return value, loop counter). Instead, use `i` for the loop counter, and `ret` for the return value. - Don't use tabs between type and name in variable declarations, for consistency with other functions in spi-nor.c. - Rewrite nested `if`s as `if (a && b)`. - Remove `info` variable, and use spi_nor_ids[i] directly. Signed-off-by: Jonathan Neuschäfer [tudor.ambarus@microchip.com: change i's type from int to unsigned int, reorder local variables] Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1224247b26cc..caf0c109cca0 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -2733,9 +2733,9 @@ static const struct flash_info spi_nor_ids[] = { static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) { - int tmp; - u8 *id = nor->bouncebuf; - const struct flash_info *info; + u8 *id = nor->bouncebuf; + unsigned int i; + int ret; if (nor->spimem) { struct spi_mem_op op = @@ -2744,22 +2744,20 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_IN(SPI_NOR_MAX_ID_LEN, id, 1)); - tmp = spi_mem_exec_op(nor->spimem, &op); + ret = spi_mem_exec_op(nor->spimem, &op); } else { - tmp = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, + ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); } - if (tmp) { - dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); - return ERR_PTR(tmp); + if (ret) { + dev_dbg(nor->dev, "error %d reading JEDEC ID\n", ret); + return ERR_PTR(ret); } - for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { - info = &spi_nor_ids[tmp]; - if (info->id_len) { - if (!memcmp(info->id, id, info->id_len)) - return &spi_nor_ids[tmp]; - } + for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { + if (spi_nor_ids[i].id_len && + !memcmp(spi_nor_ids[i].id, id, spi_nor_ids[i].id_len)) + return &spi_nor_ids[i]; } dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", SPI_NOR_MAX_ID_LEN, id); From cb6176ef25cef7a54ac1e8701f9dde822fce5cee Mon Sep 17 00:00:00 2001 From: Dirk Behme Date: Tue, 4 Feb 2020 08:10:43 +0100 Subject: [PATCH 006/103] mtd: hyperbus: Add proper error message for missing compatible In case the compatible "cypress,hyperflash" is not given output a proper error message. Signed-off-by: Dirk Behme Signed-off-by: Vignesh Raghavendra --- drivers/mtd/hyperbus/hyperbus-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c index 6af9ea34117d..c1916cca1701 100644 --- a/drivers/mtd/hyperbus/hyperbus-core.c +++ b/drivers/mtd/hyperbus/hyperbus-core.c @@ -73,8 +73,10 @@ int hyperbus_register_device(struct hyperbus_device *hbdev) np = hbdev->np; ctlr = hbdev->ctlr; - if (!of_device_is_compatible(np, "cypress,hyperflash")) + if (!of_device_is_compatible(np, "cypress,hyperflash")) { + dev_err(ctlr->dev, "\"cypress,hyperflash\" compatible missing\n"); return -ENODEV; + } hbdev->memtype = HYPERFLASH; From b6fe8bc67d2d33a9eff1b0785482bccd14979115 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 29 Jan 2020 23:37:37 +0300 Subject: [PATCH 007/103] mtd: hyperbus: move direct mapping setup to AM654 HBMC driver The Hyperbus core expects that HyperFlash is always directly mapped for both read and write, but in reality this may not always be the case, e.g. Renesas RPC-IF has read only direct mapping. Move the code setting up the direct mapping from the Hyperbus core to thh TI AM554 HBMC driver. Signed-off-by: Sergei Shtylyov Signed-off-by: Vignesh Raghavendra --- drivers/mtd/hyperbus/hbmc-am654.c | 12 ++++++++++++ drivers/mtd/hyperbus/hyperbus-core.c | 11 ----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c index 08d543b124cd..f350a0809f88 100644 --- a/drivers/mtd/hyperbus/hbmc-am654.c +++ b/drivers/mtd/hyperbus/hbmc-am654.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -57,8 +58,10 @@ static const struct hyperbus_ops am654_hbmc_ops = { static int am654_hbmc_probe(struct platform_device *pdev) { + struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct am654_hbmc_priv *priv; + struct resource res; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -67,6 +70,10 @@ static int am654_hbmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + if (of_property_read_bool(dev->of_node, "mux-controls")) { struct mux_control *control = devm_mux_control_get(dev, NULL); @@ -88,6 +95,11 @@ static int am654_hbmc_probe(struct platform_device *pdev) goto disable_pm; } + priv->hbdev.map.size = resource_size(&res); + priv->hbdev.map.virt = devm_ioremap_resource(dev, &res); + if (IS_ERR(priv->hbdev.map.virt)) + return PTR_ERR(priv->hbdev.map.virt); + priv->ctlr.dev = dev; priv->ctlr.ops = &am654_hbmc_ops; priv->hbdev.ctlr = &priv->ctlr; diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c index c1916cca1701..32685e8dd278 100644 --- a/drivers/mtd/hyperbus/hyperbus-core.c +++ b/drivers/mtd/hyperbus/hyperbus-core.c @@ -10,7 +10,6 @@ #include #include #include -#include #include static struct hyperbus_device *map_to_hbdev(struct map_info *map) @@ -62,7 +61,6 @@ int hyperbus_register_device(struct hyperbus_device *hbdev) struct hyperbus_ctlr *ctlr; struct device_node *np; struct map_info *map; - struct resource res; struct device *dev; int ret; @@ -80,17 +78,8 @@ int hyperbus_register_device(struct hyperbus_device *hbdev) hbdev->memtype = HYPERFLASH; - ret = of_address_to_resource(np, 0, &res); - if (ret) - return ret; - dev = ctlr->dev; map = &hbdev->map; - map->size = resource_size(&res); - map->virt = devm_ioremap_resource(dev, &res); - if (IS_ERR(map->virt)) - return PTR_ERR(map->virt); - map->name = dev_name(dev); map->bankwidth = 2; map->device_node = np; From 43d8b6362378913bafbc54690474131568458c42 Mon Sep 17 00:00:00 2001 From: Martin Devera Date: Thu, 16 Jan 2020 14:54:31 +0100 Subject: [PATCH 008/103] mtd: rawnand: Ensure nand_soft_waitrdy wait period is enough The used way to compute jiffies timeout brokes when jiffie difference is 1. Assume that nand_soft_waitrdy is called with timeout_ms==1. Jiffies are 1000 for example (assume something more like 1000.99 - just before incrementing to 1001). We compute timeout_ms = 1000+msecs_to_jiffies(1) = 1001. nand_read_data_op is called for the first time and returns 0. During the call jiffies changes to 1001 thus "while loop" ends here (wrongly). Notice that routine was called with expected timeout 1ms but actual timeout used was something between 0...1ms. Fixes STM32MP1 FMC2 NAND controller which sometimes failed exactly in this way. Signed-off-by: Martin Devera Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200116135431.17480-1-devik@eaxlabs.cz --- drivers/mtd/nand/raw/nand_base.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index f64e3b6605c6..8ad4af99eea4 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -683,7 +683,12 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) if (ret) return ret; - timeout_ms = jiffies + msecs_to_jiffies(timeout_ms); + /* + * +1 below is necessary because if we are now in the last fraction + * of jiffy and msecs_to_jiffies is 1 then we will wait only that + * small jiffy fraction - possibly leading to false timeout + */ + timeout_ms = jiffies + msecs_to_jiffies(timeout_ms) + 1; do { ret = nand_read_data_op(chip, &status, sizeof(status), true); if (ret) From 009264605cdf1b12962c3a46f75818d05452e890 Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Thu, 23 Jan 2020 09:22:48 +0100 Subject: [PATCH 009/103] mtd: rawnand: free the nand_device object This patch releases the resources allocated in nanddev_init function. Fixes: a7ab085d7c16 ("mtd: rawnand: Initialize the nand_device object") Signed-off-by: Christophe Kerello Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1579767768-32295-1-git-send-email-christophe.kerello@st.com --- drivers/mtd/nand/raw/nand_base.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 8ad4af99eea4..a3ed6c54963e 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5912,6 +5912,8 @@ void nand_cleanup(struct nand_chip *chip) chip->ecc.algo == NAND_ECC_BCH) nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + nanddev_cleanup(&chip->base); + /* Free bad block table memory */ kfree(chip->bbt); kfree(chip->data_buf); From 9afbe7c0140f663586edb6e823b616bd7076c00a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 27 Jan 2020 21:39:34 +0900 Subject: [PATCH 010/103] mtd: rawnand: denali: deassert write protect pin If the write protect signal from this IP is connected to the NAND device, this IP can handle the WP# pin via the WRITE_PROTECT register. The Denali NAND Flash Memory Controller User's Guide describes this register like follows: When the controller is in reset, the WP# pin is always asserted to the device. Once the reset is removed, the WP# is de-asserted. The software will then have to come and program this bit to assert/de-assert the same. 1 - Write protect de-assert 0 - Write protect assert The default value is 1, so the write protect is de-asserted after the reset is removed. The driver can write to the device unless someone has explicitly cleared register before booting the kernel. The boot ROM of some UniPhier SoCs (LD4, Pro4, sLD8, Pro5) is the case; the boot ROM clears the WRITE_PROTECT register when the system is booting from the NAND device, so the NAND device becomes read-only. Set it to 1 in the driver in order to allow the write access to the device. Signed-off-by: Masahiro Yamada Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200127123934.11847-1-yamada.masahiro@socionext.com --- drivers/mtd/nand/raw/denali.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index fafd0a0aa8e2..6a6c919b2569 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1317,6 +1317,7 @@ int denali_init(struct denali_controller *denali) iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); iowrite32(ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); + iowrite32(WRITE_PROTECT__FLAG, denali->reg + WRITE_PROTECT); denali_clear_irq_all(denali); From a91f8170df832967dc75d5bd594c496999882e22 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Fri, 7 Feb 2020 13:59:21 +0900 Subject: [PATCH 011/103] mtd: spinand: toshiba: Add comment about Kioxia ID Add a comment above NAND_MFR_TOSHIBA and SPINAND_MFR_TOSHIBA definitions that Toshiba and Kioxia ID are the same. Since its independence from Toshiba Group, Toshiba memory Co has become Kioxia Co. Signed-off-by: Yoshio Furuyama Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581051561-7302-1-git-send-email-ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/raw/internals.h | 1 + drivers/mtd/nand/spi/toshiba.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index cba6fe7dd8c4..9d0caadf940e 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -30,6 +30,7 @@ #define NAND_MFR_SAMSUNG 0xec #define NAND_MFR_SANDISK 0x45 #define NAND_MFR_STMICRO 0x20 +/* Kioxia is new name of Toshiba memory. */ #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_WINBOND 0xef diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 0db5ee4e82af..833e8f64e0a0 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -10,6 +10,7 @@ #include #include +/* Kioxia is new name of Toshiba memory. */ #define SPINAND_MFR_TOSHIBA 0x98 #define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4) From f1541773af49ecd1edae29c8ac0775253a0b0760 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sat, 8 Feb 2020 15:43:50 +0800 Subject: [PATCH 012/103] mtd: spinand: rework detect procedure for different READ_ID operation Currently there are 3 different variants of read_id implementation: 1. opcode only. Found in GD5FxGQ4xF. 2. opcode + 1 addr byte. Found in GD5GxGQ4xA/E 3. opcode + 1 dummy byte. Found in other currently supported chips. Original implementation was for variant 1 and let detect function of chips with variant 2 and 3 to ignore the first byte. This isn't robust: 1. For chips of variant 2, if SPI master doesn't keep MOSI low during read, chip will get a random id offset, and the entire id buffer will shift by that offset, causing detect failure. 2. For chips of variant 1, if it happens to get a devid that equals to manufacture id of variant 2 or 3 chips, it'll get incorrectly detected. This patch reworks detect procedure to address problems above. New logic do detection for all variants separatedly, in 1-2-3 order. Since all current detect methods do exactly the same id matching procedure, unify them into core.c and remove detect method from manufacture_ops. Tested on GD5F1GQ4UAYIG and W25N01GVZEIG. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200208074439.146296-1-gch981213@gmail.com --- drivers/mtd/nand/spi/core.c | 86 ++++++++++++++++++++++--------- drivers/mtd/nand/spi/gigadevice.c | 45 +++++----------- drivers/mtd/nand/spi/macronix.c | 30 +++-------- drivers/mtd/nand/spi/micron.c | 26 ++-------- drivers/mtd/nand/spi/paragon.c | 28 +++------- drivers/mtd/nand/spi/toshiba.c | 45 ++++++---------- drivers/mtd/nand/spi/winbond.c | 34 +++--------- include/linux/mtd/spinand.h | 66 ++++++++++++++++-------- 8 files changed, 157 insertions(+), 203 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 89f6beefb01c..a9e9cbad942f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -370,10 +371,11 @@ static int spinand_wait(struct spinand_device *spinand, u8 *s) return status & STATUS_BUSY ? -ETIMEDOUT : 0; } -static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) +static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, + u8 ndummy, u8 *buf) { - struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, - SPINAND_MAX_ID_LEN); + struct spi_mem_op op = SPINAND_READID_OP( + naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -762,24 +764,62 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &winbond_spinand_manufacturer, }; -static int spinand_manufacturer_detect(struct spinand_device *spinand) +static int spinand_manufacturer_match(struct spinand_device *spinand, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { - ret = spinand_manufacturers[i]->ops->detect(spinand); - if (ret > 0) { - spinand->manufacturer = spinand_manufacturers[i]; - return 0; - } else if (ret < 0) { - return ret; - } - } + const struct spinand_manufacturer *manufacturer = + spinand_manufacturers[i]; + if (id[0] != manufacturer->id) + continue; + + ret = spinand_match_and_init(spinand, + manufacturer->chips, + manufacturer->nchips, + rdid_method); + if (ret < 0) + continue; + + spinand->manufacturer = manufacturer; + return 0; + } return -ENOTSUPP; } +static int spinand_id_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + ret = spinand_read_id_op(spinand, 0, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 1, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_ADDR); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 0, 1, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_DUMMY); + + return ret; +} + static int spinand_manufacturer_init(struct spinand_device *spinand) { if (spinand->manufacturer->ops->init) @@ -835,9 +875,9 @@ spinand_select_op_variant(struct spinand_device *spinand, * @spinand: SPI NAND object * @table: SPI NAND device description table * @table_size: size of the device description table + * @rdid_method: read id method to match * - * Should be used by SPI NAND manufacturer drivers when they want to find a - * match between a device ID retrieved through the READ_ID command and an + * Match between a device ID retrieved through the READ_ID command and an * entry in the SPI NAND description table. If a match is found, the spinand * object will be initialized with information provided by the matching * spinand_info entry. @@ -846,8 +886,10 @@ spinand_select_op_variant(struct spinand_device *spinand, */ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u16 devid) + unsigned int table_size, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; @@ -855,13 +897,17 @@ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *info = &table[i]; const struct spi_mem_op *op; - if (devid != info->devid) + if (rdid_method != info->devid.method) + continue; + + if (memcmp(id + 1, info->devid.id, info->devid.len)) continue; nand->memorg = table[i].memorg; nand->eccreq = table[i].eccreq; spinand->eccinfo = table[i].eccinfo; spinand->flags = table[i].flags; + spinand->id.len = 1 + table[i].devid.len; spinand->select_target = table[i].select_target; op = spinand_select_op_variant(spinand, @@ -898,13 +944,7 @@ static int spinand_detect(struct spinand_device *spinand) if (ret) return ret; - ret = spinand_read_id_op(spinand, spinand->id.data); - if (ret) - return ret; - - spinand->id.len = SPINAND_MAX_ID_LEN; - - ret = spinand_manufacturer_detect(spinand); + ret = spinand_id_detect(spinand); if (ret) { dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, spinand->id.data); diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index e99d425aa93f..d219c970042a 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -195,7 +195,8 @@ static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info gigadevice_spinand_table[] = { - SPINAND_INFO("GD5F1GQ4xA", 0xF1, + SPINAND_INFO("GD5F1GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -204,7 +205,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F2GQ4xA", 0xF2, + SPINAND_INFO("GD5F2GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -213,7 +215,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F4GQ4xA", 0xF4, + SPINAND_INFO("GD5F4GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -222,7 +225,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, + SPINAND_INFO("GD5F1GQ4UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -231,7 +235,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), - SPINAND_INFO("GD5F1GQ4UFxxG", 0xb148, + SPINAND_INFO("GD5F1GQ4UFxxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, @@ -242,39 +247,13 @@ static const struct spinand_info gigadevice_spinand_table[] = { gd5fxgq4ufxxg_ecc_get_status)), }; -static int gigadevice_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - u16 did; - int ret; - - /* - * Earlier GDF5-series devices (A,E) return [0][MID][DID] - * Later (F) devices return [MID][DID1][DID2] - */ - - if (id[0] == SPINAND_MFR_GIGADEVICE) - did = (id[1] << 8) + id[2]; - else if (id[0] == 0 && id[1] == SPINAND_MFR_GIGADEVICE) - did = id[2]; - else - return 0; - - ret = spinand_match_and_init(spinand, gigadevice_spinand_table, - ARRAY_SIZE(gigadevice_spinand_table), - did); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { - .detect = gigadevice_spinand_detect, }; const struct spinand_manufacturer gigadevice_spinand_manufacturer = { .id = SPINAND_MFR_GIGADEVICE, .name = "GigaDevice", + .chips = gigadevice_spinand_table, + .nchips = ARRAY_SIZE(gigadevice_spinand_table), .ops = &gigadevice_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 21def3f8fb36..0f900f3aa21a 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -99,7 +99,8 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info macronix_spinand_table[] = { - SPINAND_INFO("MX35LF1GE4AB", 0x12, + SPINAND_INFO("MX35LF1GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -108,7 +109,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35LF2GE4AB", 0x22, + SPINAND_INFO("MX35LF2GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -118,33 +120,13 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), }; -static int macronix_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Macronix SPI NAND read ID needs a dummy byte, so the first byte in - * raw_id is garbage. - */ - if (id[1] != SPINAND_MFR_MACRONIX) - return 0; - - ret = spinand_match_and_init(spinand, macronix_spinand_table, - ARRAY_SIZE(macronix_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { - .detect = macronix_spinand_detect, }; const struct spinand_manufacturer macronix_spinand_manufacturer = { .id = SPINAND_MFR_MACRONIX, .name = "Macronix", + .chips = macronix_spinand_table, + .nchips = ARRAY_SIZE(macronix_spinand_table), .ops = ¯onix_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 7d7b1f7fcf71..f56f81325e10 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -91,7 +91,8 @@ static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info micron_spinand_table[] = { - SPINAND_INFO("MT29F2G01ABAGD", 0x24, + SPINAND_INFO("MT29F2G01ABAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -102,32 +103,13 @@ static const struct spinand_info micron_spinand_table[] = { mt29f2g01abagd_ecc_get_status)), }; -static int micron_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Micron SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_MICRON) - return 0; - - ret = spinand_match_and_init(spinand, micron_spinand_table, - ARRAY_SIZE(micron_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { - .detect = micron_spinand_detect, }; const struct spinand_manufacturer micron_spinand_manufacturer = { .id = SPINAND_MFR_MICRON, .name = "Micron", + .chips = micron_spinand_table, + .nchips = ARRAY_SIZE(micron_spinand_table), .ops = µn_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/paragon.c b/drivers/mtd/nand/spi/paragon.c index 52307681cbd0..519ade513c1f 100644 --- a/drivers/mtd/nand/spi/paragon.c +++ b/drivers/mtd/nand/spi/paragon.c @@ -97,7 +97,8 @@ static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = { static const struct spinand_info paragon_spinand_table[] = { - SPINAND_INFO("PN26G01A", 0xe1, + SPINAND_INFO("PN26G01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe1), NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -106,7 +107,8 @@ static const struct spinand_info paragon_spinand_table[] = { 0, SPINAND_ECCINFO(&pn26g0xa_ooblayout, pn26g0xa_ecc_get_status)), - SPINAND_INFO("PN26G02A", 0xe2, + SPINAND_INFO("PN26G02A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe2), NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -117,31 +119,13 @@ static const struct spinand_info paragon_spinand_table[] = { pn26g0xa_ecc_get_status)), }; -static int paragon_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* Read ID returns [0][MID][DID] */ - - if (id[1] != SPINAND_MFR_PARAGON) - return 0; - - ret = spinand_match_and_init(spinand, paragon_spinand_table, - ARRAY_SIZE(paragon_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { - .detect = paragon_spinand_detect, }; const struct spinand_manufacturer paragon_spinand_manufacturer = { .id = SPINAND_MFR_PARAGON, .name = "Paragon", + .chips = paragon_spinand_table, + .nchips = ARRAY_SIZE(paragon_spinand_table), .ops = ¶gon_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 833e8f64e0a0..d34773191700 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -96,7 +96,8 @@ static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info toshiba_spinand_table[] = { /* 3.3V 1Gb */ - SPINAND_INFO("TC58CVG0S3", 0xC2, + SPINAND_INFO("TC58CVG0S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -106,7 +107,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 2Gb */ - SPINAND_INFO("TC58CVG1S3", 0xCB, + SPINAND_INFO("TC58CVG1S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -116,7 +118,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", 0xCD, + SPINAND_INFO("TC58CVG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -126,7 +129,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", 0xED, + SPINAND_INFO("TC58CVG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -136,7 +140,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 1Gb */ - SPINAND_INFO("TC58CYG0S3", 0xB2, + SPINAND_INFO("TC58CYG0S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -146,7 +151,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 2Gb */ - SPINAND_INFO("TC58CYG1S3", 0xBB, + SPINAND_INFO("TC58CYG1S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -156,7 +162,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 4Gb */ - SPINAND_INFO("TC58CYG2S0", 0xBD, + SPINAND_INFO("TC58CYG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -167,33 +174,13 @@ static const struct spinand_info toshiba_spinand_table[] = { tc58cxgxsx_ecc_get_status)), }; -static int toshiba_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Toshiba SPI NAND read ID needs a dummy byte, - * so the first byte in id is garbage. - */ - if (id[1] != SPINAND_MFR_TOSHIBA) - return 0; - - ret = spinand_match_and_init(spinand, toshiba_spinand_table, - ARRAY_SIZE(toshiba_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { - .detect = toshiba_spinand_detect, }; const struct spinand_manufacturer toshiba_spinand_manufacturer = { .id = SPINAND_MFR_TOSHIBA, .name = "Toshiba", + .chips = toshiba_spinand_table, + .nchips = ARRAY_SIZE(toshiba_spinand_table), .ops = &toshiba_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index a6c17e0cace8..76684428354e 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -75,7 +75,8 @@ static int w25m02gv_select_target(struct spinand_device *spinand, } static const struct spinand_info winbond_spinand_table[] = { - SPINAND_INFO("W25M02GV", 0xAB, + SPINAND_INFO("W25M02GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -84,7 +85,8 @@ static const struct spinand_info winbond_spinand_table[] = { 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), - SPINAND_INFO("W25N01GV", 0xAA, + SPINAND_INFO("W25N01GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -94,31 +96,6 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), }; -/** - * winbond_spinand_detect - initialize device related part in spinand_device - * struct if it is a Winbond device. - * @spinand: SPI NAND device structure - */ -static int winbond_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Winbond SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_WINBOND) - return 0; - - ret = spinand_match_and_init(spinand, winbond_spinand_table, - ARRAY_SIZE(winbond_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static int winbond_spinand_init(struct spinand_device *spinand) { struct nand_device *nand = spinand_to_nand(spinand); @@ -138,12 +115,13 @@ static int winbond_spinand_init(struct spinand_device *spinand) } static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { - .detect = winbond_spinand_detect, .init = winbond_spinand_init, }; const struct spinand_manufacturer winbond_spinand_manufacturer = { .id = SPINAND_MFR_WINBOND, .name = "Winbond", + .chips = winbond_spinand_table, + .nchips = ARRAY_SIZE(winbond_spinand_table), .ops = &winbond_spinand_manuf_ops, }; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 4ea558bd3c46..f4c4ae87181b 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -32,9 +32,9 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPINAND_READID_OP(ndummy, buf, len) \ +#define SPINAND_READID_OP(naddr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ - SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_ADDR(naddr, 0, 1), \ SPI_MEM_OP_DUMMY(ndummy, 1), \ SPI_MEM_OP_DATA_IN(len, buf, 1)) @@ -176,37 +176,46 @@ struct spinand_device; * @data: buffer containing the id bytes. Currently 4 bytes large, but can * be extended if required * @len: ID length - * - * struct_spinand_id->data contains all bytes returned after a READ_ID command, - * including dummy bytes if the chip does not emit ID bytes right after the - * READ_ID command. The responsibility to extract real ID bytes is left to - * struct_manufacurer_ops->detect(). */ struct spinand_id { u8 data[SPINAND_MAX_ID_LEN]; int len; }; +enum spinand_readid_method { + SPINAND_READID_METHOD_OPCODE, + SPINAND_READID_METHOD_OPCODE_ADDR, + SPINAND_READID_METHOD_OPCODE_DUMMY, +}; + +/** + * struct spinand_devid - SPI NAND device id structure + * @id: device id of current chip + * @len: number of bytes in device id + * @method: method to read chip id + * There are 3 possible variants: + * SPINAND_READID_METHOD_OPCODE: chip id is returned immediately + * after read_id opcode. + * SPINAND_READID_METHOD_OPCODE_ADDR: chip id is returned after + * read_id opcode + 1-byte address. + * SPINAND_READID_METHOD_OPCODE_DUMMY: chip id is returned after + * read_id opcode + 1 dummy byte. + */ +struct spinand_devid { + const u8 *id; + const u8 len; + const enum spinand_readid_method method; +}; + /** * struct manufacurer_ops - SPI NAND manufacturer specific operations - * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed - * the core calls the struct_manufacurer_ops->detect() hook of each - * registered manufacturer until one of them return 1. Note that - * the first thing to check in this hook is that the manufacturer ID - * in struct_spinand_device->id matches the manufacturer whose - * ->detect() hook has been called. Should return 1 if there's a - * match, 0 if the manufacturer ID does not match and a negative - * error code otherwise. When true is returned, the core assumes - * that properties of the NAND chip (spinand->base.memorg and - * spinand->base.eccreq) have been filled * @init: initialize a SPI NAND device * @cleanup: cleanup a SPI NAND device * * Each SPI NAND manufacturer driver should implement this interface so that - * NAND chips coming from this vendor can be detected and initialized properly. + * NAND chips coming from this vendor can be initialized properly. */ struct spinand_manufacturer_ops { - int (*detect)(struct spinand_device *spinand); int (*init)(struct spinand_device *spinand); void (*cleanup)(struct spinand_device *spinand); }; @@ -215,11 +224,16 @@ struct spinand_manufacturer_ops { * struct spinand_manufacturer - SPI NAND manufacturer instance * @id: manufacturer ID * @name: manufacturer name + * @devid_len: number of bytes in device ID + * @chips: supported SPI NANDs under current manufacturer + * @nchips: number of SPI NANDs available in chips array * @ops: manufacturer operations */ struct spinand_manufacturer { u8 id; char *name; + const struct spinand_info *chips; + const size_t nchips; const struct spinand_manufacturer_ops *ops; }; @@ -291,7 +305,7 @@ struct spinand_ecc_info { */ struct spinand_info { const char *model; - u16 devid; + struct spinand_devid devid; u32 flags; struct nand_memory_organization memorg; struct nand_ecc_req eccreq; @@ -305,6 +319,13 @@ struct spinand_info { unsigned int target); }; +#define SPINAND_ID(__method, ...) \ + { \ + .id = (const u8[]){ __VA_ARGS__ }, \ + .len = sizeof((u8[]){ __VA_ARGS__ }), \ + .method = __method, \ + } + #define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ { \ .read_cache = __read, \ @@ -451,9 +472,10 @@ static inline void spinand_set_of_node(struct spinand_device *spinand, nanddev_set_of_node(&spinand->base, np); } -int spinand_match_and_init(struct spinand_device *dev, +int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u16 devid); + unsigned int table_size, + enum spinand_readid_method rdid_method); int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); int spinand_select_target(struct spinand_device *spinand, unsigned int target); From c4b7dd35d35936964c71db42e1d94994ee6ea411 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 11 Feb 2020 14:31:51 -0300 Subject: [PATCH 013/103] mtd: rawnand: ingenic: Use devm_platform_ioremap_resource() Use devm_platform_ioremap_resource() instead of platform_get_resource() + devm_ioremap_resource(). Signed-off-by: Paul Cercueil Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200211173151.27587-1-paul@crapouillou.net --- drivers/mtd/nand/raw/ingenic/ingenic_ecc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c index c954189606f6..8e22cd6ec71f 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c @@ -124,7 +124,6 @@ int ingenic_ecc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ingenic_ecc *ecc; - struct resource *res; ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL); if (!ecc) @@ -134,8 +133,7 @@ int ingenic_ecc_probe(struct platform_device *pdev) if (!ecc->ops) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ecc->base = devm_ioremap_resource(dev, res); + ecc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ecc->base)) return PTR_ERR(ecc->base); From 91a1abfb752357fe5d0783bd69db0d91f358e3eb Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:16 +0100 Subject: [PATCH 014/103] mtd: rawnand: ams-delta: Write protect device during probe Initialise NWP GPIO pin as asserted to protect the device from hazard during setup of other GPIO pins. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-2-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 8312182088c1..2501cfe00f43 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -251,8 +251,8 @@ static int ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - /* Set chip enabled, but */ - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH); + /* Set chip enabled but write protected */ + priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nwp)) { err = PTR_ERR(priv->gpiod_nwp); dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); @@ -309,6 +309,17 @@ static int ams_delta_init(struct platform_device *pdev) nand_controller_init(&priv->base); this->controller = &priv->base; + /* + * FIXME: We should release write protection only after nand_scan() to + * be on the safe side but we can't do that until we have a generic way + * to assert/deassert WP from the core. Even if the core shouldn't + * write things in the nand_scan() path, it should have control on this + * pin just in case we ever need to disable write protection during + * chip detection/initialization. + */ + /* Release write protection */ + gpiod_set_value(priv->gpiod_nwp, 1); + /* Scan to find existence of the device */ err = nand_scan(this, 1); if (err) @@ -336,6 +347,9 @@ static int ams_delta_cleanup(struct platform_device *pdev) struct ams_delta_nand *priv = platform_get_drvdata(pdev); struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); + /* Apply write protection */ + gpiod_set_value(priv->gpiod_nwp, 0); + /* Unregister device */ nand_release(mtd_to_nand(mtd)); From 1698ea32133a884d6def357f90265e59799242e9 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:17 +0100 Subject: [PATCH 015/103] mtd: rawnand: ams-delta: Use struct gpio_nand_platdata In order to be able to move the hardcoded Amstrad Delta partition info from the driver code to the board file, reuse gpio_nand_platdata structure owned by "gpio-nand" driver and try to obtain information on device partitions from device platform data. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-3-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 2501cfe00f43..fbab7cc14607 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -220,12 +221,20 @@ static const struct nand_controller_ops ams_delta_ops = { */ static int ams_delta_init(struct platform_device *pdev) { + struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev); + const struct mtd_partition *partitions = partition_info; + int num_partitions = ARRAY_SIZE(partition_info); struct ams_delta_nand *priv; struct nand_chip *this; struct mtd_info *mtd; struct gpio_descs *data_gpiods; int err = 0; + if (pdata) { + partitions = pdata->parts; + num_partitions = pdata->num_parts; + } + /* Allocate memory for MTD device structure and private data */ priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand), GFP_KERNEL); @@ -326,8 +335,7 @@ static int ams_delta_init(struct platform_device *pdev) return err; /* Register the partitions */ - err = mtd_device_register(mtd, partition_info, - ARRAY_SIZE(partition_info)); + err = mtd_device_register(mtd, partitions, num_partitions); if (err) goto err_nand_cleanup; From 38c30b3c96a572926346b7c5221fb3953bfb0d5e Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:18 +0100 Subject: [PATCH 016/103] ARM: OMAP1: ams-delta: Provide board specific partition info Now as the Amstrad Delta NAND driver supports fetching information on MTD partitions from device platform data, add partition info to the NAND device configuration. Signed-off-by: Janusz Krzysztofik Acked-by: Tony Lindgren Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-4-jmkrzyszt@gmail.com --- arch/arm/mach-omap1/board-ams-delta.c | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index a2aa7a12b374..f4d2ef97099e 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -294,9 +296,42 @@ struct modem_private_data { static struct modem_private_data modem_priv; +/* + * Define partitions for flash device + */ + +static struct mtd_partition partition_info[] = { + { .name = "Kernel", + .offset = 0, + .size = 3 * SZ_1M + SZ_512K }, + { .name = "u-boot", + .offset = 3 * SZ_1M + SZ_512K, + .size = SZ_256K }, + { .name = "u-boot params", + .offset = 3 * SZ_1M + SZ_512K + SZ_256K, + .size = SZ_256K }, + { .name = "Amstrad LDR", + .offset = 4 * SZ_1M, + .size = SZ_256K }, + { .name = "File system", + .offset = 4 * SZ_1M + 1 * SZ_256K, + .size = 27 * SZ_1M }, + { .name = "PBL reserved", + .offset = 32 * SZ_1M - 3 * SZ_256K, + .size = 3 * SZ_256K }, +}; + +static struct gpio_nand_platdata nand_platdata = { + .parts = partition_info, + .num_parts = ARRAY_SIZE(partition_info), +}; + static struct platform_device ams_delta_nand_device = { .name = "ams-delta-nand", .id = -1, + .dev = { + .platform_data = &nand_platdata, + }, }; #define OMAP_GPIO_LABEL "gpio-0-15" From d7ffe387cc12a5dbfdeeabae85b168dc407ac285 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:19 +0100 Subject: [PATCH 017/103] mtd: rawnand: ams-delta: Drop board specific partition info Now as we support fetching partition info from device platform data and the Amstrad Delta board file provides that info, drop it from the driver code. v2: rebase on top of gpio_nand_platdata extension Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-5-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index fbab7cc14607..25f121adea6f 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -42,31 +42,6 @@ struct ams_delta_nand { bool data_in; }; -/* - * Define partitions for flash devices - */ - -static const struct mtd_partition partition_info[] = { - { .name = "Kernel", - .offset = 0, - .size = 3 * SZ_1M + SZ_512K }, - { .name = "u-boot", - .offset = 3 * SZ_1M + SZ_512K, - .size = SZ_256K }, - { .name = "u-boot params", - .offset = 3 * SZ_1M + SZ_512K + SZ_256K, - .size = SZ_256K }, - { .name = "Amstrad LDR", - .offset = 4 * SZ_1M, - .size = SZ_256K }, - { .name = "File system", - .offset = 4 * SZ_1M + 1 * SZ_256K, - .size = 27 * SZ_1M }, - { .name = "PBL reserved", - .offset = 32 * SZ_1M - 3 * SZ_256K, - .size = 3 * SZ_256K }, -}; - static void ams_delta_write_commit(struct ams_delta_nand *priv) { gpiod_set_value(priv->gpiod_nwe, 0); @@ -222,8 +197,8 @@ static const struct nand_controller_ops ams_delta_ops = { static int ams_delta_init(struct platform_device *pdev) { struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev); - const struct mtd_partition *partitions = partition_info; - int num_partitions = ARRAY_SIZE(partition_info); + const struct mtd_partition *partitions = NULL; + int num_partitions = 0; struct ams_delta_nand *priv; struct nand_chip *this; struct mtd_info *mtd; From 2cef3d4cf4498e260f5bc2d5fab850e4a52be382 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:20 +0100 Subject: [PATCH 018/103] mtd: rawnand: ams-delta: Enable OF partition info support Provide MTD layer with device OF node info required by OF partition parser. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-6-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 25f121adea6f..fb96f6a3b0b3 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -222,6 +222,7 @@ static int ams_delta_init(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; nand_set_controller_data(this, priv); + nand_set_flash_node(this, pdev->dev.of_node); priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN); if (IS_ERR(priv->gpiod_rdy)) { From 241008ed0bb5955e52e06d7270e87c974bbaadd6 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:21 +0100 Subject: [PATCH 019/103] mtd: rawnand: ams-delta: Push inversion handling to gpiolib Let platforms take care of declaring correct GPIO pin polarity so we can just ask a GPIO line to be asserted or deasserted and gpiolib deals with the rest depending on how the platform is configured. Inspired by similar changes to regulator drivers by Linus Walleij , thanks! Signed-off-by: Janusz Krzysztofik Acked-by: Tony Lindgren Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-7-jmkrzyszt@gmail.com --- arch/arm/mach-omap1/board-ams-delta.c | 12 ++++++++---- drivers/mtd/nand/raw/ams-delta.c | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c index f4d2ef97099e..8d32894ecd2e 100644 --- a/arch/arm/mach-omap1/board-ams-delta.c +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -341,10 +341,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = { .table = { GPIO_LOOKUP(OMAP_GPIO_LABEL, AMS_DELTA_GPIO_PIN_NAND_RB, "rdy", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NCE, "nce", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NRE, "nre", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWP, "nwp", 0), - GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NCE, "nce", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NRE, "nre", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWP, "nwp", + GPIO_ACTIVE_LOW), + GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", + GPIO_ACTIVE_LOW), GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0), GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0), GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0), diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index fb96f6a3b0b3..c7aeb940accd 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -44,9 +44,9 @@ struct ams_delta_nand { static void ams_delta_write_commit(struct ams_delta_nand *priv) { - gpiod_set_value(priv->gpiod_nwe, 0); - ndelay(40); gpiod_set_value(priv->gpiod_nwe, 1); + ndelay(40); + gpiod_set_value(priv->gpiod_nwe, 0); } static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) @@ -81,13 +81,13 @@ static u8 ams_delta_io_read(struct ams_delta_nand *priv) struct gpio_descs *data_gpiods = priv->data_gpiods; DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, }; - gpiod_set_value(priv->gpiod_nre, 0); + gpiod_set_value(priv->gpiod_nre, 1); ndelay(40); gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, data_gpiods->info, values); - gpiod_set_value(priv->gpiod_nre, 1); + gpiod_set_value(priv->gpiod_nre, 0); res = values[0]; return res; @@ -129,7 +129,7 @@ static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) { - gpiod_set_value(priv->gpiod_nce, assert ? 0 : 1); + gpiod_set_value(priv->gpiod_nce, assert); } static int ams_delta_exec_op(struct nand_chip *this, @@ -237,28 +237,28 @@ static int ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, priv); /* Set chip enabled but write protected */ - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_LOW); + priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH); if (IS_ERR(priv->gpiod_nwp)) { err = PTR_ERR(priv->gpiod_nwp); dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH); + priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nce)) { err = PTR_ERR(priv->gpiod_nce); dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH); + priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nre)) { err = PTR_ERR(priv->gpiod_nre); dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH); + priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nwe)) { err = PTR_ERR(priv->gpiod_nwe); dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err); @@ -303,7 +303,7 @@ static int ams_delta_init(struct platform_device *pdev) * chip detection/initialization. */ /* Release write protection */ - gpiod_set_value(priv->gpiod_nwp, 1); + gpiod_set_value(priv->gpiod_nwp, 0); /* Scan to find existence of the device */ err = nand_scan(this, 1); @@ -332,7 +332,7 @@ static int ams_delta_cleanup(struct platform_device *pdev) struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); /* Apply write protection */ - gpiod_set_value(priv->gpiod_nwp, 0); + gpiod_set_value(priv->gpiod_nwp, 1); /* Unregister device */ nand_release(mtd_to_nand(mtd)); From ccada49b050f44df9499859f09b822b8aebc3a4d Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:22 +0100 Subject: [PATCH 020/103] mtd: rawnand: ams-delta: Don't hardcode read/write pulse widths Instead of forcing Amstrad Delta specific read/write pulse widths, use variables initialised from respective fields of chip SDR timings. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-8-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index c7aeb940accd..11689218d23a 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -40,12 +40,14 @@ struct ams_delta_nand { struct gpio_desc *gpiod_cle; struct gpio_descs *data_gpiods; bool data_in; + unsigned int tRP; + unsigned int tWP; }; static void ams_delta_write_commit(struct ams_delta_nand *priv) { gpiod_set_value(priv->gpiod_nwe, 1); - ndelay(40); + ndelay(priv->tWP); gpiod_set_value(priv->gpiod_nwe, 0); } @@ -82,7 +84,7 @@ static u8 ams_delta_io_read(struct ams_delta_nand *priv) DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, }; gpiod_set_value(priv->gpiod_nre, 1); - ndelay(40); + ndelay(priv->tRP); gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, data_gpiods->info, values); @@ -187,8 +189,31 @@ static int ams_delta_exec_op(struct nand_chip *this, return ret; } +static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, + const struct nand_data_interface *cf) +{ + struct ams_delta_nand *priv = nand_get_controller_data(this); + const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf); + struct device *dev = &nand_to_mtd(this)->dev; + + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000); + dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP); + + priv->tWP = DIV_ROUND_UP(sdr->tWP_min, 1000); + dev_dbg(dev, "using %u ns write pulse width\n", priv->tWP); + + return 0; +} + static const struct nand_controller_ops ams_delta_ops = { .exec_op = ams_delta_exec_op, + .setup_data_interface = ams_delta_setup_data_interface, }; /* From 586a746b326c35d2c0b2773dba61ffa1d4818711 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:23 +0100 Subject: [PATCH 021/103] mtd: rawnand: ams-delta: Make read pulses optional Allow platforms to omit NRE pin from device configuration by requesting that pin as optional. In that case, also don't apply read pulse width from chip SDR timings. There should be no need for further code adjustments as gpiolib can handle NULL GPIO descriptor pointers. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-9-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 11689218d23a..c481d73e3dcb 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -202,8 +202,10 @@ static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, if (csline == NAND_DATA_IFACE_CHECK_ONLY) return 0; - priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000); - dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP); + if (priv->gpiod_nre) { + priv->tRP = DIV_ROUND_UP(sdr->tRP_min, 1000); + dev_dbg(dev, "using %u ns read pulse width\n", priv->tRP); + } priv->tWP = DIV_ROUND_UP(sdr->tWP_min, 1000); dev_dbg(dev, "using %u ns write pulse width\n", priv->tWP); @@ -276,7 +278,8 @@ static int ams_delta_init(struct platform_device *pdev) return err; } - priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_LOW); + priv->gpiod_nre = devm_gpiod_get_optional(&pdev->dev, "nre", + GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nre)) { err = PTR_ERR(priv->gpiod_nre); dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err); From ea5ea9fa6db23ff5b194e84fad32e3bae5c7f357 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:24 +0100 Subject: [PATCH 022/103] mtd: rawnand: ams-delta: Handle more GPIO pins as optional In order to make the driver more useful on platforms other than Amstrad Delta, allow GPIO descriptor pointers of possibly non-critical NWP and NCE pins to be initialised as NULL. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-10-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index c481d73e3dcb..0c88e94e9b71 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -264,14 +264,16 @@ static int ams_delta_init(struct platform_device *pdev) platform_set_drvdata(pdev, priv); /* Set chip enabled but write protected */ - priv->gpiod_nwp = devm_gpiod_get(&pdev->dev, "nwp", GPIOD_OUT_HIGH); + priv->gpiod_nwp = devm_gpiod_get_optional(&pdev->dev, "nwp", + GPIOD_OUT_HIGH); if (IS_ERR(priv->gpiod_nwp)) { err = PTR_ERR(priv->gpiod_nwp); dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err); return err; } - priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_LOW); + priv->gpiod_nce = devm_gpiod_get_optional(&pdev->dev, "nce", + GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nce)) { err = PTR_ERR(priv->gpiod_nce); dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err); From 46b5889cc2c54bac7d7e727a44d28a298df23cef Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Jan 2020 10:09:52 +0100 Subject: [PATCH 023/103] mtd: implement proper partition handling Instead of collecting partitions in a flat list, create a hierarchy within the mtd_info structure: use a partitions list to keep track of the partitions of an MTD device (which might be itself a partition of another MTD device), a pointer to the parent device (NULL when the MTD device is the root one, not a partition). By also saving directly in mtd_info the offset of the partition, we can get rid of the mtd_part structure. While at it, be consistent in the naming of the mtd_info structures to ease the understanding of the new hierarchy: these structures are usually called 'mtd', unless there are multiple instances of the same structure. In this case, there is usually a parent/child bound so we will call them 'parent' and 'child'. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200114090952.11232-1-miquel.raynal@bootlin.com --- drivers/mtd/mtdchar.c | 12 +- drivers/mtd/mtdcore.c | 250 ++++++++---- drivers/mtd/mtdpart.c | 693 +++++++++------------------------ include/linux/mtd/mtd.h | 125 +++++- include/linux/mtd/partitions.h | 1 - 5 files changed, 477 insertions(+), 604 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index b841008a9eb7..c5935b2f9cd1 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -349,6 +349,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, uint64_t start, uint32_t length, void __user *ptr, uint32_t __user *retp) { + struct mtd_info *master = mtd_get_master(mtd); struct mtd_file_info *mfi = file->private_data; struct mtd_oob_ops ops = {}; uint32_t retlen; @@ -360,7 +361,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, if (length > 4096) return -EINVAL; - if (!mtd->_write_oob) + if (!master->_write_oob) return -EOPNOTSUPP; ops.ooblen = length; @@ -586,6 +587,7 @@ static int mtdchar_blkpg_ioctl(struct mtd_info *mtd, static int mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp) { + struct mtd_info *master = mtd_get_master(mtd); struct mtd_write_req req; struct mtd_oob_ops ops = {}; const void __user *usr_data, *usr_oob; @@ -597,9 +599,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, usr_data = (const void __user *)(uintptr_t)req.usr_data; usr_oob = (const void __user *)(uintptr_t)req.usr_oob; - if (!mtd->_write_oob) + if (!master->_write_oob) return -EOPNOTSUPP; - ops.mode = req.mode; ops.len = (size_t)req.len; ops.ooblen = (size_t)req.ooblen; @@ -635,6 +636,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; + struct mtd_info *master = mtd_get_master(mtd); void __user *argp = (void __user *)arg; int ret = 0; struct mtd_info_user info; @@ -824,7 +826,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_oobinfo oi; - if (!mtd->ooblayout) + if (!master->ooblayout) return -EOPNOTSUPP; ret = get_oobinfo(mtd, &oi); @@ -918,7 +920,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_ecclayout_user *usrlay; - if (!mtd->ooblayout) + if (!master->ooblayout) return -EOPNOTSUPP; usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 5fac4355b9c2..2916674208b3 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -456,13 +456,14 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state, int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, struct mtd_pairing_info *info) { - int npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd); + struct mtd_info *master = mtd_get_master(mtd); + int npairs = mtd_wunit_per_eb(master) / mtd_pairing_groups(master); if (wunit < 0 || wunit >= npairs) return -EINVAL; - if (mtd->pairing && mtd->pairing->get_info) - return mtd->pairing->get_info(mtd, wunit, info); + if (master->pairing && master->pairing->get_info) + return master->pairing->get_info(master, wunit, info); info->group = 0; info->pair = wunit; @@ -498,15 +499,16 @@ EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info); int mtd_pairing_info_to_wunit(struct mtd_info *mtd, const struct mtd_pairing_info *info) { - int ngroups = mtd_pairing_groups(mtd); - int npairs = mtd_wunit_per_eb(mtd) / ngroups; + struct mtd_info *master = mtd_get_master(mtd); + int ngroups = mtd_pairing_groups(master); + int npairs = mtd_wunit_per_eb(master) / ngroups; if (!info || info->pair < 0 || info->pair >= npairs || info->group < 0 || info->group >= ngroups) return -EINVAL; - if (mtd->pairing && mtd->pairing->get_wunit) - return mtd->pairing->get_wunit(mtd, info); + if (master->pairing && master->pairing->get_wunit) + return mtd->pairing->get_wunit(master, info); return info->pair; } @@ -524,10 +526,12 @@ EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit); */ int mtd_pairing_groups(struct mtd_info *mtd) { - if (!mtd->pairing || !mtd->pairing->ngroups) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->pairing || !master->pairing->ngroups) return 1; - return mtd->pairing->ngroups; + return master->pairing->ngroups; } EXPORT_SYMBOL_GPL(mtd_pairing_groups); @@ -587,6 +591,7 @@ static int mtd_nvmem_add(struct mtd_info *mtd) int add_mtd_device(struct mtd_info *mtd) { + struct mtd_info *master = mtd_get_master(mtd); struct mtd_notifier *not; int i, error; @@ -608,7 +613,7 @@ int add_mtd_device(struct mtd_info *mtd) (mtd->_read && mtd->_read_oob))) return -EINVAL; - if (WARN_ON((!mtd->erasesize || !mtd->_erase) && + if (WARN_ON((!mtd->erasesize || !master->_erase) && !(mtd->flags & MTD_NO_ERASE))) return -EINVAL; @@ -765,7 +770,8 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd) pr_debug("mtd device won't show a device symlink in sysfs\n"); } - mtd->orig_flags = mtd->flags; + INIT_LIST_HEAD(&mtd->partitions); + mutex_init(&mtd->master.partitions_lock); } /** @@ -971,20 +977,26 @@ EXPORT_SYMBOL_GPL(get_mtd_device); int __get_mtd_device(struct mtd_info *mtd) { + struct mtd_info *master = mtd_get_master(mtd); int err; - if (!try_module_get(mtd->owner)) + if (!try_module_get(master->owner)) return -ENODEV; - if (mtd->_get_device) { - err = mtd->_get_device(mtd); + if (master->_get_device) { + err = master->_get_device(mtd); if (err) { - module_put(mtd->owner); + module_put(master->owner); return err; } } - mtd->usecount++; + + while (mtd->parent) { + mtd->usecount++; + mtd = mtd->parent; + } + return 0; } EXPORT_SYMBOL_GPL(__get_mtd_device); @@ -1038,13 +1050,18 @@ EXPORT_SYMBOL_GPL(put_mtd_device); void __put_mtd_device(struct mtd_info *mtd) { - --mtd->usecount; - BUG_ON(mtd->usecount < 0); + struct mtd_info *master = mtd_get_master(mtd); - if (mtd->_put_device) - mtd->_put_device(mtd); + while (mtd->parent) { + --mtd->usecount; + BUG_ON(mtd->usecount < 0); + mtd = mtd->parent; + } - module_put(mtd->owner); + if (master->_put_device) + master->_put_device(master); + + module_put(master->owner); } EXPORT_SYMBOL_GPL(__put_mtd_device); @@ -1055,9 +1072,13 @@ EXPORT_SYMBOL_GPL(__put_mtd_device); */ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) { + struct mtd_info *master = mtd_get_master(mtd); + u64 mst_ofs = mtd_get_master_ofs(mtd, 0); + int ret; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - if (!mtd->erasesize || !mtd->_erase) + if (!mtd->erasesize || !master->_erase) return -ENOTSUPP; if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr) @@ -1069,7 +1090,14 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) return 0; ledtrig_mtd_activity(); - return mtd->_erase(mtd, instr); + + instr->addr += mst_ofs; + ret = master->_erase(master, instr); + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= mst_ofs; + + instr->addr -= mst_ofs; + return ret; } EXPORT_SYMBOL_GPL(mtd_erase); @@ -1079,30 +1107,36 @@ EXPORT_SYMBOL_GPL(mtd_erase); int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; *virt = NULL; if (phys) *phys = 0; - if (!mtd->_point) + if (!master->_point) return -EOPNOTSUPP; if (from < 0 || from >= mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_point(mtd, from, len, retlen, virt, phys); + + from = mtd_get_master_ofs(mtd, from); + return master->_point(master, from, len, retlen, virt, phys); } EXPORT_SYMBOL_GPL(mtd_point); /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len) { - if (!mtd->_unpoint) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_unpoint) return -EOPNOTSUPP; if (from < 0 || from >= mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_unpoint(mtd, from, len); + return master->_unpoint(master, mtd_get_master_ofs(mtd, from), len); } EXPORT_SYMBOL_GPL(mtd_unpoint); @@ -1129,6 +1163,25 @@ unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len, } EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); +static void mtd_update_ecc_stats(struct mtd_info *mtd, struct mtd_info *master, + const struct mtd_ecc_stats *old_stats) +{ + struct mtd_ecc_stats diff; + + if (master == mtd) + return; + + diff = master->ecc_stats; + diff.failed -= old_stats->failed; + diff.corrected -= old_stats->corrected; + + while (mtd->parent) { + mtd->ecc_stats.failed += diff.failed; + mtd->ecc_stats.corrected += diff.corrected; + mtd = mtd->parent; + } +} + int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -1171,8 +1224,10 @@ EXPORT_SYMBOL_GPL(mtd_write); int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; - if (!mtd->_panic_write) + if (!master->_panic_write) return -EOPNOTSUPP; if (to < 0 || to >= mtd->size || len > mtd->size - to) return -EINVAL; @@ -1183,7 +1238,8 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, if (!mtd->oops_panic_write) mtd->oops_panic_write = true; - return mtd->_panic_write(mtd, to, len, retlen, buf); + return master->_panic_write(master, mtd_get_master_ofs(mtd, to), len, + retlen, buf); } EXPORT_SYMBOL_GPL(mtd_panic_write); @@ -1222,7 +1278,10 @@ static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs, int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + struct mtd_info *master = mtd_get_master(mtd); + struct mtd_ecc_stats old_stats = master->ecc_stats; int ret_code; + ops->retlen = ops->oobretlen = 0; ret_code = mtd_check_oob_ops(mtd, from, ops); @@ -1232,14 +1291,17 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) ledtrig_mtd_activity(); /* Check the validity of a potential fallback on mtd->_read */ - if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf)) + if (!master->_read_oob && (!master->_read || ops->oobbuf)) return -EOPNOTSUPP; - if (mtd->_read_oob) - ret_code = mtd->_read_oob(mtd, from, ops); + from = mtd_get_master_ofs(mtd, from); + if (master->_read_oob) + ret_code = master->_read_oob(master, from, ops); else - ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen, - ops->datbuf); + ret_code = master->_read(master, from, ops->len, &ops->retlen, + ops->datbuf); + + mtd_update_ecc_stats(mtd, master, &old_stats); /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics @@ -1258,6 +1320,7 @@ EXPORT_SYMBOL_GPL(mtd_read_oob); int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { + struct mtd_info *master = mtd_get_master(mtd); int ret; ops->retlen = ops->oobretlen = 0; @@ -1272,14 +1335,16 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, ledtrig_mtd_activity(); /* Check the validity of a potential fallback on mtd->_write */ - if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf)) + if (!master->_write_oob && (!master->_write || ops->oobbuf)) return -EOPNOTSUPP; - if (mtd->_write_oob) - return mtd->_write_oob(mtd, to, ops); + to = mtd_get_master_ofs(mtd, to); + + if (master->_write_oob) + return master->_write_oob(master, to, ops); else - return mtd->_write(mtd, to, ops->len, &ops->retlen, - ops->datbuf); + return master->_write(master, to, ops->len, &ops->retlen, + ops->datbuf); } EXPORT_SYMBOL_GPL(mtd_write_oob); @@ -1302,15 +1367,17 @@ EXPORT_SYMBOL_GPL(mtd_write_oob); int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc) { + struct mtd_info *master = mtd_get_master(mtd); + memset(oobecc, 0, sizeof(*oobecc)); - if (!mtd || section < 0) + if (!master || section < 0) return -EINVAL; - if (!mtd->ooblayout || !mtd->ooblayout->ecc) + if (!master->ooblayout || !master->ooblayout->ecc) return -ENOTSUPP; - return mtd->ooblayout->ecc(mtd, section, oobecc); + return master->ooblayout->ecc(master, section, oobecc); } EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); @@ -1334,15 +1401,17 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); int mtd_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *oobfree) { + struct mtd_info *master = mtd_get_master(mtd); + memset(oobfree, 0, sizeof(*oobfree)); - if (!mtd || section < 0) + if (!master || section < 0) return -EINVAL; - if (!mtd->ooblayout || !mtd->ooblayout->free) + if (!master->ooblayout || !master->ooblayout->free) return -ENOTSUPP; - return mtd->ooblayout->free(mtd, section, oobfree); + return master->ooblayout->free(master, section, oobfree); } EXPORT_SYMBOL_GPL(mtd_ooblayout_free); @@ -1651,60 +1720,69 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - if (!mtd->_get_fact_prot_info) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_get_fact_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_fact_prot_info(mtd, len, retlen, buf); + return master->_get_fact_prot_info(master, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info); int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; - if (!mtd->_read_fact_prot_reg) + if (!master->_read_fact_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf); + return master->_read_fact_prot_reg(master, from, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg); int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - if (!mtd->_get_user_prot_info) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_get_user_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_user_prot_info(mtd, len, retlen, buf); + return master->_get_user_prot_info(master, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_get_user_prot_info); int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; - if (!mtd->_read_user_prot_reg) + if (!master->_read_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf); + return master->_read_user_prot_reg(master, from, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg); int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); int ret; *retlen = 0; - if (!mtd->_write_user_prot_reg) + if (!master->_write_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); + ret = master->_write_user_prot_reg(master, to, len, retlen, buf); if (ret) return ret; @@ -1718,80 +1796,105 @@ EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg); int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { - if (!mtd->_lock_user_prot_reg) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_lock_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_lock_user_prot_reg(mtd, from, len); + return master->_lock_user_prot_reg(master, from, len); } EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg); /* Chip-supported device locking */ int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - if (!mtd->_lock) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_lock) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs) return -EINVAL; if (!len) return 0; - return mtd->_lock(mtd, ofs, len); + return master->_lock(master, mtd_get_master_ofs(mtd, ofs), len); } EXPORT_SYMBOL_GPL(mtd_lock); int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - if (!mtd->_unlock) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_unlock) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs) return -EINVAL; if (!len) return 0; - return mtd->_unlock(mtd, ofs, len); + return master->_unlock(master, mtd_get_master_ofs(mtd, ofs), len); } EXPORT_SYMBOL_GPL(mtd_unlock); int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - if (!mtd->_is_locked) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_is_locked) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs) return -EINVAL; if (!len) return 0; - return mtd->_is_locked(mtd, ofs, len); + return master->_is_locked(master, mtd_get_master_ofs(mtd, ofs), len); } EXPORT_SYMBOL_GPL(mtd_is_locked); int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs) { + struct mtd_info *master = mtd_get_master(mtd); + if (ofs < 0 || ofs >= mtd->size) return -EINVAL; - if (!mtd->_block_isreserved) + if (!master->_block_isreserved) return 0; - return mtd->_block_isreserved(mtd, ofs); + return master->_block_isreserved(master, mtd_get_master_ofs(mtd, ofs)); } EXPORT_SYMBOL_GPL(mtd_block_isreserved); int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) { + struct mtd_info *master = mtd_get_master(mtd); + if (ofs < 0 || ofs >= mtd->size) return -EINVAL; - if (!mtd->_block_isbad) + if (!master->_block_isbad) return 0; - return mtd->_block_isbad(mtd, ofs); + return master->_block_isbad(master, mtd_get_master_ofs(mtd, ofs)); } EXPORT_SYMBOL_GPL(mtd_block_isbad); int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) { - if (!mtd->_block_markbad) + struct mtd_info *master = mtd_get_master(mtd); + int ret; + + if (!master->_block_markbad) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size) return -EINVAL; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - return mtd->_block_markbad(mtd, ofs); + + ret = master->_block_markbad(master, mtd_get_master_ofs(mtd, ofs)); + if (ret) + return ret; + + while (mtd->parent) { + mtd->ecc_stats.badblocks++; + mtd = mtd->parent; + } + + return 0; } EXPORT_SYMBOL_GPL(mtd_block_markbad); @@ -1841,12 +1944,17 @@ static int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - if (!mtd->_writev) + + if (!master->_writev) return default_mtd_writev(mtd, vecs, count, to, retlen); - return mtd->_writev(mtd, vecs, count, to, retlen); + + return master->_writev(master, vecs, count, + mtd_get_master_ofs(mtd, to), retlen); } EXPORT_SYMBOL_GPL(mtd_writev); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 7328c066c5ba..3f6025684f58 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -20,339 +20,52 @@ #include "mtdcore.h" -/* Our partition linked list */ -static LIST_HEAD(mtd_partitions); -static DEFINE_MUTEX(mtd_partitions_mutex); - -/** - * struct mtd_part - our partition node structure - * - * @mtd: struct holding partition details - * @parent: parent mtd - flash device or another partition - * @offset: partition offset relative to the *flash device* - */ -struct mtd_part { - struct mtd_info mtd; - struct mtd_info *parent; - uint64_t offset; - struct list_head list; -}; - -/* - * Given a pointer to the MTD object in the mtd_part structure, we can retrieve - * the pointer to that structure. - */ -static inline struct mtd_part *mtd_to_part(const struct mtd_info *mtd) -{ - return container_of(mtd, struct mtd_part, mtd); -} - -static u64 part_absolute_offset(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - - if (!mtd_is_partition(mtd)) - return 0; - - return part_absolute_offset(part->parent) + part->offset; -} - /* * MTD methods which simply translate the effective address and pass through * to the _real_ device. */ -static int part_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static inline void free_partition(struct mtd_info *mtd) { - struct mtd_part *part = mtd_to_part(mtd); - struct mtd_ecc_stats stats; - int res; - - stats = part->parent->ecc_stats; - res = part->parent->_read(part->parent, from + part->offset, len, - retlen, buf); - if (unlikely(mtd_is_eccerr(res))) - mtd->ecc_stats.failed += - part->parent->ecc_stats.failed - stats.failed; - else - mtd->ecc_stats.corrected += - part->parent->ecc_stats.corrected - stats.corrected; - return res; + kfree(mtd->name); + kfree(mtd); } -static int part_point(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, void **virt, resource_size_t *phys) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_point(part->parent, from + part->offset, len, - retlen, virt, phys); -} - -static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_unpoint(part->parent, from + part->offset, len); -} - -static int part_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct mtd_part *part = mtd_to_part(mtd); - struct mtd_ecc_stats stats; - int res; - - stats = part->parent->ecc_stats; - res = part->parent->_read_oob(part->parent, from + part->offset, ops); - if (unlikely(mtd_is_eccerr(res))) - mtd->ecc_stats.failed += - part->parent->ecc_stats.failed - stats.failed; - else - mtd->ecc_stats.corrected += - part->parent->ecc_stats.corrected - stats.corrected; - return res; -} - -static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_read_user_prot_reg(part->parent, from, len, - retlen, buf); -} - -static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, - size_t *retlen, struct otp_info *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_get_user_prot_info(part->parent, len, retlen, - buf); -} - -static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_read_fact_prot_reg(part->parent, from, len, - retlen, buf); -} - -static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, - size_t *retlen, struct otp_info *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_get_fact_prot_info(part->parent, len, retlen, - buf); -} - -static int part_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_write(part->parent, to + part->offset, len, - retlen, buf); -} - -static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_panic_write(part->parent, to + part->offset, len, - retlen, buf); -} - -static int part_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_write_oob(part->parent, to + part->offset, ops); -} - -static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_write_user_prot_reg(part->parent, from, len, - retlen, buf); -} - -static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_lock_user_prot_reg(part->parent, from, len); -} - -static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_writev(part->parent, vecs, count, - to + part->offset, retlen); -} - -static int part_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct mtd_part *part = mtd_to_part(mtd); - int ret; - - instr->addr += part->offset; - ret = part->parent->_erase(part->parent, instr); - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; - - return ret; -} - -static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_lock(part->parent, ofs + part->offset, len); -} - -static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_unlock(part->parent, ofs + part->offset, len); -} - -static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_is_locked(part->parent, ofs + part->offset, len); -} - -static void part_sync(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - part->parent->_sync(part->parent); -} - -static int part_suspend(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_suspend(part->parent); -} - -static void part_resume(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - part->parent->_resume(part->parent); -} - -static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs) -{ - struct mtd_part *part = mtd_to_part(mtd); - ofs += part->offset; - return part->parent->_block_isreserved(part->parent, ofs); -} - -static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) -{ - struct mtd_part *part = mtd_to_part(mtd); - ofs += part->offset; - return part->parent->_block_isbad(part->parent, ofs); -} - -static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct mtd_part *part = mtd_to_part(mtd); - int res; - - ofs += part->offset; - res = part->parent->_block_markbad(part->parent, ofs); - if (!res) - mtd->ecc_stats.badblocks++; - return res; -} - -static int part_get_device(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_get_device(part->parent); -} - -static void part_put_device(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - part->parent->_put_device(part->parent); -} - -static int part_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return mtd_ooblayout_ecc(part->parent, section, oobregion); -} - -static int part_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return mtd_ooblayout_free(part->parent, section, oobregion); -} - -static const struct mtd_ooblayout_ops part_ooblayout_ops = { - .ecc = part_ooblayout_ecc, - .free = part_ooblayout_free, -}; - -static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_max_bad_blocks(part->parent, - ofs + part->offset, len); -} - -static inline void free_partition(struct mtd_part *p) -{ - kfree(p->mtd.name); - kfree(p); -} - -static struct mtd_part *allocate_partition(struct mtd_info *parent, - const struct mtd_partition *part, int partno, - uint64_t cur_offset) +static struct mtd_info *allocate_partition(struct mtd_info *parent, + const struct mtd_partition *part, + int partno, uint64_t cur_offset) { int wr_alignment = (parent->flags & MTD_NO_ERASE) ? parent->writesize : parent->erasesize; - struct mtd_part *slave; + struct mtd_info *child, *master = mtd_get_master(parent); u32 remainder; char *name; u64 tmp; /* allocate the partition structure */ - slave = kzalloc(sizeof(*slave), GFP_KERNEL); + child = kzalloc(sizeof(*child), GFP_KERNEL); name = kstrdup(part->name, GFP_KERNEL); - if (!name || !slave) { + if (!name || !child) { printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", parent->name); kfree(name); - kfree(slave); + kfree(child); return ERR_PTR(-ENOMEM); } /* set up the MTD object for this partition */ - slave->mtd.type = parent->type; - slave->mtd.flags = parent->orig_flags & ~part->mask_flags; - slave->mtd.orig_flags = slave->mtd.flags; - slave->mtd.size = part->size; - slave->mtd.writesize = parent->writesize; - slave->mtd.writebufsize = parent->writebufsize; - slave->mtd.oobsize = parent->oobsize; - slave->mtd.oobavail = parent->oobavail; - slave->mtd.subpage_sft = parent->subpage_sft; - slave->mtd.pairing = parent->pairing; + child->type = parent->type; + child->part.flags = parent->flags & ~part->mask_flags; + child->flags = child->part.flags; + child->size = part->size; + child->writesize = parent->writesize; + child->writebufsize = parent->writebufsize; + child->oobsize = parent->oobsize; + child->oobavail = parent->oobavail; + child->subpage_sft = parent->subpage_sft; - slave->mtd.name = name; - slave->mtd.owner = parent->owner; + child->name = name; + child->owner = parent->owner; /* NOTE: Historically, we didn't arrange MTDs as a tree out of * concern for showing the same data in multiple partitions. @@ -360,134 +73,76 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, * so the MTD_PARTITIONED_MASTER option allows that. The master * will have device nodes etc only if this is set, so make the * parent conditional on that option. Note, this is a way to - * distinguish between the master and the partition in sysfs. + * distinguish between the parent and its partitions in sysfs. */ - slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ? - &parent->dev : - parent->dev.parent; - slave->mtd.dev.of_node = part->of_node; + child->dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ? + &parent->dev : parent->dev.parent; + child->dev.of_node = part->of_node; + child->parent = parent; + child->part.offset = part->offset; + INIT_LIST_HEAD(&child->partitions); - if (parent->_read) - slave->mtd._read = part_read; - if (parent->_write) - slave->mtd._write = part_write; - - if (parent->_panic_write) - slave->mtd._panic_write = part_panic_write; - - if (parent->_point && parent->_unpoint) { - slave->mtd._point = part_point; - slave->mtd._unpoint = part_unpoint; - } - - if (parent->_read_oob) - slave->mtd._read_oob = part_read_oob; - if (parent->_write_oob) - slave->mtd._write_oob = part_write_oob; - if (parent->_read_user_prot_reg) - slave->mtd._read_user_prot_reg = part_read_user_prot_reg; - if (parent->_read_fact_prot_reg) - slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg; - if (parent->_write_user_prot_reg) - slave->mtd._write_user_prot_reg = part_write_user_prot_reg; - if (parent->_lock_user_prot_reg) - slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg; - if (parent->_get_user_prot_info) - slave->mtd._get_user_prot_info = part_get_user_prot_info; - if (parent->_get_fact_prot_info) - slave->mtd._get_fact_prot_info = part_get_fact_prot_info; - if (parent->_sync) - slave->mtd._sync = part_sync; - if (!partno && !parent->dev.class && parent->_suspend && - parent->_resume) { - slave->mtd._suspend = part_suspend; - slave->mtd._resume = part_resume; - } - if (parent->_writev) - slave->mtd._writev = part_writev; - if (parent->_lock) - slave->mtd._lock = part_lock; - if (parent->_unlock) - slave->mtd._unlock = part_unlock; - if (parent->_is_locked) - slave->mtd._is_locked = part_is_locked; - if (parent->_block_isreserved) - slave->mtd._block_isreserved = part_block_isreserved; - if (parent->_block_isbad) - slave->mtd._block_isbad = part_block_isbad; - if (parent->_block_markbad) - slave->mtd._block_markbad = part_block_markbad; - if (parent->_max_bad_blocks) - slave->mtd._max_bad_blocks = part_max_bad_blocks; - - if (parent->_get_device) - slave->mtd._get_device = part_get_device; - if (parent->_put_device) - slave->mtd._put_device = part_put_device; - - slave->mtd._erase = part_erase; - slave->parent = parent; - slave->offset = part->offset; - - if (slave->offset == MTDPART_OFS_APPEND) - slave->offset = cur_offset; - if (slave->offset == MTDPART_OFS_NXTBLK) { + if (child->part.offset == MTDPART_OFS_APPEND) + child->part.offset = cur_offset; + if (child->part.offset == MTDPART_OFS_NXTBLK) { tmp = cur_offset; - slave->offset = cur_offset; + child->part.offset = cur_offset; remainder = do_div(tmp, wr_alignment); if (remainder) { - slave->offset += wr_alignment - remainder; + child->part.offset += wr_alignment - remainder; printk(KERN_NOTICE "Moving partition %d: " "0x%012llx -> 0x%012llx\n", partno, - (unsigned long long)cur_offset, (unsigned long long)slave->offset); + (unsigned long long)cur_offset, + child->part.offset); } } - if (slave->offset == MTDPART_OFS_RETAIN) { - slave->offset = cur_offset; - if (parent->size - slave->offset >= slave->mtd.size) { - slave->mtd.size = parent->size - slave->offset - - slave->mtd.size; + if (child->part.offset == MTDPART_OFS_RETAIN) { + child->part.offset = cur_offset; + if (parent->size - child->part.offset >= child->size) { + child->size = parent->size - child->part.offset - + child->size; } else { printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n", - part->name, parent->size - slave->offset, - slave->mtd.size); + part->name, parent->size - child->part.offset, + child->size); /* register to preserve ordering */ goto out_register; } } - if (slave->mtd.size == MTDPART_SIZ_FULL) - slave->mtd.size = parent->size - slave->offset; + if (child->size == MTDPART_SIZ_FULL) + child->size = parent->size - child->part.offset; - printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, - (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); + printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", + child->part.offset, child->part.offset + child->size, + child->name); /* let's do some sanity checks */ - if (slave->offset >= parent->size) { + if (child->part.offset >= parent->size) { /* let's register it anyway to preserve ordering */ - slave->offset = 0; - slave->mtd.size = 0; + child->part.offset = 0; + child->size = 0; /* Initialize ->erasesize to make add_mtd_device() happy. */ - slave->mtd.erasesize = parent->erasesize; - + child->erasesize = parent->erasesize; printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", part->name); goto out_register; } - if (slave->offset + slave->mtd.size > parent->size) { - slave->mtd.size = parent->size - slave->offset; + if (child->part.offset + child->size > parent->size) { + child->size = parent->size - child->part.offset; printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", - part->name, parent->name, (unsigned long long)slave->mtd.size); + part->name, parent->name, child->size); } if (parent->numeraseregions > 1) { /* Deal with variable erase size stuff */ int i, max = parent->numeraseregions; - u64 end = slave->offset + slave->mtd.size; + u64 end = child->part.offset + child->size; struct mtd_erase_region_info *regions = parent->eraseregions; /* Find the first erase regions which is part of this * partition. */ - for (i = 0; i < max && regions[i].offset <= slave->offset; i++) + for (i = 0; i < max && regions[i].offset <= child->part.offset; + i++) ; /* The loop searched for the region _behind_ the first one */ if (i > 0) @@ -495,70 +150,68 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, /* Pick biggest erasesize */ for (; i < max && regions[i].offset < end; i++) { - if (slave->mtd.erasesize < regions[i].erasesize) { - slave->mtd.erasesize = regions[i].erasesize; - } + if (child->erasesize < regions[i].erasesize) + child->erasesize = regions[i].erasesize; } - BUG_ON(slave->mtd.erasesize == 0); + BUG_ON(child->erasesize == 0); } else { /* Single erase size */ - slave->mtd.erasesize = parent->erasesize; + child->erasesize = parent->erasesize; } /* - * Slave erasesize might differ from the master one if the master + * Child erasesize might differ from the parent one if the parent * exposes several regions with different erasesize. Adjust * wr_alignment accordingly. */ - if (!(slave->mtd.flags & MTD_NO_ERASE)) - wr_alignment = slave->mtd.erasesize; + if (!(child->flags & MTD_NO_ERASE)) + wr_alignment = child->erasesize; - tmp = part_absolute_offset(parent) + slave->offset; + tmp = mtd_get_master_ofs(child, 0); remainder = do_div(tmp, wr_alignment); - if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) { + if ((child->flags & MTD_WRITEABLE) && remainder) { /* Doesn't start on a boundary of major erase size */ /* FIXME: Let it be writable if it is on a boundary of * _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; + child->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", part->name); } - tmp = part_absolute_offset(parent) + slave->mtd.size; + tmp = mtd_get_master_ofs(child, 0) + child->size; remainder = do_div(tmp, wr_alignment); - if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) { - slave->mtd.flags &= ~MTD_WRITEABLE; + if ((child->flags & MTD_WRITEABLE) && remainder) { + child->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", part->name); } - mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops); - slave->mtd.ecc_step_size = parent->ecc_step_size; - slave->mtd.ecc_strength = parent->ecc_strength; - slave->mtd.bitflip_threshold = parent->bitflip_threshold; + child->ecc_step_size = parent->ecc_step_size; + child->ecc_strength = parent->ecc_strength; + child->bitflip_threshold = parent->bitflip_threshold; - if (parent->_block_isbad) { + if (master->_block_isbad) { uint64_t offs = 0; - while (offs < slave->mtd.size) { - if (mtd_block_isreserved(parent, offs + slave->offset)) - slave->mtd.ecc_stats.bbtblocks++; - else if (mtd_block_isbad(parent, offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.erasesize; + while (offs < child->size) { + if (mtd_block_isreserved(child, offs)) + child->ecc_stats.bbtblocks++; + else if (mtd_block_isbad(child, offs)) + child->ecc_stats.badblocks++; + offs += child->erasesize; } } out_register: - return slave; + return child; } static ssize_t mtd_partition_offset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtd_info *mtd = dev_get_drvdata(dev); - struct mtd_part *part = mtd_to_part(mtd); - return snprintf(buf, PAGE_SIZE, "%llu\n", part->offset); + + return snprintf(buf, PAGE_SIZE, "%lld\n", mtd->part.offset); } static DEVICE_ATTR(offset, S_IRUGO, mtd_partition_offset_show, NULL); @@ -568,9 +221,9 @@ static const struct attribute *mtd_partition_attrs[] = { NULL }; -static int mtd_add_partition_attrs(struct mtd_part *new) +static int mtd_add_partition_attrs(struct mtd_info *new) { - int ret = sysfs_create_files(&new->mtd.dev.kobj, mtd_partition_attrs); + int ret = sysfs_create_files(&new->dev.kobj, mtd_partition_attrs); if (ret) printk(KERN_WARNING "mtd: failed to create partition attrs, err=%d\n", ret); @@ -580,8 +233,9 @@ static int mtd_add_partition_attrs(struct mtd_part *new) int mtd_add_partition(struct mtd_info *parent, const char *name, long long offset, long long length) { + struct mtd_info *master = mtd_get_master(parent); struct mtd_partition part; - struct mtd_part *new; + struct mtd_info *child; int ret = 0; /* the direct offset is expected */ @@ -600,28 +254,28 @@ int mtd_add_partition(struct mtd_info *parent, const char *name, part.size = length; part.offset = offset; - new = allocate_partition(parent, &part, -1, offset); - if (IS_ERR(new)) - return PTR_ERR(new); + child = allocate_partition(parent, &part, -1, offset); + if (IS_ERR(child)) + return PTR_ERR(child); - mutex_lock(&mtd_partitions_mutex); - list_add(&new->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_add_tail(&child->part.node, &parent->partitions); + mutex_unlock(&master->master.partitions_lock); - ret = add_mtd_device(&new->mtd); + ret = add_mtd_device(child); if (ret) goto err_remove_part; - mtd_add_partition_attrs(new); + mtd_add_partition_attrs(child); return 0; err_remove_part: - mutex_lock(&mtd_partitions_mutex); - list_del(&new->list); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_del(&child->part.node); + mutex_unlock(&master->master.partitions_lock); - free_partition(new); + free_partition(child); return ret; } @@ -630,119 +284,142 @@ EXPORT_SYMBOL_GPL(mtd_add_partition); /** * __mtd_del_partition - delete MTD partition * - * @priv: internal MTD struct for partition to be deleted + * @priv: MTD structure to be deleted * * This function must be called with the partitions mutex locked. */ -static int __mtd_del_partition(struct mtd_part *priv) +static int __mtd_del_partition(struct mtd_info *mtd) { - struct mtd_part *child, *next; + struct mtd_info *child, *next; int err; - list_for_each_entry_safe(child, next, &mtd_partitions, list) { - if (child->parent == &priv->mtd) { - err = __mtd_del_partition(child); - if (err) - return err; - } + list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { + err = __mtd_del_partition(child); + if (err) + return err; } - sysfs_remove_files(&priv->mtd.dev.kobj, mtd_partition_attrs); + sysfs_remove_files(&mtd->dev.kobj, mtd_partition_attrs); - err = del_mtd_device(&priv->mtd); + err = del_mtd_device(mtd); if (err) return err; - list_del(&priv->list); - free_partition(priv); + list_del(&child->part.node); + free_partition(mtd); return 0; } /* * This function unregisters and destroy all slave MTD objects which are - * attached to the given MTD object. + * attached to the given MTD object, recursively. */ -int del_mtd_partitions(struct mtd_info *mtd) +static int __del_mtd_partitions(struct mtd_info *mtd) { - struct mtd_part *slave, *next; + struct mtd_info *child, *next; + LIST_HEAD(tmp_list); int ret, err = 0; - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if (slave->parent == mtd) { - ret = __mtd_del_partition(slave); - if (ret < 0) - err = ret; + list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { + if (mtd_has_partitions(child)) + del_mtd_partitions(child); + + pr_info("Deleting %s MTD partition\n", child->name); + ret = del_mtd_device(child); + if (ret < 0) { + pr_err("Error when deleting partition \"%s\" (%d)\n", + child->name, ret); + err = ret; + continue; } - mutex_unlock(&mtd_partitions_mutex); + + list_del(&child->part.node); + free_partition(child); + } return err; } +int del_mtd_partitions(struct mtd_info *mtd) +{ + struct mtd_info *master = mtd_get_master(mtd); + int ret; + + pr_info("Deleting MTD partitions on \"%s\":\n", mtd->name); + + mutex_lock(&master->master.partitions_lock); + ret = __del_mtd_partitions(mtd); + mutex_unlock(&master->master.partitions_lock); + + return ret; +} + int mtd_del_partition(struct mtd_info *mtd, int partno) { - struct mtd_part *slave, *next; + struct mtd_info *child, *master = mtd_get_master(mtd); int ret = -EINVAL; - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if ((slave->parent == mtd) && - (slave->mtd.index == partno)) { - ret = __mtd_del_partition(slave); + mutex_lock(&master->master.partitions_lock); + list_for_each_entry(child, &mtd->partitions, part.node) { + if (child->index == partno) { + ret = __mtd_del_partition(child); break; } - mutex_unlock(&mtd_partitions_mutex); + } + mutex_unlock(&master->master.partitions_lock); return ret; } EXPORT_SYMBOL_GPL(mtd_del_partition); /* - * This function, given a master MTD object and a partition table, creates - * and registers slave MTD objects which are bound to the master according to - * the partition definitions. + * This function, given a parent MTD object and a partition table, creates + * and registers the child MTD objects which are bound to the parent according + * to the partition definitions. * - * For historical reasons, this function's caller only registers the master + * For historical reasons, this function's caller only registers the parent * if the MTD_PARTITIONED_MASTER config option is set. */ -int add_mtd_partitions(struct mtd_info *master, +int add_mtd_partitions(struct mtd_info *parent, const struct mtd_partition *parts, int nbparts) { - struct mtd_part *slave; + struct mtd_info *child, *master = mtd_get_master(parent); uint64_t cur_offset = 0; int i, ret; - printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", + nbparts, parent->name); for (i = 0; i < nbparts; i++) { - slave = allocate_partition(master, parts + i, i, cur_offset); - if (IS_ERR(slave)) { - ret = PTR_ERR(slave); + child = allocate_partition(parent, parts + i, i, cur_offset); + if (IS_ERR(child)) { + ret = PTR_ERR(child); goto err_del_partitions; } - mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_add_tail(&child->part.node, &parent->partitions); + mutex_unlock(&master->master.partitions_lock); - ret = add_mtd_device(&slave->mtd); + ret = add_mtd_device(child); if (ret) { - mutex_lock(&mtd_partitions_mutex); - list_del(&slave->list); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_del(&child->part.node); + mutex_unlock(&master->master.partitions_lock); - free_partition(slave); + free_partition(child); goto err_del_partitions; } - mtd_add_partition_attrs(slave); - /* Look for subpartitions */ - parse_mtd_partitions(&slave->mtd, parts[i].types, NULL); + mtd_add_partition_attrs(child); - cur_offset = slave->offset + slave->mtd.size; + /* Look for subpartitions */ + parse_mtd_partitions(child, parts[i].types, NULL); + + cur_offset = child->part.offset + child->size; } return 0; @@ -1023,29 +700,11 @@ void mtd_part_parser_cleanup(struct mtd_partitions *parts) } } -int mtd_is_partition(const struct mtd_info *mtd) -{ - struct mtd_part *part; - int ispart = 0; - - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry(part, &mtd_partitions, list) - if (&part->mtd == mtd) { - ispart = 1; - break; - } - mutex_unlock(&mtd_partitions_mutex); - - return ispart; -} -EXPORT_SYMBOL_GPL(mtd_is_partition); - /* Returns the size of the entire flash chip */ uint64_t mtd_get_device_size(const struct mtd_info *mtd) { - if (!mtd_is_partition(mtd)) - return mtd->size; + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); - return mtd_get_device_size(mtd_to_part(mtd)->parent); + return master->size; } EXPORT_SYMBOL_GPL(mtd_get_device_size); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 249e8d9bfbcd..2d1f4a61f4ac 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -194,10 +195,43 @@ struct mtd_debug_info { const char *partid; }; +/** + * struct mtd_part - MTD partition specific fields + * + * @node: list node used to add an MTD partition to the parent partition list + * @offset: offset of the partition relatively to the parent offset + * @flags: original flags (before the mtdpart logic decided to tweak them based + * on flash constraints, like eraseblock/pagesize alignment) + * + * This struct is embedded in mtd_info and contains partition-specific + * properties/fields. + */ +struct mtd_part { + struct list_head node; + u64 offset; + u32 flags; +}; + +/** + * struct mtd_master - MTD master specific fields + * + * @partitions_lock: lock protecting accesses to the partition list. Protects + * not only the master partition list, but also all + * sub-partitions. + * @suspended: et to 1 when the device is suspended, 0 otherwise + * + * This struct is embedded in mtd_info and contains master-specific + * properties/fields. The master is the root MTD device from the MTD partition + * point of view. + */ +struct mtd_master { + struct mutex partitions_lock; + unsigned int suspended : 1; +}; + struct mtd_info { u_char type; uint32_t flags; - uint32_t orig_flags; /* Flags as before running mtd checks */ uint64_t size; // Total size of the MTD /* "Major" erase size for the device. Naïve users may take this @@ -339,8 +373,52 @@ struct mtd_info { int usecount; struct mtd_debug_info dbg; struct nvmem_device *nvmem; + + /* + * Parent device from the MTD partition point of view. + * + * MTD masters do not have any parent, MTD partitions do. The parent + * MTD device can itself be a partition. + */ + struct mtd_info *parent; + + /* List of partitions attached to this MTD device */ + struct list_head partitions; + + union { + struct mtd_part part; + struct mtd_master master; + }; }; +static inline struct mtd_info *mtd_get_master(struct mtd_info *mtd) +{ + while (mtd->parent) + mtd = mtd->parent; + + return mtd; +} + +static inline u64 mtd_get_master_ofs(struct mtd_info *mtd, u64 ofs) +{ + while (mtd->parent) { + ofs += mtd->part.offset; + mtd = mtd->parent; + } + + return ofs; +} + +static inline bool mtd_is_partition(const struct mtd_info *mtd) +{ + return mtd->parent; +} + +static inline bool mtd_has_partitions(const struct mtd_info *mtd) +{ + return !list_empty(&mtd->partitions); +} + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, @@ -392,13 +470,16 @@ static inline u32 mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) static inline int mtd_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) { - if (!mtd->_max_bad_blocks) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_max_bad_blocks) return -ENOTSUPP; if (mtd->size < (len + ofs) || ofs < 0) return -EINVAL; - return mtd->_max_bad_blocks(mtd, ofs, len); + return master->_max_bad_blocks(master, mtd_get_master_ofs(mtd, ofs), + len); } int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, @@ -439,8 +520,10 @@ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, static inline void mtd_sync(struct mtd_info *mtd) { - if (mtd->_sync) - mtd->_sync(mtd); + struct mtd_info *master = mtd_get_master(mtd); + + if (master->_sync) + master->_sync(master); } int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); @@ -452,13 +535,31 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs); static inline int mtd_suspend(struct mtd_info *mtd) { - return mtd->_suspend ? mtd->_suspend(mtd) : 0; + struct mtd_info *master = mtd_get_master(mtd); + int ret; + + if (master->master.suspended) + return 0; + + ret = master->_suspend ? master->_suspend(master) : 0; + if (ret) + return ret; + + master->master.suspended = 1; + return 0; } static inline void mtd_resume(struct mtd_info *mtd) { - if (mtd->_resume) - mtd->_resume(mtd); + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->master.suspended) + return; + + if (master->_resume) + master->_resume(master); + + master->master.suspended = 0; } static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) @@ -538,7 +639,9 @@ static inline loff_t mtd_wunit_to_offset(struct mtd_info *mtd, loff_t base, static inline int mtd_has_oob(const struct mtd_info *mtd) { - return mtd->_read_oob && mtd->_write_oob; + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); + + return master->_read_oob && master->_write_oob; } static inline int mtd_type_is_nand(const struct mtd_info *mtd) @@ -548,7 +651,9 @@ static inline int mtd_type_is_nand(const struct mtd_info *mtd) static inline int mtd_can_have_bb(const struct mtd_info *mtd) { - return !!mtd->_block_isbad; + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); + + return !!master->_block_isbad; } /* Kernel-side ioctl definitions */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 11cb0c50cd84..e545c050d3e8 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -105,7 +105,6 @@ extern void deregister_mtd_parser(struct mtd_part_parser *parser); module_driver(__mtd_part_parser, register_mtd_parser, \ deregister_mtd_parser) -int mtd_is_partition(const struct mtd_info *mtd); int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length); int mtd_del_partition(struct mtd_info *master, int partno); From 13a964665fc1bedcdab1015e999f7631ec928c88 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 8 Feb 2020 11:36:12 +0000 Subject: [PATCH 024/103] mtd: fix spelling mistake "BlockMultiplerBits" -> "BlockMultiplierBits" There is a spelling mistake (missing i) in pr_info messages. Fix these. Signed-off-by: Colin Ian King Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200208113612.817988-1-colin.king@canonical.com --- drivers/mtd/inftlmount.c | 2 +- drivers/mtd/nand/raw/diskonchip.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 54b176d4319f..af16d3485de0 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -130,7 +130,7 @@ static int find_boot_record(struct INFTLrecord *inftl) " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" " NoOfBDTLPartitions = %d\n" - " BlockMultiplerBits = %d\n" + " BlockMultiplierBits = %d\n" " FormatFlgs = %d\n" " OsakVersion = 0x%x\n" " PercentUsed = %d\n", diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index c0e1a8ebe820..2833c49c1378 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1169,7 +1169,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partiti " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" " NoOfBDTLPartitions = %d\n" - " BlockMultiplerBits = %d\n" + " BlockMultiplierBits = %d\n" " FormatFlgs = %d\n" " OsakVersion = %d.%d.%d.%d\n" " PercentUsed = %d\n", From c0b66dce0ca8e628c73f673f368230a455ea6631 Mon Sep 17 00:00:00 2001 From: Vignesh Raghavendra Date: Thu, 27 Feb 2020 10:42:12 +0530 Subject: [PATCH 025/103] MAINTAINERS: Add staging branch for HyperBus Update HyperBus entry with branch used to stage patches under mtd.git. Also, add mailing list and patchwork queue information. Signed-off-by: Vignesh Raghavendra Acked-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227051212.15496-1-vigneshr@ti.com --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6158a143a13e..abe66664d5f4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7770,6 +7770,9 @@ F: Documentation/ABI/testing/debugfs-hyperv HYPERBUS SUPPORT M: Vignesh Raghavendra +L: linux-mtd@lists.infradead.org +Q: http://patchwork.ozlabs.org/project/linux-mtd/list/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git cfi/next S: Supported F: drivers/mtd/hyperbus/ F: include/linux/mtd/hyperbus.h From 4da0ea71ea934af18db4c63396ba2af1a679ef02 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 28 Feb 2020 12:25:54 +0300 Subject: [PATCH 026/103] mtd: lpddr: Fix a double free in probe() This function is only called from lpddr_probe(). We free "lpddr" both here and in the caller, so it's a double free. The best place to free "lpddr" is in lpddr_probe() so let's delete this one. Fixes: 8dc004395d5e ("[MTD] LPDDR qinfo probing.") Signed-off-by: Dan Carpenter Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200228092554.o57igp3nqhyvf66t@kili.mountain --- drivers/mtd/lpddr/lpddr_cmds.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index 1efc643c9871..9341a8a592e8 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -68,7 +68,6 @@ struct mtd_info *lpddr_cmdset(struct map_info *map) shared = kmalloc_array(lpddr->numchips, sizeof(struct flchip_shared), GFP_KERNEL); if (!shared) { - kfree(lpddr); kfree(mtd); return NULL; } From 7c2f66a960fccc165d0b6c594f40f0ad3edfc61f Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:25 +0100 Subject: [PATCH 027/103] mtd: rawnand: ams-delta: Add module device tables In preparation for merging the driver with "gpio-nand", introduce module device tables where new device models can be accommodated as soon as respective support is added. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-11-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 0c88e94e9b71..a493f1dc6677 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -370,11 +370,29 @@ static int ams_delta_cleanup(struct platform_device *pdev) return 0; } +static const struct of_device_id gpio_nand_of_id_table[] = { + { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, gpio_nand_of_id_table); + +static const struct platform_device_id gpio_nand_plat_id_table[] = { + { + .name = "ams-delta-nand", + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(platform, gpio_nand_plat_id_table); + static struct platform_driver ams_delta_nand_driver = { .probe = ams_delta_init, .remove = ams_delta_cleanup, + .id_table = gpio_nand_plat_id_table, .driver = { .name = "ams-delta-nand", + .of_match_table = of_match_ptr(gpio_nand_of_id_table), }, }; From d1b1a8f73a21c7807b23144f41174b71a5a60a40 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:26 +0100 Subject: [PATCH 028/103] mtd: rawnand: ams-delta: Support custom driver initialisation In preparation for extending the driver with custom I/O support, try to obtain device specific initialisation routine from a matching device table entry and run it as an additional step of device probe. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-12-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index a493f1dc6677..60502edfbeab 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -230,6 +231,7 @@ static int ams_delta_init(struct platform_device *pdev) struct nand_chip *this; struct mtd_info *mtd; struct gpio_descs *data_gpiods; + int (*probe)(struct platform_device *pdev, struct ams_delta_nand *priv); int err = 0; if (pdata) { @@ -319,6 +321,15 @@ static int ams_delta_init(struct platform_device *pdev) priv->data_gpiods = data_gpiods; priv->data_in = true; + if (pdev->id_entry) + probe = (void *) pdev->id_entry->driver_data; + else + probe = of_device_get_match_data(&pdev->dev); + if (probe) + err = probe(pdev, priv); + if (err) + return err; + /* Initialize the NAND controller object embedded in ams_delta_nand. */ priv->base.ops = &ams_delta_ops; nand_controller_init(&priv->base); From edfd8d9c763f5f91cda663e89f34dccacd8eb586 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:27 +0100 Subject: [PATCH 029/103] mtd: rawnand: ams-delta: Drop useless local variable For consistency with adjacent code patterns used in the driver probe function, store data GPIO array pointer directly in a respective field of the driver private structure instead of storing it intermediately in a local variable for error checking. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-13-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 60502edfbeab..d8eef3dffa66 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -230,7 +230,6 @@ static int ams_delta_init(struct platform_device *pdev) struct ams_delta_nand *priv; struct nand_chip *this; struct mtd_info *mtd; - struct gpio_descs *data_gpiods; int (*probe)(struct platform_device *pdev, struct ams_delta_nand *priv); int err = 0; @@ -312,13 +311,12 @@ static int ams_delta_init(struct platform_device *pdev) } /* Request array of data pins, initialize them as input */ - data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); - if (IS_ERR(data_gpiods)) { - err = PTR_ERR(data_gpiods); + priv->data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); + if (IS_ERR(priv->data_gpiods)) { + err = PTR_ERR(priv->data_gpiods); dev_err(&pdev->dev, "data GPIO request failed: %d\n", err); return err; } - priv->data_gpiods = data_gpiods; priv->data_in = true; if (pdev->id_entry) From 2b1dcee304b67f3c4e7e2e910be90e36eef46050 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:28 +0100 Subject: [PATCH 030/103] mtd: rawnand: ams-delta: Make the driver custom I/O ready In order to be merged with "gpio-nand", the driver must support custom (non-GPIO) I/O accessors. Allow platforms to omit data GPIO port as well as NWE pin info from device setup. For the driver to still work on such platform, custom I/O accessors as well as a custom probe function which initialises the driver private structure with those accessors must be added to the driver. Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-14-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index d8eef3dffa66..5a27170b2808 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -43,6 +43,9 @@ struct ams_delta_nand { bool data_in; unsigned int tRP; unsigned int tWP; + u8 (*io_read)(struct ams_delta_nand *this); + void (*io_write)(struct ams_delta_nand *this, + u8 byte); }; static void ams_delta_write_commit(struct ams_delta_nand *priv) @@ -116,18 +119,18 @@ static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf, ams_delta_dir_output(priv, buf[i++]); while (i < len) - ams_delta_io_write(priv, buf[i++]); + priv->io_write(priv, buf[i++]); } static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) { int i; - if (!priv->data_in) + if (priv->data_gpiods && !priv->data_in) ams_delta_dir_input(priv); for (i = 0; i < len; i++) - buf[i] = ams_delta_io_read(priv); + buf[i] = priv->io_read(priv); } static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) @@ -289,7 +292,8 @@ static int ams_delta_init(struct platform_device *pdev) return err; } - priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_LOW); + priv->gpiod_nwe = devm_gpiod_get_optional(&pdev->dev, "nwe", + GPIOD_OUT_LOW); if (IS_ERR(priv->gpiod_nwe)) { err = PTR_ERR(priv->gpiod_nwe); dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err); @@ -311,13 +315,24 @@ static int ams_delta_init(struct platform_device *pdev) } /* Request array of data pins, initialize them as input */ - priv->data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN); + priv->data_gpiods = devm_gpiod_get_array_optional(&pdev->dev, "data", + GPIOD_IN); if (IS_ERR(priv->data_gpiods)) { err = PTR_ERR(priv->data_gpiods); dev_err(&pdev->dev, "data GPIO request failed: %d\n", err); return err; } - priv->data_in = true; + if (priv->data_gpiods) { + if (!priv->gpiod_nwe) { + dev_err(&pdev->dev, + "mandatory NWE pin not provided by platform\n"); + return -ENODEV; + } + + priv->io_read = ams_delta_io_read; + priv->io_write = ams_delta_io_write; + priv->data_in = true; + } if (pdev->id_entry) probe = (void *) pdev->id_entry->driver_data; @@ -328,6 +343,11 @@ static int ams_delta_init(struct platform_device *pdev) if (err) return err; + if (!priv->io_read || !priv->io_write) { + dev_err(&pdev->dev, "incomplete device configuration\n"); + return -ENODEV; + } + /* Initialize the NAND controller object embedded in ams_delta_nand. */ priv->base.ops = &ams_delta_ops; nand_controller_init(&priv->base); From 16d00cd612068965134a08c08a43355b4b4ac58f Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Wed, 12 Feb 2020 01:39:29 +0100 Subject: [PATCH 031/103] mtd: rawnand: ams-delta: Rename structures and functions to gpio_nand* Another step in preparation for merging the driver with "gpio-nand". Signed-off-by: Janusz Krzysztofik Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200212003929.6682-15-jmkrzyszt@gmail.com --- drivers/mtd/nand/raw/ams-delta.c | 86 ++++++++++++++++---------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index 5a27170b2808..d66dab25df20 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -29,7 +29,7 @@ /* * MTD structure for E3 (Delta) */ -struct ams_delta_nand { +struct gpio_nand { struct nand_controller base; struct nand_chip nand_chip; struct gpio_desc *gpiod_rdy; @@ -43,19 +43,18 @@ struct ams_delta_nand { bool data_in; unsigned int tRP; unsigned int tWP; - u8 (*io_read)(struct ams_delta_nand *this); - void (*io_write)(struct ams_delta_nand *this, - u8 byte); + u8 (*io_read)(struct gpio_nand *this); + void (*io_write)(struct gpio_nand *this, u8 byte); }; -static void ams_delta_write_commit(struct ams_delta_nand *priv) +static void gpio_nand_write_commit(struct gpio_nand *priv) { gpiod_set_value(priv->gpiod_nwe, 1); ndelay(priv->tWP); gpiod_set_value(priv->gpiod_nwe, 0); } -static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) +static void gpio_nand_io_write(struct gpio_nand *priv, u8 byte) { struct gpio_descs *data_gpiods = priv->data_gpiods; DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; @@ -63,10 +62,10 @@ static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte) gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc, data_gpiods->info, values); - ams_delta_write_commit(priv); + gpio_nand_write_commit(priv); } -static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte) +static void gpio_nand_dir_output(struct gpio_nand *priv, u8 byte) { struct gpio_descs *data_gpiods = priv->data_gpiods; DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, }; @@ -76,12 +75,12 @@ static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte) gpiod_direction_output_raw(data_gpiods->desc[i], test_bit(i, values)); - ams_delta_write_commit(priv); + gpio_nand_write_commit(priv); priv->data_in = false; } -static u8 ams_delta_io_read(struct ams_delta_nand *priv) +static u8 gpio_nand_io_read(struct gpio_nand *priv) { u8 res; struct gpio_descs *data_gpiods = priv->data_gpiods; @@ -99,7 +98,7 @@ static u8 ams_delta_io_read(struct ams_delta_nand *priv) return res; } -static void ams_delta_dir_input(struct ams_delta_nand *priv) +static void gpio_nand_dir_input(struct gpio_nand *priv) { struct gpio_descs *data_gpiods = priv->data_gpiods; int i; @@ -110,68 +109,67 @@ static void ams_delta_dir_input(struct ams_delta_nand *priv) priv->data_in = true; } -static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf, - int len) +static void gpio_nand_write_buf(struct gpio_nand *priv, const u8 *buf, int len) { int i = 0; if (len > 0 && priv->data_in) - ams_delta_dir_output(priv, buf[i++]); + gpio_nand_dir_output(priv, buf[i++]); while (i < len) priv->io_write(priv, buf[i++]); } -static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len) +static void gpio_nand_read_buf(struct gpio_nand *priv, u8 *buf, int len) { int i; if (priv->data_gpiods && !priv->data_in) - ams_delta_dir_input(priv); + gpio_nand_dir_input(priv); for (i = 0; i < len; i++) buf[i] = priv->io_read(priv); } -static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert) +static void gpio_nand_ctrl_cs(struct gpio_nand *priv, bool assert) { gpiod_set_value(priv->gpiod_nce, assert); } -static int ams_delta_exec_op(struct nand_chip *this, +static int gpio_nand_exec_op(struct nand_chip *this, const struct nand_operation *op, bool check_only) { - struct ams_delta_nand *priv = nand_get_controller_data(this); + struct gpio_nand *priv = nand_get_controller_data(this); const struct nand_op_instr *instr; int ret = 0; if (check_only) return 0; - ams_delta_ctrl_cs(priv, 1); + gpio_nand_ctrl_cs(priv, 1); for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) { switch (instr->type) { case NAND_OP_CMD_INSTR: gpiod_set_value(priv->gpiod_cle, 1); - ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1); + gpio_nand_write_buf(priv, &instr->ctx.cmd.opcode, 1); gpiod_set_value(priv->gpiod_cle, 0); break; case NAND_OP_ADDR_INSTR: gpiod_set_value(priv->gpiod_ale, 1); - ams_delta_write_buf(priv, instr->ctx.addr.addrs, + gpio_nand_write_buf(priv, instr->ctx.addr.addrs, instr->ctx.addr.naddrs); gpiod_set_value(priv->gpiod_ale, 0); break; case NAND_OP_DATA_IN_INSTR: - ams_delta_read_buf(priv, instr->ctx.data.buf.in, + gpio_nand_read_buf(priv, instr->ctx.data.buf.in, instr->ctx.data.len); break; case NAND_OP_DATA_OUT_INSTR: - ams_delta_write_buf(priv, instr->ctx.data.buf.out, + gpio_nand_write_buf(priv, instr->ctx.data.buf.out, instr->ctx.data.len); break; @@ -188,15 +186,15 @@ static int ams_delta_exec_op(struct nand_chip *this, break; } - ams_delta_ctrl_cs(priv, 0); + gpio_nand_ctrl_cs(priv, 0); return ret; } -static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, +static int gpio_nand_setup_data_interface(struct nand_chip *this, int csline, const struct nand_data_interface *cf) { - struct ams_delta_nand *priv = nand_get_controller_data(this); + struct gpio_nand *priv = nand_get_controller_data(this); const struct nand_sdr_timings *sdr = nand_get_sdr_timings(cf); struct device *dev = &nand_to_mtd(this)->dev; @@ -217,23 +215,23 @@ static int ams_delta_setup_data_interface(struct nand_chip *this, int csline, return 0; } -static const struct nand_controller_ops ams_delta_ops = { - .exec_op = ams_delta_exec_op, - .setup_data_interface = ams_delta_setup_data_interface, +static const struct nand_controller_ops gpio_nand_ops = { + .exec_op = gpio_nand_exec_op, + .setup_data_interface = gpio_nand_setup_data_interface, }; /* * Main initialization routine */ -static int ams_delta_init(struct platform_device *pdev) +static int gpio_nand_probe(struct platform_device *pdev) { struct gpio_nand_platdata *pdata = dev_get_platdata(&pdev->dev); const struct mtd_partition *partitions = NULL; int num_partitions = 0; - struct ams_delta_nand *priv; + struct gpio_nand *priv; struct nand_chip *this; struct mtd_info *mtd; - int (*probe)(struct platform_device *pdev, struct ams_delta_nand *priv); + int (*probe)(struct platform_device *pdev, struct gpio_nand *priv); int err = 0; if (pdata) { @@ -242,7 +240,7 @@ static int ams_delta_init(struct platform_device *pdev) } /* Allocate memory for MTD device structure and private data */ - priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand), + priv = devm_kzalloc(&pdev->dev, sizeof(struct gpio_nand), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -329,8 +327,8 @@ static int ams_delta_init(struct platform_device *pdev) return -ENODEV; } - priv->io_read = ams_delta_io_read; - priv->io_write = ams_delta_io_write; + priv->io_read = gpio_nand_io_read; + priv->io_write = gpio_nand_io_write; priv->data_in = true; } @@ -348,8 +346,8 @@ static int ams_delta_init(struct platform_device *pdev) return -ENODEV; } - /* Initialize the NAND controller object embedded in ams_delta_nand. */ - priv->base.ops = &ams_delta_ops; + /* Initialize the NAND controller object embedded in gpio_nand. */ + priv->base.ops = &gpio_nand_ops; nand_controller_init(&priv->base); this->controller = &priv->base; @@ -385,9 +383,9 @@ static int ams_delta_init(struct platform_device *pdev) /* * Clean up routine */ -static int ams_delta_cleanup(struct platform_device *pdev) +static int gpio_nand_remove(struct platform_device *pdev) { - struct ams_delta_nand *priv = platform_get_drvdata(pdev); + struct gpio_nand *priv = platform_get_drvdata(pdev); struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip); /* Apply write protection */ @@ -415,9 +413,9 @@ static const struct platform_device_id gpio_nand_plat_id_table[] = { }; MODULE_DEVICE_TABLE(platform, gpio_nand_plat_id_table); -static struct platform_driver ams_delta_nand_driver = { - .probe = ams_delta_init, - .remove = ams_delta_cleanup, +static struct platform_driver gpio_nand_driver = { + .probe = gpio_nand_probe, + .remove = gpio_nand_remove, .id_table = gpio_nand_plat_id_table, .driver = { .name = "ams-delta-nand", @@ -425,7 +423,7 @@ static struct platform_driver ams_delta_nand_driver = { }, }; -module_platform_driver(ams_delta_nand_driver); +module_platform_driver(gpio_nand_driver); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jonathan McDowell "); From 84234652595f8dc29a92a33a6ae504c7df2e11c8 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Mon, 17 Feb 2020 14:56:39 +0800 Subject: [PATCH 032/103] mtd: rawnand: Add support for Macronix NAND randomizer Macronix NANDs support randomizer operation for user data scrambled, which can be enabled with a SET_FEATURE. User data written to the NAND device without randomizer is still readable after randomizer function enabled. The penalty of randomizer are subpage accesses prohibited and more time period is needed in program operation and entering deep power-down mode. i.e., tPROG 300us to 340us(randomizer enabled) For more high-reliability concern, if subpage write not available with hardware ECC and then to enable randomizer is recommended by default. Driver checks byte 167 of Vendor Blocks in ONFI parameter page table to see if this high-reliability function is supported. By adding a new specific DT property in children nodes to enable randomizer function. Signed-off-by: Mason Yang Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581922600-25461-2-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_macronix.c | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 3ff7ce00cbdb..0a2fe25f2639 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -11,6 +11,19 @@ #define MACRONIX_READ_RETRY_BIT BIT(0) #define MACRONIX_NUM_READ_RETRY_MODES 6 +#define ONFI_FEATURE_ADDR_MXIC_RANDOMIZER 0xB0 +#define MACRONIX_RANDOMIZER_BIT BIT(1) +#define MACRONIX_RANDOMIZER_ENPGM BIT(0) +#define MACRONIX_RANDOMIZER_RANDEN BIT(1) +#define MACRONIX_RANDOMIZER_RANDOPT BIT(2) +#define MACRONIX_RANDOMIZER_MODE_ENTER \ + (MACRONIX_RANDOMIZER_ENPGM | \ + MACRONIX_RANDOMIZER_RANDEN | \ + MACRONIX_RANDOMIZER_RANDOPT) +#define MACRONIX_RANDOMIZER_MODE_EXIT \ + (MACRONIX_RANDOMIZER_RANDEN | \ + MACRONIX_RANDOMIZER_RANDOPT) + struct nand_onfi_vendor_macronix { u8 reserved; u8 reliability_func; @@ -29,15 +42,83 @@ static int macronix_nand_setup_read_retry(struct nand_chip *chip, int mode) return nand_set_features(chip, ONFI_FEATURE_ADDR_READ_RETRY, feature); } +static int macronix_nand_randomizer_check_enable(struct nand_chip *chip) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + if (feature[0]) + return feature[0]; + + feature[0] = MACRONIX_RANDOMIZER_MODE_ENTER; + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + /* RANDEN and RANDOPT OTP bits are programmed */ + feature[0] = 0x0; + ret = nand_prog_page_op(chip, 0, 0, feature, 1); + if (ret < 0) + return ret; + + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + feature[0] &= MACRONIX_RANDOMIZER_MODE_EXIT; + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + feature); + if (ret < 0) + return ret; + + return 0; +} + static void macronix_nand_onfi_init(struct nand_chip *chip) { struct nand_parameters *p = &chip->parameters; struct nand_onfi_vendor_macronix *mxic; + struct device_node *dn = nand_get_flash_node(chip); + int rand_otp = 0; + int ret; if (!p->onfi) return; + if (of_find_property(dn, "mxic,enable-randomizer-otp", NULL)) + rand_otp = 1; + mxic = (struct nand_onfi_vendor_macronix *)p->onfi->vendor; + /* Subpage write is prohibited in randomizer operatoin */ + if (rand_otp && chip->options & NAND_NO_SUBPAGE_WRITE && + mxic->reliability_func & MACRONIX_RANDOMIZER_BIT) { + if (p->supports_set_get_features) { + bitmap_set(p->set_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 1); + bitmap_set(p->get_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, 1); + ret = macronix_nand_randomizer_check_enable(chip); + if (ret < 0) { + bitmap_clear(p->set_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + 1); + bitmap_clear(p->get_feature_list, + ONFI_FEATURE_ADDR_MXIC_RANDOMIZER, + 1); + pr_info("Macronix NAND randomizer failed\n"); + } else { + pr_info("Macronix NAND randomizer enabled\n"); + } + } + } + if ((mxic->reliability_func & MACRONIX_READ_RETRY_BIT) == 0) return; From 7f274f411c76ecf87c6eef7ce263ddfe0962ba46 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Mon, 17 Feb 2020 14:56:40 +0800 Subject: [PATCH 033/103] dt-bindings: mtd: Document Macronix NAND device bindings Document the bindings used by the Macronix NAND device. Signed-off-by: Mason Yang Reviewed-by: Rob Herring [ Link: https://lore.kernel.org/linux-mtd/1581922600-25461-3-git-send-email-masonccyang@mxic.com.tw --- .../devicetree/bindings/mtd/nand-macronix.txt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/nand-macronix.txt diff --git a/Documentation/devicetree/bindings/mtd/nand-macronix.txt b/Documentation/devicetree/bindings/mtd/nand-macronix.txt new file mode 100644 index 000000000000..ffab28a2c4d1 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/nand-macronix.txt @@ -0,0 +1,27 @@ +Macronix NANDs Device Tree Bindings +----------------------------------- + +Macronix NANDs support randomizer operation for scrambling user data, +which can be enabled with a SET_FEATURE. The penalty when using the +randomizer are subpage accesses prohibited and more time period needed +for program operation, i.e., tPROG 300us to 340us (randomizer enabled). +Enabling the randomizer is a one time persistent and non reversible +operation. + +For more high-reliability concern, if subpage write is not available +with hardware ECC and not enabled at UBI level, then enabling the +randomizer is recommended by default by adding a new specific property +in children nodes. + +Required NAND chip properties in children mode: +- randomizer enable: should be "mxic,enable-randomizer-otp" + +Example: + + nand: nand-controller@unit-address { + + nand@0 { + reg = <0>; + mxic,enable-randomizer-otp; + }; + }; From 2148937501ee3d663e0010e519a553fea67ad103 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Tue, 18 Feb 2020 10:05:14 +0000 Subject: [PATCH 034/103] mtd: spinand: Stop using spinand->oobbuf for buffering bad block markers For reading and writing the bad block markers, spinand->oobbuf is currently used as a buffer for the marker bytes. During the underlying read and write operations to actually get/set the content of the OOB area, the content of spinand->oobbuf is reused and changed by accessing it through spinand->oobbuf and/or spinand->databuf. This is a flaw in the original design of the SPI NAND core and at the latest from 13c15e07eedf ("mtd: spinand: Handle the case where PROGRAM LOAD does not reset the cache") on, it results in not having the bad block marker written at all, as the spinand->oobbuf is cleared to 0xff after setting the marker bytes to zero. To fix it, we now just store the two bytes for the marker on the stack and let the read/write operations copy it from/to the page buffer later. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Cc: stable@vger.kernel.org Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200218100432.32433-2-frieder.schrempf@kontron.de --- drivers/mtd/nand/spi/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index a9e9cbad942f..137d31dae3ce 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -570,18 +570,18 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to, static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos) { struct spinand_device *spinand = nand_to_spinand(nand); + u8 marker[2] = { }; struct nand_page_io_req req = { .pos = *pos, - .ooblen = 2, + .ooblen = sizeof(marker), .ooboffs = 0, - .oobbuf.in = spinand->oobbuf, + .oobbuf.in = marker, .mode = MTD_OPS_RAW, }; - memset(spinand->oobbuf, 0, 2); spinand_select_target(spinand, pos->target); spinand_read_page(spinand, &req, false); - if (spinand->oobbuf[0] != 0xff || spinand->oobbuf[1] != 0xff) + if (marker[0] != 0xff || marker[1] != 0xff) return true; return false; @@ -605,11 +605,12 @@ static int spinand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) { struct spinand_device *spinand = nand_to_spinand(nand); + u8 marker[2] = { }; struct nand_page_io_req req = { .pos = *pos, .ooboffs = 0, - .ooblen = 2, - .oobbuf.out = spinand->oobbuf, + .ooblen = sizeof(marker), + .oobbuf.out = marker, }; int ret; @@ -624,7 +625,6 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) spinand_erase_op(spinand, pos); - memset(spinand->oobbuf, 0, 2); return spinand_write_page(spinand, &req); } From 621a7b780bd8b7054647d53d5071961f2c9e0873 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Tue, 18 Feb 2020 10:05:25 +0000 Subject: [PATCH 035/103] mtd: spinand: Explicitly use MTD_OPS_RAW to write the bad block marker to OOB When writing the bad block marker to the OOB area the access mode should be set to MTD_OPS_RAW as it is done for reading the marker. Currently this only works because req.mode is initialized to MTD_OPS_PLACE_OOB (0) and spinand_write_to_cache_op() checks for req.mode != MTD_OPS_AUTO_OOB. Fix this by explicitly setting req.mode to MTD_OPS_RAW. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200218100432.32433-3-frieder.schrempf@kontron.de --- drivers/mtd/nand/spi/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 137d31dae3ce..ee1eea857367 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -611,6 +611,7 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) .ooboffs = 0, .ooblen = sizeof(marker), .oobbuf.out = marker, + .mode = MTD_OPS_RAW, }; int ret; From b645ad39d56846618704e463b24bb994c9585c7f Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Tue, 18 Feb 2020 10:05:35 +0000 Subject: [PATCH 036/103] mtd: spinand: Do not erase the block before writing a bad block marker Currently when marking a block, we use spinand_erase_op() to erase the block before writing the marker to the OOB area. Doing so without waiting for the operation to finish can lead to the marking failing silently and no bad block marker being written to the flash. In fact we don't need to do an erase at all before writing the BBM. The ECC is disabled for raw accesses to the OOB data and we don't need to work around any issues with chips reporting ECC errors as it is known to be the case for raw NAND. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Cc: stable@vger.kernel.org Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200218100432.32433-4-frieder.schrempf@kontron.de --- drivers/mtd/nand/spi/core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index ee1eea857367..b6bb358b96ce 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -615,7 +615,6 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) }; int ret; - /* Erase block before marking it bad. */ ret = spinand_select_target(spinand, pos->target); if (ret) return ret; @@ -624,8 +623,6 @@ static int spinand_markbad(struct nand_device *nand, const struct nand_pos *pos) if (ret) return ret; - spinand_erase_op(spinand, pos); - return spinand_write_page(spinand, &req); } From c6fbcb70132ffc66696a94dd3d8e6215c750254f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Sun, 23 Feb 2020 19:06:33 +0100 Subject: [PATCH 037/103] mtd: rawnand: Fix a typo ("manufecturer") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Neuschäfer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200223180634.8736-1-j.neuschaefer@gmx.net --- include/linux/mtd/rawnand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 4ab9bccfcde0..3c7c15aadcee 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1215,7 +1215,7 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) * struct nand_flash_dev - NAND Flash Device ID Structure * @name: a human-readable name of the NAND chip * @dev_id: the device ID (the second byte of the full chip ID array) - * @mfr_id: manufecturer ID part of the full chip ID array (refers the same + * @mfr_id: manufacturer ID part of the full chip ID array (refers the same * memory address as ``id[0]``) * @dev_id: device ID part of the full chip ID array (refers the same memory * address as ``id[1]``) From 49f1c33076ca56871ffddc4800b04524204ea889 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 26 Feb 2020 16:27:22 -0600 Subject: [PATCH 038/103] mtd: rawnand: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Acked-by: Masahiro Yamada Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200226222722.GA18020@embeddedor --- drivers/mtd/nand/raw/denali.h | 2 +- drivers/mtd/nand/raw/marvell_nand.c | 2 +- drivers/mtd/nand/raw/meson_nand.c | 2 +- drivers/mtd/nand/raw/mtk_nand.c | 2 +- drivers/mtd/nand/raw/nand_hynix.c | 2 +- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/denali.h b/drivers/mtd/nand/raw/denali.h index e5cdcda56d14..ac46eb7956ce 100644 --- a/drivers/mtd/nand/raw/denali.h +++ b/drivers/mtd/nand/raw/denali.h @@ -328,7 +328,7 @@ struct denali_chip { struct nand_chip chip; struct list_head node; unsigned int nsels; - struct denali_chip_sel sels[0]; + struct denali_chip_sel sels[]; }; /** diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index fb5abdcfb007..7082bef1c8a7 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -334,7 +334,7 @@ struct marvell_nand_chip { int addr_cyc; int selected_die; unsigned int nsels; - struct marvell_nand_chip_sel sels[0]; + struct marvell_nand_chip_sel sels[]; }; static inline struct marvell_nand_chip *to_marvell_nand(struct nand_chip *chip) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 9f17b5b8efbf..f6fb5c0e6255 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -118,7 +118,7 @@ struct meson_nfc_nand_chip { u8 *data_buf; __le64 *info_buf; u32 nsels; - u8 sels[0]; + u8 sels[]; }; struct meson_nand_ecc { diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index b8305e39ab51..ef149e8b26d0 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -131,7 +131,7 @@ struct mtk_nfc_nand_chip { u32 spare_per_sector; int nsels; - u8 sels[0]; + u8 sels[]; /* nothing after this field */ }; diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index 194e4227aefe..7caedaa5b9e5 100644 --- a/drivers/mtd/nand/raw/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -26,7 +26,7 @@ struct hynix_read_retry { int nregs; const u8 *regs; - u8 values[0]; + u8 values[]; }; /** diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 37a4ac0dd85b..6ede3934a5f4 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -195,7 +195,7 @@ struct sunxi_nand_chip { u32 timing_cfg; u32 timing_ctl; int nsels; - struct sunxi_nand_chip_sel sels[0]; + struct sunxi_nand_chip_sel sels[]; }; static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) From 7cd8c0adb489fde5cec2690a85517df74fa8abbb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:43 +0200 Subject: [PATCH 039/103] mtd: rawnand: gpmi: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly to return the real error code. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-2-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index b9d5d55a5edb..53b00c841aec 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -1148,20 +1148,21 @@ static int acquire_dma_channels(struct gpmi_nand_data *this) { struct platform_device *pdev = this->pdev; struct dma_chan *dma_chan; + int ret = 0; /* request dma channel */ - dma_chan = dma_request_slave_channel(&pdev->dev, "rx-tx"); - if (!dma_chan) { - dev_err(this->dev, "Failed to request DMA channel.\n"); - goto acquire_err; + dma_chan = dma_request_chan(&pdev->dev, "rx-tx"); + if (IS_ERR(dma_chan)) { + ret = PTR_ERR(dma_chan); + if (ret != -EPROBE_DEFER) + dev_err(this->dev, "DMA channel request failed: %d\n", + ret); + release_dma_channels(this); + } else { + this->dma_chans[0] = dma_chan; } - this->dma_chans[0] = dma_chan; - return 0; - -acquire_err: - release_dma_channels(this); - return -EINVAL; + return ret; } static int gpmi_get_clks(struct gpmi_nand_data *this) From aafe30baf4ad131c22a0dc730298360a6e695ce3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:44 +0200 Subject: [PATCH 040/103] mtd: rawnand: marvell: Release DMA channel on error Release the DMA channel on errors after the channel has been successfully requested. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-3-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/marvell_nand.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 7082bef1c8a7..f60d885f38bf 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2751,8 +2751,10 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - return -ENXIO; + if (!r) { + ret = -ENXIO; + goto release_channel; + } config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -2763,7 +2765,7 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) ret = dmaengine_slave_config(nfc->dma_chan, &config); if (ret < 0) { dev_err(nfc->dev, "Failed to configure DMA channel\n"); - return ret; + goto release_channel; } /* @@ -2773,12 +2775,20 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) * the provided buffer. */ nfc->dma_buf = kmalloc(MAX_CHUNK_SIZE, GFP_KERNEL | GFP_DMA); - if (!nfc->dma_buf) - return -ENOMEM; + if (!nfc->dma_buf) { + ret = -ENOMEM; + goto release_channel; + } nfc->use_dma = true; return 0; + +release_channel: + dma_release_channel(nfc->dma_chan); + nfc->dma_chan = NULL; + + return ret; } static void marvell_nfc_reset(struct marvell_nfc *nfc) @@ -2920,10 +2930,13 @@ static int marvell_nfc_probe(struct platform_device *pdev) ret = marvell_nand_chips_init(dev, nfc); if (ret) - goto unprepare_reg_clk; + goto release_dma; return 0; +release_dma: + if (nfc->use_dma) + dma_release_channel(nfc->dma_chan); unprepare_reg_clk: clk_disable_unprepare(nfc->reg_clk); unprepare_core_clk: From cf9e2389482179db6bfab41512a9c0b722540be8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:45 +0200 Subject: [PATCH 041/103] mtd: rawnand: marvell: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly to return the real error code. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-4-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/marvell_nand.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index f60d885f38bf..179f0ca585f8 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2743,11 +2743,14 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) if (ret) return ret; - nfc->dma_chan = dma_request_slave_channel(nfc->dev, "data"); - if (!nfc->dma_chan) { - dev_err(nfc->dev, - "Unable to request data DMA channel\n"); - return -ENODEV; + nfc->dma_chan = dma_request_chan(nfc->dev, "data"); + if (IS_ERR(nfc->dma_chan)) { + ret = PTR_ERR(nfc->dma_chan); + nfc->dma_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nfc->dev, "DMA channel request failed: %d\n", + ret); + return ret; } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); From ac80c55b46754fbe0394cf404a9091b551f00e22 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:46 +0200 Subject: [PATCH 042/103] mtd: rawnand: sunxi: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. By using dma_request_chan() directly the driver can support deferred probing against DMA. Signed-off-by: Peter Ujfalusi Acked-by: Maxime Ripard Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-5-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/sunxi_nand.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 6ede3934a5f4..5f3e40b79fb1 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -2123,8 +2123,16 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (ret) goto out_ahb_reset_reassert; - nfc->dmac = dma_request_slave_channel(dev, "rxtx"); - if (nfc->dmac) { + nfc->dmac = dma_request_chan(dev, "rxtx"); + if (IS_ERR(nfc->dmac)) { + ret = PTR_ERR(nfc->dmac); + if (ret == -EPROBE_DEFER) + goto out_ahb_reset_reassert; + + /* Ignore errors to fall back to PIO mode */ + dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret); + nfc->dmac = NULL; + } else { struct dma_slave_config dmac_cfg = { }; dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data; @@ -2138,9 +2146,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (nfc->caps->extra_mbus_conf) writel(readl(nfc->regs + NFC_REG_CTL) | NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL); - - } else { - dev_warn(dev, "failed to request rxtx DMA channel\n"); } platform_set_drvdata(pdev, nfc); From 80c3012e127cf8f4c0601868b2c23b1caab20b44 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:47 +0200 Subject: [PATCH 043/103] mtd: rawnand: qcom: Release resources on failure within qcom_nandc_alloc() In case when DMA channel request or alloc_bam_transaction() fails, dma_unmap_single() and any channels already requested should be released. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-6-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/qcom_nandc.c | 61 +++++++++++++++++-------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 7bb9a7e8e1e7..ca21cb3836dc 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2628,6 +2628,29 @@ static const struct nand_controller_ops qcom_nandc_ops = { .attach_chip = qcom_nand_attach_chip, }; +static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) +{ + if (nandc->props->is_bam) { + if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) + dma_unmap_single(nandc->dev, nandc->reg_read_dma, + MAX_REG_RD * + sizeof(*nandc->reg_read_buf), + DMA_FROM_DEVICE); + + if (nandc->tx_chan) + dma_release_channel(nandc->tx_chan); + + if (nandc->rx_chan) + dma_release_channel(nandc->rx_chan); + + if (nandc->cmd_chan) + dma_release_channel(nandc->cmd_chan); + } else { + if (nandc->chan) + dma_release_channel(nandc->chan); + } +} + static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) { int ret; @@ -2676,19 +2699,22 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); if (!nandc->tx_chan) { dev_err(nandc->dev, "failed to request tx channel\n"); - return -ENODEV; + ret = -ENODEV; + goto unalloc; } nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); if (!nandc->rx_chan) { dev_err(nandc->dev, "failed to request rx channel\n"); - return -ENODEV; + ret = -ENODEV; + goto unalloc; } nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); if (!nandc->cmd_chan) { dev_err(nandc->dev, "failed to request cmd channel\n"); - return -ENODEV; + ret = -ENODEV; + goto unalloc; } /* @@ -2702,7 +2728,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (!nandc->bam_txn) { dev_err(nandc->dev, "failed to allocate bam transaction\n"); - return -ENOMEM; + ret = -ENOMEM; + goto unalloc; } } else { nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); @@ -2720,29 +2747,9 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) nandc->controller.ops = &qcom_nandc_ops; return 0; -} - -static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) -{ - if (nandc->props->is_bam) { - if (!dma_mapping_error(nandc->dev, nandc->reg_read_dma)) - dma_unmap_single(nandc->dev, nandc->reg_read_dma, - MAX_REG_RD * - sizeof(*nandc->reg_read_buf), - DMA_FROM_DEVICE); - - if (nandc->tx_chan) - dma_release_channel(nandc->tx_chan); - - if (nandc->rx_chan) - dma_release_channel(nandc->rx_chan); - - if (nandc->cmd_chan) - dma_release_channel(nandc->cmd_chan); - } else { - if (nandc->chan) - dma_release_channel(nandc->chan); - } +unalloc: + qcom_nandc_unalloc(nandc); + return ret; } /* one time setup of a few nand controller registers */ From 92f0f8efbd4ad8fac2e3e902eff3c67da65f08cc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:48 +0200 Subject: [PATCH 044/103] mtd: rawnand: qcom: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly to return the real error code. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-7-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/qcom_nandc.c | 50 ++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index ca21cb3836dc..5b11c7061497 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2696,24 +2696,36 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) return -EIO; } - nandc->tx_chan = dma_request_slave_channel(nandc->dev, "tx"); - if (!nandc->tx_chan) { - dev_err(nandc->dev, "failed to request tx channel\n"); - ret = -ENODEV; + nandc->tx_chan = dma_request_chan(nandc->dev, "tx"); + if (IS_ERR(nandc->tx_chan)) { + ret = PTR_ERR(nandc->tx_chan); + nandc->tx_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "tx DMA channel request failed: %d\n", + ret); goto unalloc; } - nandc->rx_chan = dma_request_slave_channel(nandc->dev, "rx"); - if (!nandc->rx_chan) { - dev_err(nandc->dev, "failed to request rx channel\n"); - ret = -ENODEV; + nandc->rx_chan = dma_request_chan(nandc->dev, "rx"); + if (IS_ERR(nandc->rx_chan)) { + ret = PTR_ERR(nandc->rx_chan); + nandc->rx_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "rx DMA channel request failed: %d\n", + ret); goto unalloc; } - nandc->cmd_chan = dma_request_slave_channel(nandc->dev, "cmd"); - if (!nandc->cmd_chan) { - dev_err(nandc->dev, "failed to request cmd channel\n"); - ret = -ENODEV; + nandc->cmd_chan = dma_request_chan(nandc->dev, "cmd"); + if (IS_ERR(nandc->cmd_chan)) { + ret = PTR_ERR(nandc->cmd_chan); + nandc->cmd_chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "cmd DMA channel request failed: %d\n", + ret); goto unalloc; } @@ -2732,11 +2744,15 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) goto unalloc; } } else { - nandc->chan = dma_request_slave_channel(nandc->dev, "rxtx"); - if (!nandc->chan) { - dev_err(nandc->dev, - "failed to request slave channel\n"); - return -ENODEV; + nandc->chan = dma_request_chan(nandc->dev, "rxtx"); + if (IS_ERR(nandc->chan)) { + ret = PTR_ERR(nandc->chan); + nandc->chan = NULL; + if (ret != -EPROBE_DEFER) + dev_err(nandc->dev, + "rxtx DMA channel request failed: %d\n", + ret); + return ret; } } From b35f79aa461e965514e7d437c6704679552f4818 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 27 Feb 2020 14:37:49 +0200 Subject: [PATCH 045/103] mtd: rawnand: stm32_fmc2: Use dma_request_chan() instead dma_request_slave_channel() dma_request_slave_channel() is a wrapper on top of dma_request_chan() eating up the error code. Use using dma_request_chan() directly and inform user of error in case the DMA request failed. Signed-off-by: Peter Ujfalusi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200227123749.24064-8-peter.ujfalusi@ti.com --- drivers/mtd/nand/raw/stm32_fmc2_nand.c | 44 ++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 3ba73f18841f..b6d45cd911ae 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1606,15 +1606,36 @@ static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr, /* DMA configuration */ static int stm32_fmc2_dma_setup(struct stm32_fmc2_nfc *fmc2) { - int ret; + int ret = 0; - fmc2->dma_tx_ch = dma_request_slave_channel(fmc2->dev, "tx"); - fmc2->dma_rx_ch = dma_request_slave_channel(fmc2->dev, "rx"); - fmc2->dma_ecc_ch = dma_request_slave_channel(fmc2->dev, "ecc"); + fmc2->dma_tx_ch = dma_request_chan(fmc2->dev, "tx"); + if (IS_ERR(fmc2->dma_tx_ch)) { + ret = PTR_ERR(fmc2->dma_tx_ch); + if (ret != -ENODEV) + dev_err(fmc2->dev, + "failed to request tx DMA channel: %d\n", ret); + fmc2->dma_tx_ch = NULL; + goto err_dma; + } - if (!fmc2->dma_tx_ch || !fmc2->dma_rx_ch || !fmc2->dma_ecc_ch) { - dev_warn(fmc2->dev, "DMAs not defined in the device tree, polling mode is used\n"); - return 0; + fmc2->dma_rx_ch = dma_request_chan(fmc2->dev, "rx"); + if (IS_ERR(fmc2->dma_rx_ch)) { + ret = PTR_ERR(fmc2->dma_rx_ch); + if (ret != -ENODEV) + dev_err(fmc2->dev, + "failed to request rx DMA channel: %d\n", ret); + fmc2->dma_rx_ch = NULL; + goto err_dma; + } + + fmc2->dma_ecc_ch = dma_request_chan(fmc2->dev, "ecc"); + if (IS_ERR(fmc2->dma_ecc_ch)) { + ret = PTR_ERR(fmc2->dma_ecc_ch); + if (ret != -ENODEV) + dev_err(fmc2->dev, + "failed to request ecc DMA channel: %d\n", ret); + fmc2->dma_ecc_ch = NULL; + goto err_dma; } ret = sg_alloc_table(&fmc2->dma_ecc_sg, FMC2_MAX_SG, GFP_KERNEL); @@ -1635,6 +1656,15 @@ static int stm32_fmc2_dma_setup(struct stm32_fmc2_nfc *fmc2) init_completion(&fmc2->dma_ecc_complete); return 0; + +err_dma: + if (ret == -ENODEV) { + dev_warn(fmc2->dev, + "DMAs not defined in the DT, polling mode is used\n"); + ret = 0; + } + + return ret; } /* NAND callbacks setup */ From e015d72f321e8b54aa3aef680e85dcd87f912703 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 2 Mar 2020 15:45:09 -0300 Subject: [PATCH 046/103] mtd: rawnand: ingenic: Add dependency on MIPS || COMPILE_TEST This driver has no arch-specific instructions but is only ever useful on MIPS; so disable this driver if we're not compiling for MIPS, unless the driver is compile-tested. Signed-off-by: Paul Cercueil Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200302184509.10666-1-paul@crapouillou.net --- drivers/mtd/nand/raw/ingenic/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/ingenic/Kconfig b/drivers/mtd/nand/raw/ingenic/Kconfig index e30feb56b650..96c5ae8b1bbc 100644 --- a/drivers/mtd/nand/raw/ingenic/Kconfig +++ b/drivers/mtd/nand/raw/ingenic/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config MTD_NAND_JZ4780 tristate "JZ4780 NAND controller" + depends on MIPS || COMPILE_TEST depends on JZ4780_NEMC help Enables support for NAND Flash connected to the NEMC on JZ4780 SoC From 92270086b7e5ada7ab381c06cc3da2e95ed17088 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Tue, 3 Mar 2020 15:21:21 +0800 Subject: [PATCH 047/103] mtd: rawnand: Add support for manufacturer specific lock/unlock operation Add nand_lock() & nand_unlock() for manufacturer specific lock & unlock operation while the device supports Block Portection function. Signed-off-by: Mason Yang Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1583220084-10890-2-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_base.c | 36 ++++++++++++++++++++++++++++++-- include/linux/mtd/rawnand.h | 5 +++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a3ed6c54963e..a13b91aa3780 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4365,6 +4365,38 @@ static void nand_shutdown(struct mtd_info *mtd) nand_suspend(mtd); } +/** + * nand_lock - [MTD Interface] Lock the NAND flash + * @mtd: MTD device structure + * @ofs: offset byte address + * @len: number of bytes to lock (must be a multiple of block/page size) + */ +static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->lock_area) + return -ENOTSUPP; + + return chip->lock_area(chip, ofs, len); +} + +/** + * nand_unlock - [MTD Interface] Unlock the NAND flash + * @mtd: MTD device structure + * @ofs: offset byte address + * @len: number of bytes to unlock (must be a multiple of block/page size) + */ +static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->unlock_area) + return -ENOTSUPP; + + return chip->unlock_area(chip, ofs, len); +} + /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { @@ -5791,8 +5823,8 @@ static int nand_scan_tail(struct nand_chip *chip) mtd->_read_oob = nand_read_oob; mtd->_write_oob = nand_write_oob; mtd->_sync = nand_sync; - mtd->_lock = NULL; - mtd->_unlock = NULL; + mtd->_lock = nand_lock; + mtd->_unlock = nand_unlock; mtd->_suspend = nand_suspend; mtd->_resume = nand_resume; mtd->_reboot = nand_shutdown; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3c7c15aadcee..49ed50fb44ab 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1077,6 +1077,8 @@ struct nand_legacy { * @manufacturer: [INTERN] Contains manufacturer information * @manufacturer.desc: [INTERN] Contains manufacturer's description * @manufacturer.priv: [INTERN] Contains manufacturer private information + * @lock_area: [REPLACEABLE] specific NAND chip lock operation + * @unlock_area: [REPLACEABLE] specific NAND chip unlock operation */ struct nand_chip { @@ -1136,6 +1138,9 @@ struct nand_chip { const struct nand_manufacturer *desc; void *priv; } manufacturer; + + int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len); + int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len); }; extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; From 03a539c7a118427a6609a26461358c56ac8f3a06 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Tue, 3 Mar 2020 15:21:22 +0800 Subject: [PATCH 048/103] mtd: rawnand: Macronix: Add support for block protection Macronix AC/AD series support using SET_FEATURES to change block protection and unprotection. Block protection support can be checked with GET_FEATURES. Signed-off-by: Mason Yang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1583220084-10890-3-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_macronix.c | 72 ++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index 0a2fe25f2639..fbe2fff0cf1b 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -11,6 +11,10 @@ #define MACRONIX_READ_RETRY_BIT BIT(0) #define MACRONIX_NUM_READ_RETRY_MODES 6 +#define ONFI_FEATURE_ADDR_MXIC_PROTECTION 0xA0 +#define MXIC_BLOCK_PROTECTION_ALL_LOCK 0x38 +#define MXIC_BLOCK_PROTECTION_ALL_UNLOCK 0x0 + #define ONFI_FEATURE_ADDR_MXIC_RANDOMIZER 0xB0 #define MACRONIX_RANDOMIZER_BIT BIT(1) #define MACRONIX_RANDOMIZER_ENPGM BIT(0) @@ -172,6 +176,73 @@ static void macronix_nand_fix_broken_get_timings(struct nand_chip *chip) ONFI_FEATURE_ADDR_TIMING_MODE, 1); } +/* + * Macronix NAND supports Block Protection by Protectoin(PT) pin; + * active high at power-on which protects the entire chip even the #WP is + * disabled. Lock/unlock protection area can be partition according to + * protection bits, i.e. upper 1/2 locked, upper 1/4 locked and so on. + */ +static int mxic_nand_lock(struct nand_chip *chip, loff_t ofs, uint64_t len) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + feature[0] = MXIC_BLOCK_PROTECTION_ALL_LOCK; + nand_select_target(chip, 0); + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, + feature); + nand_deselect_target(chip); + if (ret) + pr_err("%s all blocks failed\n", __func__); + + return ret; +} + +static int mxic_nand_unlock(struct nand_chip *chip, loff_t ofs, uint64_t len) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; + nand_select_target(chip, 0); + ret = nand_set_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, + feature); + nand_deselect_target(chip); + if (ret) + pr_err("%s all blocks failed\n", __func__); + + return ret; +} + +static void macronix_nand_block_protection_support(struct nand_chip *chip) +{ + u8 feature[ONFI_SUBFEATURE_PARAM_LEN]; + int ret; + + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); + + feature[0] = MXIC_BLOCK_PROTECTION_ALL_UNLOCK; + nand_select_target(chip, 0); + ret = nand_get_features(chip, ONFI_FEATURE_ADDR_MXIC_PROTECTION, + feature); + nand_deselect_target(chip); + if (ret || feature[0] != MXIC_BLOCK_PROTECTION_ALL_LOCK) { + if (ret) + pr_err("Block protection check failed\n"); + + bitmap_clear(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); + return; + } + + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_MXIC_PROTECTION, 1); + + chip->lock_area = mxic_nand_lock; + chip->unlock_area = mxic_nand_unlock; +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -179,6 +250,7 @@ static int macronix_nand_init(struct nand_chip *chip) macronix_nand_fix_broken_get_timings(chip); macronix_nand_onfi_init(chip); + macronix_nand_block_protection_support(chip); return 0; } From 397deafc02e1b91668fa1ee95fdd52d14fa82135 Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:25 +0100 Subject: [PATCH 049/103] mtd: rawnand: cadence: get meta data size from registers Add checking size of BCH meta data size in capabilities registers instead of using fixed value. BCH meta data is used to keep data from NAND flash OOB area. Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-1-git-send-email-piotrs@cadence.com --- .../mtd/nand/raw/cadence-nand-controller.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index f6c7102a1e32..5063a8b493a4 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -30,7 +30,6 @@ * Generic mode is used for executing rest of commands. */ -#define MAX_OOB_SIZE_PER_SECTOR 32 #define MAX_ADDRESS_CYC 6 #define MAX_ERASE_ADDRESS_CYC 3 #define MAX_DATA_SIZE 0xFFFC @@ -190,6 +189,7 @@ /* BCH Engine identification register 3. */ #define BCH_CFG_3 0x844 +#define BCH_CFG_3_METADATA_SIZE GENMASK(23, 16) /* Ready/Busy# line status. */ #define RBN_SETINGS 0x1004 @@ -499,6 +499,7 @@ struct cdns_nand_ctrl { unsigned long assigned_cs; struct list_head chips; + u8 bch_metadata_size; }; struct cdns_nand_chip { @@ -1077,6 +1078,14 @@ static int cadence_nand_read_bch_caps(struct cdns_nand_ctrl *cdns_ctrl) int max_step_size = 0, nstrengths, i; u32 reg; + reg = readl_relaxed(cdns_ctrl->reg + BCH_CFG_3); + cdns_ctrl->bch_metadata_size = FIELD_GET(BCH_CFG_3_METADATA_SIZE, reg); + if (cdns_ctrl->bch_metadata_size < 4) { + dev_err(cdns_ctrl->dev, + "Driver needs at least 4 bytes of BCH meta data\n"); + return -EIO; + } + reg = readl_relaxed(cdns_ctrl->reg + BCH_CFG_0); cdns_ctrl->ecc_strengths[0] = FIELD_GET(BCH_CFG_0_CORR_CAP_0, reg); cdns_ctrl->ecc_strengths[1] = FIELD_GET(BCH_CFG_0_CORR_CAP_1, reg); @@ -1170,7 +1179,8 @@ static int cadence_nand_hw_init(struct cdns_nand_ctrl *cdns_ctrl) writel_relaxed(0xFFFFFFFF, cdns_ctrl->reg + INTR_STATUS); cadence_nand_get_caps(cdns_ctrl); - cadence_nand_read_bch_caps(cdns_ctrl); + if (cadence_nand_read_bch_caps(cdns_ctrl)) + return -EIO; /* * Set IO width access to 8. @@ -2587,7 +2597,6 @@ int cadence_nand_attach_chip(struct nand_chip *chip) struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); u32 ecc_size = cdns_chip->sector_count * chip->ecc.bytes; struct mtd_info *mtd = nand_to_mtd(chip); - u32 max_oob_data_size; int ret; if (chip->options & NAND_BUSWIDTH_16) { @@ -2628,10 +2637,8 @@ int cadence_nand_attach_chip(struct nand_chip *chip) cdns_chip->avail_oob_size = mtd->oobsize - ecc_size; - max_oob_data_size = MAX_OOB_SIZE_PER_SECTOR; - - if (cdns_chip->avail_oob_size > max_oob_data_size) - cdns_chip->avail_oob_size = max_oob_data_size; + if (cdns_chip->avail_oob_size > cdns_ctrl->bch_metadata_size) + cdns_chip->avail_oob_size = cdns_ctrl->bch_metadata_size; if ((cdns_chip->avail_oob_size + cdns_chip->bbm_len + ecc_size) > mtd->oobsize) From e4578af0354176ff6b4ae78b9998b4f479f7c31c Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:26 +0100 Subject: [PATCH 050/103] mtd: rawnand: cadence: fix the calculation of the avaialble OOB size The value of cdns_chip->sector_count is not known at the moment of the derivation of ecc_size, leading to a zero value. Fix this by assigning ecc_size later in the code. Fixes: ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem") Cc: stable@vger.kernel.org Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-2-git-send-email-piotrs@cadence.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 5063a8b493a4..2ebfd0934739 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -2595,7 +2595,7 @@ int cadence_nand_attach_chip(struct nand_chip *chip) { struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller); struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip); - u32 ecc_size = cdns_chip->sector_count * chip->ecc.bytes; + u32 ecc_size; struct mtd_info *mtd = nand_to_mtd(chip); int ret; @@ -2634,6 +2634,7 @@ int cadence_nand_attach_chip(struct nand_chip *chip) /* Error correction configuration. */ cdns_chip->sector_size = chip->ecc.size; cdns_chip->sector_count = mtd->writesize / cdns_chip->sector_size; + ecc_size = cdns_chip->sector_count * chip->ecc.bytes; cdns_chip->avail_oob_size = mtd->oobsize - ecc_size; From 9bf1903bed7a2e84f5a8deedb38f7e0ac5e8bfc6 Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:27 +0100 Subject: [PATCH 051/103] mtd: rawnand: cadence: change bad block marker size Increase bad block marker size from one byte to two bytes. Bad block marker is handled by skip bytes feature of HPNFC. Controller expects this value to be an even number. Fixes: ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem") Cc: stable@vger.kernel.org Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-3-git-send-email-piotrs@cadence.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 2ebfd0934739..5c1bbb05ab51 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -2612,12 +2612,9 @@ int cadence_nand_attach_chip(struct nand_chip *chip) chip->options |= NAND_NO_SUBPAGE_WRITE; cdns_chip->bbm_offs = chip->badblockpos; - if (chip->options & NAND_BUSWIDTH_16) { - cdns_chip->bbm_offs &= ~0x01; - cdns_chip->bbm_len = 2; - } else { - cdns_chip->bbm_len = 1; - } + cdns_chip->bbm_offs &= ~0x01; + /* this value should be even number */ + cdns_chip->bbm_len = 2; ret = nand_ecc_choose_conf(chip, &cdns_ctrl->ecc_caps, From 0d7d6c8183aadb1dcc13f415941404a7913b46b3 Mon Sep 17 00:00:00 2001 From: Piotr Sroka Date: Mon, 10 Feb 2020 10:55:28 +0100 Subject: [PATCH 052/103] mtd: rawnand: cadence: reinit completion before executing a new command Reing the completion object before executing CDMA command to make sure the 'done' flag is OK. Fixes: ec4ba01e894d ("mtd: rawnand: Add new Cadence NAND driver to MTD subsystem") Cc: stable@vger.kernel.org Signed-off-by: Piotr Sroka Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1581328530-29966-4-git-send-email-piotrs@cadence.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 5c1bbb05ab51..efddc5c68afb 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -998,6 +998,7 @@ static int cadence_nand_cdma_send(struct cdns_nand_ctrl *cdns_ctrl, return status; cadence_nand_reset_irq(cdns_ctrl); + reinit_completion(&cdns_ctrl->complete); writel_relaxed((u32)cdns_ctrl->dma_cdma_desc, cdns_ctrl->reg + CMD_REG2); From cdc6aba6719b9d7d85c6d411a43345ee12223268 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 22 Jan 2020 16:33:11 -0500 Subject: [PATCH 053/103] dt: bindings: brcmnand: Add support for flash-edu Adding support for EBI DMA unit (EDU). Signed-off-by: Kamal Dasu Reviewed-by: Rob Herring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200122213313.35820-2-kdasu.kdev@gmail.com --- .../devicetree/bindings/mtd/brcm,brcmnand.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt index 82156dc8f304..05651a654c66 100644 --- a/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt +++ b/Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt @@ -35,11 +35,11 @@ Required properties: (optional) NAND flash cache range (if at non-standard offset) - reg-names : a list of the names corresponding to the previous register ranges. Should contain "nand" and (optionally) - "flash-dma" and/or "nand-cache". -- interrupts : The NAND CTLRDY interrupt and (if Flash DMA is available) - FLASH_DMA_DONE -- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done", if broken out as - individual interrupts. + "flash-dma" or "flash-edu" and/or "nand-cache". +- interrupts : The NAND CTLRDY interrupt, (if Flash DMA is available) + FLASH_DMA_DONE and if EDU is avaialble and used FLASH_EDU_DONE +- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done" or "flash_edu_done", + if broken out as individual interrupts. May be "nand", if the SoC has the individual NAND interrupts multiplexed behind another custom piece of hardware From 634088e2621310d2473e4ec3b69843e32d5cee20 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 22 Jan 2020 16:33:12 -0500 Subject: [PATCH 054/103] arch: mips: brcm: Add 7425 flash-edu support Nand controller v5.0 and v6.0 have nand edu blocks that enable dma nand flash transfers. This allows for faster read and write access. Signed-off-by: Kamal Dasu Acked-by: Paul Burton Reviewed-by: Florian Fainelli Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200122213313.35820-3-kdasu.kdev@gmail.com --- arch/mips/boot/dts/brcm/bcm7425.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/boot/dts/brcm/bcm7425.dtsi b/arch/mips/boot/dts/brcm/bcm7425.dtsi index 410e61ebaf9e..aa0b2d39c902 100644 --- a/arch/mips/boot/dts/brcm/bcm7425.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7425.dtsi @@ -403,8 +403,8 @@ nand: nand@41b800 { compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand"; #address-cells = <1>; #size-cells = <0>; - reg-names = "nand"; - reg = <0x41b800 0x400>; + reg-names = "nand", "flash-edu"; + reg = <0x41b800 0x400>, <0x41bc00 0x24>; interrupt-parent = <&hif_l2_intc>; interrupts = <24>; status = "disabled"; From a5d53ad26a8b441325eb9de8e9bb816584ebca7c Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 22 Jan 2020 16:33:13 -0500 Subject: [PATCH 055/103] mtd: rawnand: brcmnand: Add support for flash-edu for dma transfers Legacy mips soc platforms that have controller v5.0 and 6.0 use flash-edu block for dma transfers. This change adds support for nand dma transfers using the EDU block. Signed-off-by: Kamal Dasu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200122213313.35820-4-kdasu.kdev@gmail.com --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 293 ++++++++++++++++++++++- 1 file changed, 287 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 44518dada75b..e4e3ceeac38f 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -102,6 +102,45 @@ struct brcm_nand_dma_desc { #define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY) #define NAND_POLL_STATUS_TIMEOUT_MS 100 +#define EDU_CMD_WRITE 0x00 +#define EDU_CMD_READ 0x01 +#define EDU_STATUS_ACTIVE BIT(0) +#define EDU_ERR_STATUS_ERRACK BIT(0) +#define EDU_DONE_MASK GENMASK(1, 0) + +#define EDU_CONFIG_MODE_NAND BIT(0) +#define EDU_CONFIG_SWAP_BYTE BIT(1) +#ifdef CONFIG_CPU_BIG_ENDIAN +#define EDU_CONFIG_SWAP_CFG EDU_CONFIG_SWAP_BYTE +#else +#define EDU_CONFIG_SWAP_CFG 0 +#endif + +/* edu registers */ +enum edu_reg { + EDU_CONFIG = 0, + EDU_DRAM_ADDR, + EDU_EXT_ADDR, + EDU_LENGTH, + EDU_CMD, + EDU_STOP, + EDU_STATUS, + EDU_DONE, + EDU_ERR_STATUS, +}; + +static const u16 edu_regs[] = { + [EDU_CONFIG] = 0x00, + [EDU_DRAM_ADDR] = 0x04, + [EDU_EXT_ADDR] = 0x08, + [EDU_LENGTH] = 0x0c, + [EDU_CMD] = 0x10, + [EDU_STOP] = 0x14, + [EDU_STATUS] = 0x18, + [EDU_DONE] = 0x1c, + [EDU_ERR_STATUS] = 0x20, +}; + /* flash_dma registers */ enum flash_dma_reg { FLASH_DMA_REVISION = 0, @@ -167,6 +206,8 @@ enum { BRCMNAND_HAS_WP = BIT(3), }; +struct brcmnand_host; + struct brcmnand_controller { struct device *dev; struct nand_controller controller; @@ -185,17 +226,32 @@ struct brcmnand_controller { int cmd_pending; bool dma_pending; + bool edu_pending; struct completion done; struct completion dma_done; + struct completion edu_done; /* List of NAND hosts (one for each chip-select) */ struct list_head host_list; + /* EDU info, per-transaction */ + const u16 *edu_offsets; + void __iomem *edu_base; + int edu_irq; + int edu_count; + u64 edu_dram_addr; + u32 edu_ext_addr; + u32 edu_cmd; + u32 edu_config; + /* flash_dma reg */ const u16 *flash_dma_offsets; struct brcm_nand_dma_desc *dma_desc; dma_addr_t dma_pa; + int (*dma_trans)(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 dma_cmd); + /* in-memory cache of the FLASH_CACHE, used only for some commands */ u8 flash_cache[FC_BYTES]; @@ -216,6 +272,7 @@ struct brcmnand_controller { u32 nand_cs_nand_xor; u32 corr_stat_threshold; u32 flash_dma_mode; + u32 flash_edu_mode; bool pio_poll_mode; }; @@ -657,6 +714,22 @@ static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl, __raw_writel(val, ctrl->nand_fc + word * 4); } +static inline void edu_writel(struct brcmnand_controller *ctrl, + enum edu_reg reg, u32 val) +{ + u16 offs = ctrl->edu_offsets[reg]; + + brcmnand_writel(val, ctrl->edu_base + offs); +} + +static inline u32 edu_readl(struct brcmnand_controller *ctrl, + enum edu_reg reg) +{ + u16 offs = ctrl->edu_offsets[reg]; + + return brcmnand_readl(ctrl->edu_base + offs); +} + static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl) { @@ -926,6 +999,16 @@ static inline bool has_flash_dma(struct brcmnand_controller *ctrl) return ctrl->flash_dma_base; } +static inline bool has_edu(struct brcmnand_controller *ctrl) +{ + return ctrl->edu_base; +} + +static inline bool use_dma(struct brcmnand_controller *ctrl) +{ + return has_flash_dma(ctrl) || has_edu(ctrl); +} + static inline void disable_ctrl_irqs(struct brcmnand_controller *ctrl) { if (ctrl->pio_poll_mode) @@ -1299,6 +1382,52 @@ static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, return tbytes; } +static void brcmnand_edu_init(struct brcmnand_controller *ctrl) +{ + /* initialize edu */ + edu_writel(ctrl, EDU_ERR_STATUS, 0); + edu_readl(ctrl, EDU_ERR_STATUS); + edu_writel(ctrl, EDU_DONE, 0); + edu_writel(ctrl, EDU_DONE, 0); + edu_writel(ctrl, EDU_DONE, 0); + edu_writel(ctrl, EDU_DONE, 0); + edu_readl(ctrl, EDU_DONE); +} + +/* edu irq */ +static irqreturn_t brcmnand_edu_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + if (ctrl->edu_count) { + ctrl->edu_count--; + while (!(edu_readl(ctrl, EDU_DONE) & EDU_DONE_MASK)) + udelay(1); + edu_writel(ctrl, EDU_DONE, 0); + edu_readl(ctrl, EDU_DONE); + } + + if (ctrl->edu_count) { + ctrl->edu_dram_addr += FC_BYTES; + ctrl->edu_ext_addr += FC_BYTES; + + edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr); + edu_readl(ctrl, EDU_DRAM_ADDR); + edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr); + edu_readl(ctrl, EDU_EXT_ADDR); + + mb(); /* flush previous writes */ + edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd); + edu_readl(ctrl, EDU_CMD); + + return IRQ_HANDLED; + } + + complete(&ctrl->edu_done); + + return IRQ_HANDLED; +} + static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) { struct brcmnand_controller *ctrl = data; @@ -1307,6 +1436,16 @@ static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) if (ctrl->dma_pending) return IRQ_HANDLED; + /* check if you need to piggy back on the ctrlrdy irq */ + if (ctrl->edu_pending) { + if (irq == ctrl->irq && ((int)ctrl->edu_irq >= 0)) + /* Discard interrupts while using dedicated edu irq */ + return IRQ_HANDLED; + + /* no registered edu irq, call handler */ + return brcmnand_edu_irq(irq, data); + } + complete(&ctrl->done); return IRQ_HANDLED; } @@ -1644,6 +1783,81 @@ static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf, } } +/** + * Kick EDU engine + */ +static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf, + u32 len, u8 cmd) +{ + struct brcmnand_controller *ctrl = host->ctrl; + unsigned long timeo = msecs_to_jiffies(200); + int ret = 0; + int dir = (cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + u8 edu_cmd = (cmd == CMD_PAGE_READ ? EDU_CMD_READ : EDU_CMD_WRITE); + unsigned int trans = len >> FC_SHIFT; + dma_addr_t pa; + + pa = dma_map_single(ctrl->dev, buf, len, dir); + if (dma_mapping_error(ctrl->dev, pa)) { + dev_err(ctrl->dev, "unable to map buffer for EDU DMA\n"); + return -ENOMEM; + } + + ctrl->edu_pending = true; + ctrl->edu_dram_addr = pa; + ctrl->edu_ext_addr = addr; + ctrl->edu_cmd = edu_cmd; + ctrl->edu_count = trans; + + edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr); + edu_readl(ctrl, EDU_DRAM_ADDR); + edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr); + edu_readl(ctrl, EDU_EXT_ADDR); + edu_writel(ctrl, EDU_LENGTH, FC_BYTES); + edu_readl(ctrl, EDU_LENGTH); + + /* Start edu engine */ + mb(); /* flush previous writes */ + edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd); + edu_readl(ctrl, EDU_CMD); + + if (wait_for_completion_timeout(&ctrl->edu_done, timeo) <= 0) { + dev_err(ctrl->dev, + "timeout waiting for EDU; status %#x, error status %#x\n", + edu_readl(ctrl, EDU_STATUS), + edu_readl(ctrl, EDU_ERR_STATUS)); + } + + dma_unmap_single(ctrl->dev, pa, len, dir); + + /* for program page check NAND status */ + if (((brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & + INTFC_FLASH_STATUS) & NAND_STATUS_FAIL) && + edu_cmd == EDU_CMD_WRITE) { + dev_info(ctrl->dev, "program failed at %llx\n", + (unsigned long long)addr); + ret = -EIO; + } + + /* Make sure the EDU status is clean */ + if (edu_readl(ctrl, EDU_STATUS) & EDU_STATUS_ACTIVE) + dev_warn(ctrl->dev, "EDU still active: %#x\n", + edu_readl(ctrl, EDU_STATUS)); + + if (unlikely(edu_readl(ctrl, EDU_ERR_STATUS) & EDU_ERR_STATUS_ERRACK)) { + dev_warn(ctrl->dev, "EDU RBUS error at addr %llx\n", + (unsigned long long)addr); + ret = -EIO; + } + + ctrl->edu_pending = false; + brcmnand_edu_init(ctrl); + edu_writel(ctrl, EDU_STOP, 0); /* force stop */ + edu_readl(ctrl, EDU_STOP); + + return ret; +} + /** * Construct a FLASH_DMA descriptor as part of a linked list. You must know the * following ahead of time: @@ -1850,9 +2064,11 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, try_dmaread: brcmnand_clear_ecc_addr(ctrl); - if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { - err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES, - CMD_PAGE_READ); + if (ctrl->dma_trans && !oob && flash_dma_buf_ok(buf)) { + err = ctrl->dma_trans(host, addr, buf, + trans * FC_BYTES, + CMD_PAGE_READ); + if (err) { if (mtd_is_bitflip_or_eccerr(err)) err_addr = addr; @@ -1988,10 +2204,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < ctrl->max_oob; i += 4) oob_reg_write(ctrl, i, 0xffffffff); - if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { - if (brcmnand_dma_trans(host, addr, (u32 *)buf, - mtd->writesize, CMD_PROGRAM_PAGE)) + if (use_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { + if (ctrl->dma_trans(host, addr, (u32 *)buf, mtd->writesize, + CMD_PROGRAM_PAGE)) + ret = -EIO; + goto out; } @@ -2494,6 +2712,8 @@ static int brcmnand_suspend(struct device *dev) if (has_flash_dma(ctrl)) ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE); + else if (has_edu(ctrl)) + ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG); return 0; } @@ -2508,6 +2728,14 @@ static int brcmnand_resume(struct device *dev) flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0); } + if (has_edu(ctrl)) + ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG); + else { + edu_writel(ctrl, EDU_CONFIG, ctrl->edu_config); + edu_readl(ctrl, EDU_CONFIG); + brcmnand_edu_init(ctrl); + } + brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select); brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, @@ -2553,6 +2781,49 @@ MODULE_DEVICE_TABLE(of, brcmnand_of_match); /*********************************************************************** * Platform driver setup (per controller) ***********************************************************************/ +static int brcmnand_edu_setup(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-edu"); + if (res) { + ctrl->edu_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctrl->edu_base)) + return PTR_ERR(ctrl->edu_base); + + ctrl->edu_offsets = edu_regs; + + edu_writel(ctrl, EDU_CONFIG, EDU_CONFIG_MODE_NAND | + EDU_CONFIG_SWAP_CFG); + edu_readl(ctrl, EDU_CONFIG); + + /* initialize edu */ + brcmnand_edu_init(ctrl); + + ctrl->edu_irq = platform_get_irq_optional(pdev, 1); + if (ctrl->edu_irq < 0) { + dev_warn(dev, + "FLASH EDU enabled, using ctlrdy irq\n"); + } else { + ret = devm_request_irq(dev, ctrl->edu_irq, + brcmnand_edu_irq, 0, + "brcmnand-edu", ctrl); + if (ret < 0) { + dev_err(ctrl->dev, "can't allocate IRQ %d: error %d\n", + ctrl->edu_irq, ret); + return ret; + } + + dev_info(dev, "FLASH EDU enabled using irq %u\n", + ctrl->edu_irq); + } + } + + return 0; +} int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) { @@ -2578,6 +2849,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) init_completion(&ctrl->done); init_completion(&ctrl->dma_done); + init_completion(&ctrl->edu_done); nand_controller_init(&ctrl->controller); ctrl->controller.ops = &brcmnand_controller_ops; INIT_LIST_HEAD(&ctrl->host_list); @@ -2675,6 +2947,15 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) } dev_info(dev, "enabling FLASH_DMA\n"); + /* set flash dma transfer function to call */ + ctrl->dma_trans = brcmnand_dma_trans; + } else { + ret = brcmnand_edu_setup(pdev); + if (ret < 0) + goto err; + + /* set edu transfer function to call */ + ctrl->dma_trans = brcmnand_edu_trans; } /* Disable automatic device ID config, direct addressing */ From d3137043440fb1faaaf2481184f35b9ed0c1f2c2 Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:30 +0100 Subject: [PATCH 056/103] mtd: spinand: micron: Generalize the OOB layout structure and function names In order to add new Micron SPI NAND devices, we generalized the OOB layout structure and function names. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-2-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index f56f81325e10..cc1ee68421c8 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -34,38 +34,38 @@ static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); -static int mt29f2g01abagd_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section) return -ERANGE; - region->offset = 64; - region->length = 64; + region->offset = mtd->oobsize / 2; + region->length = mtd->oobsize / 2; return 0; } -static int mt29f2g01abagd_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int micron_8_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section) return -ERANGE; /* Reserve 2 bytes for the BBM. */ region->offset = 2; - region->length = 62; + region->length = (mtd->oobsize / 2) - 2; return 0; } -static const struct mtd_ooblayout_ops mt29f2g01abagd_ooblayout = { - .ecc = mt29f2g01abagd_ooblayout_ecc, - .free = mt29f2g01abagd_ooblayout_free, +static const struct mtd_ooblayout_ops micron_8_ooblayout = { + .ecc = micron_8_ooblayout_ecc, + .free = micron_8_ooblayout_free, }; -static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, - u8 status) +static int micron_8_ecc_get_status(struct spinand_device *spinand, + u8 status) { switch (status & MICRON_STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: @@ -99,8 +99,8 @@ static const struct spinand_info micron_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&mt29f2g01abagd_ooblayout, - mt29f2g01abagd_ecc_get_status)), + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), }; static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { From 8511a3a9937e30949b34bea46c3dc3f65d11034b Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:31 +0100 Subject: [PATCH 057/103] mtd: spinand: micron: Describe the SPI NAND device MT29F2G01ABAGD Add the SPI NAND device MT29F2G01ABAGD series number, size and voltage details as a comment. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-3-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index cc1ee68421c8..4727933c894b 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -91,6 +91,7 @@ static int micron_8_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info micron_spinand_table[] = { + /* M79A 2Gb 3.3V */ SPINAND_INFO("MT29F2G01ABAGD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), From a15335a17f4abf48ed9739c3b119232f9392cb60 Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:32 +0100 Subject: [PATCH 058/103] mtd: spinand: micron: Add new Micron SPI NAND devices Add device table for M79A and M78A series Micron SPI NAND devices. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-4-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 4727933c894b..26925714a9fb 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -102,6 +102,39 @@ static const struct spinand_info micron_spinand_table[] = { 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M79A 2Gb 1.8V */ + SPINAND_INFO("MT29F2G01ABBGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M78A 1Gb 3.3V */ + SPINAND_INFO("MT29F1G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M78A 1Gb 1.8V */ + SPINAND_INFO("MT29F1G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), }; static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { From 0bc68af9137dc3f30b161de4ce546c7799f88d1e Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:33 +0100 Subject: [PATCH 059/103] mtd: spinand: micron: identify SPI NAND device with Continuous Read mode Add SPINAND_HAS_CR_FEAT_BIT flag to identify the SPI NAND device with the Continuous Read mode. Some of the Micron SPI NAND devices have the "Continuous Read" feature enabled by default, which does not fit the subsystem needs. In this mode, the READ CACHE command doesn't require the starting column address. The device always output the data starting from the first column of the cache register, and once the end of the cache register reached, the data output continues through the next page. With the continuous read mode, it is possible to read out the entire block using a single READ command, and once the end of the block reached, the output pins become High-Z state. However, during this mode the read command doesn't output the OOB area. Hence, we disable the feature at probe time. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-5-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 16 ++++++++++++++++ include/linux/mtd/spinand.h | 1 + 2 files changed, 17 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 26925714a9fb..956f7710aca2 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -18,6 +18,8 @@ #define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) #define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) +#define MICRON_CFG_CR BIT(0) + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), @@ -137,7 +139,21 @@ static const struct spinand_info micron_spinand_table[] = { micron_8_ecc_get_status)), }; +static int micron_spinand_init(struct spinand_device *spinand) +{ + /* + * M70A device series enable Continuous Read feature at Power-up, + * which is not supported. Disable this bit to avoid any possible + * failure. + */ + if (spinand->flags & SPINAND_HAS_CR_FEAT_BIT) + return spinand_upd_cfg(spinand, MICRON_CFG_CR, 0); + + return 0; +} + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { + .init = micron_spinand_init, }; const struct spinand_manufacturer micron_spinand_manufacturer = { diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index f4c4ae87181b..1077c45721ff 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -284,6 +284,7 @@ struct spinand_ecc_info { }; #define SPINAND_HAS_QE_BIT BIT(0) +#define SPINAND_HAS_CR_FEAT_BIT BIT(1) /** * struct spinand_info - Structure used to describe SPI NAND chips From a7e5daccc310c3b892ae5e598cadb7a9274c2547 Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:34 +0100 Subject: [PATCH 060/103] mtd: spinand: micron: Add M70A series Micron SPI NAND devices Add device table for M70A series Micron SPI NAND devices. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-6-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 956f7710aca2..d6fd63008782 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -137,6 +137,28 @@ static const struct spinand_info micron_spinand_table[] = { 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M70A 4Gb 3.3V */ + SPINAND_INFO("MT29F4G01ABAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M70A 4Gb 1.8V */ + SPINAND_INFO("MT29F4G01ABBFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), }; static int micron_spinand_init(struct spinand_device *spinand) From 9f9ae0c253c1e058fbc845e26c4a32a7d777f0dc Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:35 +0100 Subject: [PATCH 061/103] mtd: spinand: micron: Add new Micron SPI NAND devices with multiple dies Add device table for new Micron SPI NAND devices, which have multiple dies. Also, enable support to select the dies. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-7-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index d6fd63008782..5d370cfcdaaa 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -20,6 +20,14 @@ #define MICRON_CFG_CR BIT(0) +/* + * As per datasheet, die selection is done by the 6th bit of Die + * Select Register (Address 0xD0). + */ +#define MICRON_DIE_SELECT_REG 0xD0 + +#define MICRON_SELECT_DIE(x) ((x) << 6) + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), @@ -66,6 +74,20 @@ static const struct mtd_ooblayout_ops micron_8_ooblayout = { .free = micron_8_ooblayout_free, }; +static int micron_select_target(struct spinand_device *spinand, + unsigned int target) +{ + struct spi_mem_op op = SPINAND_SET_FEATURE_OP(MICRON_DIE_SELECT_REG, + spinand->scratchbuf); + + if (target > 1) + return -EINVAL; + + *spinand->scratchbuf = MICRON_SELECT_DIE(target); + + return spi_mem_exec_op(spinand->spimem, &op); +} + static int micron_8_ecc_get_status(struct spinand_device *spinand, u8 status) { @@ -137,6 +159,18 @@ static const struct spinand_info micron_spinand_table[] = { 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M79A 4Gb 3.3V */ + SPINAND_INFO("MT29F4G01ADAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), + NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), /* M70A 4Gb 3.3V */ SPINAND_INFO("MT29F4G01ABAFD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), @@ -159,6 +193,30 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), + /* M70A 8Gb 3.3V */ + SPINAND_INFO("MT29F8G01ADAFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), + /* M70A 8Gb 1.8V */ + SPINAND_INFO("MT29F8G01ADBFD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_CR_FEAT_BIT, + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), }; static int micron_spinand_init(struct spinand_device *spinand) From 7648a720d9ed8ab413e104f1a65923a31b22a411 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:34 +0000 Subject: [PATCH 062/103] mtd: spi-nor: Stop prefixing generic functions with a manufacturer name Replace the manufacturer prefix by something describing more precisely what those functions do. Signed-off-by: Boris Brezillon [tudor.ambarus@microchip.com: prepend spi_nor_ to all modified methods.] Signed-off-by: Tudor Ambarus Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/spi-nor.c | 70 +++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index caf0c109cca0..0b8fac0b0299 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -755,13 +755,13 @@ static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) } /** - * s3an_sr_ready() - Query the Status Register of the S3AN flash to see if the - * flash is ready for new commands. + * spi_nor_xsr_ready() - Query the Status Register of the S3AN flash to see if + * the flash is ready for new commands. * @nor: pointer to 'struct spi_nor'. * * Return: 0 on success, -errno otherwise. */ -static int s3an_sr_ready(struct spi_nor *nor) +static int spi_nor_xsr_ready(struct spi_nor *nor) { int ret; @@ -892,7 +892,7 @@ static int spi_nor_ready(struct spi_nor *nor) int sr, fsr; if (nor->flags & SNOR_F_READY_XSR_RDY) - sr = s3an_sr_ready(nor); + sr = spi_nor_xsr_ready(nor); else sr = spi_nor_sr_ready(nor); if (sr < 0) @@ -1784,8 +1784,8 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; } -static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, - uint64_t *len) +static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, + uint64_t *len) { struct mtd_info *mtd = &nor->mtd; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; @@ -1813,8 +1813,8 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, * Return 1 if the entire region is locked (if @locked is true) or unlocked (if * @locked is false); 0 otherwise */ -static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, - u8 sr, bool locked) +static int spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, + uint64_t len, u8 sr, bool locked) { loff_t lock_offs; uint64_t lock_len; @@ -1822,7 +1822,7 @@ static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t le if (!len) return 1; - stm_get_locked_range(nor, sr, &lock_offs, &lock_len); + spi_nor_get_locked_range_sr(nor, sr, &lock_offs, &lock_len); if (locked) /* Requested range is a sub-range of locked range */ @@ -1832,16 +1832,16 @@ static int stm_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, uint64_t le return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs); } -static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, - u8 sr) +static int spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + u8 sr) { - return stm_check_lock_status_sr(nor, ofs, len, sr, true); + return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); } -static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, - u8 sr) +static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + u8 sr) { - return stm_check_lock_status_sr(nor, ofs, len, sr, false); + return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); } /* @@ -1876,7 +1876,7 @@ static int stm_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, * * Returns negative on errors, 0 on success. */ -static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; int ret, status_old, status_new; @@ -1894,16 +1894,16 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) status_old = nor->bouncebuf[0]; /* If nothing in our range is unlocked, we don't need to do anything */ - if (stm_is_locked_sr(nor, ofs, len, status_old)) + if (spi_nor_is_locked_sr(nor, ofs, len, status_old)) return 0; /* If anything below us is unlocked, we can't use 'bottom' protection */ - if (!stm_is_locked_sr(nor, 0, ofs, status_old)) + if (!spi_nor_is_locked_sr(nor, 0, ofs, status_old)) can_be_bottom = false; /* If anything above us is unlocked, we can't use 'top' protection */ - if (!stm_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len), - status_old)) + if (!spi_nor_is_locked_sr(nor, ofs + len, mtd->size - (ofs + len), + status_old)) can_be_top = false; if (!can_be_bottom && !can_be_top) @@ -1958,11 +1958,11 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) } /* - * Unlock a region of the flash. See stm_lock() for more info + * Unlock a region of the flash. See spi_nor_sr_lock() for more info * * Returns negative on errors, 0 on success. */ -static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; int ret, status_old, status_new; @@ -1980,16 +1980,16 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) status_old = nor->bouncebuf[0]; /* If nothing in our range is locked, we don't need to do anything */ - if (stm_is_unlocked_sr(nor, ofs, len, status_old)) + if (spi_nor_is_unlocked_sr(nor, ofs, len, status_old)) return 0; /* If anything below us is locked, we can't use 'top' protection */ - if (!stm_is_unlocked_sr(nor, 0, ofs, status_old)) + if (!spi_nor_is_unlocked_sr(nor, 0, ofs, status_old)) can_be_top = false; /* If anything above us is locked, we can't use 'bottom' protection */ - if (!stm_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len), - status_old)) + if (!spi_nor_is_unlocked_sr(nor, ofs + len, mtd->size - (ofs + len), + status_old)) can_be_bottom = false; if (!can_be_bottom && !can_be_top) @@ -2046,13 +2046,13 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) } /* - * Check if a region of the flash is (completely) locked. See stm_lock() for - * more info. + * Check if a region of the flash is (completely) locked. See spi_nor_sr_lock() + * for more info. * * Returns 1 if entire region is locked, 0 if any portion is unlocked, and * negative on errors. */ -static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) { int ret; @@ -2060,13 +2060,13 @@ static int stm_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) if (ret) return ret; - return stm_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]); + return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]); } -static const struct spi_nor_locking_ops stm_locking_ops = { - .lock = stm_lock, - .unlock = stm_unlock, - .is_locked = stm_is_locked, +static const struct spi_nor_locking_ops spi_nor_sr_locking_ops = { + .lock = spi_nor_sr_lock, + .unlock = spi_nor_sr_unlock, + .is_locked = spi_nor_sr_is_locked, }; static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -4895,7 +4895,7 @@ static void spi_nor_late_init_params(struct spi_nor *nor) * the default ones. */ if (nor->flags & SNOR_F_HAS_LOCK && !nor->params.locking_ops) - nor->params.locking_ops = &stm_locking_ops; + nor->params.locking_ops = &spi_nor_sr_locking_ops; } /** From 81924dae51941018afdaf25638da804be4807ce5 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 13 Mar 2020 19:42:35 +0000 Subject: [PATCH 063/103] mtd: spi-nor: Emphasise which is the generic set_4byte_addr_mode() method Rename (*set_4byte)() to (*set_4byte_addr_mode)() for a better differentiation between the 4 byte address mode and opcodes. Rename macronix_set_4byte() to spi_nor_set_4byte_addr_mode(), it will be the only 4 byte address mode method exposed to the manufacturer drivers. Here's how the manufacturers enter and exit the 4 byte address mode: - eon, gidadevice, issi, macronix, xmc use EN4B/EX4B - micron-st needs WEN. st_micron_set_4byte_addr_mode() will become a private method, as they are the only ones that need WEN before the EN4B/EX4B commands. - newer spansion have a 4BAM opcode (this translates to a new, public command). Older spansion flashes use the BRWR command (legacy in core.c -> spansion_set_4byte_addr_mode()) - winbond's method is hackish and may be reason for just a flash fixup hook -> private method Signed-off-by: Tudor Ambarus Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/spi-nor.c | 34 ++++++++++++++++++---------------- include/linux/mtd/spi-nor.h | 4 ++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 0b8fac0b0299..8616673ddb7c 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -568,14 +568,14 @@ static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) } /** - * macronix_set_4byte() - Set 4-byte address mode for Macronix flashes. + * spi_nor_set_4byte_addr_mode() - Enter/Exit 4-byte address mode. * @nor: pointer to 'struct spi_nor'. * @enable: true to enter the 4-byte address mode, false to exit the 4-byte * address mode. * * Return: 0 on success, -errno otherwise. */ -static int macronix_set_4byte(struct spi_nor *nor, bool enable) +static int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) { int ret; @@ -604,14 +604,15 @@ static int macronix_set_4byte(struct spi_nor *nor, bool enable) } /** - * st_micron_set_4byte() - Set 4-byte address mode for ST and Micron flashes. + * st_micron_set_4byte_addr_mode() - Set 4-byte address mode for ST and Micron + * flashes. * @nor: pointer to 'struct spi_nor'. * @enable: true to enter the 4-byte address mode, false to exit the 4-byte * address mode. * * Return: 0 on success, -errno otherwise. */ -static int st_micron_set_4byte(struct spi_nor *nor, bool enable) +static int st_micron_set_4byte_addr_mode(struct spi_nor *nor, bool enable) { int ret; @@ -619,7 +620,7 @@ static int st_micron_set_4byte(struct spi_nor *nor, bool enable) if (ret) return ret; - ret = macronix_set_4byte(nor, enable); + ret = spi_nor_set_4byte_addr_mode(nor, enable); if (ret) return ret; @@ -627,14 +628,15 @@ static int st_micron_set_4byte(struct spi_nor *nor, bool enable) } /** - * spansion_set_4byte() - Set 4-byte address mode for Spansion flashes. + * spansion_set_4byte_addr_mode() - Set 4-byte address mode for Spansion + * flashes. * @nor: pointer to 'struct spi_nor'. * @enable: true to enter the 4-byte address mode, false to exit the 4-byte * address mode. * * Return: 0 on success, -errno otherwise. */ -static int spansion_set_4byte(struct spi_nor *nor, bool enable) +static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable) { int ret; @@ -692,18 +694,18 @@ static int spi_nor_write_ear(struct spi_nor *nor, u8 ear) } /** - * winbond_set_4byte() - Set 4-byte address mode for Winbond flashes. + * winbond_set_4byte_addr_mode() - Set 4-byte address mode for Winbond flashes. * @nor: pointer to 'struct spi_nor'. * @enable: true to enter the 4-byte address mode, false to exit the 4-byte * address mode. * * Return: 0 on success, -errno otherwise. */ -static int winbond_set_4byte(struct spi_nor *nor, bool enable) +static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) { int ret; - ret = macronix_set_4byte(nor, enable); + ret = spi_nor_set_4byte_addr_mode(nor, enable); if (ret || enable) return ret; @@ -4655,7 +4657,7 @@ static void issi_set_default_init(struct spi_nor *nor) static void macronix_set_default_init(struct spi_nor *nor) { nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; - nor->params.set_4byte = macronix_set_4byte; + nor->params.set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; } static void sst_set_default_init(struct spi_nor *nor) @@ -4668,12 +4670,12 @@ static void st_micron_set_default_init(struct spi_nor *nor) nor->flags |= SNOR_F_HAS_LOCK; nor->flags &= ~SNOR_F_HAS_16BIT_SR; nor->params.quad_enable = NULL; - nor->params.set_4byte = st_micron_set_4byte; + nor->params.set_4byte_addr_mode = st_micron_set_4byte_addr_mode; } static void winbond_set_default_init(struct spi_nor *nor) { - nor->params.set_4byte = winbond_set_4byte; + nor->params.set_4byte_addr_mode = winbond_set_4byte_addr_mode; } /** @@ -4759,7 +4761,7 @@ static void spi_nor_info_init_params(struct spi_nor *nor) /* Initialize legacy flash parameters and settings. */ params->quad_enable = spi_nor_sr2_bit1_quad_enable; - params->set_4byte = spansion_set_4byte; + params->set_4byte_addr_mode = spansion_set_4byte_addr_mode; params->setup = spi_nor_default_setup; /* Default to 16-bit Write Status (01h) Command */ nor->flags |= SNOR_F_HAS_16BIT_SR; @@ -5011,7 +5013,7 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - nor->params.set_4byte(nor, true); + nor->params.set_4byte_addr_mode(nor, true); } return 0; @@ -5035,7 +5037,7 @@ void spi_nor_restore(struct spi_nor *nor) /* restore the addressing mode */ if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) - nor->params.set_4byte(nor, false); + nor->params.set_4byte_addr_mode(nor, false); } EXPORT_SYMBOL_GPL(spi_nor_restore); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index de90724f62f1..2b9717b0cd62 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -520,7 +520,7 @@ struct spi_nor_locking_ops { * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. * @quad_enable: enables SPI NOR quad mode. - * @set_4byte: puts the SPI NOR in 4 byte addressing mode. + * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. * @convert_addr: converts an absolute address into something the flash * will understand. Particularly useful when pagesize is * not a power-of-2. @@ -541,7 +541,7 @@ struct spi_nor_flash_parameter { struct spi_nor_erase_map erase_map; int (*quad_enable)(struct spi_nor *nor); - int (*set_4byte)(struct spi_nor *nor, bool enable); + int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); u32 (*convert_addr)(struct spi_nor *nor, u32 addr); int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps); From a0900d0195d2dcce464f4109445a788d5860b970 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:36 +0000 Subject: [PATCH 064/103] mtd: spi-nor: Prepare core / manufacturer code split Move all SPI NOR controller drivers to a controllers/ sub-directory so that we only have SPI NOR related source files under drivers/mtd/spi-nor/. Rename spi-nor.c into core.c, we are about to split this file in multiple source files (one per manufacturer, plus one for the SFDP parsing logic). Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/Kconfig | 75 +------------------ drivers/mtd/spi-nor/Makefile | 9 +-- drivers/mtd/spi-nor/controllers/Kconfig | 75 +++++++++++++++++++ drivers/mtd/spi-nor/controllers/Makefile | 8 ++ .../spi-nor/{ => controllers}/aspeed-smc.c | 0 .../{ => controllers}/cadence-quadspi.c | 0 .../mtd/spi-nor/{ => controllers}/hisi-sfc.c | 0 .../spi-nor/{ => controllers}/intel-spi-pci.c | 0 .../{ => controllers}/intel-spi-platform.c | 0 .../mtd/spi-nor/{ => controllers}/intel-spi.c | 0 .../mtd/spi-nor/{ => controllers}/intel-spi.h | 0 .../mtd/spi-nor/{ => controllers}/nxp-spifi.c | 0 drivers/mtd/spi-nor/{spi-nor.c => core.c} | 0 13 files changed, 86 insertions(+), 81 deletions(-) create mode 100644 drivers/mtd/spi-nor/controllers/Kconfig create mode 100644 drivers/mtd/spi-nor/controllers/Makefile rename drivers/mtd/spi-nor/{ => controllers}/aspeed-smc.c (100%) rename drivers/mtd/spi-nor/{ => controllers}/cadence-quadspi.c (100%) rename drivers/mtd/spi-nor/{ => controllers}/hisi-sfc.c (100%) rename drivers/mtd/spi-nor/{ => controllers}/intel-spi-pci.c (100%) rename drivers/mtd/spi-nor/{ => controllers}/intel-spi-platform.c (100%) rename drivers/mtd/spi-nor/{ => controllers}/intel-spi.c (100%) rename drivers/mtd/spi-nor/{ => controllers}/intel-spi.h (100%) rename drivers/mtd/spi-nor/{ => controllers}/nxp-spifi.c (100%) rename drivers/mtd/spi-nor/{spi-nor.c => core.c} (100%) diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index 267b9000782e..6e816eafb312 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -24,79 +24,6 @@ config MTD_SPI_NOR_USE_4K_SECTORS Please note that some tools/drivers/filesystems may not work with 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). -config SPI_ASPEED_SMC - tristate "Aspeed flash controllers in SPI mode" - depends on ARCH_ASPEED || COMPILE_TEST - depends on HAS_IOMEM && OF - help - This enables support for the Firmware Memory controller (FMC) - in the Aspeed AST2500/AST2400 SoCs when attached to SPI NOR chips, - and support for the SPI flash memory controller (SPI) for - the host firmware. The implementation only supports SPI NOR. - -config SPI_CADENCE_QUADSPI - tristate "Cadence Quad SPI controller" - depends on OF && (ARM || ARM64 || COMPILE_TEST) - help - Enable support for the Cadence Quad SPI Flash controller. - - Cadence QSPI is a specialized controller for connecting an SPI - Flash over 1/2/4-bit wide bus. Enable this option if you have a - device with a Cadence QSPI controller and want to access the - Flash as an MTD device. - -config SPI_HISI_SFC - tristate "Hisilicon FMC SPI-NOR Flash Controller(SFC)" - depends on ARCH_HISI || COMPILE_TEST - depends on HAS_IOMEM - help - This enables support for HiSilicon FMC SPI-NOR flash controller. - -config SPI_NXP_SPIFI - tristate "NXP SPI Flash Interface (SPIFI)" - depends on OF && (ARCH_LPC18XX || COMPILE_TEST) - depends on HAS_IOMEM - help - Enable support for the NXP LPC SPI Flash Interface controller. - - SPIFI is a specialized controller for connecting serial SPI - Flash. Enable this option if you have a device with a SPIFI - controller and want to access the Flash as a mtd device. - -config SPI_INTEL_SPI - tristate - -config SPI_INTEL_SPI_PCI - tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)" - depends on X86 && PCI - select SPI_INTEL_SPI - help - This enables PCI support for the Intel PCH/PCU SPI controller in - master mode. This controller is present in modern Intel hardware - and is used to hold BIOS and other persistent settings. Using - this driver it is possible to upgrade BIOS directly from Linux. - - Say N here unless you know what you are doing. Overwriting the - SPI flash may render the system unbootable. - - To compile this driver as a module, choose M here: the module - will be called intel-spi-pci. - -config SPI_INTEL_SPI_PLATFORM - tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)" - depends on X86 - select SPI_INTEL_SPI - help - This enables platform support for the Intel PCH/PCU SPI - controller in master mode. This controller is present in modern - Intel hardware and is used to hold BIOS and other persistent - settings. Using this driver it is possible to upgrade BIOS - directly from Linux. - - Say N here unless you know what you are doing. Overwriting the - SPI flash may render the system unbootable. - - To compile this driver as a module, choose M here: the module - will be called intel-spi-platform. +source "drivers/mtd/spi-nor/controllers/Kconfig" endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 738dfd74cf76..d6fc70ab4a32 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,9 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 + +spi-nor-objs := core.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o -obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o -obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o -obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o -obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o -obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o -obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o -obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o diff --git a/drivers/mtd/spi-nor/controllers/Kconfig b/drivers/mtd/spi-nor/controllers/Kconfig new file mode 100644 index 000000000000..10b86660b821 --- /dev/null +++ b/drivers/mtd/spi-nor/controllers/Kconfig @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only +config SPI_ASPEED_SMC + tristate "Aspeed flash controllers in SPI mode" + depends on ARCH_ASPEED || COMPILE_TEST + depends on HAS_IOMEM && OF + help + This enables support for the Firmware Memory controller (FMC) + in the Aspeed AST2500/AST2400 SoCs when attached to SPI NOR chips, + and support for the SPI flash memory controller (SPI) for + the host firmware. The implementation only supports SPI NOR. + +config SPI_CADENCE_QUADSPI + tristate "Cadence Quad SPI controller" + depends on OF && (ARM || ARM64 || COMPILE_TEST) + help + Enable support for the Cadence Quad SPI Flash controller. + + Cadence QSPI is a specialized controller for connecting an SPI + Flash over 1/2/4-bit wide bus. Enable this option if you have a + device with a Cadence QSPI controller and want to access the + Flash as an MTD device. + +config SPI_HISI_SFC + tristate "Hisilicon FMC SPI-NOR Flash Controller(SFC)" + depends on ARCH_HISI || COMPILE_TEST + depends on HAS_IOMEM + help + This enables support for HiSilicon FMC SPI-NOR flash controller. + +config SPI_NXP_SPIFI + tristate "NXP SPI Flash Interface (SPIFI)" + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) + depends on HAS_IOMEM + help + Enable support for the NXP LPC SPI Flash Interface controller. + + SPIFI is a specialized controller for connecting serial SPI + Flash. Enable this option if you have a device with a SPIFI + controller and want to access the Flash as a mtd device. + +config SPI_INTEL_SPI + tristate + +config SPI_INTEL_SPI_PCI + tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)" + depends on X86 && PCI + select SPI_INTEL_SPI + help + This enables PCI support for the Intel PCH/PCU SPI controller in + master mode. This controller is present in modern Intel hardware + and is used to hold BIOS and other persistent settings. Using + this driver it is possible to upgrade BIOS directly from Linux. + + Say N here unless you know what you are doing. Overwriting the + SPI flash may render the system unbootable. + + To compile this driver as a module, choose M here: the module + will be called intel-spi-pci. + +config SPI_INTEL_SPI_PLATFORM + tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)" + depends on X86 + select SPI_INTEL_SPI + help + This enables platform support for the Intel PCH/PCU SPI + controller in master mode. This controller is present in modern + Intel hardware and is used to hold BIOS and other persistent + settings. Using this driver it is possible to upgrade BIOS + directly from Linux. + + Say N here unless you know what you are doing. Overwriting the + SPI flash may render the system unbootable. + + To compile this driver as a module, choose M here: the module + will be called intel-spi-platform. diff --git a/drivers/mtd/spi-nor/controllers/Makefile b/drivers/mtd/spi-nor/controllers/Makefile new file mode 100644 index 000000000000..46e6fbe586e3 --- /dev/null +++ b/drivers/mtd/spi-nor/controllers/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o +obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o +obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o +obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o +obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o +obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o +obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c similarity index 100% rename from drivers/mtd/spi-nor/aspeed-smc.c rename to drivers/mtd/spi-nor/controllers/aspeed-smc.c diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/controllers/cadence-quadspi.c similarity index 100% rename from drivers/mtd/spi-nor/cadence-quadspi.c rename to drivers/mtd/spi-nor/controllers/cadence-quadspi.c diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/controllers/hisi-sfc.c similarity index 100% rename from drivers/mtd/spi-nor/hisi-sfc.c rename to drivers/mtd/spi-nor/controllers/hisi-sfc.c diff --git a/drivers/mtd/spi-nor/intel-spi-pci.c b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c similarity index 100% rename from drivers/mtd/spi-nor/intel-spi-pci.c rename to drivers/mtd/spi-nor/controllers/intel-spi-pci.c diff --git a/drivers/mtd/spi-nor/intel-spi-platform.c b/drivers/mtd/spi-nor/controllers/intel-spi-platform.c similarity index 100% rename from drivers/mtd/spi-nor/intel-spi-platform.c rename to drivers/mtd/spi-nor/controllers/intel-spi-platform.c diff --git a/drivers/mtd/spi-nor/intel-spi.c b/drivers/mtd/spi-nor/controllers/intel-spi.c similarity index 100% rename from drivers/mtd/spi-nor/intel-spi.c rename to drivers/mtd/spi-nor/controllers/intel-spi.c diff --git a/drivers/mtd/spi-nor/intel-spi.h b/drivers/mtd/spi-nor/controllers/intel-spi.h similarity index 100% rename from drivers/mtd/spi-nor/intel-spi.h rename to drivers/mtd/spi-nor/controllers/intel-spi.h diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/controllers/nxp-spifi.c similarity index 100% rename from drivers/mtd/spi-nor/nxp-spifi.c rename to drivers/mtd/spi-nor/controllers/nxp-spifi.c diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/core.c similarity index 100% rename from drivers/mtd/spi-nor/spi-nor.c rename to drivers/mtd/spi-nor/core.c From cb481b92d10fdb0027c7f96576b640c28a5e4179 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 13 Mar 2020 19:42:37 +0000 Subject: [PATCH 065/103] mtd: spi-nor: Move SFDP logic out of the core It makes the core file a bit smaller and provides better separation between the SFDP parsing and core logic. Keep the core.h and sfdp.h definitions private in drivers/mtd/spi-nor/. Both expose just the definitions that are required by the core and manufacturer drivers. None of the SPI NOR controller drivers should include them. Signed-off-by: Tudor Ambarus Reviewed-by: Boris Brezillon Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/Makefile | 2 +- drivers/mtd/spi-nor/core.c | 1315 +--------------------------------- drivers/mtd/spi-nor/core.h | 36 + drivers/mtd/spi-nor/sfdp.c | 1195 ++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/sfdp.h | 98 +++ 5 files changed, 1349 insertions(+), 1297 deletions(-) create mode 100644 drivers/mtd/spi-nor/core.h create mode 100644 drivers/mtd/spi-nor/sfdp.c create mode 100644 drivers/mtd/spi-nor/sfdp.h diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index d6fc70ab4a32..6bcdb6f1615a 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -spi-nor-objs := core.o +spi-nor-objs := core.o sfdp.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 8616673ddb7c..4ae79c1c8bec 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -23,6 +22,8 @@ #include #include +#include "core.h" + /* Define max times to check status register before we give up. */ /* @@ -40,118 +41,6 @@ #define SPI_NOR_MAX_ID_LEN 6 #define SPI_NOR_MAX_ADDR_WIDTH 4 -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) (((p)->id_msb << 8) | (p)->id_lsb) -#define SFDP_PARAM_HEADER_PTP(p) \ - (((p)->parameter_table_pointer[2] << 16) | \ - ((p)->parameter_table_pointer[1] << 8) | \ - ((p)->parameter_table_pointer[0] << 0)) - -#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ -#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ -#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction 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; /* Ox50444653U <=> "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 */ - -/* - * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs. - * They are indexed from 1 but C arrays are indexed from 0. - */ -#define BFPT_DWORD(i) ((i) - 1) -#define BFPT_DWORD_MAX 16 - -/* The first version of JESD216 defined only 9 DWORDs. */ -#define BFPT_DWORD_MAX_JESD216 9 - -/* 1st DWORD. */ -#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16) -#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17) -#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17) -#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17) -#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17) -#define BFPT_DWORD1_DTR BIT(19) -#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20) -#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21) -#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22) - -/* 5th DWORD. */ -#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0) -#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4) - -/* 11th DWORD. */ -#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4 -#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4) - -/* 15th DWORD. */ - -/* - * (from JESD216 rev B) - * 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 phase. - * - 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 Write Status instruction 01h with - * two data bytes where bit 1 of the second byte is one. - * [...] - */ -#define BFPT_DWORD15_QER_MASK GENMASK(22, 20) -#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ -#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) -#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ -#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) -#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) -#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ - -struct sfdp_bfpt { - u32 dwords[BFPT_DWORD_MAX]; -}; - /** * struct spi_nor_fixups - SPI NOR fixup hooks * @default_init: called after default flash parameters init. Used to tweak @@ -345,8 +234,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, * * Return: number of bytes read successfully, -errno otherwise */ -static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, - u8 *buf) +ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf) { if (nor->spimem) return spi_nor_spimem_read_data(nor, from, len, buf); @@ -1271,7 +1159,7 @@ static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) return opcode; } -static u8 spi_nor_convert_3to4_read(u8 opcode) +u8 spi_nor_convert_3to4_read(u8 opcode) { static const u8 spi_nor_3to4_read[][2] = { { SPINOR_OP_READ, SPINOR_OP_READ_4B }, @@ -1496,7 +1384,7 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map, * * Return: the next spi nor region or NULL if last region. */ -static struct spi_nor_erase_region * +struct spi_nor_erase_region * spi_nor_region_next(struct spi_nor_erase_region *region) { if (spi_nor_region_is_last(region)) @@ -2125,7 +2013,7 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) +int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) { int ret; @@ -2150,7 +2038,7 @@ static int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) +int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) { int ret; @@ -2181,7 +2069,7 @@ static int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) +int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) { u8 *sr2 = nor->bouncebuf; int ret; @@ -3029,10 +2917,8 @@ spi_nor_set_read_settings(struct spi_nor_read_command *read, read->proto = proto; } -static void -spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, - u8 opcode, - enum spi_nor_protocol proto) +void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, + enum spi_nor_protocol proto) { pp->opcode = opcode; pp->proto = proto; @@ -3049,7 +2935,7 @@ static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size) return -EINVAL; } -static int spi_nor_hwcaps_read2cmd(u32 hwcaps) +int spi_nor_hwcaps_read2cmd(u32 hwcaps) { static const int hwcaps_read2cmd[][2] = { { SNOR_HWCAPS_READ, SNOR_CMD_READ }, @@ -3089,76 +2975,6 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) ARRAY_SIZE(hwcaps_pp2cmd)); } -/* - * Serial Flash Discoverable Parameters (SFDP) parsing. - */ - -/** - * spi_nor_read_raw() - raw read of serial flash memory. read_opcode, - * addr_width and read_dummy members of the struct spi_nor - * should be previously - * set. - * @nor: pointer to a 'struct spi_nor' - * @addr: offset in the serial flash memory - * @len: number of bytes to read - * @buf: buffer where the data is copied into (dma-safe memory) - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf) -{ - ssize_t ret; - - while (len) { - ret = spi_nor_read_data(nor, addr, len, buf); - if (ret < 0) - return ret; - if (!ret || ret > len) - return -EIO; - - buf += ret; - addr += ret; - len -= ret; - } - return 0; -} - -/** - * spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters. - * @nor: pointer to a 'struct spi_nor' - * @addr: offset in the SFDP area to start reading data from - * @len: number of bytes to read - * @buf: buffer where the SFDP data are copied into (dma-safe memory) - * - * Whatever the actual numbers of bytes for address and dummy cycles are - * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always - * followed by a 3-byte address and 8 dummy clock cycles. - * - * Return: 0 on success, -errno otherwise. - */ -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 = spi_nor_read_raw(nor, addr, len, buf); - - nor->read_opcode = read_opcode; - nor->addr_width = addr_width; - nor->read_dummy = read_dummy; - - return ret; -} - /** * spi_nor_spimem_check_op - check if the operation is supported * by controller @@ -3279,153 +3095,14 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) } } -/** - * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters. - * @nor: pointer to a 'struct spi_nor' - * @addr: offset in the SFDP area to start reading data from - * @len: number of bytes to read - * @buf: buffer where the SFDP data are copied into - * - * Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not - * guaranteed to be dma-safe. - * - * Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp() - * otherwise. - */ -static int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr, - size_t len, void *buf) -{ - void *dma_safe_buf; - int ret; - - dma_safe_buf = kmalloc(len, GFP_KERNEL); - if (!dma_safe_buf) - return -ENOMEM; - - ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf); - memcpy(buf, dma_safe_buf, len); - kfree(dma_safe_buf); - - return ret; -} - -/* Fast Read settings. */ - -static void -spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read, - u16 half, - enum spi_nor_protocol proto) -{ - read->num_mode_clocks = (half >> 5) & 0x07; - read->num_wait_states = (half >> 0) & 0x1f; - read->opcode = (half >> 8) & 0xff; - read->proto = proto; -} - -struct sfdp_bfpt_read { - /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */ - u32 hwcaps; - - /* - * The bit in BFPT DWORD tells us - * whether the Fast Read x-y-z command is supported. - */ - u32 supported_dword; - u32 supported_bit; - - /* - * The half-word at offset in BFPT DWORD - * encodes the op code, the number of mode clocks and the number of wait - * states to be used by Fast Read x-y-z command. - */ - u32 settings_dword; - u32 settings_shift; - - /* The SPI protocol for this Fast Read x-y-z command. */ - enum spi_nor_protocol proto; -}; - -static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = { - /* Fast Read 1-1-2 */ - { - SNOR_HWCAPS_READ_1_1_2, - BFPT_DWORD(1), BIT(16), /* Supported bit */ - BFPT_DWORD(4), 0, /* Settings */ - SNOR_PROTO_1_1_2, - }, - - /* Fast Read 1-2-2 */ - { - SNOR_HWCAPS_READ_1_2_2, - BFPT_DWORD(1), BIT(20), /* Supported bit */ - BFPT_DWORD(4), 16, /* Settings */ - SNOR_PROTO_1_2_2, - }, - - /* Fast Read 2-2-2 */ - { - SNOR_HWCAPS_READ_2_2_2, - BFPT_DWORD(5), BIT(0), /* Supported bit */ - BFPT_DWORD(6), 16, /* Settings */ - SNOR_PROTO_2_2_2, - }, - - /* Fast Read 1-1-4 */ - { - SNOR_HWCAPS_READ_1_1_4, - BFPT_DWORD(1), BIT(22), /* Supported bit */ - BFPT_DWORD(3), 16, /* Settings */ - SNOR_PROTO_1_1_4, - }, - - /* Fast Read 1-4-4 */ - { - SNOR_HWCAPS_READ_1_4_4, - BFPT_DWORD(1), BIT(21), /* Supported bit */ - BFPT_DWORD(3), 0, /* Settings */ - SNOR_PROTO_1_4_4, - }, - - /* Fast Read 4-4-4 */ - { - SNOR_HWCAPS_READ_4_4_4, - BFPT_DWORD(5), BIT(4), /* Supported bit */ - BFPT_DWORD(7), 16, /* Settings */ - SNOR_PROTO_4_4_4, - }, -}; - -struct sfdp_bfpt_erase { - /* - * The half-word at offset in DWORD encodes the - * op code and erase sector size to be used by Sector Erase commands. - */ - u32 dword; - u32 shift; -}; - -static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = { - /* Erase Type 1 in DWORD8 bits[15:0] */ - {BFPT_DWORD(8), 0}, - - /* Erase Type 2 in DWORD8 bits[31:16] */ - {BFPT_DWORD(8), 16}, - - /* Erase Type 3 in DWORD9 bits[15:0] */ - {BFPT_DWORD(9), 0}, - - /* Erase Type 4 in DWORD9 bits[31:16] */ - {BFPT_DWORD(9), 16}, -}; - /** * spi_nor_set_erase_type() - set a SPI NOR erase type * @erase: pointer to a structure that describes a SPI NOR erase type * @size: the size of the sector/block erased by the erase type * @opcode: the SPI command op code to erase the sector/block */ -static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase, - u32 size, u8 opcode) +void spi_nor_set_erase_type(struct spi_nor_erase_type *erase, u32 size, + u8 opcode) { erase->size = size; erase->opcode = opcode; @@ -3434,104 +3111,6 @@ static void spi_nor_set_erase_type(struct spi_nor_erase_type *erase, erase->size_mask = (1 << erase->size_shift) - 1; } -/** - * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT - * @erase: pointer to a structure that describes a SPI NOR erase type - * @size: the size of the sector/block erased by the erase type - * @opcode: the SPI command op code to erase the sector/block - * @i: erase type index as sorted in the Basic Flash Parameter Table - * - * The supported Erase Types will be sorted at init in ascending order, with - * the smallest Erase Type size being the first member in the erase_type array - * of the spi_nor_erase_map structure. Save the Erase Type index as sorted in - * the Basic Flash Parameter Table since it will be used later on to - * synchronize with the supported Erase Types defined in SFDP optional tables. - */ -static void -spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase, - u32 size, u8 opcode, u8 i) -{ - erase->idx = i; - spi_nor_set_erase_type(erase, size, opcode); -} - -/** - * spi_nor_map_cmp_erase_type() - compare the map's erase types by size - * @l: member in the left half of the map's erase_type array - * @r: member in the right half of the map's erase_type array - * - * Comparison function used in the sort() call to sort in ascending order the - * map's erase types, the smallest erase type size being the first member in the - * sorted erase_type array. - * - * Return: the result of @l->size - @r->size - */ -static int spi_nor_map_cmp_erase_type(const void *l, const void *r) -{ - const struct spi_nor_erase_type *left = l, *right = r; - - return left->size - right->size; -} - -/** - * spi_nor_sort_erase_mask() - sort erase mask - * @map: the erase map of the SPI NOR - * @erase_mask: the erase type mask to be sorted - * - * Replicate the sort done for the map's erase types in BFPT: sort the erase - * mask in ascending order with the smallest erase type size starting from - * BIT(0) in the sorted erase mask. - * - * Return: sorted erase mask. - */ -static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask) -{ - struct spi_nor_erase_type *erase_type = map->erase_type; - int i; - u8 sorted_erase_mask = 0; - - if (!erase_mask) - return 0; - - /* Replicate the sort done for the map's erase types. */ - for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) - if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx)) - sorted_erase_mask |= BIT(i); - - return sorted_erase_mask; -} - -/** - * spi_nor_regions_sort_erase_types() - sort erase types in each region - * @map: the erase map of the SPI NOR - * - * Function assumes that the erase types defined in the erase map are already - * sorted in ascending order, with the smallest erase type size being the first - * member in the erase_type array. It replicates the sort done for the map's - * erase types. Each region's erase bitmask will indicate which erase types are - * supported from the sorted erase types defined in the erase map. - * Sort the all region's erase type at init in order to speed up the process of - * finding the best erase command at runtime. - */ -static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) -{ - struct spi_nor_erase_region *region = map->regions; - u8 region_erase_mask, sorted_erase_mask; - - while (region) { - region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK; - - sorted_erase_mask = spi_nor_sort_erase_mask(map, - region_erase_mask); - - /* Overwrite erase mask. */ - region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) | - sorted_erase_mask; - - region = spi_nor_region_next(region); - } -} - /** * spi_nor_init_uniform_erase_map() - Initialize uniform erase map * @map: the erase map of the SPI NOR @@ -3539,8 +3118,8 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) * flash memory * @flash_size: the spi nor flash memory size */ -static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, - u8 erase_mask, u64 flash_size) +void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, + u8 erase_mask, u64 flash_size) { /* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */ map->uniform_region.offset = (erase_mask & SNOR_ERASE_TYPE_MASK) | @@ -3550,11 +3129,10 @@ static void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, map->uniform_erase_type = erase_mask; } -static int -spi_nor_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt, - struct spi_nor_flash_parameter *params) +int spi_nor_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) { if (nor->info->fixups && nor->info->fixups->post_bfpt) return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt, @@ -3563,861 +3141,6 @@ spi_nor_post_bfpt_fixups(struct spi_nor *nor, return 0; } -/** - * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. - * @nor: pointer to a 'struct spi_nor' - * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing - * the Basic Flash Parameter Table length and version - * @params: pointer to the 'struct spi_nor_flash_parameter' to be - * filled - * - * The Basic Flash Parameter Table is the main and only mandatory table as - * defined by the SFDP (JESD216) specification. - * It provides us with the total size (memory density) of the data array and - * the number of address bytes for Fast Read, Page Program and Sector Erase - * commands. - * For Fast READ commands, it also gives the number of mode clock cycles and - * wait states (regrouped in the number of dummy clock cycles) for each - * supported instruction op code. - * For Page Program, the page size is now available since JESD216 rev A, however - * the supported instruction op codes are still not provided. - * For Sector Erase commands, this table stores the supported instruction op - * codes and the associated sector sizes. - * Finally, the Quad Enable Requirements (QER) are also available since JESD216 - * rev A. The QER bits encode the manufacturer dependent procedure to be - * executed to set the Quad Enable (QE) bit in some internal register of the - * Quad SPI memory. Indeed the QE bit, when it exists, must be set before - * sending any Quad SPI command to the memory. Actually, setting the QE bit - * tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2 - * and IO3 hence enabling 4 (Quad) I/O lines. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_parse_bfpt(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - struct spi_nor_flash_parameter *params) -{ - struct spi_nor_erase_map *map = ¶ms->erase_map; - struct spi_nor_erase_type *erase_type = map->erase_type; - struct sfdp_bfpt bfpt; - size_t len; - int i, cmd, err; - u32 addr; - u16 half; - u8 erase_mask; - - /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */ - if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) - return -EINVAL; - - /* Read the Basic Flash Parameter Table. */ - len = min_t(size_t, sizeof(bfpt), - bfpt_header->length * sizeof(u32)); - addr = SFDP_PARAM_HEADER_PTP(bfpt_header); - memset(&bfpt, 0, sizeof(bfpt)); - err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt); - if (err < 0) - return err; - - /* Fix endianness of the BFPT DWORDs. */ - le32_to_cpu_array(bfpt.dwords, BFPT_DWORD_MAX); - - /* Number of address bytes. */ - switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { - case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: - nor->addr_width = 3; - break; - - case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: - nor->addr_width = 4; - break; - - default: - break; - } - - /* Flash Memory Density (in bits). */ - params->size = bfpt.dwords[BFPT_DWORD(2)]; - if (params->size & BIT(31)) { - params->size &= ~BIT(31); - - /* - * Prevent overflows on params->size. Anyway, a NOR of 2^64 - * bits is unlikely to exist so this error probably means - * the BFPT we are reading is corrupted/wrong. - */ - if (params->size > 63) - return -EINVAL; - - params->size = 1ULL << params->size; - } else { - params->size++; - } - params->size >>= 3; /* Convert to bytes. */ - - /* Fast Read settings. */ - for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) { - const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i]; - struct spi_nor_read_command *read; - - if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) { - params->hwcaps.mask &= ~rd->hwcaps; - continue; - } - - params->hwcaps.mask |= rd->hwcaps; - cmd = spi_nor_hwcaps_read2cmd(rd->hwcaps); - read = ¶ms->reads[cmd]; - half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift; - spi_nor_set_read_settings_from_bfpt(read, half, rd->proto); - } - - /* - * Sector Erase settings. Reinitialize the uniform erase map using the - * Erase Types defined in the bfpt table. - */ - erase_mask = 0; - memset(¶ms->erase_map, 0, sizeof(params->erase_map)); - for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) { - const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i]; - u32 erasesize; - u8 opcode; - - half = bfpt.dwords[er->dword] >> er->shift; - erasesize = half & 0xff; - - /* erasesize == 0 means this Erase Type is not supported. */ - if (!erasesize) - continue; - - erasesize = 1U << erasesize; - opcode = (half >> 8) & 0xff; - erase_mask |= BIT(i); - spi_nor_set_erase_settings_from_bfpt(&erase_type[i], erasesize, - opcode, i); - } - spi_nor_init_uniform_erase_map(map, erase_mask, params->size); - /* - * Sort all the map's Erase Types in ascending order with the smallest - * erase size being the first member in the erase_type array. - */ - sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]), - spi_nor_map_cmp_erase_type, NULL); - /* - * Sort the erase types in the uniform region in order to update the - * uniform_erase_type bitmask. The bitmask will be used later on when - * selecting the uniform erase. - */ - spi_nor_regions_sort_erase_types(map); - map->uniform_erase_type = map->uniform_region.offset & - SNOR_ERASE_TYPE_MASK; - - /* Stop here if not JESD216 rev A or later. */ - if (bfpt_header->length < BFPT_DWORD_MAX) - return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, - params); - - /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ - params->page_size = bfpt.dwords[BFPT_DWORD(11)]; - params->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK; - params->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; - params->page_size = 1U << params->page_size; - - /* Quad Enable Requirements. */ - switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) { - case BFPT_DWORD15_QER_NONE: - params->quad_enable = NULL; - break; - - case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: - /* - * Writing only one byte to the Status Register has the - * side-effect of clearing Status Register 2. - */ - case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: - /* - * Read Configuration Register (35h) instruction is not - * supported. - */ - nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR; - params->quad_enable = spi_nor_sr2_bit1_quad_enable; - break; - - case BFPT_DWORD15_QER_SR1_BIT6: - nor->flags &= ~SNOR_F_HAS_16BIT_SR; - params->quad_enable = spi_nor_sr1_bit6_quad_enable; - break; - - case BFPT_DWORD15_QER_SR2_BIT7: - nor->flags &= ~SNOR_F_HAS_16BIT_SR; - params->quad_enable = spi_nor_sr2_bit7_quad_enable; - break; - - case BFPT_DWORD15_QER_SR2_BIT1: - /* - * JESD216 rev B or later does not specify if writing only one - * byte to the Status Register clears or not the Status - * Register 2, so let's be cautious and keep the default - * assumption of a 16-bit Write Status (01h) command. - */ - nor->flags |= SNOR_F_HAS_16BIT_SR; - - params->quad_enable = spi_nor_sr2_bit1_quad_enable; - break; - - default: - return -EINVAL; - } - - return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); -} - -#define SMPT_CMD_ADDRESS_LEN_MASK GENMASK(23, 22) -#define SMPT_CMD_ADDRESS_LEN_0 (0x0UL << 22) -#define SMPT_CMD_ADDRESS_LEN_3 (0x1UL << 22) -#define SMPT_CMD_ADDRESS_LEN_4 (0x2UL << 22) -#define SMPT_CMD_ADDRESS_LEN_USE_CURRENT (0x3UL << 22) - -#define SMPT_CMD_READ_DUMMY_MASK GENMASK(19, 16) -#define SMPT_CMD_READ_DUMMY_SHIFT 16 -#define SMPT_CMD_READ_DUMMY(_cmd) \ - (((_cmd) & SMPT_CMD_READ_DUMMY_MASK) >> SMPT_CMD_READ_DUMMY_SHIFT) -#define SMPT_CMD_READ_DUMMY_IS_VARIABLE 0xfUL - -#define SMPT_CMD_READ_DATA_MASK GENMASK(31, 24) -#define SMPT_CMD_READ_DATA_SHIFT 24 -#define SMPT_CMD_READ_DATA(_cmd) \ - (((_cmd) & SMPT_CMD_READ_DATA_MASK) >> SMPT_CMD_READ_DATA_SHIFT) - -#define SMPT_CMD_OPCODE_MASK GENMASK(15, 8) -#define SMPT_CMD_OPCODE_SHIFT 8 -#define SMPT_CMD_OPCODE(_cmd) \ - (((_cmd) & SMPT_CMD_OPCODE_MASK) >> SMPT_CMD_OPCODE_SHIFT) - -#define SMPT_MAP_REGION_COUNT_MASK GENMASK(23, 16) -#define SMPT_MAP_REGION_COUNT_SHIFT 16 -#define SMPT_MAP_REGION_COUNT(_header) \ - ((((_header) & SMPT_MAP_REGION_COUNT_MASK) >> \ - SMPT_MAP_REGION_COUNT_SHIFT) + 1) - -#define SMPT_MAP_ID_MASK GENMASK(15, 8) -#define SMPT_MAP_ID_SHIFT 8 -#define SMPT_MAP_ID(_header) \ - (((_header) & SMPT_MAP_ID_MASK) >> SMPT_MAP_ID_SHIFT) - -#define SMPT_MAP_REGION_SIZE_MASK GENMASK(31, 8) -#define SMPT_MAP_REGION_SIZE_SHIFT 8 -#define SMPT_MAP_REGION_SIZE(_region) \ - (((((_region) & SMPT_MAP_REGION_SIZE_MASK) >> \ - SMPT_MAP_REGION_SIZE_SHIFT) + 1) * 256) - -#define SMPT_MAP_REGION_ERASE_TYPE_MASK GENMASK(3, 0) -#define SMPT_MAP_REGION_ERASE_TYPE(_region) \ - ((_region) & SMPT_MAP_REGION_ERASE_TYPE_MASK) - -#define SMPT_DESC_TYPE_MAP BIT(1) -#define SMPT_DESC_END BIT(0) - -/** - * spi_nor_smpt_addr_width() - return the address width used in the - * configuration detection command. - * @nor: pointer to a 'struct spi_nor' - * @settings: configuration detection command descriptor, dword1 - */ -static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) -{ - switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) { - case SMPT_CMD_ADDRESS_LEN_0: - return 0; - case SMPT_CMD_ADDRESS_LEN_3: - return 3; - case SMPT_CMD_ADDRESS_LEN_4: - return 4; - case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: - /* fall through */ - default: - return nor->addr_width; - } -} - -/** - * spi_nor_smpt_read_dummy() - return the configuration detection command read - * latency, in clock cycles. - * @nor: pointer to a 'struct spi_nor' - * @settings: configuration detection command descriptor, dword1 - * - * Return: the number of dummy cycles for an SMPT read - */ -static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings) -{ - u8 read_dummy = SMPT_CMD_READ_DUMMY(settings); - - if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) - return nor->read_dummy; - return read_dummy; -} - -/** - * spi_nor_get_map_in_use() - get the configuration map in use - * @nor: pointer to a 'struct spi_nor' - * @smpt: pointer to the sector map parameter table - * @smpt_len: sector map parameter table length - * - * Return: pointer to the map in use, ERR_PTR(-errno) otherwise. - */ -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, - u8 smpt_len) -{ - const u32 *ret; - u8 *buf; - u32 addr; - int err; - u8 i; - u8 addr_width, read_opcode, read_dummy; - u8 read_data_mask, map_id; - - /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ - buf = kmalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - return ERR_PTR(-ENOMEM); - - addr_width = nor->addr_width; - read_dummy = nor->read_dummy; - read_opcode = nor->read_opcode; - - map_id = 0; - /* Determine if there are any optional Detection Command Descriptors */ - for (i = 0; i < smpt_len; i += 2) { - if (smpt[i] & SMPT_DESC_TYPE_MAP) - break; - - read_data_mask = SMPT_CMD_READ_DATA(smpt[i]); - nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]); - nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]); - nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]); - addr = smpt[i + 1]; - - err = spi_nor_read_raw(nor, addr, 1, buf); - if (err) { - ret = ERR_PTR(err); - goto out; - } - - /* - * Build an index value that is used to select the Sector Map - * Configuration that is currently in use. - */ - map_id = map_id << 1 | !!(*buf & read_data_mask); - } - - /* - * If command descriptors are provided, they always precede map - * descriptors in the table. There is no need to start the iteration - * over smpt array all over again. - * - * Find the matching configuration map. - */ - ret = ERR_PTR(-EINVAL); - while (i < smpt_len) { - if (SMPT_MAP_ID(smpt[i]) == map_id) { - ret = smpt + i; - break; - } - - /* - * If there are no more configuration map descriptors and no - * configuration ID matched the configuration identifier, the - * sector address map is unknown. - */ - if (smpt[i] & SMPT_DESC_END) - break; - - /* increment the table index to the next map */ - i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1; - } - - /* fall through */ -out: - kfree(buf); - nor->addr_width = addr_width; - nor->read_dummy = read_dummy; - nor->read_opcode = read_opcode; - return ret; -} - -/** - * spi_nor_region_check_overlay() - set overlay bit when the region is overlaid - * @region: pointer to a structure that describes a SPI NOR erase region - * @erase: pointer to a structure that describes a SPI NOR erase type - * @erase_type: erase type bitmask - */ -static void -spi_nor_region_check_overlay(struct spi_nor_erase_region *region, - const struct spi_nor_erase_type *erase, - const u8 erase_type) -{ - int i; - - for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { - if (!(erase_type & BIT(i))) - continue; - if (region->size & erase[i].size_mask) { - spi_nor_region_mark_overlay(region); - return; - } - } -} - -/** - * spi_nor_init_non_uniform_erase_map() - initialize the non-uniform erase map - * @nor: pointer to a 'struct spi_nor' - * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' that is - * used for storing SFDP parsed data - * @smpt: pointer to the sector map parameter table - * - * Return: 0 on success, -errno otherwise. - */ -static int -spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, - struct spi_nor_flash_parameter *params, - const u32 *smpt) -{ - struct spi_nor_erase_map *map = ¶ms->erase_map; - struct spi_nor_erase_type *erase = map->erase_type; - struct spi_nor_erase_region *region; - u64 offset; - u32 region_count; - int i, j; - u8 uniform_erase_type, save_uniform_erase_type; - u8 erase_type, regions_erase_type; - - region_count = SMPT_MAP_REGION_COUNT(*smpt); - /* - * The regions will be freed when the driver detaches from the - * device. - */ - region = devm_kcalloc(nor->dev, region_count, sizeof(*region), - GFP_KERNEL); - if (!region) - return -ENOMEM; - map->regions = region; - - uniform_erase_type = 0xff; - regions_erase_type = 0; - offset = 0; - /* Populate regions. */ - for (i = 0; i < region_count; i++) { - j = i + 1; /* index for the region dword */ - region[i].size = SMPT_MAP_REGION_SIZE(smpt[j]); - erase_type = SMPT_MAP_REGION_ERASE_TYPE(smpt[j]); - region[i].offset = offset | erase_type; - - spi_nor_region_check_overlay(®ion[i], erase, erase_type); - - /* - * Save the erase types that are supported in all regions and - * can erase the entire flash memory. - */ - uniform_erase_type &= erase_type; - - /* - * regions_erase_type mask will indicate all the erase types - * supported in this configuration map. - */ - regions_erase_type |= erase_type; - - offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) + - region[i].size; - } - - save_uniform_erase_type = map->uniform_erase_type; - map->uniform_erase_type = spi_nor_sort_erase_mask(map, - uniform_erase_type); - - if (!regions_erase_type) { - /* - * Roll back to the previous uniform_erase_type mask, SMPT is - * broken. - */ - map->uniform_erase_type = save_uniform_erase_type; - return -EINVAL; - } - - /* - * BFPT advertises all the erase types supported by all the possible - * map configurations. Mask out the erase types that are not supported - * by the current map configuration. - */ - for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) - if (!(regions_erase_type & BIT(erase[i].idx))) - spi_nor_set_erase_type(&erase[i], 0, 0xFF); - - spi_nor_region_mark_end(®ion[i - 1]); - - return 0; -} - -/** - * spi_nor_parse_smpt() - parse Sector Map Parameter Table - * @nor: pointer to a 'struct spi_nor' - * @smpt_header: sector map parameter table header - * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' - * that is used for storing SFDP parsed data - * - * This table is optional, but when available, we parse it to identify the - * location and size of sectors within the main data array of the flash memory - * device and to identify which Erase Types are supported by each sector. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_parse_smpt(struct spi_nor *nor, - const struct sfdp_parameter_header *smpt_header, - struct spi_nor_flash_parameter *params) -{ - const u32 *sector_map; - u32 *smpt; - size_t len; - u32 addr; - int ret; - - /* Read the Sector Map Parameter Table. */ - len = smpt_header->length * sizeof(*smpt); - smpt = kmalloc(len, GFP_KERNEL); - if (!smpt) - return -ENOMEM; - - addr = SFDP_PARAM_HEADER_PTP(smpt_header); - ret = spi_nor_read_sfdp(nor, addr, len, smpt); - if (ret) - goto out; - - /* Fix endianness of the SMPT DWORDs. */ - le32_to_cpu_array(smpt, smpt_header->length); - - sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length); - if (IS_ERR(sector_map)) { - ret = PTR_ERR(sector_map); - goto out; - } - - ret = spi_nor_init_non_uniform_erase_map(nor, params, sector_map); - if (ret) - goto out; - - spi_nor_regions_sort_erase_types(¶ms->erase_map); - /* fall through */ -out: - kfree(smpt); - return ret; -} - -#define SFDP_4BAIT_DWORD_MAX 2 - -struct sfdp_4bait { - /* The hardware capability. */ - u32 hwcaps; - - /* - * The bit in DWORD1 of the 4BAIT tells us whether - * the associated 4-byte address op code is supported. - */ - u32 supported_bit; -}; - -/** - * spi_nor_parse_4bait() - parse the 4-Byte Address Instruction Table - * @nor: pointer to a 'struct spi_nor'. - * @param_header: pointer to the 'struct sfdp_parameter_header' describing - * the 4-Byte Address Instruction Table length and version. - * @params: pointer to the 'struct spi_nor_flash_parameter' to be. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_parse_4bait(struct spi_nor *nor, - const struct sfdp_parameter_header *param_header, - struct spi_nor_flash_parameter *params) -{ - static const struct sfdp_4bait reads[] = { - { SNOR_HWCAPS_READ, BIT(0) }, - { SNOR_HWCAPS_READ_FAST, BIT(1) }, - { SNOR_HWCAPS_READ_1_1_2, BIT(2) }, - { SNOR_HWCAPS_READ_1_2_2, BIT(3) }, - { SNOR_HWCAPS_READ_1_1_4, BIT(4) }, - { SNOR_HWCAPS_READ_1_4_4, BIT(5) }, - { SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) }, - { SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) }, - { SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) }, - }; - static const struct sfdp_4bait programs[] = { - { SNOR_HWCAPS_PP, BIT(6) }, - { SNOR_HWCAPS_PP_1_1_4, BIT(7) }, - { SNOR_HWCAPS_PP_1_4_4, BIT(8) }, - }; - static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = { - { 0u /* not used */, BIT(9) }, - { 0u /* not used */, BIT(10) }, - { 0u /* not used */, BIT(11) }, - { 0u /* not used */, BIT(12) }, - }; - struct spi_nor_pp_command *params_pp = params->page_programs; - struct spi_nor_erase_map *map = ¶ms->erase_map; - struct spi_nor_erase_type *erase_type = map->erase_type; - u32 *dwords; - size_t len; - u32 addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask; - int i, ret; - - if (param_header->major != SFDP_JESD216_MAJOR || - param_header->length < SFDP_4BAIT_DWORD_MAX) - return -EINVAL; - - /* Read the 4-byte Address Instruction Table. */ - len = sizeof(*dwords) * SFDP_4BAIT_DWORD_MAX; - - /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ - dwords = kmalloc(len, GFP_KERNEL); - if (!dwords) - return -ENOMEM; - - addr = SFDP_PARAM_HEADER_PTP(param_header); - ret = spi_nor_read_sfdp(nor, addr, len, dwords); - if (ret) - goto out; - - /* Fix endianness of the 4BAIT DWORDs. */ - le32_to_cpu_array(dwords, SFDP_4BAIT_DWORD_MAX); - - /* - * Compute the subset of (Fast) Read commands for which the 4-byte - * version is supported. - */ - discard_hwcaps = 0; - read_hwcaps = 0; - for (i = 0; i < ARRAY_SIZE(reads); i++) { - const struct sfdp_4bait *read = &reads[i]; - - discard_hwcaps |= read->hwcaps; - if ((params->hwcaps.mask & read->hwcaps) && - (dwords[0] & read->supported_bit)) - read_hwcaps |= read->hwcaps; - } - - /* - * Compute the subset of Page Program commands for which the 4-byte - * version is supported. - */ - pp_hwcaps = 0; - for (i = 0; i < ARRAY_SIZE(programs); i++) { - const struct sfdp_4bait *program = &programs[i]; - - /* - * The 4 Byte Address Instruction (Optional) Table is the only - * SFDP table that indicates support for Page Program Commands. - * Bypass the params->hwcaps.mask and consider 4BAIT the biggest - * authority for specifying Page Program support. - */ - discard_hwcaps |= program->hwcaps; - if (dwords[0] & program->supported_bit) - pp_hwcaps |= program->hwcaps; - } - - /* - * Compute the subset of Sector Erase commands for which the 4-byte - * version is supported. - */ - erase_mask = 0; - for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { - const struct sfdp_4bait *erase = &erases[i]; - - if (dwords[0] & erase->supported_bit) - erase_mask |= BIT(i); - } - - /* Replicate the sort done for the map's erase types in BFPT. */ - erase_mask = spi_nor_sort_erase_mask(map, erase_mask); - - /* - * 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 (!read_hwcaps || !pp_hwcaps || !erase_mask) - goto out; - - /* - * Discard all operations from the 4-byte instruction set which are - * not supported by this memory. - */ - params->hwcaps.mask &= ~discard_hwcaps; - params->hwcaps.mask |= (read_hwcaps | pp_hwcaps); - - /* Use the 4-byte address instruction set. */ - for (i = 0; i < SNOR_CMD_READ_MAX; i++) { - struct spi_nor_read_command *read_cmd = ¶ms->reads[i]; - - read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode); - } - - /* 4BAIT is the only SFDP table that indicates page program support. */ - if (pp_hwcaps & SNOR_HWCAPS_PP) - spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP], - SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); - if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4) - spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_4], - SPINOR_OP_PP_1_1_4_4B, - SNOR_PROTO_1_1_4); - if (pp_hwcaps & SNOR_HWCAPS_PP_1_4_4) - spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_4_4], - SPINOR_OP_PP_1_4_4_4B, - SNOR_PROTO_1_4_4); - - for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { - if (erase_mask & BIT(i)) - erase_type[i].opcode = (dwords[1] >> - erase_type[i].idx * 8) & 0xFF; - else - spi_nor_set_erase_type(&erase_type[i], 0u, 0xFF); - } - - /* - * We set SNOR_F_HAS_4BAIT in order to skip spi_nor_set_4byte_opcodes() - * later because we already did the conversion to 4byte opcodes. Also, - * this latest function implements a legacy quirk for the erase size of - * Spansion memory. However this quirk is no longer needed with new - * SFDP compliant memories. - */ - nor->addr_width = 4; - nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; - - /* fall through */ -out: - kfree(dwords); - return ret; -} - -/** - * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. - * @nor: pointer to a 'struct spi_nor' - * @params: pointer to the 'struct spi_nor_flash_parameter' to be - * filled - * - * The Serial Flash Discoverable Parameters are described by the JEDEC JESD216 - * specification. This is a standard which tends to supported by almost all - * (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at - * runtime the main parameters needed to perform basic SPI flash operations such - * as Fast Read, Page Program or Sector Erase commands. - * - * Return: 0 on success, -errno otherwise. - */ -static int spi_nor_parse_sfdp(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) -{ - const struct sfdp_parameter_header *param_header, *bfpt_header; - struct sfdp_parameter_header *param_headers = NULL; - struct sfdp_header header; - struct device *dev = nor->dev; - size_t psize; - int i, err; - - /* Get the SFDP header. */ - err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header); - if (err < 0) - return err; - - /* Check the SFDP header version. */ - if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || - header.major != SFDP_JESD216_MAJOR) - 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 then read all parameter headers with a single - * Read SFDP command. These parameter headers will actually be parsed - * twice: a first time to get the latest revision of the basic flash - * parameter table, then a second time to handle the supported optional - * tables. - * Hence we read the parameter headers once for all to reduce the - * processing time. Also we use kmalloc() instead of devm_kmalloc() - * because we don't need to keep these parameter headers: the allocated - * memory is always released with kfree() before exiting this function. - */ - if (header.nph) { - psize = header.nph * sizeof(*param_headers); - - param_headers = kmalloc(psize, GFP_KERNEL); - if (!param_headers) - return -ENOMEM; - - err = spi_nor_read_sfdp(nor, sizeof(header), - psize, param_headers); - if (err < 0) { - dev_dbg(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; - - /* Parse optional parameter tables. */ - for (i = 0; i < header.nph; i++) { - param_header = ¶m_headers[i]; - - switch (SFDP_PARAM_HEADER_ID(param_header)) { - case SFDP_SECTOR_MAP_ID: - err = spi_nor_parse_smpt(nor, param_header, params); - break; - - case SFDP_4BAIT_ID: - err = spi_nor_parse_4bait(nor, param_header, params); - break; - - default: - break; - } - - if (err) { - dev_warn(dev, "Failed to parse optional parameter table: %04x\n", - SFDP_PARAM_HEADER_ID(param_header)); - /* - * Let's not drop all information we extracted so far - * if optional table parsers fail. In case of failing, - * each optional parser is responsible to roll back to - * the previously known spi_nor data. - */ - err = 0; - } - } - -exit: - kfree(param_headers); - return err; -} - static int spi_nor_select_read(struct spi_nor *nor, u32 shared_hwcaps) { diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h new file mode 100644 index 000000000000..e1256fe50d12 --- /dev/null +++ b/drivers/mtd/spi-nor/core.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#ifndef __LINUX_MTD_SPI_NOR_INTERNAL_H +#define __LINUX_MTD_SPI_NOR_INTERNAL_H + +#include "sfdp.h" + +int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); +int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); +int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); + +ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, + u8 *buf); + +int spi_nor_hwcaps_read2cmd(u32 hwcaps); +u8 spi_nor_convert_3to4_read(u8 opcode); +void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, + enum spi_nor_protocol proto); + +void spi_nor_set_erase_type(struct spi_nor_erase_type *erase, u32 size, + u8 opcode); +struct spi_nor_erase_region * +spi_nor_region_next(struct spi_nor_erase_region *region); +void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, + u8 erase_mask, u64 flash_size); + +int spi_nor_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params); + +#endif /* __LINUX_MTD_SPI_NOR_INTERNAL_H */ diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c new file mode 100644 index 000000000000..c162015d19b1 --- /dev/null +++ b/drivers/mtd/spi-nor/sfdp.c @@ -0,0 +1,1195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include +#include +#include + +#include "core.h" + +#define SFDP_PARAM_HEADER_ID(p) (((p)->id_msb << 8) | (p)->id_lsb) +#define SFDP_PARAM_HEADER_PTP(p) \ + (((p)->parameter_table_pointer[2] << 16) | \ + ((p)->parameter_table_pointer[1] << 8) | \ + ((p)->parameter_table_pointer[0] << 0)) + +#define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ +#define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ +#define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction 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; /* Ox50444653U <=> "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; +}; + +/* Fast Read settings. */ +struct sfdp_bfpt_read { + /* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */ + u32 hwcaps; + + /* + * The bit in BFPT DWORD tells us + * whether the Fast Read x-y-z command is supported. + */ + u32 supported_dword; + u32 supported_bit; + + /* + * The half-word at offset in BFPT DWORD + * encodes the op code, the number of mode clocks and the number of wait + * states to be used by Fast Read x-y-z command. + */ + u32 settings_dword; + u32 settings_shift; + + /* The SPI protocol for this Fast Read x-y-z command. */ + enum spi_nor_protocol proto; +}; + +struct sfdp_bfpt_erase { + /* + * The half-word at offset in DWORD encodes the + * op code and erase sector size to be used by Sector Erase commands. + */ + u32 dword; + u32 shift; +}; + +#define SMPT_CMD_ADDRESS_LEN_MASK GENMASK(23, 22) +#define SMPT_CMD_ADDRESS_LEN_0 (0x0UL << 22) +#define SMPT_CMD_ADDRESS_LEN_3 (0x1UL << 22) +#define SMPT_CMD_ADDRESS_LEN_4 (0x2UL << 22) +#define SMPT_CMD_ADDRESS_LEN_USE_CURRENT (0x3UL << 22) + +#define SMPT_CMD_READ_DUMMY_MASK GENMASK(19, 16) +#define SMPT_CMD_READ_DUMMY_SHIFT 16 +#define SMPT_CMD_READ_DUMMY(_cmd) \ + (((_cmd) & SMPT_CMD_READ_DUMMY_MASK) >> SMPT_CMD_READ_DUMMY_SHIFT) +#define SMPT_CMD_READ_DUMMY_IS_VARIABLE 0xfUL + +#define SMPT_CMD_READ_DATA_MASK GENMASK(31, 24) +#define SMPT_CMD_READ_DATA_SHIFT 24 +#define SMPT_CMD_READ_DATA(_cmd) \ + (((_cmd) & SMPT_CMD_READ_DATA_MASK) >> SMPT_CMD_READ_DATA_SHIFT) + +#define SMPT_CMD_OPCODE_MASK GENMASK(15, 8) +#define SMPT_CMD_OPCODE_SHIFT 8 +#define SMPT_CMD_OPCODE(_cmd) \ + (((_cmd) & SMPT_CMD_OPCODE_MASK) >> SMPT_CMD_OPCODE_SHIFT) + +#define SMPT_MAP_REGION_COUNT_MASK GENMASK(23, 16) +#define SMPT_MAP_REGION_COUNT_SHIFT 16 +#define SMPT_MAP_REGION_COUNT(_header) \ + ((((_header) & SMPT_MAP_REGION_COUNT_MASK) >> \ + SMPT_MAP_REGION_COUNT_SHIFT) + 1) + +#define SMPT_MAP_ID_MASK GENMASK(15, 8) +#define SMPT_MAP_ID_SHIFT 8 +#define SMPT_MAP_ID(_header) \ + (((_header) & SMPT_MAP_ID_MASK) >> SMPT_MAP_ID_SHIFT) + +#define SMPT_MAP_REGION_SIZE_MASK GENMASK(31, 8) +#define SMPT_MAP_REGION_SIZE_SHIFT 8 +#define SMPT_MAP_REGION_SIZE(_region) \ + (((((_region) & SMPT_MAP_REGION_SIZE_MASK) >> \ + SMPT_MAP_REGION_SIZE_SHIFT) + 1) * 256) + +#define SMPT_MAP_REGION_ERASE_TYPE_MASK GENMASK(3, 0) +#define SMPT_MAP_REGION_ERASE_TYPE(_region) \ + ((_region) & SMPT_MAP_REGION_ERASE_TYPE_MASK) + +#define SMPT_DESC_TYPE_MAP BIT(1) +#define SMPT_DESC_END BIT(0) + +#define SFDP_4BAIT_DWORD_MAX 2 + +struct sfdp_4bait { + /* The hardware capability. */ + u32 hwcaps; + + /* + * The bit in DWORD1 of the 4BAIT tells us whether + * the associated 4-byte address op code is supported. + */ + u32 supported_bit; +}; + +/** + * spi_nor_read_raw() - raw read of serial flash memory. read_opcode, + * addr_width and read_dummy members of the struct spi_nor + * should be previously + * set. + * @nor: pointer to a 'struct spi_nor' + * @addr: offset in the serial flash memory + * @len: number of bytes to read + * @buf: buffer where the data is copied into (dma-safe memory) + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf) +{ + ssize_t ret; + + while (len) { + ret = spi_nor_read_data(nor, addr, len, buf); + if (ret < 0) + return ret; + if (!ret || ret > len) + return -EIO; + + buf += ret; + addr += ret; + len -= ret; + } + return 0; +} + +/** + * spi_nor_read_sfdp() - read Serial Flash Discoverable Parameters. + * @nor: pointer to a 'struct spi_nor' + * @addr: offset in the SFDP area to start reading data from + * @len: number of bytes to read + * @buf: buffer where the SFDP data are copied into (dma-safe memory) + * + * Whatever the actual numbers of bytes for address and dummy cycles are + * for (Fast) Read commands, the Read SFDP (5Ah) instruction is always + * followed by a 3-byte address and 8 dummy clock cycles. + * + * Return: 0 on success, -errno otherwise. + */ +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 = spi_nor_read_raw(nor, addr, len, buf); + + nor->read_opcode = read_opcode; + nor->addr_width = addr_width; + nor->read_dummy = read_dummy; + + return ret; +} + +/** + * spi_nor_read_sfdp_dma_unsafe() - read Serial Flash Discoverable Parameters. + * @nor: pointer to a 'struct spi_nor' + * @addr: offset in the SFDP area to start reading data from + * @len: number of bytes to read + * @buf: buffer where the SFDP data are copied into + * + * Wrap spi_nor_read_sfdp() using a kmalloc'ed bounce buffer as @buf is now not + * guaranteed to be dma-safe. + * + * Return: -ENOMEM if kmalloc() fails, the return code of spi_nor_read_sfdp() + * otherwise. + */ +static int spi_nor_read_sfdp_dma_unsafe(struct spi_nor *nor, u32 addr, + size_t len, void *buf) +{ + void *dma_safe_buf; + int ret; + + dma_safe_buf = kmalloc(len, GFP_KERNEL); + if (!dma_safe_buf) + return -ENOMEM; + + ret = spi_nor_read_sfdp(nor, addr, len, dma_safe_buf); + memcpy(buf, dma_safe_buf, len); + kfree(dma_safe_buf); + + return ret; +} + +static void +spi_nor_set_read_settings_from_bfpt(struct spi_nor_read_command *read, + u16 half, + enum spi_nor_protocol proto) +{ + read->num_mode_clocks = (half >> 5) & 0x07; + read->num_wait_states = (half >> 0) & 0x1f; + read->opcode = (half >> 8) & 0xff; + read->proto = proto; +} + +static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = { + /* Fast Read 1-1-2 */ + { + SNOR_HWCAPS_READ_1_1_2, + BFPT_DWORD(1), BIT(16), /* Supported bit */ + BFPT_DWORD(4), 0, /* Settings */ + SNOR_PROTO_1_1_2, + }, + + /* Fast Read 1-2-2 */ + { + SNOR_HWCAPS_READ_1_2_2, + BFPT_DWORD(1), BIT(20), /* Supported bit */ + BFPT_DWORD(4), 16, /* Settings */ + SNOR_PROTO_1_2_2, + }, + + /* Fast Read 2-2-2 */ + { + SNOR_HWCAPS_READ_2_2_2, + BFPT_DWORD(5), BIT(0), /* Supported bit */ + BFPT_DWORD(6), 16, /* Settings */ + SNOR_PROTO_2_2_2, + }, + + /* Fast Read 1-1-4 */ + { + SNOR_HWCAPS_READ_1_1_4, + BFPT_DWORD(1), BIT(22), /* Supported bit */ + BFPT_DWORD(3), 16, /* Settings */ + SNOR_PROTO_1_1_4, + }, + + /* Fast Read 1-4-4 */ + { + SNOR_HWCAPS_READ_1_4_4, + BFPT_DWORD(1), BIT(21), /* Supported bit */ + BFPT_DWORD(3), 0, /* Settings */ + SNOR_PROTO_1_4_4, + }, + + /* Fast Read 4-4-4 */ + { + SNOR_HWCAPS_READ_4_4_4, + BFPT_DWORD(5), BIT(4), /* Supported bit */ + BFPT_DWORD(7), 16, /* Settings */ + SNOR_PROTO_4_4_4, + }, +}; + +static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = { + /* Erase Type 1 in DWORD8 bits[15:0] */ + {BFPT_DWORD(8), 0}, + + /* Erase Type 2 in DWORD8 bits[31:16] */ + {BFPT_DWORD(8), 16}, + + /* Erase Type 3 in DWORD9 bits[15:0] */ + {BFPT_DWORD(9), 0}, + + /* Erase Type 4 in DWORD9 bits[31:16] */ + {BFPT_DWORD(9), 16}, +}; + +/** + * spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT + * @erase: pointer to a structure that describes a SPI NOR erase type + * @size: the size of the sector/block erased by the erase type + * @opcode: the SPI command op code to erase the sector/block + * @i: erase type index as sorted in the Basic Flash Parameter Table + * + * The supported Erase Types will be sorted at init in ascending order, with + * the smallest Erase Type size being the first member in the erase_type array + * of the spi_nor_erase_map structure. Save the Erase Type index as sorted in + * the Basic Flash Parameter Table since it will be used later on to + * synchronize with the supported Erase Types defined in SFDP optional tables. + */ +static void +spi_nor_set_erase_settings_from_bfpt(struct spi_nor_erase_type *erase, + u32 size, u8 opcode, u8 i) +{ + erase->idx = i; + spi_nor_set_erase_type(erase, size, opcode); +} + +/** + * spi_nor_map_cmp_erase_type() - compare the map's erase types by size + * @l: member in the left half of the map's erase_type array + * @r: member in the right half of the map's erase_type array + * + * Comparison function used in the sort() call to sort in ascending order the + * map's erase types, the smallest erase type size being the first member in the + * sorted erase_type array. + * + * Return: the result of @l->size - @r->size + */ +static int spi_nor_map_cmp_erase_type(const void *l, const void *r) +{ + const struct spi_nor_erase_type *left = l, *right = r; + + return left->size - right->size; +} + +/** + * spi_nor_sort_erase_mask() - sort erase mask + * @map: the erase map of the SPI NOR + * @erase_mask: the erase type mask to be sorted + * + * Replicate the sort done for the map's erase types in BFPT: sort the erase + * mask in ascending order with the smallest erase type size starting from + * BIT(0) in the sorted erase mask. + * + * Return: sorted erase mask. + */ +static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask) +{ + struct spi_nor_erase_type *erase_type = map->erase_type; + int i; + u8 sorted_erase_mask = 0; + + if (!erase_mask) + return 0; + + /* Replicate the sort done for the map's erase types. */ + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) + if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx)) + sorted_erase_mask |= BIT(i); + + return sorted_erase_mask; +} + +/** + * spi_nor_regions_sort_erase_types() - sort erase types in each region + * @map: the erase map of the SPI NOR + * + * Function assumes that the erase types defined in the erase map are already + * sorted in ascending order, with the smallest erase type size being the first + * member in the erase_type array. It replicates the sort done for the map's + * erase types. Each region's erase bitmask will indicate which erase types are + * supported from the sorted erase types defined in the erase map. + * Sort the all region's erase type at init in order to speed up the process of + * finding the best erase command at runtime. + */ +static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) +{ + struct spi_nor_erase_region *region = map->regions; + u8 region_erase_mask, sorted_erase_mask; + + while (region) { + region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK; + + sorted_erase_mask = spi_nor_sort_erase_mask(map, + region_erase_mask); + + /* Overwrite erase mask. */ + region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) | + sorted_erase_mask; + + region = spi_nor_region_next(region); + } +} + +/** + * spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table. + * @nor: pointer to a 'struct spi_nor' + * @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing + * the Basic Flash Parameter Table length and version + * @params: pointer to the 'struct spi_nor_flash_parameter' to be + * filled + * + * The Basic Flash Parameter Table is the main and only mandatory table as + * defined by the SFDP (JESD216) specification. + * It provides us with the total size (memory density) of the data array and + * the number of address bytes for Fast Read, Page Program and Sector Erase + * commands. + * For Fast READ commands, it also gives the number of mode clock cycles and + * wait states (regrouped in the number of dummy clock cycles) for each + * supported instruction op code. + * For Page Program, the page size is now available since JESD216 rev A, however + * the supported instruction op codes are still not provided. + * For Sector Erase commands, this table stores the supported instruction op + * codes and the associated sector sizes. + * Finally, the Quad Enable Requirements (QER) are also available since JESD216 + * rev A. The QER bits encode the manufacturer dependent procedure to be + * executed to set the Quad Enable (QE) bit in some internal register of the + * Quad SPI memory. Indeed the QE bit, when it exists, must be set before + * sending any Quad SPI command to the memory. Actually, setting the QE bit + * tells the memory to reassign its WP# and HOLD#/RESET# pins to functions IO2 + * and IO3 hence enabling 4 (Quad) I/O lines. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_bfpt(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + struct spi_nor_flash_parameter *params) +{ + struct spi_nor_erase_map *map = ¶ms->erase_map; + struct spi_nor_erase_type *erase_type = map->erase_type; + struct sfdp_bfpt bfpt; + size_t len; + int i, cmd, err; + u32 addr; + u16 half; + u8 erase_mask; + + /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */ + if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) + return -EINVAL; + + /* Read the Basic Flash Parameter Table. */ + len = min_t(size_t, sizeof(bfpt), + bfpt_header->length * sizeof(u32)); + addr = SFDP_PARAM_HEADER_PTP(bfpt_header); + memset(&bfpt, 0, sizeof(bfpt)); + err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt); + if (err < 0) + return err; + + /* Fix endianness of the BFPT DWORDs. */ + le32_to_cpu_array(bfpt.dwords, BFPT_DWORD_MAX); + + /* Number of address bytes. */ + switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { + case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: + nor->addr_width = 3; + break; + + case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: + nor->addr_width = 4; + break; + + default: + break; + } + + /* Flash Memory Density (in bits). */ + params->size = bfpt.dwords[BFPT_DWORD(2)]; + if (params->size & BIT(31)) { + params->size &= ~BIT(31); + + /* + * Prevent overflows on params->size. Anyway, a NOR of 2^64 + * bits is unlikely to exist so this error probably means + * the BFPT we are reading is corrupted/wrong. + */ + if (params->size > 63) + return -EINVAL; + + params->size = 1ULL << params->size; + } else { + params->size++; + } + params->size >>= 3; /* Convert to bytes. */ + + /* Fast Read settings. */ + for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_reads); i++) { + const struct sfdp_bfpt_read *rd = &sfdp_bfpt_reads[i]; + struct spi_nor_read_command *read; + + if (!(bfpt.dwords[rd->supported_dword] & rd->supported_bit)) { + params->hwcaps.mask &= ~rd->hwcaps; + continue; + } + + params->hwcaps.mask |= rd->hwcaps; + cmd = spi_nor_hwcaps_read2cmd(rd->hwcaps); + read = ¶ms->reads[cmd]; + half = bfpt.dwords[rd->settings_dword] >> rd->settings_shift; + spi_nor_set_read_settings_from_bfpt(read, half, rd->proto); + } + + /* + * Sector Erase settings. Reinitialize the uniform erase map using the + * Erase Types defined in the bfpt table. + */ + erase_mask = 0; + memset(¶ms->erase_map, 0, sizeof(params->erase_map)); + for (i = 0; i < ARRAY_SIZE(sfdp_bfpt_erases); i++) { + const struct sfdp_bfpt_erase *er = &sfdp_bfpt_erases[i]; + u32 erasesize; + u8 opcode; + + half = bfpt.dwords[er->dword] >> er->shift; + erasesize = half & 0xff; + + /* erasesize == 0 means this Erase Type is not supported. */ + if (!erasesize) + continue; + + erasesize = 1U << erasesize; + opcode = (half >> 8) & 0xff; + erase_mask |= BIT(i); + spi_nor_set_erase_settings_from_bfpt(&erase_type[i], erasesize, + opcode, i); + } + spi_nor_init_uniform_erase_map(map, erase_mask, params->size); + /* + * Sort all the map's Erase Types in ascending order with the smallest + * erase size being the first member in the erase_type array. + */ + sort(erase_type, SNOR_ERASE_TYPE_MAX, sizeof(erase_type[0]), + spi_nor_map_cmp_erase_type, NULL); + /* + * Sort the erase types in the uniform region in order to update the + * uniform_erase_type bitmask. The bitmask will be used later on when + * selecting the uniform erase. + */ + spi_nor_regions_sort_erase_types(map); + map->uniform_erase_type = map->uniform_region.offset & + SNOR_ERASE_TYPE_MASK; + + /* Stop here if not JESD216 rev A or later. */ + if (bfpt_header->length < BFPT_DWORD_MAX) + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, + params); + + /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ + params->page_size = bfpt.dwords[BFPT_DWORD(11)]; + params->page_size &= BFPT_DWORD11_PAGE_SIZE_MASK; + params->page_size >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; + params->page_size = 1U << params->page_size; + + /* Quad Enable Requirements. */ + switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) { + case BFPT_DWORD15_QER_NONE: + params->quad_enable = NULL; + break; + + case BFPT_DWORD15_QER_SR2_BIT1_BUGGY: + /* + * Writing only one byte to the Status Register has the + * side-effect of clearing Status Register 2. + */ + case BFPT_DWORD15_QER_SR2_BIT1_NO_RD: + /* + * Read Configuration Register (35h) instruction is not + * supported. + */ + nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR; + params->quad_enable = spi_nor_sr2_bit1_quad_enable; + break; + + case BFPT_DWORD15_QER_SR1_BIT6: + nor->flags &= ~SNOR_F_HAS_16BIT_SR; + params->quad_enable = spi_nor_sr1_bit6_quad_enable; + break; + + case BFPT_DWORD15_QER_SR2_BIT7: + nor->flags &= ~SNOR_F_HAS_16BIT_SR; + params->quad_enable = spi_nor_sr2_bit7_quad_enable; + break; + + case BFPT_DWORD15_QER_SR2_BIT1: + /* + * JESD216 rev B or later does not specify if writing only one + * byte to the Status Register clears or not the Status + * Register 2, so let's be cautious and keep the default + * assumption of a 16-bit Write Status (01h) command. + */ + nor->flags |= SNOR_F_HAS_16BIT_SR; + + params->quad_enable = spi_nor_sr2_bit1_quad_enable; + break; + + default: + return -EINVAL; + } + + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); +} + +/** + * spi_nor_smpt_addr_width() - return the address width used in the + * configuration detection command. + * @nor: pointer to a 'struct spi_nor' + * @settings: configuration detection command descriptor, dword1 + */ +static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) +{ + switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) { + case SMPT_CMD_ADDRESS_LEN_0: + return 0; + case SMPT_CMD_ADDRESS_LEN_3: + return 3; + case SMPT_CMD_ADDRESS_LEN_4: + return 4; + case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: + /* fall through */ + default: + return nor->addr_width; + } +} + +/** + * spi_nor_smpt_read_dummy() - return the configuration detection command read + * latency, in clock cycles. + * @nor: pointer to a 'struct spi_nor' + * @settings: configuration detection command descriptor, dword1 + * + * Return: the number of dummy cycles for an SMPT read + */ +static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings) +{ + u8 read_dummy = SMPT_CMD_READ_DUMMY(settings); + + if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) + return nor->read_dummy; + return read_dummy; +} + +/** + * spi_nor_get_map_in_use() - get the configuration map in use + * @nor: pointer to a 'struct spi_nor' + * @smpt: pointer to the sector map parameter table + * @smpt_len: sector map parameter table length + * + * Return: pointer to the map in use, ERR_PTR(-errno) otherwise. + */ +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, + u8 smpt_len) +{ + const u32 *ret; + u8 *buf; + u32 addr; + int err; + u8 i; + u8 addr_width, read_opcode, read_dummy; + u8 read_data_mask, map_id; + + /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + addr_width = nor->addr_width; + read_dummy = nor->read_dummy; + read_opcode = nor->read_opcode; + + map_id = 0; + /* Determine if there are any optional Detection Command Descriptors */ + for (i = 0; i < smpt_len; i += 2) { + if (smpt[i] & SMPT_DESC_TYPE_MAP) + break; + + read_data_mask = SMPT_CMD_READ_DATA(smpt[i]); + nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]); + nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]); + nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]); + addr = smpt[i + 1]; + + err = spi_nor_read_raw(nor, addr, 1, buf); + if (err) { + ret = ERR_PTR(err); + goto out; + } + + /* + * Build an index value that is used to select the Sector Map + * Configuration that is currently in use. + */ + map_id = map_id << 1 | !!(*buf & read_data_mask); + } + + /* + * If command descriptors are provided, they always precede map + * descriptors in the table. There is no need to start the iteration + * over smpt array all over again. + * + * Find the matching configuration map. + */ + ret = ERR_PTR(-EINVAL); + while (i < smpt_len) { + if (SMPT_MAP_ID(smpt[i]) == map_id) { + ret = smpt + i; + break; + } + + /* + * If there are no more configuration map descriptors and no + * configuration ID matched the configuration identifier, the + * sector address map is unknown. + */ + if (smpt[i] & SMPT_DESC_END) + break; + + /* increment the table index to the next map */ + i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1; + } + + /* fall through */ +out: + kfree(buf); + nor->addr_width = addr_width; + nor->read_dummy = read_dummy; + nor->read_opcode = read_opcode; + return ret; +} + +/** + * spi_nor_region_check_overlay() - set overlay bit when the region is overlaid + * @region: pointer to a structure that describes a SPI NOR erase region + * @erase: pointer to a structure that describes a SPI NOR erase type + * @erase_type: erase type bitmask + */ +static void +spi_nor_region_check_overlay(struct spi_nor_erase_region *region, + const struct spi_nor_erase_type *erase, + const u8 erase_type) +{ + int i; + + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + if (!(erase_type & BIT(i))) + continue; + if (region->size & erase[i].size_mask) { + spi_nor_region_mark_overlay(region); + return; + } + } +} + +/** + * spi_nor_init_non_uniform_erase_map() - initialize the non-uniform erase map + * @nor: pointer to a 'struct spi_nor' + * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' that is + * used for storing SFDP parsed data + * @smpt: pointer to the sector map parameter table + * + * Return: 0 on success, -errno otherwise. + */ +static int +spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, + struct spi_nor_flash_parameter *params, + const u32 *smpt) +{ + struct spi_nor_erase_map *map = ¶ms->erase_map; + struct spi_nor_erase_type *erase = map->erase_type; + struct spi_nor_erase_region *region; + u64 offset; + u32 region_count; + int i, j; + u8 uniform_erase_type, save_uniform_erase_type; + u8 erase_type, regions_erase_type; + + region_count = SMPT_MAP_REGION_COUNT(*smpt); + /* + * The regions will be freed when the driver detaches from the + * device. + */ + region = devm_kcalloc(nor->dev, region_count, sizeof(*region), + GFP_KERNEL); + if (!region) + return -ENOMEM; + map->regions = region; + + uniform_erase_type = 0xff; + regions_erase_type = 0; + offset = 0; + /* Populate regions. */ + for (i = 0; i < region_count; i++) { + j = i + 1; /* index for the region dword */ + region[i].size = SMPT_MAP_REGION_SIZE(smpt[j]); + erase_type = SMPT_MAP_REGION_ERASE_TYPE(smpt[j]); + region[i].offset = offset | erase_type; + + spi_nor_region_check_overlay(®ion[i], erase, erase_type); + + /* + * Save the erase types that are supported in all regions and + * can erase the entire flash memory. + */ + uniform_erase_type &= erase_type; + + /* + * regions_erase_type mask will indicate all the erase types + * supported in this configuration map. + */ + regions_erase_type |= erase_type; + + offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) + + region[i].size; + } + + save_uniform_erase_type = map->uniform_erase_type; + map->uniform_erase_type = spi_nor_sort_erase_mask(map, + uniform_erase_type); + + if (!regions_erase_type) { + /* + * Roll back to the previous uniform_erase_type mask, SMPT is + * broken. + */ + map->uniform_erase_type = save_uniform_erase_type; + return -EINVAL; + } + + /* + * BFPT advertises all the erase types supported by all the possible + * map configurations. Mask out the erase types that are not supported + * by the current map configuration. + */ + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) + if (!(regions_erase_type & BIT(erase[i].idx))) + spi_nor_set_erase_type(&erase[i], 0, 0xFF); + + spi_nor_region_mark_end(®ion[i - 1]); + + return 0; +} + +/** + * spi_nor_parse_smpt() - parse Sector Map Parameter Table + * @nor: pointer to a 'struct spi_nor' + * @smpt_header: sector map parameter table header + * @params: pointer to a duplicate 'struct spi_nor_flash_parameter' + * that is used for storing SFDP parsed data + * + * This table is optional, but when available, we parse it to identify the + * location and size of sectors within the main data array of the flash memory + * device and to identify which Erase Types are supported by each sector. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_smpt(struct spi_nor *nor, + const struct sfdp_parameter_header *smpt_header, + struct spi_nor_flash_parameter *params) +{ + const u32 *sector_map; + u32 *smpt; + size_t len; + u32 addr; + int ret; + + /* Read the Sector Map Parameter Table. */ + len = smpt_header->length * sizeof(*smpt); + smpt = kmalloc(len, GFP_KERNEL); + if (!smpt) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(smpt_header); + ret = spi_nor_read_sfdp(nor, addr, len, smpt); + if (ret) + goto out; + + /* Fix endianness of the SMPT DWORDs. */ + le32_to_cpu_array(smpt, smpt_header->length); + + sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length); + if (IS_ERR(sector_map)) { + ret = PTR_ERR(sector_map); + goto out; + } + + ret = spi_nor_init_non_uniform_erase_map(nor, params, sector_map); + if (ret) + goto out; + + spi_nor_regions_sort_erase_types(¶ms->erase_map); + /* fall through */ +out: + kfree(smpt); + return ret; +} + +/** + * spi_nor_parse_4bait() - parse the 4-Byte Address Instruction Table + * @nor: pointer to a 'struct spi_nor'. + * @param_header: pointer to the 'struct sfdp_parameter_header' describing + * the 4-Byte Address Instruction Table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_4bait(struct spi_nor *nor, + const struct sfdp_parameter_header *param_header, + struct spi_nor_flash_parameter *params) +{ + static const struct sfdp_4bait reads[] = { + { SNOR_HWCAPS_READ, BIT(0) }, + { SNOR_HWCAPS_READ_FAST, BIT(1) }, + { SNOR_HWCAPS_READ_1_1_2, BIT(2) }, + { SNOR_HWCAPS_READ_1_2_2, BIT(3) }, + { SNOR_HWCAPS_READ_1_1_4, BIT(4) }, + { SNOR_HWCAPS_READ_1_4_4, BIT(5) }, + { SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) }, + { SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) }, + { SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) }, + }; + static const struct sfdp_4bait programs[] = { + { SNOR_HWCAPS_PP, BIT(6) }, + { SNOR_HWCAPS_PP_1_1_4, BIT(7) }, + { SNOR_HWCAPS_PP_1_4_4, BIT(8) }, + }; + static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = { + { 0u /* not used */, BIT(9) }, + { 0u /* not used */, BIT(10) }, + { 0u /* not used */, BIT(11) }, + { 0u /* not used */, BIT(12) }, + }; + struct spi_nor_pp_command *params_pp = params->page_programs; + struct spi_nor_erase_map *map = ¶ms->erase_map; + struct spi_nor_erase_type *erase_type = map->erase_type; + u32 *dwords; + size_t len; + u32 addr, discard_hwcaps, read_hwcaps, pp_hwcaps, erase_mask; + int i, ret; + + if (param_header->major != SFDP_JESD216_MAJOR || + param_header->length < SFDP_4BAIT_DWORD_MAX) + return -EINVAL; + + /* Read the 4-byte Address Instruction Table. */ + len = sizeof(*dwords) * SFDP_4BAIT_DWORD_MAX; + + /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ + dwords = kmalloc(len, GFP_KERNEL); + if (!dwords) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(param_header); + ret = spi_nor_read_sfdp(nor, addr, len, dwords); + if (ret) + goto out; + + /* Fix endianness of the 4BAIT DWORDs. */ + le32_to_cpu_array(dwords, SFDP_4BAIT_DWORD_MAX); + + /* + * Compute the subset of (Fast) Read commands for which the 4-byte + * version is supported. + */ + discard_hwcaps = 0; + read_hwcaps = 0; + for (i = 0; i < ARRAY_SIZE(reads); i++) { + const struct sfdp_4bait *read = &reads[i]; + + discard_hwcaps |= read->hwcaps; + if ((params->hwcaps.mask & read->hwcaps) && + (dwords[0] & read->supported_bit)) + read_hwcaps |= read->hwcaps; + } + + /* + * Compute the subset of Page Program commands for which the 4-byte + * version is supported. + */ + pp_hwcaps = 0; + for (i = 0; i < ARRAY_SIZE(programs); i++) { + const struct sfdp_4bait *program = &programs[i]; + + /* + * The 4 Byte Address Instruction (Optional) Table is the only + * SFDP table that indicates support for Page Program Commands. + * Bypass the params->hwcaps.mask and consider 4BAIT the biggest + * authority for specifying Page Program support. + */ + discard_hwcaps |= program->hwcaps; + if (dwords[0] & program->supported_bit) + pp_hwcaps |= program->hwcaps; + } + + /* + * Compute the subset of Sector Erase commands for which the 4-byte + * version is supported. + */ + erase_mask = 0; + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + const struct sfdp_4bait *erase = &erases[i]; + + if (dwords[0] & erase->supported_bit) + erase_mask |= BIT(i); + } + + /* Replicate the sort done for the map's erase types in BFPT. */ + erase_mask = spi_nor_sort_erase_mask(map, erase_mask); + + /* + * 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 (!read_hwcaps || !pp_hwcaps || !erase_mask) + goto out; + + /* + * Discard all operations from the 4-byte instruction set which are + * not supported by this memory. + */ + params->hwcaps.mask &= ~discard_hwcaps; + params->hwcaps.mask |= (read_hwcaps | pp_hwcaps); + + /* Use the 4-byte address instruction set. */ + for (i = 0; i < SNOR_CMD_READ_MAX; i++) { + struct spi_nor_read_command *read_cmd = ¶ms->reads[i]; + + read_cmd->opcode = spi_nor_convert_3to4_read(read_cmd->opcode); + } + + /* 4BAIT is the only SFDP table that indicates page program support. */ + if (pp_hwcaps & SNOR_HWCAPS_PP) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP], + SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_4], + SPINOR_OP_PP_1_1_4_4B, + SNOR_PROTO_1_1_4); + if (pp_hwcaps & SNOR_HWCAPS_PP_1_4_4) + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_4_4], + SPINOR_OP_PP_1_4_4_4B, + SNOR_PROTO_1_4_4); + + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + if (erase_mask & BIT(i)) + erase_type[i].opcode = (dwords[1] >> + erase_type[i].idx * 8) & 0xFF; + else + spi_nor_set_erase_type(&erase_type[i], 0u, 0xFF); + } + + /* + * We set SNOR_F_HAS_4BAIT in order to skip spi_nor_set_4byte_opcodes() + * later because we already did the conversion to 4byte opcodes. Also, + * this latest function implements a legacy quirk for the erase size of + * Spansion memory. However this quirk is no longer needed with new + * SFDP compliant memories. + */ + nor->addr_width = 4; + nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; + + /* fall through */ +out: + kfree(dwords); + return ret; +} + +/** + * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. + * @nor: pointer to a 'struct spi_nor' + * @params: pointer to the 'struct spi_nor_flash_parameter' to be + * filled + * + * The Serial Flash Discoverable Parameters are described by the JEDEC JESD216 + * specification. This is a standard which tends to supported by almost all + * (Q)SPI memory manufacturers. Those hard-coded tables allow us to learn at + * runtime the main parameters needed to perform basic SPI flash operations such + * as Fast Read, Page Program or Sector Erase commands. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_parse_sfdp(struct spi_nor *nor, + struct spi_nor_flash_parameter *params) +{ + const struct sfdp_parameter_header *param_header, *bfpt_header; + struct sfdp_parameter_header *param_headers = NULL; + struct sfdp_header header; + struct device *dev = nor->dev; + size_t psize; + int i, err; + + /* Get the SFDP header. */ + err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header); + if (err < 0) + return err; + + /* Check the SFDP header version. */ + if (le32_to_cpu(header.signature) != SFDP_SIGNATURE || + header.major != SFDP_JESD216_MAJOR) + 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 then read all parameter headers with a single + * Read SFDP command. These parameter headers will actually be parsed + * twice: a first time to get the latest revision of the basic flash + * parameter table, then a second time to handle the supported optional + * tables. + * Hence we read the parameter headers once for all to reduce the + * processing time. Also we use kmalloc() instead of devm_kmalloc() + * because we don't need to keep these parameter headers: the allocated + * memory is always released with kfree() before exiting this function. + */ + if (header.nph) { + psize = header.nph * sizeof(*param_headers); + + param_headers = kmalloc(psize, GFP_KERNEL); + if (!param_headers) + return -ENOMEM; + + err = spi_nor_read_sfdp(nor, sizeof(header), + psize, param_headers); + if (err < 0) { + dev_dbg(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; + + /* Parse optional parameter tables. */ + for (i = 0; i < header.nph; i++) { + param_header = ¶m_headers[i]; + + switch (SFDP_PARAM_HEADER_ID(param_header)) { + case SFDP_SECTOR_MAP_ID: + err = spi_nor_parse_smpt(nor, param_header, params); + break; + + case SFDP_4BAIT_ID: + err = spi_nor_parse_4bait(nor, param_header, params); + break; + + default: + break; + } + + if (err) { + dev_warn(dev, "Failed to parse optional parameter table: %04x\n", + SFDP_PARAM_HEADER_ID(param_header)); + /* + * Let's not drop all information we extracted so far + * if optional table parsers fail. In case of failing, + * each optional parser is responsible to roll back to + * the previously known spi_nor data. + */ + err = 0; + } + } + +exit: + kfree(param_headers); + return err; +} diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h new file mode 100644 index 000000000000..e0a8ded04890 --- /dev/null +++ b/drivers/mtd/spi-nor/sfdp.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#ifndef __LINUX_MTD_SFDP_H +#define __LINUX_MTD_SFDP_H + +/* Basic Flash Parameter Table */ + +/* + * JESD216 rev B defines a Basic Flash Parameter Table of 16 DWORDs. + * They are indexed from 1 but C arrays are indexed from 0. + */ +#define BFPT_DWORD(i) ((i) - 1) +#define BFPT_DWORD_MAX 16 + +struct sfdp_bfpt { + u32 dwords[BFPT_DWORD_MAX]; +}; + +/* The first version of JESD216 defined only 9 DWORDs. */ +#define BFPT_DWORD_MAX_JESD216 9 + +/* 1st DWORD. */ +#define BFPT_DWORD1_FAST_READ_1_1_2 BIT(16) +#define BFPT_DWORD1_ADDRESS_BYTES_MASK GENMASK(18, 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_ONLY (0x0UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 (0x1UL << 17) +#define BFPT_DWORD1_ADDRESS_BYTES_4_ONLY (0x2UL << 17) +#define BFPT_DWORD1_DTR BIT(19) +#define BFPT_DWORD1_FAST_READ_1_2_2 BIT(20) +#define BFPT_DWORD1_FAST_READ_1_4_4 BIT(21) +#define BFPT_DWORD1_FAST_READ_1_1_4 BIT(22) + +/* 5th DWORD. */ +#define BFPT_DWORD5_FAST_READ_2_2_2 BIT(0) +#define BFPT_DWORD5_FAST_READ_4_4_4 BIT(4) + +/* 11th DWORD. */ +#define BFPT_DWORD11_PAGE_SIZE_SHIFT 4 +#define BFPT_DWORD11_PAGE_SIZE_MASK GENMASK(7, 4) + +/* 15th DWORD. */ + +/* + * (from JESD216 rev B) + * 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 phase. + * - 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 Write Status instruction 01h with + * two data bytes where bit 1 of the second byte is one. + * [...] + */ +#define BFPT_DWORD15_QER_MASK GENMASK(22, 20) +#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */ +#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20) +#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */ +#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) +#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ + +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; +}; + +int spi_nor_parse_sfdp(struct spi_nor *nor, + struct spi_nor_flash_parameter *params); + +#endif /* __LINUX_MTD_SFDP_H */ From 4f50e102e26a96824f032a7775ef805a8c6476de Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:38 +0000 Subject: [PATCH 066/103] mtd: spi-nor: Expose stuctures and functions to manufacturer drivers Expose the flash_info struct and some function prototypes that will be used by manufacturers. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/core.c | 169 +++---------------------------------- drivers/mtd/spi-nor/core.h | 158 ++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 158 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 4ae79c1c8bec..aae94e4250f6 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -38,101 +38,9 @@ */ #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) -#define SPI_NOR_MAX_ID_LEN 6 #define SPI_NOR_MAX_ADDR_WIDTH 4 -/** - * struct spi_nor_fixups - SPI NOR fixup hooks - * @default_init: called after default flash parameters init. Used to tweak - * flash parameters when information provided by the flash_info - * table is incomplete or wrong. - * @post_bfpt: called after the BFPT table has been parsed - * @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs - * that do not support RDSFDP). Typically used to tweak various - * parameters that could not be extracted by other means (i.e. - * when information provided by the SFDP/flash_info tables are - * incomplete or wrong). - * - * Those hooks can be used to tweak the SPI NOR configuration when the SFDP - * table is broken or not available. - */ -struct spi_nor_fixups { - void (*default_init)(struct spi_nor *nor); - int (*post_bfpt)(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt, - struct spi_nor_flash_parameter *params); - void (*post_sfdp)(struct spi_nor *nor); -}; - -struct flash_info { - char *name; - - /* - * This array stores the ID bytes. - * The first three bytes are the JEDIC ID. - * JEDEC ID zero means "no ID" (mostly older chips). - */ - u8 id[SPI_NOR_MAX_ID_LEN]; - u8 id_len; - - /* The size listed here is what works with SPINOR_OP_SE, which isn't - * necessarily called a "sector" by the vendor. - */ - unsigned sector_size; - u16 n_sectors; - - u16 page_size; - u16 addr_width; - - u32 flags; -#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */ -#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */ -#define SST_WRITE BIT(2) /* use SST byte programming */ -#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */ -#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */ -#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */ -#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */ -#define USE_FSR BIT(7) /* use flag status register */ -#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */ -#define SPI_NOR_HAS_TB BIT(9) /* - * Flash SR has Top/Bottom (TB) protect - * bit. Must be used with - * SPI_NOR_HAS_LOCK. - */ -#define SPI_NOR_XSR_RDY BIT(10) /* - * S3AN flashes have specific opcode to - * read the status register. - * Flags SPI_NOR_XSR_RDY and SPI_S3AN - * use the same bit as one implies the - * other, but we will get rid of - * SPI_S3AN soon. - */ -#define SPI_S3AN BIT(10) /* - * Xilinx Spartan 3AN In-System Flash - * (MFR cannot be used for probing - * because it has the same value as - * ATMEL flashes) - */ -#define SPI_NOR_4B_OPCODES BIT(11) /* - * Use dedicated 4byte address op codes - * to support memory size above 128Mib. - */ -#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ -#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ -#define USE_CLSR BIT(14) /* use CLSR command */ -#define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */ -#define SPI_NOR_TB_SR_BIT6 BIT(16) /* - * Top/Bottom (TB) is bit 6 of - * status register. Must be used with - * SPI_NOR_HAS_TB. - */ - - /* Part specific fixup hooks. */ - const struct spi_nor_fixups *fixups; -}; - -#define JEDEC_MFR(info) ((info)->id[0]) +#define JEDEC_MFR(info) ((info)->id[0]) /** * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data @@ -295,8 +203,8 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, * * Return: number of bytes written successfully, -errno otherwise */ -static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, - const u8 *buf) +ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, + const u8 *buf) { if (nor->spimem) return spi_nor_spimem_write_data(nor, to, len, buf); @@ -310,7 +218,7 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_enable(struct spi_nor *nor) +int spi_nor_write_enable(struct spi_nor *nor) { int ret; @@ -339,7 +247,7 @@ static int spi_nor_write_enable(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_disable(struct spi_nor *nor) +int spi_nor_write_disable(struct spi_nor *nor) { int ret; @@ -463,7 +371,7 @@ static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) +int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) { int ret; @@ -556,7 +464,7 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_ear(struct spi_nor *nor, u8 ear) +int spi_nor_write_ear(struct spi_nor *nor, u8 ear) { int ret; @@ -621,7 +529,7 @@ static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) +int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) { int ret; @@ -834,7 +742,7 @@ static int spi_nor_wait_till_ready_with_timeout(struct spi_nor *nor, * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_wait_till_ready(struct spi_nor *nor) +int spi_nor_wait_till_ready(struct spi_nor *nor) { return spi_nor_wait_till_ready_with_timeout(nor, DEFAULT_READY_WAIT_JIFFIES); @@ -1142,11 +1050,6 @@ static int spi_nor_erase_chip(struct spi_nor *nor) return ret; } -static struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) -{ - return mtd->priv; -} - static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size) { size_t i; @@ -1225,7 +1128,7 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) } } -static int spi_nor_lock_and_prep(struct spi_nor *nor) +int spi_nor_lock_and_prep(struct spi_nor *nor) { int ret = 0; @@ -1241,7 +1144,7 @@ static int spi_nor_lock_and_prep(struct spi_nor *nor) return ret; } -static void spi_nor_unlock_and_unprep(struct spi_nor *nor) +void spi_nor_unlock_and_unprep(struct spi_nor *nor) { if (nor->controller_ops && nor->controller_ops->unprepare) nor->controller_ops->unprepare(nor); @@ -2104,56 +2007,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) return 0; } -/* Used when the "_ext_id" is two bytes at most */ -#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff, \ - ((_ext_id) >> 8) & 0xff, \ - (_ext_id) & 0xff, \ - }, \ - .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ - .flags = (_flags), - -#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff, \ - ((_ext_id) >> 16) & 0xff, \ - ((_ext_id) >> 8) & 0xff, \ - (_ext_id) & 0xff, \ - }, \ - .id_len = 6, \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ - .flags = (_flags), - -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = (_page_size), \ - .addr_width = (_addr_width), \ - .flags = (_flags), - -#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff \ - }, \ - .id_len = 3, \ - .sector_size = (8*_page_size), \ - .n_sectors = (_n_sectors), \ - .page_size = _page_size, \ - .addr_width = 3, \ - .flags = SPI_NOR_NO_FR | SPI_S3AN, - static int is25lp256_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index e1256fe50d12..abec65081519 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -9,12 +9,165 @@ #include "sfdp.h" +#define SPI_NOR_MAX_ID_LEN 6 + +/** + * struct spi_nor_fixups - SPI NOR fixup hooks + * @default_init: called after default flash parameters init. Used to tweak + * flash parameters when information provided by the flash_info + * table is incomplete or wrong. + * @post_bfpt: called after the BFPT table has been parsed + * @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs + * that do not support RDSFDP). Typically used to tweak various + * parameters that could not be extracted by other means (i.e. + * when information provided by the SFDP/flash_info tables are + * incomplete or wrong). + * + * Those hooks can be used to tweak the SPI NOR configuration when the SFDP + * table is broken or not available. + */ +struct spi_nor_fixups { + void (*default_init)(struct spi_nor *nor); + int (*post_bfpt)(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params); + void (*post_sfdp)(struct spi_nor *nor); +}; + +struct flash_info { + char *name; + + /* + * This array stores the ID bytes. + * The first three bytes are the JEDIC ID. + * JEDEC ID zero means "no ID" (mostly older chips). + */ + u8 id[SPI_NOR_MAX_ID_LEN]; + u8 id_len; + + /* The size listed here is what works with SPINOR_OP_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + + u16 page_size; + u16 addr_width; + + u32 flags; +#define SECT_4K BIT(0) /* SPINOR_OP_BE_4K works uniformly */ +#define SPI_NOR_NO_ERASE BIT(1) /* No erase command needed */ +#define SST_WRITE BIT(2) /* use SST byte programming */ +#define SPI_NOR_NO_FR BIT(3) /* Can't do fastread */ +#define SECT_4K_PMC BIT(4) /* SPINOR_OP_BE_4K_PMC works uniformly */ +#define SPI_NOR_DUAL_READ BIT(5) /* Flash supports Dual Read */ +#define SPI_NOR_QUAD_READ BIT(6) /* Flash supports Quad Read */ +#define USE_FSR BIT(7) /* use flag status register */ +#define SPI_NOR_HAS_LOCK BIT(8) /* Flash supports lock/unlock via SR */ +#define SPI_NOR_HAS_TB BIT(9) /* + * Flash SR has Top/Bottom (TB) protect + * bit. Must be used with + * SPI_NOR_HAS_LOCK. + */ +#define SPI_NOR_XSR_RDY BIT(10) /* + * S3AN flashes have specific opcode to + * read the status register. + * Flags SPI_NOR_XSR_RDY and SPI_S3AN + * use the same bit as one implies the + * other, but we will get rid of + * SPI_S3AN soon. + */ +#define SPI_S3AN BIT(10) /* + * Xilinx Spartan 3AN In-System Flash + * (MFR cannot be used for probing + * because it has the same value as + * ATMEL flashes) + */ +#define SPI_NOR_4B_OPCODES BIT(11) /* + * Use dedicated 4byte address op codes + * to support memory size above 128Mib. + */ +#define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ +#define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ +#define USE_CLSR BIT(14) /* use CLSR command */ +#define SPI_NOR_OCTAL_READ BIT(15) /* Flash supports Octal Read */ +#define SPI_NOR_TB_SR_BIT6 BIT(16) /* + * Top/Bottom (TB) is bit 6 of + * status register. Must be used with + * SPI_NOR_HAS_TB. + */ + + /* Part specific fixup hooks. */ + const struct spi_nor_fixups *fixups; +}; + +/* Used when the "_ext_id" is two bytes at most */ +#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 16) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = 6, \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256, \ + .flags = (_flags), + +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = (_page_size), \ + .addr_width = (_addr_width), \ + .flags = (_flags), + +#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff \ + }, \ + .id_len = 3, \ + .sector_size = (8*_page_size), \ + .n_sectors = (_n_sectors), \ + .page_size = _page_size, \ + .addr_width = 3, \ + .flags = SPI_NOR_NO_FR | SPI_S3AN, + +int spi_nor_write_enable(struct spi_nor *nor); +int spi_nor_write_disable(struct spi_nor *nor); +int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); +int spi_nor_write_ear(struct spi_nor *nor, u8 ear); +int spi_nor_wait_till_ready(struct spi_nor *nor); +int spi_nor_lock_and_prep(struct spi_nor *nor); +void spi_nor_unlock_and_unprep(struct spi_nor *nor); int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); +int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr); ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf); +ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, + const u8 *buf); int spi_nor_hwcaps_read2cmd(u32 hwcaps); u8 spi_nor_convert_3to4_read(u8 opcode); @@ -33,4 +186,9 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_bfpt *bfpt, struct spi_nor_flash_parameter *params); +static struct spi_nor __maybe_unused *mtd_to_spi_nor(struct mtd_info *mtd) +{ + return mtd->priv; +} + #endif /* __LINUX_MTD_SPI_NOR_INTERNAL_H */ From 9ec4bbcb2044ea1f380c9feceb10654dd5a35a95 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:39 +0000 Subject: [PATCH 067/103] mtd: spi-nor: Add the concept of SPI NOR manufacturer driver Declare a spi_nor_manufacturer struct and add basic building blocks to move manufacturer specific code outside of the core. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/core.c | 78 +++++++++++++++++++++++++++++++------ drivers/mtd/spi-nor/core.h | 14 +++++++ include/linux/mtd/spi-nor.h | 8 ++++ 3 files changed, 89 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index aae94e4250f6..4494959cd937 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2474,8 +2474,26 @@ static const struct flash_info spi_nor_ids[] = { { }, }; +static const struct spi_nor_manufacturer *manufacturers[0]; + +static const struct flash_info * +spi_nor_search_part_by_id(const struct flash_info *parts, unsigned int nparts, + const u8 *id) +{ + unsigned int i; + + for (i = 0; i < nparts; i++) { + if (parts[i].id_len && + !memcmp(parts[i].id, id, parts[i].id_len)) + return &parts[i]; + } + + return NULL; +} + static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) { + const struct flash_info *info; u8 *id = nor->bouncebuf; unsigned int i; int ret; @@ -2497,11 +2515,21 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) return ERR_PTR(ret); } - for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { - if (spi_nor_ids[i].id_len && - !memcmp(spi_nor_ids[i].id, id, spi_nor_ids[i].id_len)) - return &spi_nor_ids[i]; + for (i = 0; i < ARRAY_SIZE(manufacturers); i++) { + info = spi_nor_search_part_by_id(manufacturers[i]->parts, + manufacturers[i]->nparts, + id); + if (info) { + nor->manufacturer = manufacturers[i]; + return info; + } } + + info = spi_nor_search_part_by_id(spi_nor_ids, + ARRAY_SIZE(spi_nor_ids) - 1, id); + if (info) + return info; + dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", SPI_NOR_MAX_ID_LEN, id); return ERR_PTR(-ENODEV); @@ -2987,6 +3015,16 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_bfpt *bfpt, struct spi_nor_flash_parameter *params) { + int ret; + + if (nor->manufacturer && nor->manufacturer->fixups && + nor->manufacturer->fixups->post_bfpt) { + ret = nor->manufacturer->fixups->post_bfpt(nor, bfpt_header, + bfpt, params); + if (ret) + return ret; + } + if (nor->info->fixups && nor->info->fixups->post_bfpt) return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt, params); @@ -3296,6 +3334,10 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) break; } + if (nor->manufacturer && nor->manufacturer->fixups && + nor->manufacturer->fixups->default_init) + nor->manufacturer->fixups->default_init(nor); + if (nor->info->fixups && nor->info->fixups->default_init) nor->info->fixups->default_init(nor); } @@ -3455,6 +3497,10 @@ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor) if (nor->info->flags & SPI_S3AN) s3an_post_sfdp_fixups(nor); + if (nor->manufacturer && nor->manufacturer->fixups && + nor->manufacturer->fixups->post_sfdp) + nor->manufacturer->fixups->post_sfdp(nor); + if (nor->info->fixups && nor->info->fixups->post_sfdp) nor->info->fixups->post_sfdp(nor); } @@ -3617,15 +3663,25 @@ void spi_nor_restore(struct spi_nor *nor) } EXPORT_SYMBOL_GPL(spi_nor_restore); -static const struct flash_info *spi_nor_match_id(const char *name) +static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, + const char *name) { - const struct flash_info *id = spi_nor_ids; + unsigned int i, j; - while (id->name) { - if (!strcmp(name, id->name)) - return id; - id++; + for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { + if (!strcmp(name, spi_nor_ids[i].name)) + return &spi_nor_ids[i]; } + + for (i = 0; i < ARRAY_SIZE(manufacturers); i++) { + for (j = 0; j < manufacturers[i]->nparts; j++) { + if (!strcmp(name, manufacturers[i]->parts[j].name)) { + nor->manufacturer = manufacturers[i]; + return &manufacturers[i]->parts[j]; + } + } + } + return NULL; } @@ -3672,7 +3728,7 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, const struct flash_info *info = NULL; if (name) - info = spi_nor_match_id(name); + info = spi_nor_match_id(nor, name); /* Try to auto-detect if chip name wasn't specified or not found */ if (!info) info = spi_nor_read_id(nor); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index abec65081519..8599796dfc40 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -152,6 +152,20 @@ struct flash_info { .addr_width = 3, \ .flags = SPI_NOR_NO_FR | SPI_S3AN, +/** + * struct spi_nor_manufacturer - SPI NOR manufacturer object + * @name: manufacturer name + * @parts: array of parts supported by this manufacturer + * @nparts: number of entries in the parts array + * @fixups: hooks called at various points in time during spi_nor_scan() + */ +struct spi_nor_manufacturer { + const char *name; + const struct flash_info *parts; + unsigned int nparts; + const struct spi_nor_fixups *fixups; +}; + int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 2b9717b0cd62..bf37bfc68797 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -554,6 +554,12 @@ struct spi_nor_flash_parameter { */ struct flash_info; +/** + * struct spi_nor_manufacturer - Forward declaration of a structure used + * internally by the core and manufacturer drivers. + */ +struct spi_nor_manufacturer; + /** * struct spi_nor - Structure for defining a the SPI NOR layer * @mtd: point to a mtd_info structure @@ -564,6 +570,7 @@ struct flash_info; * layer is not DMA-able * @bouncebuf_size: size of the bounce buffer * @info: spi-nor part JDEC MFR id and other info + * @manufacturer: spi-nor manufacturer * @page_size: the page size of the SPI NOR * @addr_width: number of address bytes * @erase_opcode: the opcode for erasing a sector @@ -591,6 +598,7 @@ struct spi_nor { u8 *bouncebuf; size_t bouncebuf_size; const struct flash_info *info; + const struct spi_nor_manufacturer *manufacturer; u32 page_size; u8 addr_width; u8 erase_opcode; From f7242bfc02b8f8cb820eb32d726c11e7f337e942 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:39 +0000 Subject: [PATCH 068/103] mtd: spi-nor: Move Atmel bits out of core.c Create a SPI NOR manufacturer driver for Atmel chips, and move the Atmel definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/atmel.c | 46 ++++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/core.c | 32 +++---------------------- drivers/mtd/spi-nor/core.h | 3 +++ 4 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 drivers/mtd/spi-nor/atmel.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 6bcdb6f1615a..0a243592e416 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 spi-nor-objs := core.o sfdp.o +spi-nor-objs += atmel.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c new file mode 100644 index 000000000000..3f5f21a473a6 --- /dev/null +++ b/drivers/mtd/spi-nor/atmel.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info atmel_parts[] = { + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, + + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, + { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, + + { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + + { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, + + { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, +}; + +static void atmel_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; +} + +static const struct spi_nor_fixups atmel_fixups = { + .default_init = atmel_default_init, +}; + +const struct spi_nor_manufacturer spi_nor_atmel = { + .name = "atmel", + .parts = atmel_parts, + .nparts = ARRAY_SIZE(atmel_parts), + .fixups = &atmel_fixups, +}; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 4494959cd937..56def4d0bbd0 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2081,25 +2081,6 @@ static struct spi_nor_fixups gd25q256_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, - { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, - - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, - { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, - - { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - - { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - - { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, - /* EON -- en25xxx */ { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, @@ -2474,7 +2455,9 @@ static const struct flash_info spi_nor_ids[] = { { }, }; -static const struct spi_nor_manufacturer *manufacturers[0]; +static const struct spi_nor_manufacturer *manufacturers[] = { + &spi_nor_atmel, +}; static const struct flash_info * spi_nor_search_part_by_id(const struct flash_info *parts, unsigned int nparts, @@ -3253,11 +3236,6 @@ static int spi_nor_setup(struct spi_nor *nor, return nor->params.setup(nor, hwcaps); } -static void atmel_set_default_init(struct spi_nor *nor) -{ - nor->flags |= SNOR_F_HAS_LOCK; -} - static void intel_set_default_init(struct spi_nor *nor) { nor->flags |= SNOR_F_HAS_LOCK; @@ -3301,10 +3279,6 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { /* Init flash parameters based on MFR */ switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_ATMEL: - atmel_set_default_init(nor); - break; - case SNOR_MFR_INTEL: intel_set_default_init(nor); break; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 8599796dfc40..76a5c91abe23 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -166,6 +166,9 @@ struct spi_nor_manufacturer { const struct spi_nor_fixups *fixups; }; +/* Manufacturer drivers. */ +extern const struct spi_nor_manufacturer spi_nor_atmel; + int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); From d22a3be613b32d24515a33020105e95b11570a45 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:40 +0000 Subject: [PATCH 069/103] mtd: spi-nor: Move Eon bits out of core.c Create a SPI NOR manufacturer driver for Eon chips, and move the Eon definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 18 +----------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/eon.c | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 drivers/mtd/spi-nor/eon.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 0a243592e416..e1bc8ccfe14d 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -2,4 +2,5 @@ spi-nor-objs := core.o sfdp.o spi-nor-objs += atmel.o +spi-nor-objs += eon.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 56def4d0bbd0..66873af7e546 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2081,23 +2081,6 @@ static struct spi_nor_fixups gd25q256_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* EON -- en25xxx */ - { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, - { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, - { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, - { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, - { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, - { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16, - SECT_4K | SPI_NOR_DUAL_READ) }, - { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_DUAL_READ) }, - { "en25qh32", INFO(0x1c7016, 0, 64 * 1024, 64, 0) }, - { "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_DUAL_READ) }, - { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) }, - { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, - { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, - /* ESMT */ { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, @@ -2457,6 +2440,7 @@ static const struct flash_info spi_nor_ids[] = { static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_atmel, + &spi_nor_eon, }; static const struct flash_info * diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 76a5c91abe23..efa154a6bdf5 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -168,6 +168,7 @@ struct spi_nor_manufacturer { /* Manufacturer drivers. */ extern const struct spi_nor_manufacturer spi_nor_atmel; +extern const struct spi_nor_manufacturer spi_nor_eon; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/eon.c b/drivers/mtd/spi-nor/eon.c new file mode 100644 index 000000000000..ddb8e3650835 --- /dev/null +++ b/drivers/mtd/spi-nor/eon.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info eon_parts[] = { + /* EON -- en25xxx */ + { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh32", INFO(0x1c7016, 0, 64 * 1024, 64, 0) }, + { "en25qh64", INFO(0x1c7017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "en25qh128", INFO(0x1c7018, 0, 64 * 1024, 256, 0) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, +}; + +const struct spi_nor_manufacturer spi_nor_eon = { + .name = "eon", + .parts = eon_parts, + .nparts = ARRAY_SIZE(eon_parts), +}; From 74c7e0e3b91144662f612b26ea4f7b3b79ba734a Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:41 +0000 Subject: [PATCH 070/103] mtd: spi-nor: Move ESMT bits out of core.c Create a SPI NOR manufacturer driver for ESMT chips, and move the ESMT definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 6 +----- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/esmt.c | 25 +++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 drivers/mtd/spi-nor/esmt.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index e1bc8ccfe14d..4e5ef10e4fd7 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -3,4 +3,5 @@ spi-nor-objs := core.o sfdp.o spi-nor-objs += atmel.o spi-nor-objs += eon.o +spi-nor-objs += esmt.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 66873af7e546..b52fece97579 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2081,11 +2081,6 @@ static struct spi_nor_fixups gd25q256_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* ESMT */ - { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) }, - /* Everspin */ { "mr25h128", CAT25_INFO( 16 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, @@ -2441,6 +2436,7 @@ static const struct flash_info spi_nor_ids[] = { static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_atmel, &spi_nor_eon, + &spi_nor_esmt, }; static const struct flash_info * diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index efa154a6bdf5..33f2c63be596 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -169,6 +169,7 @@ struct spi_nor_manufacturer { /* Manufacturer drivers. */ extern const struct spi_nor_manufacturer spi_nor_atmel; extern const struct spi_nor_manufacturer spi_nor_eon; +extern const struct spi_nor_manufacturer spi_nor_esmt; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c new file mode 100644 index 000000000000..c93170008118 --- /dev/null +++ b/drivers/mtd/spi-nor/esmt.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info esmt_parts[] = { + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_HAS_LOCK) }, + { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_HAS_LOCK) }, + { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_HAS_LOCK) }, +}; + +const struct spi_nor_manufacturer spi_nor_esmt = { + .name = "esmt", + .parts = esmt_parts, + .nparts = ARRAY_SIZE(esmt_parts), +}; From 7bdbd1ceb3a124aa96ec000cd5f462fdcd90c91e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:41 +0000 Subject: [PATCH 071/103] mtd: spi-nor: Move Everspin bits out of core.c Create a SPI NOR manufacturer driver for Everspin chips, and move the Everspin definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 7 +------ drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/everspin.c | 27 +++++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 drivers/mtd/spi-nor/everspin.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 4e5ef10e4fd7..384c520689d8 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -4,4 +4,5 @@ spi-nor-objs := core.o sfdp.o spi-nor-objs += atmel.o spi-nor-objs += eon.o spi-nor-objs += esmt.o +spi-nor-objs += everspin.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index b52fece97579..bb824539fa97 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2081,12 +2081,6 @@ static struct spi_nor_fixups gd25q256_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Everspin */ - { "mr25h128", CAT25_INFO( 16 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "mr25h40", CAT25_INFO(512 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - /* Fujitsu */ { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) }, @@ -2437,6 +2431,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_atmel, &spi_nor_eon, &spi_nor_esmt, + &spi_nor_everspin, }; static const struct flash_info * diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 33f2c63be596..1a930b22ac45 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -170,6 +170,7 @@ struct spi_nor_manufacturer { extern const struct spi_nor_manufacturer spi_nor_atmel; extern const struct spi_nor_manufacturer spi_nor_eon; extern const struct spi_nor_manufacturer spi_nor_esmt; +extern const struct spi_nor_manufacturer spi_nor_everspin; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/everspin.c b/drivers/mtd/spi-nor/everspin.c new file mode 100644 index 000000000000..04a177a32283 --- /dev/null +++ b/drivers/mtd/spi-nor/everspin.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info everspin_parts[] = { + /* Everspin */ + { "mr25h128", CAT25_INFO(16 * 1024, 1, 256, 2, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "mr25h256", CAT25_INFO(32 * 1024, 1, 256, 2, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "mr25h40", CAT25_INFO(512 * 1024, 1, 256, 3, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +}; + +const struct spi_nor_manufacturer spi_nor_everspin = { + .name = "everspin", + .parts = everspin_parts, + .nparts = ARRAY_SIZE(everspin_parts), +}; From 893218a8e828648989f2257f2a6c95ae04a37f7c Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:42 +0000 Subject: [PATCH 072/103] mtd: spi-nor: Move Fujitsu bits out of core.c Create a SPI NOR manufacturer driver for Fujitsu chips, and move the Fujitsu definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 4 +--- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/fujitsu.c | 20 ++++++++++++++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 drivers/mtd/spi-nor/fujitsu.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 384c520689d8..ca6222d98b0f 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -5,4 +5,5 @@ spi-nor-objs += atmel.o spi-nor-objs += eon.o spi-nor-objs += esmt.o spi-nor-objs += everspin.o +spi-nor-objs += fujitsu.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index bb824539fa97..3850c638f95a 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2081,9 +2081,6 @@ static struct spi_nor_fixups gd25q256_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Fujitsu */ - { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) }, - /* GigaDevice */ { "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32, @@ -2432,6 +2429,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_eon, &spi_nor_esmt, &spi_nor_everspin, + &spi_nor_fujitsu, }; static const struct flash_info * diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 1a930b22ac45..d094112aa4a2 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -171,6 +171,7 @@ extern const struct spi_nor_manufacturer spi_nor_atmel; extern const struct spi_nor_manufacturer spi_nor_eon; extern const struct spi_nor_manufacturer spi_nor_esmt; extern const struct spi_nor_manufacturer spi_nor_everspin; +extern const struct spi_nor_manufacturer spi_nor_fujitsu; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/fujitsu.c b/drivers/mtd/spi-nor/fujitsu.c new file mode 100644 index 000000000000..e385d93e756c --- /dev/null +++ b/drivers/mtd/spi-nor/fujitsu.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info fujitsu_parts[] = { + /* Fujitsu */ + { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) }, +}; + +const struct spi_nor_manufacturer spi_nor_fujitsu = { + .name = "fujitsu", + .parts = fujitsu_parts, + .nparts = ARRAY_SIZE(fujitsu_parts), +}; From acb96ecd59f7fdd8b2791f225d101dfdc2ab38a9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:43 +0000 Subject: [PATCH 073/103] mtd: spi-nor: Move GigaDevice bits out of core.c Create a SPI NOR manufacturer driver for GigaDevice chips, and move the GigaDevice definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 60 +------------------------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/gigadevice.c | 59 +++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 59 deletions(-) create mode 100644 drivers/mtd/spi-nor/gigadevice.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index ca6222d98b0f..38f704be4b03 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -6,4 +6,5 @@ spi-nor-objs += eon.o spi-nor-objs += esmt.o spi-nor-objs += everspin.o spi-nor-objs += fujitsu.o +spi-nor-objs += gigadevice.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 3850c638f95a..236632d15c42 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2054,21 +2054,6 @@ static struct spi_nor_fixups mx25l25635_fixups = { .post_bfpt = mx25l25635_post_bfpt_fixups, }; -static void gd25q256_default_init(struct spi_nor *nor) -{ - /* - * Some manufacturer like GigaDevice may use different - * bit to set QE on different memories, so the MFR can't - * indicate the quad_enable method for this case, we need - * to set it in the default_init fixup hook. - */ - nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; -} - -static struct spi_nor_fixups gd25q256_fixups = { - .default_init = gd25q256_default_init, -}; - /* NOTE: double check command sets and memory organization when you add * more nor chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. @@ -2081,50 +2066,6 @@ static struct spi_nor_fixups gd25q256_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* GigaDevice */ - { - "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "gd25lq32", INFO(0xc86016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "gd25lq128d", INFO(0xc86018, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | - SPI_NOR_TB_SR_BIT6) - .fixups = &gd25q256_fixups, - }, - /* Intel/Numonyx -- xxxs33b */ { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, @@ -2430,6 +2371,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_esmt, &spi_nor_everspin, &spi_nor_fujitsu, + &spi_nor_gigadevice, }; static const struct flash_info * diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index d094112aa4a2..da88d7e55c76 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -172,6 +172,7 @@ extern const struct spi_nor_manufacturer spi_nor_eon; extern const struct spi_nor_manufacturer spi_nor_esmt; extern const struct spi_nor_manufacturer spi_nor_everspin; extern const struct spi_nor_manufacturer spi_nor_fujitsu; +extern const struct spi_nor_manufacturer spi_nor_gigadevice; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c new file mode 100644 index 000000000000..7930e4490dab --- /dev/null +++ b/drivers/mtd/spi-nor/gigadevice.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static void gd25q256_default_init(struct spi_nor *nor) +{ + /* + * Some manufacturer like GigaDevice may use different + * bit to set QE on different memories, so the MFR can't + * indicate the quad_enable method for this case, we need + * to set it in the default_init fixup hook. + */ + nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; +} + +static struct spi_nor_fixups gd25q256_fixups = { + .default_init = gd25q256_default_init, +}; + +static const struct flash_info gigadevice_parts[] = { + { "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25lq32", INFO(0xc86016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25lq64c", INFO(0xc86017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25lq128d", INFO(0xc86018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | + SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6) + .fixups = &gd25q256_fixups }, +}; + +const struct spi_nor_manufacturer spi_nor_gigadevice = { + .name = "gigadevice", + .parts = gigadevice_parts, + .nparts = ARRAY_SIZE(gigadevice_parts), +}; From aa6351877f3e8a7006d1e17f6be3e15dc12b943e Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:43 +0000 Subject: [PATCH 074/103] mtd: spi-nor: Move Intel bits out of core.c Create a SPI NOR manufacturer driver for Intel chips, and move the Intel definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus Reviewed-by: Mika Westerberg --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 15 +-------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/intel.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 drivers/mtd/spi-nor/intel.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 38f704be4b03..8eb741a27fa7 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -7,4 +7,5 @@ spi-nor-objs += esmt.o spi-nor-objs += everspin.o spi-nor-objs += fujitsu.o spi-nor-objs += gigadevice.o +spi-nor-objs += intel.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 236632d15c42..e860f4efdeba 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2066,11 +2066,6 @@ static struct spi_nor_fixups mx25l25635_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Intel/Numonyx -- xxxs33b */ - { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, - { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, - { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, - /* ISSI */ { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) }, { "is25lq040b", INFO(0x9d4013, 0, 64 * 1024, 8, @@ -2372,6 +2367,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_everspin, &spi_nor_fujitsu, &spi_nor_gigadevice, + &spi_nor_intel, }; static const struct flash_info * @@ -3151,11 +3147,6 @@ static int spi_nor_setup(struct spi_nor *nor, return nor->params.setup(nor, hwcaps); } -static void intel_set_default_init(struct spi_nor *nor) -{ - nor->flags |= SNOR_F_HAS_LOCK; -} - static void issi_set_default_init(struct spi_nor *nor) { nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; @@ -3194,10 +3185,6 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { /* Init flash parameters based on MFR */ switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_INTEL: - intel_set_default_init(nor); - break; - case SNOR_MFR_ISSI: issi_set_default_init(nor); break; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index da88d7e55c76..3d31e7fc4ac4 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -173,6 +173,7 @@ extern const struct spi_nor_manufacturer spi_nor_esmt; extern const struct spi_nor_manufacturer spi_nor_everspin; extern const struct spi_nor_manufacturer spi_nor_fujitsu; extern const struct spi_nor_manufacturer spi_nor_gigadevice; +extern const struct spi_nor_manufacturer spi_nor_intel; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/intel.c b/drivers/mtd/spi-nor/intel.c new file mode 100644 index 000000000000..d8196f101368 --- /dev/null +++ b/drivers/mtd/spi-nor/intel.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info intel_parts[] = { + /* Intel/Numonyx -- xxxs33b */ + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, +}; + +static void intel_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; +} + +static const struct spi_nor_fixups intel_fixups = { + .default_init = intel_default_init, +}; + +const struct spi_nor_manufacturer spi_nor_intel = { + .name = "intel", + .parts = intel_parts, + .nparts = ARRAY_SIZE(intel_parts), + .fixups = &intel_fixups, +}; From 0a37198183c60fc73c23583d6ad23d4b832ce250 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:44 +0000 Subject: [PATCH 075/103] mtd: spi-nor: Move ISSI bits out of core.c Create a SPI NOR manufacturer driver for ISSI chips, and move the ISSI definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 66 +--------------------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/issi.c | 83 ++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 65 deletions(-) create mode 100644 drivers/mtd/spi-nor/issi.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 8eb741a27fa7..5c849f104cc4 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -8,4 +8,5 @@ spi-nor-objs += everspin.o spi-nor-objs += fujitsu.o spi-nor-objs += gigadevice.o spi-nor-objs += intel.o +spi-nor-objs += issi.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index e860f4efdeba..a982d8ea811c 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2007,28 +2007,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) return 0; } -static int -is25lp256_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt, - struct spi_nor_flash_parameter *params) -{ - /* - * IS25LP256 supports 4B opcodes, but the BFPT advertises a - * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width. - * Overwrite the address width advertised by the BFPT. - */ - if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == - BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) - nor->addr_width = 4; - - return 0; -} - -static struct spi_nor_fixups is25lp256_fixups = { - .post_bfpt = is25lp256_post_bfpt_fixups, -}; - static int mx25l25635_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -2066,35 +2044,6 @@ static struct spi_nor_fixups mx25l25635_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* ISSI */ - { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) }, - { "is25lq040b", INFO(0x9d4013, 0, 64 * 1024, 8, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "is25lp016d", INFO(0x9d6015, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "is25lp080d", INFO(0x9d6014, 0, 64 * 1024, 16, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "is25lp032", INFO(0x9d6016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ) }, - { "is25lp064", INFO(0x9d6017, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_DUAL_READ) }, - { "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ) }, - { "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES) - .fixups = &is25lp256_fixups }, - { "is25wp032", INFO(0x9d7016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "is25wp064", INFO(0x9d7017, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES) - .fixups = &is25lp256_fixups }, - /* Macronix */ { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, @@ -2175,11 +2124,6 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, - /* PMC */ - { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, - { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, - { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, - /* Spansion/Cypress -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ @@ -2368,6 +2312,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_fujitsu, &spi_nor_gigadevice, &spi_nor_intel, + &spi_nor_issi, }; static const struct flash_info * @@ -3147,11 +3092,6 @@ static int spi_nor_setup(struct spi_nor *nor, return nor->params.setup(nor, hwcaps); } -static void issi_set_default_init(struct spi_nor *nor) -{ - nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; -} - static void macronix_set_default_init(struct spi_nor *nor) { nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; @@ -3185,10 +3125,6 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { /* Init flash parameters based on MFR */ switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_ISSI: - issi_set_default_init(nor); - break; - case SNOR_MFR_MACRONIX: macronix_set_default_init(nor); break; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 3d31e7fc4ac4..0967c84235ce 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -174,6 +174,7 @@ extern const struct spi_nor_manufacturer spi_nor_everspin; extern const struct spi_nor_manufacturer spi_nor_fujitsu; extern const struct spi_nor_manufacturer spi_nor_gigadevice; extern const struct spi_nor_manufacturer spi_nor_intel; +extern const struct spi_nor_manufacturer spi_nor_issi; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c new file mode 100644 index 000000000000..3a1c34c41388 --- /dev/null +++ b/drivers/mtd/spi-nor/issi.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static int +is25lp256_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + /* + * IS25LP256 supports 4B opcodes, but the BFPT advertises a + * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width. + * Overwrite the address width advertised by the BFPT. + */ + if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == + BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) + nor->addr_width = 4; + + return 0; +} + +static struct spi_nor_fixups is25lp256_fixups = { + .post_bfpt = is25lp256_post_bfpt_fixups, +}; + +static const struct flash_info issi_parts[] = { + /* ISSI */ + { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2, SECT_4K) }, + { "is25lq040b", INFO(0x9d4013, 0, 64 * 1024, 8, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25lp016d", INFO(0x9d6015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25lp080d", INFO(0x9d6014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25lp032", INFO(0x9d6016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "is25lp064", INFO(0x9d6017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "is25lp128", INFO(0x9d6018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "is25lp256", INFO(0x9d6019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) + .fixups = &is25lp256_fixups }, + { "is25wp032", INFO(0x9d7016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25wp064", INFO(0x9d7017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25wp128", INFO(0x9d7018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "is25wp256", INFO(0x9d7019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) + .fixups = &is25lp256_fixups }, + + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, +}; + +static void issi_default_init(struct spi_nor *nor) +{ + nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; +} + +static const struct spi_nor_fixups issi_fixups = { + .default_init = issi_default_init, +}; + +const struct spi_nor_manufacturer spi_nor_issi = { + .name = "issi", + .parts = issi_parts, + .nparts = ARRAY_SIZE(issi_parts), + .fixups = &issi_fixups, +}; From 10526d85e4c6cb2f83e582b4b03a9587d7bc09d9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:45 +0000 Subject: [PATCH 076/103] mtd: spi-nor: Move Macronix bits out of core.c Create a SPI NOR manufacturer driver for Macronix chips, and move the Macronix definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus Tested-by: Xiang Chen --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 69 +----------------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/macronix.c | 98 ++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 68 deletions(-) create mode 100644 drivers/mtd/spi-nor/macronix.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 5c849f104cc4..c94798987801 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -9,4 +9,5 @@ spi-nor-objs += fujitsu.o spi-nor-objs += gigadevice.o spi-nor-objs += intel.o spi-nor-objs += issi.o +spi-nor-objs += macronix.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index a982d8ea811c..beb3c7372647 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2007,31 +2007,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) return 0; } -static int -mx25l25635_post_bfpt_fixups(struct spi_nor *nor, - const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt, - struct spi_nor_flash_parameter *params) -{ - /* - * MX25L25635F supports 4B opcodes but MX25L25635E does not. - * Unfortunately, Macronix has re-used the same JEDEC ID for both - * variants which prevents us from defining a new entry in the parts - * table. - * We need a way to differentiate MX25L25635E and MX25L25635F, and it - * seems that the F version advertises support for Fast Read 4-4-4 in - * its BFPT table. - */ - if (bfpt->dwords[BFPT_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) - nor->flags |= SNOR_F_4B_OPCODES; - - return 0; -} - -static struct spi_nor_fixups mx25l25635_fixups = { - .post_bfpt = mx25l25635_post_bfpt_fixups, -}; - /* NOTE: double check command sets and memory organization when you add * more nor chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. @@ -2044,39 +2019,6 @@ static struct spi_nor_fixups mx25l25635_fixups = { * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Macronix */ - { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, - { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, - { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, - { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, - { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, - { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) }, - { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, - { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) }, - { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) }, - { "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) }, - { "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) }, - { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, - { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, - { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, - { "mx25r3235f", INFO(0xc22816, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, - SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) - .fixups = &mx25l25635_fixups }, - { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) }, - { "mx25v8035f", INFO(0xc22314, 0, 64 * 1024, 16, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) }, - /* Micron <--> ST Micro */ { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_QUAD_READ) }, { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) }, @@ -2313,6 +2255,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_gigadevice, &spi_nor_intel, &spi_nor_issi, + &spi_nor_macronix, }; static const struct flash_info * @@ -3092,12 +3035,6 @@ static int spi_nor_setup(struct spi_nor *nor, return nor->params.setup(nor, hwcaps); } -static void macronix_set_default_init(struct spi_nor *nor) -{ - nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; - nor->params.set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; -} - static void sst_set_default_init(struct spi_nor *nor) { nor->flags |= SNOR_F_HAS_LOCK; @@ -3125,10 +3062,6 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { /* Init flash parameters based on MFR */ switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_MACRONIX: - macronix_set_default_init(nor); - break; - case SNOR_MFR_ST: case SNOR_MFR_MICRON: st_micron_set_default_init(nor); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 0967c84235ce..8ef5acc5d052 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -175,6 +175,7 @@ extern const struct spi_nor_manufacturer spi_nor_fujitsu; extern const struct spi_nor_manufacturer spi_nor_gigadevice; extern const struct spi_nor_manufacturer spi_nor_intel; extern const struct spi_nor_manufacturer spi_nor_issi; +extern const struct spi_nor_manufacturer spi_nor_macronix; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c new file mode 100644 index 000000000000..c9b6b45d8f99 --- /dev/null +++ b/drivers/mtd/spi-nor/macronix.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static int +mx25l25635_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + /* + * MX25L25635F supports 4B opcodes but MX25L25635E does not. + * Unfortunately, Macronix has re-used the same JEDEC ID for both + * variants which prevents us from defining a new entry in the parts + * table. + * We need a way to differentiate MX25L25635E and MX25L25635F, and it + * seems that the F version advertises support for Fast Read 4-4-4 in + * its BFPT table. + */ + if (bfpt->dwords[BFPT_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) + nor->flags |= SNOR_F_4B_OPCODES; + + return 0; +} + +static struct spi_nor_fixups mx25l25635_fixups = { + .post_bfpt = mx25l25635_post_bfpt_fixups, +}; + +static const struct flash_info macronix_parts[] = { + /* Macronix */ + { "mx25l512e", INFO(0xc22010, 0, 64 * 1024, 1, SECT_4K) }, + { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) }, + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, + { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) }, + { "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4, SECT_4K) }, + { "mx25u3235f", INFO(0xc22536, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, + { "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) }, + { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, + { "mx25r3235f", INFO(0xc22816, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, + { "mx25u12835f", INFO(0xc22538, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, + { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) + .fixups = &mx25l25635_fixups }, + { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_4B_OPCODES) }, + { "mx25v8035f", INFO(0xc22314, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, + { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, + { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) }, + { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, + SPI_NOR_QUAD_READ) }, +}; + +static void macronix_default_init(struct spi_nor *nor) +{ + nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params.set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; +} + +static const struct spi_nor_fixups macronix_fixups = { + .default_init = macronix_default_init, +}; + +const struct spi_nor_manufacturer spi_nor_macronix = { + .name = "macronix", + .parts = macronix_parts, + .nparts = ARRAY_SIZE(macronix_parts), + .fixups = ¯onix_fixups, +}; From 15f5c7e54e658aa56e815192cfca59a8b4c6a439 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:46 +0000 Subject: [PATCH 077/103] mtd: spi-nor: Move Micron/ST bits out of core.c Create a SPI NOR manufacturer driver for Micron/ST chips, and move the Micron/ST definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 122 +------------------------ drivers/mtd/spi-nor/core.h | 2 + drivers/mtd/spi-nor/micron-st.c | 153 ++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 120 deletions(-) create mode 100644 drivers/mtd/spi-nor/micron-st.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index c94798987801..c7e5fb908bec 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -10,4 +10,5 @@ spi-nor-objs += gigadevice.o spi-nor-objs += intel.o spi-nor-objs += issi.o spi-nor-objs += macronix.o +spi-nor-objs += micron-st.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index beb3c7372647..4885607dc917 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -399,30 +399,6 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) return ret; } -/** - * st_micron_set_4byte_addr_mode() - Set 4-byte address mode for ST and Micron - * flashes. - * @nor: pointer to 'struct spi_nor'. - * @enable: true to enter the 4-byte address mode, false to exit the 4-byte - * address mode. - * - * Return: 0 on success, -errno otherwise. - */ -static int st_micron_set_4byte_addr_mode(struct spi_nor *nor, bool enable) -{ - int ret; - - ret = spi_nor_write_enable(nor); - if (ret) - return ret; - - ret = spi_nor_set_4byte_addr_mode(nor, enable); - if (ret) - return ret; - - return spi_nor_write_disable(nor); -} - /** * spansion_set_4byte_addr_mode() - Set 4-byte address mode for Spansion * flashes. @@ -2019,53 +1995,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Micron <--> ST Micro */ - { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_QUAD_READ) }, - { "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 | - USE_FSR | SPI_NOR_QUAD_READ) }, - { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | - USE_FSR | SPI_NOR_QUAD_READ) }, - { "mt25ql256a", INFO6(0x20ba19, 0x104400, 64 * 1024, 512, - SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | - USE_FSR | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ) }, - { "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512, - SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | - USE_FSR | SPI_NOR_QUAD_READ) }, - { "mt25ql512a", INFO6(0x20ba20, 0x104400, 64 * 1024, 1024, - SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, - { "mt25qu512a", INFO6(0x20bb20, 0x104400, 64 * 1024, 1024, - SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | - SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "n25q512a", INFO(0x20bb20, 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 | NO_CHIP_ERASE) }, - { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, - { "mt25ql02g", INFO(0x20ba22, 0, 64 * 1024, 4096, - SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | - NO_CHIP_ERASE) }, - { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, - - /* Micron */ - { - "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512, - SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | - SPI_NOR_4B_OPCODES) - }, - { "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048, - SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | - SPI_NOR_4B_OPCODES) }, - /* Spansion/Cypress -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ @@ -2123,42 +2052,6 @@ static const struct flash_info spi_nor_ids[] = { SPI_NOR_DUAL_READ) }, { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - /* ST Microelectronics -- newer production may have feature updates */ - { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, - { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, - { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, - { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, - { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, - { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, - { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, - { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, - { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, - - { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, - { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, - { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, - { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, - { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, - { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, - { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, - { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, - { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, - - { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, - { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, - { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, - - { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, - { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, - { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, - - { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, - { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, - { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, - { "m25px80", INFO(0x207114, 0, 64 * 1024, 16, 0) }, - /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1, SECT_4K) }, { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, @@ -2256,6 +2149,8 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_intel, &spi_nor_issi, &spi_nor_macronix, + &spi_nor_micron, + &spi_nor_st, }; static const struct flash_info * @@ -3040,14 +2935,6 @@ static void sst_set_default_init(struct spi_nor *nor) nor->flags |= SNOR_F_HAS_LOCK; } -static void st_micron_set_default_init(struct spi_nor *nor) -{ - nor->flags |= SNOR_F_HAS_LOCK; - nor->flags &= ~SNOR_F_HAS_16BIT_SR; - nor->params.quad_enable = NULL; - nor->params.set_4byte_addr_mode = st_micron_set_4byte_addr_mode; -} - static void winbond_set_default_init(struct spi_nor *nor) { nor->params.set_4byte_addr_mode = winbond_set_4byte_addr_mode; @@ -3062,11 +2949,6 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { /* Init flash parameters based on MFR */ switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_ST: - case SNOR_MFR_MICRON: - st_micron_set_default_init(nor); - break; - case SNOR_MFR_SST: sst_set_default_init(nor); break; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 8ef5acc5d052..fc4a70d8713c 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -176,6 +176,8 @@ extern const struct spi_nor_manufacturer spi_nor_gigadevice; extern const struct spi_nor_manufacturer spi_nor_intel; extern const struct spi_nor_manufacturer spi_nor_issi; extern const struct spi_nor_manufacturer spi_nor_macronix; +extern const struct spi_nor_manufacturer spi_nor_micron; +extern const struct spi_nor_manufacturer spi_nor_st; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c new file mode 100644 index 000000000000..9d32ee0ef5a5 --- /dev/null +++ b/drivers/mtd/spi-nor/micron-st.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info micron_parts[] = { + { "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512, + SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | + SPI_NOR_4B_OPCODES) }, + { "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048, + SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | + SPI_NOR_4B_OPCODES) }, +}; + +static const struct flash_info st_parts[] = { + { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_QUAD_READ) }, + { "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 | USE_FSR | SPI_NOR_QUAD_READ) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "mt25ql256a", INFO6(0x20ba19, 0x104400, 64 * 1024, 512, + SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | + USE_FSR | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, + { "mt25qu256a", INFO6(0x20bb19, 0x104400, 64 * 1024, 512, + SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "mt25ql512a", INFO6(0x20ba20, 0x104400, 64 * 1024, 1024, + SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + { "mt25qu512a", INFO6(0x20bb20, 0x104400, 64 * 1024, 1024, + SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { "n25q512a", INFO(0x20bb20, 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 | + NO_CHIP_ERASE) }, + { "n25q00a", INFO(0x20bb21, 0, 64 * 1024, 2048, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | + NO_CHIP_ERASE) }, + { "mt25ql02g", INFO(0x20ba22, 0, 64 * 1024, 4096, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | + NO_CHIP_ERASE) }, + { "mt25qu02g", INFO(0x20bb22, 0, 64 * 1024, 4096, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | + NO_CHIP_ERASE) }, + + { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, + { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, + { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, + { "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) }, + { "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) }, + { "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) }, + { "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) }, + { "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) }, + { "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) }, + + { "m25p05-nonjedec", INFO(0, 0, 32 * 1024, 2, 0) }, + { "m25p10-nonjedec", INFO(0, 0, 32 * 1024, 4, 0) }, + { "m25p20-nonjedec", INFO(0, 0, 64 * 1024, 4, 0) }, + { "m25p40-nonjedec", INFO(0, 0, 64 * 1024, 8, 0) }, + { "m25p80-nonjedec", INFO(0, 0, 64 * 1024, 16, 0) }, + { "m25p16-nonjedec", INFO(0, 0, 64 * 1024, 32, 0) }, + { "m25p32-nonjedec", INFO(0, 0, 64 * 1024, 64, 0) }, + { "m25p64-nonjedec", INFO(0, 0, 64 * 1024, 128, 0) }, + { "m25p128-nonjedec", INFO(0, 0, 256 * 1024, 64, 0) }, + + { "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) }, + { "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) }, + { "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) }, + + { "m25pe20", INFO(0x208012, 0, 64 * 1024, 4, 0) }, + { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, + { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, + + { "m25px16", INFO(0x207115, 0, 64 * 1024, 32, SECT_4K) }, + { "m25px32", INFO(0x207116, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s0", INFO(0x207316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px32-s1", INFO(0x206316, 0, 64 * 1024, 64, SECT_4K) }, + { "m25px64", INFO(0x207117, 0, 64 * 1024, 128, 0) }, + { "m25px80", INFO(0x207114, 0, 64 * 1024, 16, 0) }, +}; + +/** + * st_micron_set_4byte_addr_mode() - Set 4-byte address mode for ST and Micron + * flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int st_micron_set_4byte_addr_mode(struct spi_nor *nor, bool enable) +{ + int ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + ret = spi_nor_set_4byte_addr_mode(nor, enable); + if (ret) + return ret; + + return spi_nor_write_disable(nor); +} + +static void micron_st_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; + nor->flags &= ~SNOR_F_HAS_16BIT_SR; + nor->params.quad_enable = NULL; + nor->params.set_4byte_addr_mode = st_micron_set_4byte_addr_mode; +} + +static const struct spi_nor_fixups micron_st_fixups = { + .default_init = micron_st_default_init, +}; + +const struct spi_nor_manufacturer spi_nor_micron = { + .name = "micron", + .parts = micron_parts, + .nparts = ARRAY_SIZE(micron_parts), + .fixups = µn_st_fixups, +}; + +const struct spi_nor_manufacturer spi_nor_st = { + .name = "st", + .parts = st_parts, + .nparts = ARRAY_SIZE(st_parts), + .fixups = µn_st_fixups, +}; From 0173c32a0ebd42c3b0c3f53005590c7749d0652c Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:46 +0000 Subject: [PATCH 078/103] mtd: spi-nor: Move Spansion bits out of core.c Create a SPI NOR manufacturer driver for Spansion chips, and move the Spansion definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 59 +-------------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/spansion.c | 95 ++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 58 deletions(-) create mode 100644 drivers/mtd/spi-nor/spansion.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index c7e5fb908bec..cb06ee50bf68 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -11,4 +11,5 @@ spi-nor-objs += intel.o spi-nor-objs += issi.o spi-nor-objs += macronix.o spi-nor-objs += micron-st.o +spi-nor-objs += spansion.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 4885607dc917..1500951254d9 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1995,44 +1995,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Spansion/Cypress -- single (large) sector size only, at least - * for the chips listed here (without boot sectors). - */ - { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl128s0", INFO6(0x012018, 0x4d0080, 256 * 1024, 64, - SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, - { "s25fl128s1", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, - SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, - { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, - { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256, - SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | USE_CLSR) }, - { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, - { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, - { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, - { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, - { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, - { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, - { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, - { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, - { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, - { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, - { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, - { "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - { "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, - { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) }, - { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) }, - { "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) }, - { "s25fl064l", INFO(0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "s25fl128l", INFO(0x016018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, - /* SST -- large erase sizes are "overlays", "sectors" are 4K */ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, @@ -2151,6 +2113,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_macronix, &spi_nor_micron, &spi_nor_st, + &spi_nor_spansion, }; static const struct flash_info * @@ -3084,17 +3047,6 @@ static void spi_nor_info_init_params(struct spi_nor *nor) spi_nor_init_uniform_erase_map(map, erase_mask, params->size); } -static void spansion_post_sfdp_fixups(struct spi_nor *nor) -{ - if (nor->params.size <= SZ_16M) - return; - - nor->flags |= SNOR_F_4B_OPCODES; - /* No small sector erase for 4-byte command set */ - nor->erase_opcode = SPINOR_OP_SE; - nor->mtd.erasesize = nor->info->sector_size; -} - static void s3an_post_sfdp_fixups(struct spi_nor *nor) { nor->params.setup = s3an_nor_setup; @@ -3112,15 +3064,6 @@ static void s3an_post_sfdp_fixups(struct spi_nor *nor) */ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor) { - switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_SPANSION: - spansion_post_sfdp_fixups(nor); - break; - - default: - break; - } - if (nor->info->flags & SPI_S3AN) s3an_post_sfdp_fixups(nor); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index fc4a70d8713c..470025131d47 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -178,6 +178,7 @@ extern const struct spi_nor_manufacturer spi_nor_issi; extern const struct spi_nor_manufacturer spi_nor_macronix; extern const struct spi_nor_manufacturer spi_nor_micron; extern const struct spi_nor_manufacturer spi_nor_st; +extern const struct spi_nor_manufacturer spi_nor_spansion; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c new file mode 100644 index 000000000000..16683983a20e --- /dev/null +++ b/drivers/mtd/spi-nor/spansion.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info spansion_parts[] = { + /* Spansion/Cypress -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl128s0", INFO6(0x012018, 0x4d0080, 256 * 1024, 64, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + USE_CLSR) }, + { "s25fl128s1", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + USE_CLSR) }, + { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, USE_CLSR) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + USE_CLSR) }, + { "s25fl512s", INFO6(0x010220, 0x4d0080, 256 * 1024, 256, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | USE_CLSR) }, + { "s25fs512s", INFO6(0x010220, 0x4d0081, 256 * 1024, 256, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + USE_CLSR) }, + { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, + { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, + { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, + { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + USE_CLSR) }, + { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, + SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + USE_CLSR) }, + { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) }, + { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, + { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, + { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, + { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl004k", INFO(0xef4013, 0, 64 * 1024, 8, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "s25fl116k", INFO(0x014015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) }, + { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) }, + { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "s25fl064l", INFO(0x016017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) }, + { "s25fl128l", INFO(0x016018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) }, + { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) }, +}; + +static void spansion_post_sfdp_fixups(struct spi_nor *nor) +{ + if (nor->params.size <= SZ_16M) + return; + + nor->flags |= SNOR_F_4B_OPCODES; + /* No small sector erase for 4-byte command set */ + nor->erase_opcode = SPINOR_OP_SE; + nor->mtd.erasesize = nor->info->sector_size; +} + +static const struct spi_nor_fixups spansion_fixups = { + .post_sfdp = spansion_post_sfdp_fixups, +}; + +const struct spi_nor_manufacturer spi_nor_spansion = { + .name = "spansion", + .parts = spansion_parts, + .nparts = ARRAY_SIZE(spansion_parts), + .fixups = &spansion_fixups, +}; From c53b3f92b405d611e72a0f4cfd4ae1794130e70f Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:47 +0000 Subject: [PATCH 079/103] mtd: spi-nor: Move SST bits out of core.c Create a SPI NOR manufacturer driver for SST chips, and move the SST definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 123 +--------------------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/sst.c | 151 +++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 120 deletions(-) create mode 100644 drivers/mtd/spi-nor/sst.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index cb06ee50bf68..ef7afc654a15 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -12,4 +12,5 @@ spi-nor-objs += issi.o spi-nor-objs += macronix.o spi-nor-objs += micron-st.o spi-nor-objs += spansion.o +spi-nor-objs += sst.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 1500951254d9..c81eac6e9731 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1995,25 +1995,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* SST -- large erase sizes are "overlays", "sectors" are 4K */ - { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, - { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) }, - { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, - { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, - { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, - { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) }, - { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) }, - { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) }, - { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) }, - { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, - { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, - { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32, SECT_4K | - SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32, SECT_4K | - SPI_NOR_DUAL_READ) }, - { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1, SECT_4K) }, { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, @@ -2114,6 +2095,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_micron, &spi_nor_st, &spi_nor_spansion, + &spi_nor_sst, }; static const struct flash_info * @@ -2214,92 +2196,6 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, return ret; } -static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct spi_nor *nor = mtd_to_spi_nor(mtd); - size_t actual = 0; - int ret; - - dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); - - ret = spi_nor_lock_and_prep(nor); - if (ret) - return ret; - - ret = spi_nor_write_enable(nor); - if (ret) - goto out; - - nor->sst_write_second = false; - - /* Start write from odd address. */ - if (to % 2) { - nor->program_opcode = SPINOR_OP_BP; - - /* write one byte. */ - ret = spi_nor_write_data(nor, to, 1, buf); - if (ret < 0) - goto out; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto out; - - to++; - actual++; - } - - /* Write out most of the data here. */ - for (; actual < len - 1; actual += 2) { - nor->program_opcode = SPINOR_OP_AAI_WP; - - /* write two bytes. */ - ret = spi_nor_write_data(nor, to, 2, buf + actual); - if (ret < 0) - goto out; - WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto out; - to += 2; - nor->sst_write_second = true; - } - nor->sst_write_second = false; - - ret = spi_nor_write_disable(nor); - if (ret) - goto out; - - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto out; - - /* Write out trailing byte if it exists. */ - if (actual != len) { - ret = spi_nor_write_enable(nor); - if (ret) - goto out; - - nor->program_opcode = SPINOR_OP_BP; - ret = spi_nor_write_data(nor, to, 1, buf + actual); - if (ret < 0) - goto out; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto out; - - actual += 1; - - ret = spi_nor_write_disable(nor); - } -out: - *retlen += actual; - spi_nor_unlock_and_unprep(nor); - return ret; -} - /* * Write an address range to the nor chip. Data must be written in * FLASH_PAGESIZE chunks. The address range may be any size provided @@ -2893,11 +2789,6 @@ static int spi_nor_setup(struct spi_nor *nor, return nor->params.setup(nor, hwcaps); } -static void sst_set_default_init(struct spi_nor *nor) -{ - nor->flags |= SNOR_F_HAS_LOCK; -} - static void winbond_set_default_init(struct spi_nor *nor) { nor->params.set_4byte_addr_mode = winbond_set_4byte_addr_mode; @@ -2912,10 +2803,6 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { /* Init flash parameters based on MFR */ switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_SST: - sst_set_default_init(nor); - break; - case SNOR_MFR_WINBOND: winbond_set_default_init(nor); break; @@ -3387,6 +3274,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (info->flags & SPI_NOR_HAS_LOCK) nor->flags |= SNOR_F_HAS_LOCK; + mtd->_write = spi_nor_write; + /* Init flash parameters based on flash_info struct and SFDP */ spi_nor_init_params(nor); @@ -3407,12 +3296,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->_is_locked = spi_nor_is_locked; } - /* sst nor chips use AAI word program */ - if (info->flags & SST_WRITE) - mtd->_write = sst_write; - else - mtd->_write = spi_nor_write; - if (info->flags & USE_FSR) nor->flags |= SNOR_F_USE_FSR; if (info->flags & SPI_NOR_HAS_TB) { diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 470025131d47..e49a2200a90d 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -179,6 +179,7 @@ extern const struct spi_nor_manufacturer spi_nor_macronix; extern const struct spi_nor_manufacturer spi_nor_micron; extern const struct spi_nor_manufacturer spi_nor_st; extern const struct spi_nor_manufacturer spi_nor_spansion; +extern const struct spi_nor_manufacturer spi_nor_sst; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c new file mode 100644 index 000000000000..e0af6d25d573 --- /dev/null +++ b/drivers/mtd/spi-nor/sst.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info sst_parts[] = { + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, + SECT_4K | SST_WRITE) }, + { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, + SECT_4K | SST_WRITE) }, + { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, + SECT_4K | SST_WRITE) }, + { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, + SECT_4K | SST_WRITE) }, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, + { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, + SECT_4K | SST_WRITE) }, + { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, + SECT_4K | SST_WRITE) }, + { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, + SECT_4K | SST_WRITE) }, + { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) }, + { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) }, + { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, + SECT_4K | SST_WRITE) }, + { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, + SECT_4K | SST_WRITE) }, + { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, + { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ) }, + { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ) }, +}; + +static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + size_t actual = 0; + int ret; + + dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len); + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = spi_nor_write_enable(nor); + if (ret) + goto out; + + nor->sst_write_second = false; + + /* Start write from odd address. */ + if (to % 2) { + nor->program_opcode = SPINOR_OP_BP; + + /* write one byte. */ + ret = spi_nor_write_data(nor, to, 1, buf); + if (ret < 0) + goto out; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto out; + + to++; + actual++; + } + + /* Write out most of the data here. */ + for (; actual < len - 1; actual += 2) { + nor->program_opcode = SPINOR_OP_AAI_WP; + + /* write two bytes. */ + ret = spi_nor_write_data(nor, to, 2, buf + actual); + if (ret < 0) + goto out; + WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto out; + to += 2; + nor->sst_write_second = true; + } + nor->sst_write_second = false; + + ret = spi_nor_write_disable(nor); + if (ret) + goto out; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto out; + + /* Write out trailing byte if it exists. */ + if (actual != len) { + ret = spi_nor_write_enable(nor); + if (ret) + goto out; + + nor->program_opcode = SPINOR_OP_BP; + ret = spi_nor_write_data(nor, to, 1, buf + actual); + if (ret < 0) + goto out; + WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); + ret = spi_nor_wait_till_ready(nor); + if (ret) + goto out; + + actual += 1; + + ret = spi_nor_write_disable(nor); + } +out: + *retlen += actual; + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static void sst_default_init(struct spi_nor *nor) +{ + nor->flags |= SNOR_F_HAS_LOCK; +} + +static void sst_post_sfdp_fixups(struct spi_nor *nor) +{ + if (nor->info->flags & SST_WRITE) + nor->mtd._write = sst_write; +} + +static const struct spi_nor_fixups sst_fixups = { + .default_init = sst_default_init, + .post_sfdp = sst_post_sfdp_fixups, +}; + +const struct spi_nor_manufacturer spi_nor_sst = { + .name = "sst", + .parts = sst_parts, + .nparts = ARRAY_SIZE(sst_parts), + .fixups = &sst_fixups, +}; From 7b8b22010af95f67aee6e9f7a116056e811ec417 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:48 +0000 Subject: [PATCH 080/103] mtd: spi-nor: Move Winbond bits out of core.c Create a SPI NOR manufacturer driver for Winbond chips, and move the Winbond definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 115 +--------------------------------- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/winbond.c | 112 +++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 114 deletions(-) create mode 100644 drivers/mtd/spi-nor/winbond.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index ef7afc654a15..33b6f834a14f 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -13,4 +13,5 @@ spi-nor-objs += macronix.o spi-nor-objs += micron-st.o spi-nor-objs += spansion.o spi-nor-objs += sst.o +spi-nor-objs += winbond.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index c81eac6e9731..c89d3000c46e 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -465,38 +465,6 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear) return ret; } -/** - * winbond_set_4byte_addr_mode() - Set 4-byte address mode for Winbond flashes. - * @nor: pointer to 'struct spi_nor'. - * @enable: true to enter the 4-byte address mode, false to exit the 4-byte - * address mode. - * - * Return: 0 on success, -errno otherwise. - */ -static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) -{ - int ret; - - ret = spi_nor_set_4byte_addr_mode(nor, enable); - if (ret || enable) - return ret; - - /* - * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address - * Register to be set to 1, so all 3-byte-address reads come from the - * second 16M. We must clear the register to enable normal behavior. - */ - ret = spi_nor_write_enable(nor); - if (ret) - return ret; - - ret = spi_nor_write_ear(nor, 0); - if (ret) - return ret; - - return spi_nor_write_disable(nor); -} - /** * spi_nor_xread_sr() - Read the Status Register on S3AN flashes. * @nor: pointer to 'struct spi_nor'. @@ -1995,73 +1963,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ - { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1, SECT_4K) }, - { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, - { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, - { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, - { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, - { - "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, - { - "w25q16jv-im/jm", INFO(0xef7015, 0, 64 * 1024, 32, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) }, - { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) }, - { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) }, - { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, - { - "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "w25q32jv", INFO(0xef7016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "w25q32jwm", INFO(0xef8016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, - { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, - { - "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { - "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, - { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES) }, - { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024, - SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) }, - /* Catalyst / On Semiconductor -- non-JEDEC */ { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, @@ -2096,6 +1997,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_st, &spi_nor_spansion, &spi_nor_sst, + &spi_nor_winbond, }; static const struct flash_info * @@ -2789,11 +2691,6 @@ static int spi_nor_setup(struct spi_nor *nor, return nor->params.setup(nor, hwcaps); } -static void winbond_set_default_init(struct spi_nor *nor) -{ - nor->params.set_4byte_addr_mode = winbond_set_4byte_addr_mode; -} - /** * spi_nor_manufacturer_init_params() - Initialize the flash's parameters and * settings based on MFR register and ->default_init() hook. @@ -2801,16 +2698,6 @@ static void winbond_set_default_init(struct spi_nor *nor) */ static void spi_nor_manufacturer_init_params(struct spi_nor *nor) { - /* Init flash parameters based on MFR */ - switch (JEDEC_MFR(nor->info)) { - case SNOR_MFR_WINBOND: - winbond_set_default_init(nor); - break; - - default: - break; - } - if (nor->manufacturer && nor->manufacturer->fixups && nor->manufacturer->fixups->default_init) nor->manufacturer->fixups->default_init(nor); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index e49a2200a90d..fa65fbb6e0d2 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -180,6 +180,7 @@ extern const struct spi_nor_manufacturer spi_nor_micron; extern const struct spi_nor_manufacturer spi_nor_st; extern const struct spi_nor_manufacturer spi_nor_spansion; extern const struct spi_nor_manufacturer spi_nor_sst; +extern const struct spi_nor_manufacturer spi_nor_winbond; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c new file mode 100644 index 000000000000..3f8c568091d3 --- /dev/null +++ b/drivers/mtd/spi-nor/winbond.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info winbond_parts[] = { + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1, SECT_4K) }, + { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, + { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) }, + { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) }, + { "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q16jv-im/jm", INFO(0xef7015, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_DUAL_READ | + SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | + SPI_NOR_HAS_TB) }, + { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4, SECT_4K) }, + { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, + { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q32jv", INFO(0xef7016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { "w25q32jwm", INFO(0xef8016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, + { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_4B_OPCODES) }, + { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) }, +}; + +/** + * winbond_set_4byte_addr_mode() - Set 4-byte address mode for Winbond flashes. + * @nor: pointer to 'struct spi_nor'. + * @enable: true to enter the 4-byte address mode, false to exit the 4-byte + * address mode. + * + * Return: 0 on success, -errno otherwise. + */ +static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) +{ + int ret; + + ret = spi_nor_set_4byte_addr_mode(nor, enable); + if (ret || enable) + return ret; + + /* + * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address + * Register to be set to 1, so all 3-byte-address reads come from the + * second 16M. We must clear the register to enable normal behavior. + */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + ret = spi_nor_write_ear(nor, 0); + if (ret) + return ret; + + return spi_nor_write_disable(nor); +} + +static void winbond_default_init(struct spi_nor *nor) +{ + nor->params.set_4byte_addr_mode = winbond_set_4byte_addr_mode; +} + +static const struct spi_nor_fixups winbond_fixups = { + .default_init = winbond_default_init, +}; + +const struct spi_nor_manufacturer spi_nor_winbond = { + .name = "winbond", + .parts = winbond_parts, + .nparts = ARRAY_SIZE(winbond_parts), + .fixups = &winbond_fixups, +}; From d82592572662bcca7684eeb84e68daad16d91524 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:49 +0000 Subject: [PATCH 081/103] mtd: spi-nor: Move Catalyst bits out of core.c Create a SPI NOR manufacturer driver for Catalyst chips, and move the Catalyst definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/catalyst.c | 29 +++++++++++++++++++++++++++++ drivers/mtd/spi-nor/core.c | 8 +------- drivers/mtd/spi-nor/core.h | 1 + 4 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 drivers/mtd/spi-nor/catalyst.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 33b6f834a14f..cd8d95b727c9 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -2,6 +2,7 @@ spi-nor-objs := core.o sfdp.o spi-nor-objs += atmel.o +spi-nor-objs += catalyst.o spi-nor-objs += eon.o spi-nor-objs += esmt.o spi-nor-objs += everspin.o diff --git a/drivers/mtd/spi-nor/catalyst.c b/drivers/mtd/spi-nor/catalyst.c new file mode 100644 index 000000000000..011b83e99e95 --- /dev/null +++ b/drivers/mtd/spi-nor/catalyst.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info catalyst_parts[] = { + /* Catalyst / On Semiconductor -- non-JEDEC */ + { "cat25c11", CAT25_INFO(16, 8, 16, 1, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c03", CAT25_INFO(32, 8, 16, 2, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c09", CAT25_INFO(128, 8, 32, 2, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25c17", CAT25_INFO(256, 8, 32, 2, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, + SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, +}; + +const struct spi_nor_manufacturer spi_nor_catalyst = { + .name = "catalyst", + .parts = catalyst_parts, + .nparts = ARRAY_SIZE(catalyst_parts), +}; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index c89d3000c46e..99da7d8e9097 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1963,13 +1963,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, - /* Xilinx S3AN Internal Flash */ { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, @@ -1985,6 +1978,7 @@ static const struct flash_info spi_nor_ids[] = { static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_atmel, + &spi_nor_catalyst, &spi_nor_eon, &spi_nor_esmt, &spi_nor_everspin, diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index fa65fbb6e0d2..aaa2a460a159 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -168,6 +168,7 @@ struct spi_nor_manufacturer { /* Manufacturer drivers. */ extern const struct spi_nor_manufacturer spi_nor_atmel; +extern const struct spi_nor_manufacturer spi_nor_catalyst; extern const struct spi_nor_manufacturer spi_nor_eon; extern const struct spi_nor_manufacturer spi_nor_esmt; extern const struct spi_nor_manufacturer spi_nor_everspin; From 2d47cac1eee76a75c28886c15f82323eb0ec0eb5 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:50 +0000 Subject: [PATCH 082/103] mtd: spi-nor: Move Xilinx bits out of core.c Create a SPI NOR manufacturer driver for Xilinx chips, and move the Xilinx definitions outside of core.c. While at it, remove the SPI_S3AN flag which is now useless. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 76 +---------------------------- drivers/mtd/spi-nor/core.h | 13 +---- drivers/mtd/spi-nor/xilinx.c | 94 ++++++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 86 deletions(-) create mode 100644 drivers/mtd/spi-nor/xilinx.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index cd8d95b727c9..fa03513dd160 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -15,4 +15,5 @@ spi-nor-objs += micron-st.o spi-nor-objs += spansion.o spi-nor-objs += sst.o spi-nor-objs += winbond.o +spi-nor-objs += xilinx.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 99da7d8e9097..ca2f441e2806 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1095,26 +1095,6 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor) mutex_unlock(&nor->lock); } -/* - * This code converts an address to the Default Address Mode, that has non - * power of two page sizes. We must support this mode because it is the default - * mode supported by Xilinx tools, it can access the whole flash area and - * changing over to the Power-of-two mode is irreversible and corrupts the - * original data. - * Addr can safely be unsigned int, the biggest S3AN device is smaller than - * 4 MiB. - */ -static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr) -{ - u32 offset, page; - - offset = addr % nor->page_size; - page = addr / nor->page_size; - page <<= (nor->page_size > 512) ? 10 : 9; - - return page | offset; -} - static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr) { if (!nor->params.convert_addr) @@ -1963,13 +1943,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* Xilinx S3AN Internal Flash */ - { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, - { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, - { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, - { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, - { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, - /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */ { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, @@ -1992,6 +1965,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_spansion, &spi_nor_sst, &spi_nor_winbond, + &spi_nor_xilinx, }; static const struct flash_info * @@ -2177,46 +2151,6 @@ static int spi_nor_check(struct spi_nor *nor) return 0; } -static int s3an_nor_setup(struct spi_nor *nor, - const struct spi_nor_hwcaps *hwcaps) -{ - int ret; - - ret = spi_nor_xread_sr(nor, nor->bouncebuf); - if (ret) - return ret; - - nor->erase_opcode = SPINOR_OP_XSE; - nor->program_opcode = SPINOR_OP_XPP; - nor->read_opcode = SPINOR_OP_READ; - nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; - - /* - * This flashes have a page size of 264 or 528 bytes (known as - * Default addressing mode). It can be changed to a more standard - * Power of two mode where the page size is 256/512. This comes - * with a price: there is 3% less of space, the data is corrupted - * and the page size cannot be changed back to default addressing - * mode. - * - * The current addressing mode can be read from the XRDSR register - * and should not be changed, because is a destructive operation. - */ - if (nor->bouncebuf[0] & XSR_PAGESIZE) { - /* Flash in Power of 2 mode */ - nor->page_size = (nor->page_size == 264) ? 256 : 512; - nor->mtd.writebufsize = nor->page_size; - nor->mtd.size = 8 * nor->page_size * nor->info->n_sectors; - nor->mtd.erasesize = 8 * nor->page_size; - } else { - /* Flash in Default addressing mode */ - nor->params.convert_addr = s3an_convert_addr; - nor->mtd.erasesize = nor->info->sector_size; - } - - return 0; -} - static void spi_nor_set_read_settings(struct spi_nor_read_command *read, u8 num_mode_clocks, @@ -2815,11 +2749,6 @@ static void spi_nor_info_init_params(struct spi_nor *nor) spi_nor_init_uniform_erase_map(map, erase_mask, params->size); } -static void s3an_post_sfdp_fixups(struct spi_nor *nor) -{ - nor->params.setup = s3an_nor_setup; -} - /** * spi_nor_post_sfdp_fixups() - Updates the flash's parameters and settings * after SFDP has been parsed (is also called for SPI NORs that do not @@ -2832,9 +2761,6 @@ static void s3an_post_sfdp_fixups(struct spi_nor *nor) */ static void spi_nor_post_sfdp_fixups(struct spi_nor *nor) { - if (nor->info->flags & SPI_S3AN) - s3an_post_sfdp_fixups(nor); - if (nor->manufacturer && nor->manufacturer->fixups && nor->manufacturer->fixups->post_sfdp) nor->manufacturer->fixups->post_sfdp(nor); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index aaa2a460a159..8c666bff5768 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -73,16 +73,6 @@ struct flash_info { #define SPI_NOR_XSR_RDY BIT(10) /* * S3AN flashes have specific opcode to * read the status register. - * Flags SPI_NOR_XSR_RDY and SPI_S3AN - * use the same bit as one implies the - * other, but we will get rid of - * SPI_S3AN soon. - */ -#define SPI_S3AN BIT(10) /* - * Xilinx Spartan 3AN In-System Flash - * (MFR cannot be used for probing - * because it has the same value as - * ATMEL flashes) */ #define SPI_NOR_4B_OPCODES BIT(11) /* * Use dedicated 4byte address op codes @@ -150,7 +140,7 @@ struct flash_info { .n_sectors = (_n_sectors), \ .page_size = _page_size, \ .addr_width = 3, \ - .flags = SPI_NOR_NO_FR | SPI_S3AN, + .flags = SPI_NOR_NO_FR | SPI_NOR_XSR_RDY, /** * struct spi_nor_manufacturer - SPI NOR manufacturer object @@ -182,6 +172,7 @@ extern const struct spi_nor_manufacturer spi_nor_st; extern const struct spi_nor_manufacturer spi_nor_spansion; extern const struct spi_nor_manufacturer spi_nor_sst; extern const struct spi_nor_manufacturer spi_nor_winbond; +extern const struct spi_nor_manufacturer spi_nor_xilinx; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c new file mode 100644 index 000000000000..fcf635d89f65 --- /dev/null +++ b/drivers/mtd/spi-nor/xilinx.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info xilinx_parts[] = { + /* Xilinx S3AN Internal Flash */ + { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) }, + { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) }, + { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) }, + { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) }, + { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) }, +}; + +/* + * This code converts an address to the Default Address Mode, that has non + * power of two page sizes. We must support this mode because it is the default + * mode supported by Xilinx tools, it can access the whole flash area and + * changing over to the Power-of-two mode is irreversible and corrupts the + * original data. + * Addr can safely be unsigned int, the biggest S3AN device is smaller than + * 4 MiB. + */ +static u32 s3an_convert_addr(struct spi_nor *nor, u32 addr) +{ + u32 offset, page; + + offset = addr % nor->page_size; + page = addr / nor->page_size; + page <<= (nor->page_size > 512) ? 10 : 9; + + return page | offset; +} + +static int xilinx_nor_setup(struct spi_nor *nor, + const struct spi_nor_hwcaps *hwcaps) +{ + int ret; + + ret = spi_nor_xread_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + nor->erase_opcode = SPINOR_OP_XSE; + nor->program_opcode = SPINOR_OP_XPP; + nor->read_opcode = SPINOR_OP_READ; + nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; + + /* + * This flashes have a page size of 264 or 528 bytes (known as + * Default addressing mode). It can be changed to a more standard + * Power of two mode where the page size is 256/512. This comes + * with a price: there is 3% less of space, the data is corrupted + * and the page size cannot be changed back to default addressing + * mode. + * + * The current addressing mode can be read from the XRDSR register + * and should not be changed, because is a destructive operation. + */ + if (nor->bouncebuf[0] & XSR_PAGESIZE) { + /* Flash in Power of 2 mode */ + nor->page_size = (nor->page_size == 264) ? 256 : 512; + nor->mtd.writebufsize = nor->page_size; + nor->mtd.size = 8 * nor->page_size * nor->info->n_sectors; + nor->mtd.erasesize = 8 * nor->page_size; + } else { + /* Flash in Default addressing mode */ + nor->params.convert_addr = s3an_convert_addr; + nor->mtd.erasesize = nor->info->sector_size; + } + + return 0; +} + +static void xilinx_post_sfdp_fixups(struct spi_nor *nor) +{ + nor->params.setup = xilinx_nor_setup; +} + +static const struct spi_nor_fixups xilinx_fixups = { + .post_sfdp = xilinx_post_sfdp_fixups, +}; + +const struct spi_nor_manufacturer spi_nor_xilinx = { + .name = "xilinx", + .parts = xilinx_parts, + .nparts = ARRAY_SIZE(xilinx_parts), + .fixups = &xilinx_fixups, +}; From a674d5a6c8c094c3c6c3f88b01021c21550ee76b Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:50 +0000 Subject: [PATCH 083/103] mtd: spi-nor: Move XMC bits out of core.c Create a SPI NOR manufacturer driver for XMC chips, and move the XMC definitions outside of core.c. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/Makefile | 1 + drivers/mtd/spi-nor/core.c | 4 +--- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/xmc.c | 23 +++++++++++++++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 drivers/mtd/spi-nor/xmc.c diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index fa03513dd160..7ddb742de1fe 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -16,4 +16,5 @@ spi-nor-objs += spansion.o spi-nor-objs += sst.o spi-nor-objs += winbond.o spi-nor-objs += xilinx.o +spi-nor-objs += xmc.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ca2f441e2806..941ba37c8a5c 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1943,9 +1943,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) * old entries may be missing 4K flag. */ static const struct flash_info spi_nor_ids[] = { - /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */ - { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { }, }; @@ -1966,6 +1963,7 @@ static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_sst, &spi_nor_winbond, &spi_nor_xilinx, + &spi_nor_xmc, }; static const struct flash_info * diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 8c666bff5768..2bc620708d6f 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -173,6 +173,7 @@ extern const struct spi_nor_manufacturer spi_nor_spansion; extern const struct spi_nor_manufacturer spi_nor_sst; extern const struct spi_nor_manufacturer spi_nor_winbond; extern const struct spi_nor_manufacturer spi_nor_xilinx; +extern const struct spi_nor_manufacturer spi_nor_xmc; int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); diff --git a/drivers/mtd/spi-nor/xmc.c b/drivers/mtd/spi-nor/xmc.c new file mode 100644 index 000000000000..2c7773b68993 --- /dev/null +++ b/drivers/mtd/spi-nor/xmc.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ + +#include + +#include "core.h" + +static const struct flash_info xmc_parts[] = { + /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */ + { "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, +}; + +const struct spi_nor_manufacturer spi_nor_xmc = { + .name = "xmc", + .parts = xmc_parts, + .nparts = ARRAY_SIZE(xmc_parts), +}; From c4fdfdc140564285ccb4bd11b55bfe76b04fe5a9 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 13 Mar 2020 19:42:51 +0000 Subject: [PATCH 084/103] mtd: spi-nor: Get rid of the now empty spi_nor_ids[] table All entries have been moved to manufacturer drivers. Get rid of this empty table. Signed-off-by: Boris Brezillon Signed-off-by: Tudor Ambarus Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/core.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 941ba37c8a5c..baee58fd8b04 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1931,21 +1931,6 @@ int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor) return 0; } -/* NOTE: double check command sets and memory organization when you add - * more nor chips. This current list focusses on newer chips, which - * have been converging on command sets which including JEDEC ID. - * - * All newly added entries should describe *hardware* and should use SECT_4K - * (or SECT_4K_PMC) if hardware supports erasing 4 KiB sectors. For usage - * scenarios excluding small sectors there is config option that can be - * disabled: CONFIG_MTD_SPI_NOR_USE_4K_SECTORS. - * For historical (and compatibility) reasons (before we got above config) some - * old entries may be missing 4K flag. - */ -static const struct flash_info spi_nor_ids[] = { - { }, -}; - static const struct spi_nor_manufacturer *manufacturers[] = { &spi_nor_atmel, &spi_nor_catalyst, @@ -2015,11 +2000,6 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) } } - info = spi_nor_search_part_by_id(spi_nor_ids, - ARRAY_SIZE(spi_nor_ids) - 1, id); - if (info) - return info; - dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n", SPI_NOR_MAX_ID_LEN, id); return ERR_PTR(-ENODEV); @@ -2930,11 +2910,6 @@ static const struct flash_info *spi_nor_match_id(struct spi_nor *nor, { unsigned int i, j; - for (i = 0; i < ARRAY_SIZE(spi_nor_ids) - 1; i++) { - if (!strcmp(name, spi_nor_ids[i].name)) - return &spi_nor_ids[i]; - } - for (i = 0; i < ARRAY_SIZE(manufacturers); i++) { for (j = 0; j < manufacturers[i]->nparts; j++) { if (!strcmp(name, manufacturers[i]->parts[j].name)) { From d3c4bb31bf627ede607d7b1827e6be43f1b26be7 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 13 Mar 2020 19:42:52 +0000 Subject: [PATCH 085/103] mtd: spi-nor: Drop the MFR definitions Cross manufacturer code is unlikely and discouraged, get rid of the MFR definitions. Suggested-by: Vignesh Raghavendra Signed-off-by: Tudor Ambarus Reviewed-by: Boris Brezillon --- drivers/mtd/spi-nor/core.c | 2 -- include/linux/mtd/spi-nor.h | 17 ----------------- 2 files changed, 19 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index baee58fd8b04..b07e66f10995 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -40,8 +40,6 @@ #define SPI_NOR_MAX_ADDR_WIDTH 4 -#define JEDEC_MFR(info) ((info)->id[0]) - /** * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data * transfer diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index bf37bfc68797..2f7725525460 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -11,23 +11,6 @@ #include #include -/* - * Manufacturer IDs - * - * The first byte returned from the flash after sending opcode SPINOR_OP_RDID. - * Sometimes these are the same as CFI IDs, but sometimes they aren't. - */ -#define SNOR_MFR_ATMEL CFI_MFR_ATMEL -#define SNOR_MFR_GIGADEVICE 0xc8 -#define SNOR_MFR_INTEL CFI_MFR_INTEL -#define SNOR_MFR_ST CFI_MFR_ST /* ST Micro */ -#define SNOR_MFR_MICRON CFI_MFR_MICRON /* Micron */ -#define SNOR_MFR_ISSI CFI_MFR_PMC -#define SNOR_MFR_MACRONIX CFI_MFR_MACRONIX -#define SNOR_MFR_SPANSION CFI_MFR_AMD -#define SNOR_MFR_SST CFI_MFR_SST -#define SNOR_MFR_WINBOND 0xef /* Also used by some Spansion */ - /* * Note on opcode nomenclature: some opcodes have a format like * SPINOR_OP_FUNCTION{4,}_x_y_z. The numbers x, y, and z stand for the number From 829ec6408dc58dbf27522bbd57d0a85b0a3d1a0e Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 13 Mar 2020 19:42:53 +0000 Subject: [PATCH 086/103] mtd: spi-nor: Trim what is exposed in spi-nor.h The SPI NOR controllers drivers must not be able to use structures that are meant just for the SPI NOR core. struct spi_nor_flash_parameter is filled at run-time with info gathered from flash_info, manufacturer and sfdp data. struct spi_nor_flash_parameter should be opaque to the SPI NOR controller drivers, make sure it is. spi_nor_option_flags, spi_nor_read_command, spi_nor_pp_command, spi_nor_read_command_index and spi_nor_pp_command_index are defined for the core use, make sure they are opaque to the SPI NOR controller drivers. Signed-off-by: Tudor Ambarus Reviewed-by: Boris Brezillon Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/core.c | 86 ++++++---- drivers/mtd/spi-nor/core.h | 214 +++++++++++++++++++++++++ drivers/mtd/spi-nor/gigadevice.c | 2 +- drivers/mtd/spi-nor/issi.c | 2 +- drivers/mtd/spi-nor/macronix.c | 4 +- drivers/mtd/spi-nor/micron-st.c | 4 +- drivers/mtd/spi-nor/sfdp.c | 10 ++ drivers/mtd/spi-nor/spansion.c | 2 +- drivers/mtd/spi-nor/winbond.c | 2 +- drivers/mtd/spi-nor/xilinx.c | 4 +- include/linux/mtd/spi-nor.h | 260 +------------------------------ 11 files changed, 294 insertions(+), 296 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index b07e66f10995..877557dbda7f 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -778,7 +778,7 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) ret = spi_nor_read_cr(nor, &sr_cr[1]); if (ret) return ret; - } else if (nor->params.quad_enable) { + } else if (nor->params->quad_enable) { /* * If the Status Register 2 Read command (35h) is not * supported, we should at least be sure we don't @@ -786,7 +786,7 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) * * We can safely assume that when the Quad Enable method is * set, the value of the QE bit is one, as a consequence of the - * nor->params.quad_enable() call. + * nor->params->quad_enable() call. * * We can safely assume that the Quad Enable bit is present in * the Status Register 2 at BIT(1). According to the JESD216 @@ -1051,6 +1051,11 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode) ARRAY_SIZE(spi_nor_3to4_erase)); } +static bool spi_nor_has_uniform_erase(const struct spi_nor *nor) +{ + return !!nor->params->erase_map.uniform_erase_type; +} + static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) { nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode); @@ -1058,7 +1063,7 @@ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor) nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode); if (!spi_nor_has_uniform_erase(nor)) { - struct spi_nor_erase_map *map = &nor->params.erase_map; + struct spi_nor_erase_map *map = &nor->params->erase_map; struct spi_nor_erase_type *erase; int i; @@ -1095,10 +1100,10 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor) static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr) { - if (!nor->params.convert_addr) + if (!nor->params->convert_addr) return addr; - return nor->params.convert_addr(nor, addr); + return nor->params->convert_addr(nor, addr); } /* @@ -1203,6 +1208,16 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map, return NULL; } +static u64 spi_nor_region_is_last(const struct spi_nor_erase_region *region) +{ + return region->offset & SNOR_LAST_REGION; +} + +static u64 spi_nor_region_end(const struct spi_nor_erase_region *region) +{ + return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size; +} + /** * spi_nor_region_next() - get the next spi nor region * @region: pointer to a structure that describes a SPI NOR erase region @@ -1307,7 +1322,7 @@ static int spi_nor_init_erase_cmd_list(struct spi_nor *nor, struct list_head *erase_list, u64 addr, u32 len) { - const struct spi_nor_erase_map *map = &nor->params.erase_map; + const struct spi_nor_erase_map *map = &nor->params->erase_map; const struct spi_nor_erase_type *erase, *prev_erase = NULL; struct spi_nor_erase_region *region; struct spi_nor_erase_command *cmd = NULL; @@ -1793,7 +1808,7 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ret) return ret; - ret = nor->params.locking_ops->lock(nor, ofs, len); + ret = nor->params->locking_ops->lock(nor, ofs, len); spi_nor_unlock_and_unprep(nor); return ret; @@ -1808,7 +1823,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ret) return ret; - ret = nor->params.locking_ops->unlock(nor, ofs, len); + ret = nor->params->locking_ops->unlock(nor, ofs, len); spi_nor_unlock_and_unprep(nor); return ret; @@ -1823,7 +1838,7 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) if (ret) return ret; - ret = nor->params.locking_ops->is_locked(nor, ofs, len); + ret = nor->params->locking_ops->is_locked(nor, ofs, len); spi_nor_unlock_and_unprep(nor); return ret; @@ -2288,7 +2303,7 @@ static int spi_nor_spimem_check_pp(struct spi_nor *nor, static void spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) { - struct spi_nor_flash_parameter *params = &nor->params; + struct spi_nor_flash_parameter *params = nor->params; unsigned int cap; /* DTR modes are not supported yet, mask them all. */ @@ -2387,7 +2402,7 @@ static int spi_nor_select_read(struct spi_nor *nor, if (cmd < 0) return -EINVAL; - read = &nor->params.reads[cmd]; + read = &nor->params->reads[cmd]; nor->read_opcode = read->opcode; nor->read_proto = read->proto; @@ -2418,7 +2433,7 @@ static int spi_nor_select_pp(struct spi_nor *nor, if (cmd < 0) return -EINVAL; - pp = &nor->params.page_programs[cmd]; + pp = &nor->params->page_programs[cmd]; nor->program_opcode = pp->opcode; nor->write_proto = pp->proto; return 0; @@ -2479,7 +2494,7 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map, static int spi_nor_select_erase(struct spi_nor *nor) { - struct spi_nor_erase_map *map = &nor->params.erase_map; + struct spi_nor_erase_map *map = &nor->params->erase_map; const struct spi_nor_erase_type *erase = NULL; struct mtd_info *mtd = &nor->mtd; u32 wanted_size = nor->info->sector_size; @@ -2528,7 +2543,7 @@ static int spi_nor_select_erase(struct spi_nor *nor) static int spi_nor_default_setup(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps) { - struct spi_nor_flash_parameter *params = &nor->params; + struct spi_nor_flash_parameter *params = nor->params; u32 ignored_mask, shared_mask; int err; @@ -2589,10 +2604,10 @@ static int spi_nor_default_setup(struct spi_nor *nor, static int spi_nor_setup(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps) { - if (!nor->params.setup) + if (!nor->params->setup) return 0; - return nor->params.setup(nor, hwcaps); + return nor->params->setup(nor, hwcaps); } /** @@ -2622,13 +2637,13 @@ static void spi_nor_sfdp_init_params(struct spi_nor *nor) { struct spi_nor_flash_parameter sfdp_params; - memcpy(&sfdp_params, &nor->params, sizeof(sfdp_params)); + memcpy(&sfdp_params, nor->params, sizeof(sfdp_params)); if (spi_nor_parse_sfdp(nor, &sfdp_params)) { nor->addr_width = 0; nor->flags &= ~SNOR_F_4B_OPCODES; } else { - memcpy(&nor->params, &sfdp_params, sizeof(nor->params)); + memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); } } @@ -2639,7 +2654,7 @@ static void spi_nor_sfdp_init_params(struct spi_nor *nor) */ static void spi_nor_info_init_params(struct spi_nor *nor) { - struct spi_nor_flash_parameter *params = &nor->params; + struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_erase_map *map = ¶ms->erase_map; const struct flash_info *info = nor->info; struct device_node *np = spi_nor_get_flash_node(nor); @@ -2758,8 +2773,8 @@ static void spi_nor_late_init_params(struct spi_nor *nor) * NOR protection support. When locking_ops are not provided, we pick * the default ones. */ - if (nor->flags & SNOR_F_HAS_LOCK && !nor->params.locking_ops) - nor->params.locking_ops = &spi_nor_sr_locking_ops; + if (nor->flags & SNOR_F_HAS_LOCK && !nor->params->locking_ops) + nor->params->locking_ops = &spi_nor_sr_locking_ops; } /** @@ -2799,8 +2814,12 @@ static void spi_nor_late_init_params(struct spi_nor *nor) * ->default_init() hook or the SFDP parser do not set specific params. * spi_nor_late_init_params() */ -static void spi_nor_init_params(struct spi_nor *nor) +static int spi_nor_init_params(struct spi_nor *nor) { + nor->params = devm_kzalloc(nor->dev, sizeof(*nor->params), GFP_KERNEL); + if (!nor->params) + return -ENOMEM; + spi_nor_info_init_params(nor); spi_nor_manufacturer_init_params(nor); @@ -2812,6 +2831,8 @@ static void spi_nor_init_params(struct spi_nor *nor) spi_nor_post_sfdp_fixups(nor); spi_nor_late_init_params(nor); + + return 0; } /** @@ -2822,14 +2843,14 @@ static void spi_nor_init_params(struct spi_nor *nor) */ static int spi_nor_quad_enable(struct spi_nor *nor) { - if (!nor->params.quad_enable) + if (!nor->params->quad_enable) return 0; if (!(spi_nor_get_protocol_width(nor->read_proto) == 4 || spi_nor_get_protocol_width(nor->write_proto) == 4)) return 0; - return nor->params.quad_enable(nor); + return nor->params->quad_enable(nor); } /** @@ -2844,7 +2865,7 @@ static int spi_nor_quad_enable(struct spi_nor *nor) static int spi_nor_unlock_all(struct spi_nor *nor) { if (nor->flags & SNOR_F_HAS_LOCK) - return spi_nor_unlock(&nor->mtd, 0, nor->params.size); + return spi_nor_unlock(&nor->mtd, 0, nor->params->size); return 0; } @@ -2875,7 +2896,7 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - nor->params.set_4byte_addr_mode(nor, true); + nor->params->set_4byte_addr_mode(nor, true); } return 0; @@ -2899,7 +2920,7 @@ void spi_nor_restore(struct spi_nor *nor) /* restore the addressing mode */ if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) - nor->params.set_4byte_addr_mode(nor, false); + nor->params->set_4byte_addr_mode(nor, false); } EXPORT_SYMBOL_GPL(spi_nor_restore); @@ -3004,7 +3025,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, struct device *dev = nor->dev; struct mtd_info *mtd = &nor->mtd; struct device_node *np = spi_nor_get_flash_node(nor); - struct spi_nor_flash_parameter *params = &nor->params; int ret; int i; @@ -3055,7 +3075,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->_write = spi_nor_write; /* Init flash parameters based on flash_info struct and SFDP */ - spi_nor_init_params(nor); + ret = spi_nor_init_params(nor); + if (ret) + return ret; if (!mtd->name) mtd->name = dev_name(dev); @@ -3063,12 +3085,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->type = MTD_NORFLASH; mtd->writesize = 1; mtd->flags = MTD_CAP_NORFLASH; - mtd->size = params->size; + mtd->size = nor->params->size; mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; mtd->_resume = spi_nor_resume; - if (nor->params.locking_ops) { + if (nor->params->locking_ops) { mtd->_lock = spi_nor_lock; mtd->_unlock = spi_nor_unlock; mtd->_is_locked = spi_nor_is_locked; @@ -3091,7 +3113,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->flags |= MTD_NO_ERASE; mtd->dev.parent = dev; - nor->page_size = params->page_size; + nor->page_size = nor->params->page_size; mtd->writebufsize = nor->page_size; if (of_property_read_bool(np, "broken-flash-reset")) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 2bc620708d6f..3ce826b35ad1 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -11,6 +11,220 @@ #define SPI_NOR_MAX_ID_LEN 6 +enum spi_nor_option_flags { + SNOR_F_USE_FSR = BIT(0), + SNOR_F_HAS_SR_TB = BIT(1), + SNOR_F_NO_OP_CHIP_ERASE = BIT(2), + SNOR_F_READY_XSR_RDY = BIT(3), + SNOR_F_USE_CLSR = BIT(4), + SNOR_F_BROKEN_RESET = BIT(5), + SNOR_F_4B_OPCODES = BIT(6), + SNOR_F_HAS_4BAIT = BIT(7), + SNOR_F_HAS_LOCK = BIT(8), + SNOR_F_HAS_16BIT_SR = BIT(9), + SNOR_F_NO_READ_CR = BIT(10), + SNOR_F_HAS_SR_TB_BIT6 = BIT(11), +}; + +struct spi_nor_read_command { + u8 num_mode_clocks; + u8 num_wait_states; + u8 opcode; + enum spi_nor_protocol proto; +}; + +struct spi_nor_pp_command { + u8 opcode; + enum spi_nor_protocol proto; +}; + +enum spi_nor_read_command_index { + SNOR_CMD_READ, + SNOR_CMD_READ_FAST, + SNOR_CMD_READ_1_1_1_DTR, + + /* Dual SPI */ + SNOR_CMD_READ_1_1_2, + SNOR_CMD_READ_1_2_2, + SNOR_CMD_READ_2_2_2, + SNOR_CMD_READ_1_2_2_DTR, + + /* Quad SPI */ + SNOR_CMD_READ_1_1_4, + SNOR_CMD_READ_1_4_4, + SNOR_CMD_READ_4_4_4, + SNOR_CMD_READ_1_4_4_DTR, + + /* Octal SPI */ + SNOR_CMD_READ_1_1_8, + SNOR_CMD_READ_1_8_8, + SNOR_CMD_READ_8_8_8, + SNOR_CMD_READ_1_8_8_DTR, + + SNOR_CMD_READ_MAX +}; + +enum spi_nor_pp_command_index { + SNOR_CMD_PP, + + /* Quad SPI */ + SNOR_CMD_PP_1_1_4, + SNOR_CMD_PP_1_4_4, + SNOR_CMD_PP_4_4_4, + + /* Octal SPI */ + SNOR_CMD_PP_1_1_8, + SNOR_CMD_PP_1_8_8, + SNOR_CMD_PP_8_8_8, + + SNOR_CMD_PP_MAX +}; + +/** + * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type + * @size: the size of the sector/block erased by the erase type. + * JEDEC JESD216B imposes erase sizes to be a power of 2. + * @size_shift: @size is a power of 2, the shift is stored in + * @size_shift. + * @size_mask: the size mask based on @size_shift. + * @opcode: the SPI command op code to erase the sector/block. + * @idx: Erase Type index as sorted in the Basic Flash Parameter + * Table. It will be used to synchronize the supported + * Erase Types with the ones identified in the SFDP + * optional tables. + */ +struct spi_nor_erase_type { + u32 size; + u32 size_shift; + u32 size_mask; + u8 opcode; + u8 idx; +}; + +/** + * struct spi_nor_erase_command - Used for non-uniform erases + * The structure is used to describe a list of erase commands to be executed + * once we validate that the erase can be performed. The elements in the list + * are run-length encoded. + * @list: for inclusion into the list of erase commands. + * @count: how many times the same erase command should be + * consecutively used. + * @size: the size of the sector/block erased by the command. + * @opcode: the SPI command op code to erase the sector/block. + */ +struct spi_nor_erase_command { + struct list_head list; + u32 count; + u32 size; + u8 opcode; +}; + +/** + * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region + * @offset: the offset in the data array of erase region start. + * LSB bits are used as a bitmask encoding flags to + * determine if this region is overlaid, if this region is + * the last in the SPI NOR flash memory and to indicate + * all the supported erase commands inside this region. + * The erase types are sorted in ascending order with the + * smallest Erase Type size being at BIT(0). + * @size: the size of the region in bytes. + */ +struct spi_nor_erase_region { + u64 offset; + u64 size; +}; + +#define SNOR_ERASE_TYPE_MAX 4 +#define SNOR_ERASE_TYPE_MASK GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0) + +#define SNOR_LAST_REGION BIT(4) +#define SNOR_OVERLAID_REGION BIT(5) + +#define SNOR_ERASE_FLAGS_MAX 6 +#define SNOR_ERASE_FLAGS_MASK GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0) + +/** + * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map + * @regions: array of erase regions. The regions are consecutive in + * address space. Walking through the regions is done + * incrementally. + * @uniform_region: a pre-allocated erase region for SPI NOR with a uniform + * sector size (legacy implementation). + * @erase_type: an array of erase types shared by all the regions. + * The erase types are sorted in ascending order, with the + * smallest Erase Type size being the first member in the + * erase_type array. + * @uniform_erase_type: bitmask encoding erase types that can erase the + * entire memory. This member is completed at init by + * uniform and non-uniform SPI NOR flash memories if they + * support at least one erase type that can erase the + * entire memory. + */ +struct spi_nor_erase_map { + struct spi_nor_erase_region *regions; + struct spi_nor_erase_region uniform_region; + struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX]; + u8 uniform_erase_type; +}; + +/** + * struct spi_nor_locking_ops - SPI NOR locking methods + * @lock: lock a region of the SPI NOR. + * @unlock: unlock a region of the SPI NOR. + * @is_locked: check if a region of the SPI NOR is completely locked + */ +struct spi_nor_locking_ops { + int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); + int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); + int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); +}; + +/** + * struct spi_nor_flash_parameter - SPI NOR flash parameters and settings. + * Includes legacy flash parameters and settings that can be overwritten + * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216 + * Serial Flash Discoverable Parameters (SFDP) tables. + * + * @size: the flash memory density in bytes. + * @page_size: the page size of the SPI NOR flash memory. + * @hwcaps: describes the read and page program hardware + * capabilities. + * @reads: read capabilities ordered by priority: the higher index + * in the array, the higher priority. + * @page_programs: page program capabilities ordered by priority: the + * higher index in the array, the higher priority. + * @erase_map: the erase map parsed from the SFDP Sector Map Parameter + * Table. + * @quad_enable: enables SPI NOR quad mode. + * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. + * @convert_addr: converts an absolute address into something the flash + * will understand. Particularly useful when pagesize is + * not a power-of-2. + * @setup: configures the SPI NOR memory. Useful for SPI NOR + * flashes that have peculiarities to the SPI NOR standard + * e.g. different opcodes, specific address calculation, + * page size, etc. + * @locking_ops: SPI NOR locking methods. + */ +struct spi_nor_flash_parameter { + u64 size; + u32 page_size; + + struct spi_nor_hwcaps hwcaps; + struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; + struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; + + struct spi_nor_erase_map erase_map; + + int (*quad_enable)(struct spi_nor *nor); + int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); + u32 (*convert_addr)(struct spi_nor *nor, u32 addr); + int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps); + + const struct spi_nor_locking_ops *locking_ops; +}; + /** * struct spi_nor_fixups - SPI NOR fixup hooks * @default_init: called after default flash parameters init. Used to tweak diff --git a/drivers/mtd/spi-nor/gigadevice.c b/drivers/mtd/spi-nor/gigadevice.c index 7930e4490dab..447d84bb2128 100644 --- a/drivers/mtd/spi-nor/gigadevice.c +++ b/drivers/mtd/spi-nor/gigadevice.c @@ -16,7 +16,7 @@ static void gd25q256_default_init(struct spi_nor *nor) * indicate the quad_enable method for this case, we need * to set it in the default_init fixup hook. */ - nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; } static struct spi_nor_fixups gd25q256_fixups = { diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 3a1c34c41388..ffcb60e54a80 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -68,7 +68,7 @@ static const struct flash_info issi_parts[] = { static void issi_default_init(struct spi_nor *nor) { - nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; } static const struct spi_nor_fixups issi_fixups = { diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index c9b6b45d8f99..ab0f963d630c 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -82,8 +82,8 @@ static const struct flash_info macronix_parts[] = { static void macronix_default_init(struct spi_nor *nor) { - nor->params.quad_enable = spi_nor_sr1_bit6_quad_enable; - nor->params.set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; + nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; + nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode; } static const struct spi_nor_fixups macronix_fixups = { diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 9d32ee0ef5a5..3874a62d8b47 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -130,8 +130,8 @@ static void micron_st_default_init(struct spi_nor *nor) { nor->flags |= SNOR_F_HAS_LOCK; nor->flags &= ~SNOR_F_HAS_16BIT_SR; - nor->params.quad_enable = NULL; - nor->params.set_4byte_addr_mode = st_micron_set_4byte_addr_mode; + nor->params->quad_enable = NULL; + nor->params->set_4byte_addr_mode = st_micron_set_4byte_addr_mode; } static const struct spi_nor_fixups micron_st_fixups = { diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index c162015d19b1..df967f1f4951 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -734,6 +734,16 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, return ret; } +static void spi_nor_region_mark_end(struct spi_nor_erase_region *region) +{ + region->offset |= SNOR_LAST_REGION; +} + +static void spi_nor_region_mark_overlay(struct spi_nor_erase_region *region) +{ + region->offset |= SNOR_OVERLAID_REGION; +} + /** * spi_nor_region_check_overlay() - set overlay bit when the region is overlaid * @region: pointer to a structure that describes a SPI NOR erase region diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 16683983a20e..6756202ace4b 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -74,7 +74,7 @@ static const struct flash_info spansion_parts[] = { static void spansion_post_sfdp_fixups(struct spi_nor *nor) { - if (nor->params.size <= SZ_16M) + if (nor->params->size <= SZ_16M) return; nor->flags |= SNOR_F_4B_OPCODES; diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 3f8c568091d3..17deabad57e1 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -97,7 +97,7 @@ static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) static void winbond_default_init(struct spi_nor *nor) { - nor->params.set_4byte_addr_mode = winbond_set_4byte_addr_mode; + nor->params->set_4byte_addr_mode = winbond_set_4byte_addr_mode; } static const struct spi_nor_fixups winbond_fixups = { diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c index fcf635d89f65..1138bdbf4199 100644 --- a/drivers/mtd/spi-nor/xilinx.c +++ b/drivers/mtd/spi-nor/xilinx.c @@ -70,7 +70,7 @@ static int xilinx_nor_setup(struct spi_nor *nor, nor->mtd.erasesize = 8 * nor->page_size; } else { /* Flash in Default addressing mode */ - nor->params.convert_addr = s3an_convert_addr; + nor->params->convert_addr = s3an_convert_addr; nor->mtd.erasesize = nor->info->sector_size; } @@ -79,7 +79,7 @@ static int xilinx_nor_setup(struct spi_nor *nor, static void xilinx_post_sfdp_fixups(struct spi_nor *nor) { - nor->params.setup = xilinx_nor_setup; + nor->params->setup = xilinx_nor_setup; } static const struct spi_nor_fixups xilinx_fixups = { diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 2f7725525460..e656858b50a5 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -210,110 +210,6 @@ static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto) return spi_nor_get_protocol_data_nbits(proto); } -enum spi_nor_option_flags { - SNOR_F_USE_FSR = BIT(0), - SNOR_F_HAS_SR_TB = BIT(1), - SNOR_F_NO_OP_CHIP_ERASE = BIT(2), - SNOR_F_READY_XSR_RDY = BIT(3), - SNOR_F_USE_CLSR = BIT(4), - SNOR_F_BROKEN_RESET = BIT(5), - SNOR_F_4B_OPCODES = BIT(6), - SNOR_F_HAS_4BAIT = BIT(7), - SNOR_F_HAS_LOCK = BIT(8), - SNOR_F_HAS_16BIT_SR = BIT(9), - SNOR_F_NO_READ_CR = BIT(10), - SNOR_F_HAS_SR_TB_BIT6 = BIT(11), - -}; - -/** - * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type - * @size: the size of the sector/block erased by the erase type. - * JEDEC JESD216B imposes erase sizes to be a power of 2. - * @size_shift: @size is a power of 2, the shift is stored in - * @size_shift. - * @size_mask: the size mask based on @size_shift. - * @opcode: the SPI command op code to erase the sector/block. - * @idx: Erase Type index as sorted in the Basic Flash Parameter - * Table. It will be used to synchronize the supported - * Erase Types with the ones identified in the SFDP - * optional tables. - */ -struct spi_nor_erase_type { - u32 size; - u32 size_shift; - u32 size_mask; - u8 opcode; - u8 idx; -}; - -/** - * struct spi_nor_erase_command - Used for non-uniform erases - * The structure is used to describe a list of erase commands to be executed - * once we validate that the erase can be performed. The elements in the list - * are run-length encoded. - * @list: for inclusion into the list of erase commands. - * @count: how many times the same erase command should be - * consecutively used. - * @size: the size of the sector/block erased by the command. - * @opcode: the SPI command op code to erase the sector/block. - */ -struct spi_nor_erase_command { - struct list_head list; - u32 count; - u32 size; - u8 opcode; -}; - -/** - * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region - * @offset: the offset in the data array of erase region start. - * LSB bits are used as a bitmask encoding flags to - * determine if this region is overlaid, if this region is - * the last in the SPI NOR flash memory and to indicate - * all the supported erase commands inside this region. - * The erase types are sorted in ascending order with the - * smallest Erase Type size being at BIT(0). - * @size: the size of the region in bytes. - */ -struct spi_nor_erase_region { - u64 offset; - u64 size; -}; - -#define SNOR_ERASE_TYPE_MAX 4 -#define SNOR_ERASE_TYPE_MASK GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0) - -#define SNOR_LAST_REGION BIT(4) -#define SNOR_OVERLAID_REGION BIT(5) - -#define SNOR_ERASE_FLAGS_MAX 6 -#define SNOR_ERASE_FLAGS_MASK GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0) - -/** - * struct spi_nor_erase_map - Structure to describe the SPI NOR erase map - * @regions: array of erase regions. The regions are consecutive in - * address space. Walking through the regions is done - * incrementally. - * @uniform_region: a pre-allocated erase region for SPI NOR with a uniform - * sector size (legacy implementation). - * @erase_type: an array of erase types shared by all the regions. - * The erase types are sorted in ascending order, with the - * smallest Erase Type size being the first member in the - * erase_type array. - * @uniform_erase_type: bitmask encoding erase types that can erase the - * entire memory. This member is completed at init by - * uniform and non-uniform SPI NOR flash memories if they - * support at least one erase type that can erase the - * entire memory. - */ -struct spi_nor_erase_map { - struct spi_nor_erase_region *regions; - struct spi_nor_erase_region uniform_region; - struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX]; - u8 uniform_erase_type; -}; - /** * struct spi_nor_hwcaps - Structure for describing the hardware capabilies * supported by the SPI controller (bus master). @@ -389,61 +285,7 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \ SNOR_HWCAPS_PP_MASK) -struct spi_nor_read_command { - u8 num_mode_clocks; - u8 num_wait_states; - u8 opcode; - enum spi_nor_protocol proto; -}; - -struct spi_nor_pp_command { - u8 opcode; - enum spi_nor_protocol proto; -}; - -enum spi_nor_read_command_index { - SNOR_CMD_READ, - SNOR_CMD_READ_FAST, - SNOR_CMD_READ_1_1_1_DTR, - - /* Dual SPI */ - SNOR_CMD_READ_1_1_2, - SNOR_CMD_READ_1_2_2, - SNOR_CMD_READ_2_2_2, - SNOR_CMD_READ_1_2_2_DTR, - - /* Quad SPI */ - SNOR_CMD_READ_1_1_4, - SNOR_CMD_READ_1_4_4, - SNOR_CMD_READ_4_4_4, - SNOR_CMD_READ_1_4_4_DTR, - - /* Octal SPI */ - SNOR_CMD_READ_1_1_8, - SNOR_CMD_READ_1_8_8, - SNOR_CMD_READ_8_8_8, - SNOR_CMD_READ_1_8_8_DTR, - - SNOR_CMD_READ_MAX -}; - -enum spi_nor_pp_command_index { - SNOR_CMD_PP, - - /* Quad SPI */ - SNOR_CMD_PP_1_1_4, - SNOR_CMD_PP_1_4_4, - SNOR_CMD_PP_4_4_4, - - /* Octal SPI */ - SNOR_CMD_PP_1_1_8, - SNOR_CMD_PP_1_8_8, - SNOR_CMD_PP_8_8_8, - - SNOR_CMD_PP_MAX -}; - -/* Forward declaration that will be used in 'struct spi_nor_flash_parameter' */ +/* Forward declaration that is used in 'struct spi_nor_controller_ops' */ struct spi_nor; /** @@ -474,74 +316,13 @@ struct spi_nor_controller_ops { int (*erase)(struct spi_nor *nor, loff_t offs); }; -/** - * struct spi_nor_locking_ops - SPI NOR locking methods - * @lock: lock a region of the SPI NOR. - * @unlock: unlock a region of the SPI NOR. - * @is_locked: check if a region of the SPI NOR is completely locked - */ -struct spi_nor_locking_ops { - int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); - int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); - int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); -}; - -/** - * struct spi_nor_flash_parameter - SPI NOR flash parameters and settings. - * Includes legacy flash parameters and settings that can be overwritten - * by the spi_nor_fixups hooks, or dynamically when parsing the JESD216 - * Serial Flash Discoverable Parameters (SFDP) tables. - * - * @size: the flash memory density in bytes. - * @page_size: the page size of the SPI NOR flash memory. - * @hwcaps: describes the read and page program hardware - * capabilities. - * @reads: read capabilities ordered by priority: the higher index - * in the array, the higher priority. - * @page_programs: page program capabilities ordered by priority: the - * higher index in the array, the higher priority. - * @erase_map: the erase map parsed from the SFDP Sector Map Parameter - * Table. - * @quad_enable: enables SPI NOR quad mode. - * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. - * @convert_addr: converts an absolute address into something the flash - * will understand. Particularly useful when pagesize is - * not a power-of-2. - * @setup: configures the SPI NOR memory. Useful for SPI NOR - * flashes that have peculiarities to the SPI NOR standard - * e.g. different opcodes, specific address calculation, - * page size, etc. - * @locking_ops: SPI NOR locking methods. - */ -struct spi_nor_flash_parameter { - u64 size; - u32 page_size; - - struct spi_nor_hwcaps hwcaps; - struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; - struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; - - struct spi_nor_erase_map erase_map; - - int (*quad_enable)(struct spi_nor *nor); - int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); - u32 (*convert_addr)(struct spi_nor *nor, u32 addr); - int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps); - - const struct spi_nor_locking_ops *locking_ops; -}; - -/** - * struct flash_info - Forward declaration of a structure used internally by - * spi_nor_scan() +/* + * Forward declarations that are used internally by the core and manufacturer + * drivers. */ struct flash_info; - -/** - * struct spi_nor_manufacturer - Forward declaration of a structure used - * internally by the core and manufacturer drivers. - */ struct spi_nor_manufacturer; +struct spi_nor_flash_parameter; /** * struct spi_nor - Structure for defining a the SPI NOR layer @@ -596,7 +377,7 @@ struct spi_nor { const struct spi_nor_controller_ops *controller_ops; - struct spi_nor_flash_parameter params; + struct spi_nor_flash_parameter *params; struct { struct spi_mem_dirmap_desc *rdesc; @@ -606,35 +387,6 @@ struct spi_nor { void *priv; }; -static u64 __maybe_unused -spi_nor_region_is_last(const struct spi_nor_erase_region *region) -{ - return region->offset & SNOR_LAST_REGION; -} - -static u64 __maybe_unused -spi_nor_region_end(const struct spi_nor_erase_region *region) -{ - return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size; -} - -static void __maybe_unused -spi_nor_region_mark_end(struct spi_nor_erase_region *region) -{ - region->offset |= SNOR_LAST_REGION; -} - -static void __maybe_unused -spi_nor_region_mark_overlay(struct spi_nor_erase_region *region) -{ - region->offset |= SNOR_OVERLAID_REGION; -} - -static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor) -{ - return !!nor->params.erase_map.uniform_erase_type; -} - static inline void spi_nor_set_flash_node(struct spi_nor *nor, struct device_node *np) { From 6d4e346435b9b90bb7cca1d296adb6f6c7930c2d Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Sat, 21 Mar 2020 07:42:17 +0100 Subject: [PATCH 087/103] MAINTAINERS: update entry after SPI NOR controller move Commit a0900d0195d2 ("mtd: spi-nor: Prepare core / manufacturer code split") moved all SPI NOR controller drivers to a controllers/ sub-directory. However, the moved nxp-spifi.c file was referenced in the ARM/LPC18XX ARCHITECTURE entry in MAINTAINERS. Hence, since then, ./scripts/get_maintainer.pl --self-test complains: warning: no file matches F: drivers/mtd/spi-nor/nxp-spifi.c Update the file entry in MAINTAINERS to its new location. Signed-off-by: Lukas Bulwahn Signed-off-by: Tudor Ambarus --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a0d86490c2c6..f5143e248170 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1928,7 +1928,7 @@ F: Documentation/devicetree/bindings/i2c/i2c-lpc2k.txt F: arch/arm/boot/dts/lpc43* F: drivers/i2c/busses/i2c-lpc2k.c F: drivers/memory/pl172.c -F: drivers/mtd/spi-nor/nxp-spifi.c +F: drivers/mtd/spi-nor/controllers/nxp-spifi.c F: drivers/rtc/rtc-lpc24xx.c N: lpc18xx From e0fe5339d4886c48cdd68a2c49d602c4c1864c38 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 23 Mar 2020 19:50:40 +0200 Subject: [PATCH 088/103] mtd: spi-nor: Clear WEL bit when erase or program errors occur When an Erase or Program error occurs on a spansion/cypress or a micron flash, the WEL bit remains set to one and should be cleared with a WRDI command in order to protect against inadvertent writes that can possible corrupt the contents of the memory. Winbond, macronix, gd, etc., do not support the E_ERR and P_ERR bits in the Status Register and always clear the WEL bit regardless of the outcome of the erase or page program operation (ex w25q40bw, MX25L25635E). Issue a WRDI command when erase or page program errors occur. Reported-by: John Garry Signed-off-by: Tudor Ambarus Tested-by: John Garry --- drivers/mtd/spi-nor/core.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 877557dbda7f..e0021dd34bfe 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -559,6 +559,17 @@ static int spi_nor_sr_ready(struct spi_nor *nor) dev_err(nor->dev, "Programming Error occurred\n"); spi_nor_clear_sr(nor); + + /* + * WEL bit remains set to one when an erase or page program + * error occurs. Issue a Write Disable command to protect + * against inadvertent writes that can possibly corrupt the + * contents of the memory. + */ + ret = spi_nor_write_disable(nor); + if (ret) + return ret; + return -EIO; } @@ -615,6 +626,17 @@ static int spi_nor_fsr_ready(struct spi_nor *nor) "Attempted to modify a protected sector.\n"); spi_nor_clear_fsr(nor); + + /* + * WEL bit remains set to one when an erase or page program + * error occurs. Issue a Write Disable command to protect + * against inadvertent writes that can possibly corrupt the + * contents of the memory. + */ + ret = spi_nor_write_disable(nor); + if (ret) + return ret; + return -EIO; } From 5bb783cc92f0da8c36c8cc288d586a4ceb6c742a Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 19 Mar 2020 17:43:26 -0500 Subject: [PATCH 089/103] mtd: spi-nor: controllers: aspeed-smc: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/controllers/aspeed-smc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/controllers/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c index 395127349aa8..e26a1897db0e 100644 --- a/drivers/mtd/spi-nor/controllers/aspeed-smc.c +++ b/drivers/mtd/spi-nor/controllers/aspeed-smc.c @@ -109,7 +109,7 @@ struct aspeed_smc_controller { void __iomem *ahb_base; /* per-chip windows resource */ u32 ahb_window_size; /* full mapping window size */ - struct aspeed_smc_chip *chips[0]; /* pointers to attached chips */ + struct aspeed_smc_chip *chips[]; /* pointers to attached chips */ }; /* From b0e2d252f9280835a24e007dcd6feaef5a8809cd Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 23 Mar 2020 07:29:29 +0200 Subject: [PATCH 090/103] mtd: spi-nor: Set all BP bits to one when lock_len == mtd->size When there are more BP settings than needed for defining the protected areas of the flash memory, most flashes will define the remaining settings as "protect all", i.e. the equivalent of having all the BP bits set to one. But there are flashes where the in-between BP values are undefined (not mentioned), and only the "all bits set" is protecting the entire memory. One such example is w25q80, where BP[2:0]=0b101 and 0b110 are not defined. Set all the BP bits to one when lock_len == mtd->size, to treat this special case. Suggested-by: Michael Walle Signed-off-by: Tudor Ambarus Reviewed-by: Jungseung Lee Reviewed-by: Michael Walle --- drivers/mtd/spi-nor/core.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index e0021dd34bfe..bc57b1b5ec62 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1682,13 +1682,19 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) * * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) */ - pow = ilog2(mtd->size) - ilog2(lock_len); - val = mask - (pow << SR_BP_SHIFT); - if (val & ~mask) - return -EINVAL; - /* Don't "lock" with no region! */ - if (!(val & mask)) - return -EINVAL; + if (lock_len == mtd->size) { + val = mask; + } else { + pow = ilog2(mtd->size) - ilog2(lock_len); + val = mask - (pow << SR_BP_SHIFT); + + if (val & ~mask) + return -EINVAL; + + /* Don't "lock" with no region! */ + if (!(val & mask)) + return -EINVAL; + } status_new = (status_old & ~mask & ~tb_mask) | val; From 2d284768b49bcf1c643c08a201ff2161041178ef Mon Sep 17 00:00:00 2001 From: Jungseung Lee Date: Wed, 18 Mar 2020 21:06:13 +0900 Subject: [PATCH 091/103] mtd: spi-nor: Add generic formula for SR block protection handling The current mainline locking was restricted and could only be applied to flashes that have 3 block protection bits and fixed locking ratio. A new method of normalization was reached at the end of the discussion [1]. (1) - if bp slot is insufficient. (2) - if bp slot is sufficient. if (bp_slots_needed > bp_slots) // (1) min_prot_length = sector_size << (bp_slots_needed - bp_slots); else // (2) min_prot_length = sector_size; This patch changes logic to handle block protection based on min_prot_length. It is suitable for the overall flashes with exception of some corner cases (see EON and catalyst) and easy to extend and apply for the case of 2bit or 4bit block protection. [1] http://lists.infradead.org/pipermail/linux-mtd/2020-February/093934.html Signed-off-by: Jungseung Lee Reviewed-by: Michael Walle Tested-by: Michael Walle Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.c | 72 ++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index bc57b1b5ec62..8146d82afe61 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1536,29 +1536,51 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; } +static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) +{ + unsigned int bp_slots, bp_slots_needed; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + + /* Reserved one for "protect none" and one for "protect all". */ + bp_slots = (mask >> SR_BP_SHIFT) + 1 - 2; + bp_slots_needed = ilog2(nor->info->n_sectors); + + if (bp_slots_needed > bp_slots) + return nor->info->sector_size << + (bp_slots_needed - bp_slots); + else + return nor->info->sector_size; +} + static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, uint64_t *len) { struct mtd_info *mtd = &nor->mtd; + u64 min_prot_len; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; - int pow; + u8 bp = (sr & mask) >> SR_BP_SHIFT; if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) tb_mask = SR_TB_BIT6; - if (!(sr & mask)) { + if (!bp) { /* No protection */ *ofs = 0; *len = 0; - } else { - pow = ((sr & mask) ^ mask) >> SR_BP_SHIFT; - *len = mtd->size >> pow; - if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask) - *ofs = 0; - else - *ofs = mtd->size - *len; + return; } + + min_prot_len = spi_nor_get_min_prot_length_sr(nor); + *len = min_prot_len << (bp - 1); + + if (*len > mtd->size) + *len = mtd->size; + + if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask) + *ofs = 0; + else + *ofs = mtd->size - *len; } /* @@ -1631,6 +1653,7 @@ static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; + u64 min_prot_len; int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; @@ -1673,20 +1696,12 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) tb_mask = SR_TB_BIT6; - /* - * Need smallest pow such that: - * - * 1 / (2^pow) <= (len / size) - * - * so (assuming power-of-2 size) we do: - * - * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) - */ if (lock_len == mtd->size) { val = mask; } else { - pow = ilog2(mtd->size) - ilog2(lock_len); - val = mask - (pow << SR_BP_SHIFT); + min_prot_len = spi_nor_get_min_prot_length_sr(nor); + pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; + val = pow << SR_BP_SHIFT; if (val & ~mask) return -EINVAL; @@ -1723,6 +1738,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) { struct mtd_info *mtd = &nor->mtd; + u64 min_prot_len; int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; @@ -1764,20 +1780,14 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) tb_mask = SR_TB_BIT6; - /* - * Need largest pow such that: - * - * 1 / (2^pow) >= (len / size) - * - * so (assuming power-of-2 size) we do: - * - * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len)) - */ - pow = ilog2(mtd->size) - order_base_2(lock_len); + if (lock_len == 0) { val = 0; /* fully unlocked */ } else { - val = mask - (pow << SR_BP_SHIFT); + min_prot_len = spi_nor_get_min_prot_length_sr(nor); + pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; + val = pow << SR_BP_SHIFT; + /* Some power-of-two sizes are not supported */ if (val & ~mask) return -EINVAL; From 05635c14a292de0e1a221dc31c04aba3913f03c8 Mon Sep 17 00:00:00 2001 From: Jungseung Lee Date: Wed, 18 Mar 2020 21:06:14 +0900 Subject: [PATCH 092/103] mtd: spi-nor: Add SR 4bit block protection support Currently we are supporting block protection only for flash chips with 3 block protection bits (BP0-2) in the SR register. Enable block protection support for flashes with 4 block protection bits (BP0-3). Add a flash_info flag for flashes that describe 4 block protection bits. Add another flash_info flag for flashes in which BP3 bit is not adjacent to the BP0-2 bits. Tested with a n25q512ax3 (BP0-3) and w25q128 (BP0-2). Signed-off-by: Jungseung Lee Reviewed-by: Michael Walle Tested-by: Michael Walle Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.c | 66 +++++++++++++++++++++++++++---------- drivers/mtd/spi-nor/core.h | 10 ++++++ include/linux/mtd/spi-nor.h | 2 ++ 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 8146d82afe61..cc68ea84318e 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1536,13 +1536,34 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) return ret; } +static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor) +{ + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + + if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6) + return mask | SR_BP3_BIT6; + + if (nor->flags & SNOR_F_HAS_4BIT_BP) + return mask | SR_BP3; + + return mask; +} + +static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor) +{ + if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) + return SR_TB_BIT6; + else + return SR_TB_BIT5; +} + static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) { unsigned int bp_slots, bp_slots_needed; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 mask = spi_nor_get_sr_bp_mask(nor); /* Reserved one for "protect none" and one for "protect all". */ - bp_slots = (mask >> SR_BP_SHIFT) + 1 - 2; + bp_slots = (1 << hweight8(mask)) - 2; bp_slots_needed = ilog2(nor->info->n_sectors); if (bp_slots_needed > bp_slots) @@ -1557,12 +1578,14 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, { struct mtd_info *mtd = &nor->mtd; u64 min_prot_len; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - u8 tb_mask = SR_TB_BIT5; - u8 bp = (sr & mask) >> SR_BP_SHIFT; + u8 mask = spi_nor_get_sr_bp_mask(nor); + u8 tb_mask = spi_nor_get_sr_tb_mask(nor); + u8 bp, val = sr & mask; - if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) - tb_mask = SR_TB_BIT6; + if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6) + val = (val & ~SR_BP3_BIT6) | SR_BP3; + + bp = val >> SR_BP_SHIFT; if (!bp) { /* No protection */ @@ -1620,7 +1643,8 @@ static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, /* * Lock a region of the flash. Compatible with ST Micro and similar flash. - * Supports the block protection bits BP{0,1,2} in the status register + * Supports the block protection bits BP{0,1,2}/BP{0,1,2,3} in the status + * register * (SR). Does not support these features found in newer SR bitfields: * - SEC: sector/block protect - only handle SEC=0 (block protect) * - CMP: complement protect - only support CMP=0 (range is not complemented) @@ -1655,8 +1679,8 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) struct mtd_info *mtd = &nor->mtd; u64 min_prot_len; int ret, status_old, status_new; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - u8 tb_mask = SR_TB_BIT5; + u8 mask = spi_nor_get_sr_bp_mask(nor); + u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; @@ -1693,9 +1717,6 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) else lock_len = ofs + len; - if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) - tb_mask = SR_TB_BIT6; - if (lock_len == mtd->size) { val = mask; } else { @@ -1703,6 +1724,9 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; val = pow << SR_BP_SHIFT; + if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) + val = (val & ~SR_BP3) | SR_BP3_BIT6; + if (val & ~mask) return -EINVAL; @@ -1740,8 +1764,8 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) struct mtd_info *mtd = &nor->mtd; u64 min_prot_len; int ret, status_old, status_new; - u8 mask = SR_BP2 | SR_BP1 | SR_BP0; - u8 tb_mask = SR_TB_BIT5; + u8 mask = spi_nor_get_sr_bp_mask(nor); + u8 tb_mask = spi_nor_get_sr_tb_mask(nor); u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; @@ -1778,9 +1802,6 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) else lock_len = ofs; - if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) - tb_mask = SR_TB_BIT6; - if (lock_len == 0) { val = 0; /* fully unlocked */ } else { @@ -1788,6 +1809,9 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) pow = ilog2(lock_len) - ilog2(min_prot_len) + 1; val = pow << SR_BP_SHIFT; + if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) + val = (val & ~SR_BP3) | SR_BP3_BIT6; + /* Some power-of-two sizes are not supported */ if (val & ~mask) return -EINVAL; @@ -3147,6 +3171,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (info->flags & USE_CLSR) nor->flags |= SNOR_F_USE_CLSR; + if (info->flags & SPI_NOR_4BIT_BP) { + nor->flags |= SNOR_F_HAS_4BIT_BP; + if (info->flags & SPI_NOR_BP3_SR_BIT6) + nor->flags |= SNOR_F_HAS_SR_BP3_BIT6; + } + if (info->flags & SPI_NOR_NO_ERASE) mtd->flags |= MTD_NO_ERASE; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 3ce826b35ad1..6f2f6b27173f 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -24,6 +24,8 @@ enum spi_nor_option_flags { SNOR_F_HAS_16BIT_SR = BIT(9), SNOR_F_NO_READ_CR = BIT(10), SNOR_F_HAS_SR_TB_BIT6 = BIT(11), + SNOR_F_HAS_4BIT_BP = BIT(12), + SNOR_F_HAS_SR_BP3_BIT6 = BIT(13), }; struct spi_nor_read_command { @@ -301,6 +303,14 @@ struct flash_info { * status register. Must be used with * SPI_NOR_HAS_TB. */ +#define SPI_NOR_4BIT_BP BIT(17) /* + * Flash SR has 4 bit fields (BP0-3) + * for block protection. + */ +#define SPI_NOR_BP3_SR_BIT6 BIT(18) /* + * BP3 is bit 6 of status register. + * Must be used with SPI_NOR_4BIT_BP. + */ /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index e656858b50a5..1e2af0ec1f03 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -111,7 +111,9 @@ #define SR_BP0 BIT(2) /* Block protect 0 */ #define SR_BP1 BIT(3) /* Block protect 1 */ #define SR_BP2 BIT(4) /* Block protect 2 */ +#define SR_BP3 BIT(5) /* Block protect 3 */ #define SR_TB_BIT5 BIT(5) /* Top/Bottom protect */ +#define SR_BP3_BIT6 BIT(6) /* Block protect 3 */ #define SR_TB_BIT6 BIT(6) /* Top/Bottom protect */ #define SR_SRWD BIT(7) /* SR write protect */ /* Spansion/Cypress specific status bits */ From f3f2b7eb2f1c5889b0a7162b6b5a69c0f357befd Mon Sep 17 00:00:00 2001 From: Jungseung Lee Date: Wed, 18 Mar 2020 21:06:15 +0900 Subject: [PATCH 093/103] mtd: spi-nor: Enable locking for n25q512ax3/n25q512a n25q512ax3 and n25q512a use the 4 bit Block Protection scheme. Enable locking for both. Tested on n25q512ax3. The other is modified following the datasheet. Signed-off-by: Jungseung Lee Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/micron-st.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 3874a62d8b47..6c034b9718e2 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -47,12 +47,16 @@ static const struct flash_info st_parts[] = { SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, - SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_4BIT_BP | SPI_NOR_BP3_SR_BIT6) }, { "mt25qu512a", INFO6(0x20bb20, 0x104400, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, - SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) }, + SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_4BIT_BP | SPI_NOR_BP3_SR_BIT6) }, { "n25q00", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, From adc6162b9a0c60a81cf6a107196924526cd186f6 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Wed, 18 Mar 2020 15:42:27 +0800 Subject: [PATCH 094/103] mtd: rawnand: Add support for manufacturer specific suspend/resume operation Patch nand_suspend() & nand_resume() to let manufacturers overwrite suspend/resume operations. Signed-off-by: Mason Yang Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1584517348-14486-2-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_base.c | 17 +++++++++++++---- include/linux/mtd/rawnand.h | 4 ++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a13b91aa3780..985a15a735af 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4326,16 +4326,22 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) /** * nand_suspend - [MTD Interface] Suspend the NAND flash * @mtd: MTD device structure + * + * Returns 0 for success or negative error code otherwise. */ static int nand_suspend(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); + int ret = 0; mutex_lock(&chip->lock); - chip->suspended = 1; + if (chip->suspend) + ret = chip->suspend(chip); + if (!ret) + chip->suspended = 1; mutex_unlock(&chip->lock); - return 0; + return ret; } /** @@ -4347,11 +4353,14 @@ static void nand_resume(struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); mutex_lock(&chip->lock); - if (chip->suspended) + if (chip->suspended) { + if (chip->resume) + chip->resume(chip); chip->suspended = 0; - else + } else { pr_err("%s called for a chip which is not in suspended state\n", __func__); + } mutex_unlock(&chip->lock); } diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 49ed50fb44ab..1e76196f9829 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1064,6 +1064,8 @@ struct nand_legacy { * @lock: lock protecting the suspended field. Also used to * serialize accesses to the NAND device. * @suspended: set to 1 when the device is suspended, 0 when it's not. + * @suspend: [REPLACEABLE] specific NAND device suspend operation + * @resume: [REPLACEABLE] specific NAND device resume operation * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash * lookup. @@ -1119,6 +1121,8 @@ struct nand_chip { struct mutex lock; unsigned int suspended : 1; + int (*suspend)(struct nand_chip *chip); + void (*resume)(struct nand_chip *chip); uint8_t *oob_poi; struct nand_controller *controller; From 19301d54997df35e9829b4f707c59f3284530d71 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Wed, 18 Mar 2020 15:42:28 +0800 Subject: [PATCH 095/103] mtd: rawnand: macronix: Add support for deep power down mode Macronix AD series support deep power down mode for a minimum power consumption state. Overload nand_suspend() & nand_resume() in Macronix specific code to support deep power down mode. Signed-off-by: Mason Yang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_macronix.c | 74 ++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index fbe2fff0cf1b..09c254c97b5c 100644 --- a/drivers/mtd/nand/raw/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -6,6 +6,7 @@ * Author: Boris Brezillon */ +#include "linux/delay.h" #include "internals.h" #define MACRONIX_READ_RETRY_BIT BIT(0) @@ -28,6 +29,8 @@ (MACRONIX_RANDOMIZER_RANDEN | \ MACRONIX_RANDOMIZER_RANDOPT) +#define MXIC_CMD_POWER_DOWN 0xB9 + struct nand_onfi_vendor_macronix { u8 reserved; u8 reliability_func; @@ -243,6 +246,76 @@ static void macronix_nand_block_protection_support(struct nand_chip *chip) chip->unlock_area = mxic_nand_unlock; } +static int nand_power_down_op(struct nand_chip *chip) +{ + int ret; + + if (nand_has_exec_op(chip)) { + struct nand_op_instr instrs[] = { + NAND_OP_CMD(MXIC_CMD_POWER_DOWN, 0), + }; + + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + + ret = nand_exec_op(chip, &op); + if (ret) + return ret; + + } else { + chip->legacy.cmdfunc(chip, MXIC_CMD_POWER_DOWN, -1, -1); + } + + return 0; +} + +static int mxic_nand_suspend(struct nand_chip *chip) +{ + int ret; + + nand_select_target(chip, 0); + ret = nand_power_down_op(chip); + if (ret < 0) + pr_err("Suspending MXIC NAND chip failed (%d)\n", ret); + nand_deselect_target(chip); + + return ret; +} + +static void mxic_nand_resume(struct nand_chip *chip) +{ + /* + * Toggle #CS pin to resume NAND device and don't care + * of the others CLE, #WE, #RE pins status. + * A NAND controller ensure it is able to assert/de-assert #CS + * by sending any byte over the NAND bus. + * i.e., + * NAND power down command or reset command w/o R/B# status checking. + */ + nand_select_target(chip, 0); + nand_power_down_op(chip); + /* The minimum of a recovery time tRDP is 35 us */ + usleep_range(35, 100); + nand_deselect_target(chip); +} + +static void macronix_nand_deep_power_down_support(struct nand_chip *chip) +{ + int i; + static const char * const deep_power_down_dev[] = { + "MX30UF1G28AD", + "MX30UF2G28AD", + "MX30UF4G28AD", + }; + + i = match_string(deep_power_down_dev, ARRAY_SIZE(deep_power_down_dev), + chip->parameters.model); + if (i < 0) + return; + + chip->suspend = mxic_nand_suspend; + chip->resume = mxic_nand_resume; +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -251,6 +324,7 @@ static int macronix_nand_init(struct nand_chip *chip) macronix_nand_fix_broken_get_timings(chip); macronix_nand_onfi_init(chip); macronix_nand_block_protection_support(chip); + macronix_nand_deep_power_down_support(chip); return 0; } From 6b49e58d6d9dab031a16af2af5439f28a37c4cd9 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Tue, 24 Mar 2020 15:49:44 +0900 Subject: [PATCH 096/103] mtd: spinand: toshiba: Rename function name to change suffix and prefix (8Gbit) The suffix was changed from "G" to "J" to classify between 1st generation and 2nd generation serial NAND devices (which now belong to the Kioxia brand). As reference that's 1st generation device of 1Gbit product is "TC58CVG0S3HRAIG" 2nd generation device of 1Gbit product is "TC58CVG0S3HRAIJ". The 8Gbit type "TH58CxG3S0HRAIJ" is new to Kioxia's serial NAND lineup and the prefix was changed from "TC58" to "TH58". Thus the functions were renamed from tc58cxgxsx_*() to tx58cxgxsxraix_*(). Signed-off-by: Yoshio Furuyama Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/0dedd9869569a17625822dba87878254d253ba0e.1584949601.git.ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/spi/toshiba.c | 60 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index d34773191700..5d217dd4b253 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -26,8 +26,8 @@ static SPINAND_OP_VARIANTS(write_cache_variants, static SPINAND_OP_VARIANTS(update_cache_variants, SPINAND_PROG_LOAD(false, 0, NULL, 0)); -static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int tx58cxgxsxraix_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section > 0) return -ERANGE; @@ -38,8 +38,8 @@ static int tc58cxgxsx_ooblayout_ecc(struct mtd_info *mtd, int section, return 0; } -static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) +static int tx58cxgxsxraix_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) { if (section > 0) return -ERANGE; @@ -51,13 +51,13 @@ static int tc58cxgxsx_ooblayout_free(struct mtd_info *mtd, int section, return 0; } -static const struct mtd_ooblayout_ops tc58cxgxsx_ooblayout = { - .ecc = tc58cxgxsx_ooblayout_ecc, - .free = tc58cxgxsx_ooblayout_free, +static const struct mtd_ooblayout_ops tx58cxgxsxraix_ooblayout = { + .ecc = tx58cxgxsxraix_ooblayout_ecc, + .free = tx58cxgxsxraix_ooblayout_free, }; -static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, - u8 status) +static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, + u8 status) { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; @@ -96,7 +96,7 @@ static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info toshiba_spinand_table[] = { /* 3.3V 1Gb */ - SPINAND_INFO("TC58CVG0S3", + SPINAND_INFO("TC58CVG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -104,10 +104,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 3.3V 2Gb */ - SPINAND_INFO("TC58CVG1S3", + SPINAND_INFO("TC58CVG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -115,10 +115,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", + SPINAND_INFO("TC58CVG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -126,10 +126,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", + SPINAND_INFO("TC58CVG2S0HRAIJ", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -137,10 +137,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 1.8V 1Gb */ - SPINAND_INFO("TC58CYG0S3", + SPINAND_INFO("TC58CYG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -148,10 +148,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 1.8V 2Gb */ - SPINAND_INFO("TC58CYG1S3", + SPINAND_INFO("TC58CYG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -159,10 +159,10 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), /* 1.8V 4Gb */ - SPINAND_INFO("TC58CYG2S0", + SPINAND_INFO("TC58CYG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), @@ -170,8 +170,8 @@ static const struct spinand_info toshiba_spinand_table[] = { &write_cache_variants, &update_cache_variants), 0, - SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, - tc58cxgxsx_ecc_get_status)), + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), }; static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { From 798fcdd010006e87b3154d6454c657af7b033002 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Tue, 24 Mar 2020 15:49:55 +0900 Subject: [PATCH 097/103] mtd: spinand: toshiba: Support for new Kioxia Serial NAND Add support for new Kioxia products. The new Kioxia products support program load x4 command, and have HOLD_D bit which is equivalent to QE bit. Signed-off-by: Yoshio Furuyama Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/aa69e455beedc5ce0d7141359b9364ed8aec9e65.1584949601.git.ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/spi/toshiba.c | 128 ++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 5d217dd4b253..bc801d83343e 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -20,6 +20,18 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); +static SPINAND_OP_VARIANTS(write_cache_x4_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_x4_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +/** + * Backward compatibility for 1st generation Serial NAND devices + * which don't support Quad Program Load operation. + */ static SPINAND_OP_VARIANTS(write_cache_variants, SPINAND_PROG_LOAD(true, 0, NULL, 0)); @@ -95,7 +107,7 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info toshiba_spinand_table[] = { - /* 3.3V 1Gb */ + /* 3.3V 1Gb (1st generation) */ SPINAND_INFO("TC58CVG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -106,7 +118,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 3.3V 2Gb */ + /* 3.3V 2Gb (1st generation) */ SPINAND_INFO("TC58CVG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -117,7 +129,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 3.3V 4Gb */ + /* 3.3V 4Gb (1st generation) */ SPINAND_INFO("TC58CVG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -128,18 +140,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0HRAIJ", - SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), - NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), - NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), - 0, - SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, - tx58cxgxsxraix_ecc_get_status)), - /* 1.8V 1Gb */ + /* 1.8V 1Gb (1st generation) */ SPINAND_INFO("TC58CYG0S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -150,7 +151,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 1.8V 2Gb */ + /* 1.8V 2Gb (1st generation) */ SPINAND_INFO("TC58CYG1S3HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -161,7 +162,7 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), - /* 1.8V 4Gb */ + /* 1.8V 4Gb (1st generation) */ SPINAND_INFO("TC58CYG2S0HRAIG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -172,6 +173,99 @@ static const struct spinand_info toshiba_spinand_table[] = { 0, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), + + /* + * 2nd generation serial nand has HOLD_D which is equivalent to + * QE_BIT. + */ + /* 3.3V 1Gb (2nd generation) */ + SPINAND_INFO("TC58CVG0S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 2Gb (2nd generation) */ + SPINAND_INFO("TC58CVG1S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 4Gb (2nd generation) */ + SPINAND_INFO("TC58CVG2S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 8Gb (2nd generation) */ + SPINAND_INFO("TH58CVG3S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 1Gb (2nd generation) */ + SPINAND_INFO("TC58CYG0S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 2Gb (2nd generation) */ + SPINAND_INFO("TC58CYG1S3HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 4Gb (2nd generation) */ + SPINAND_INFO("TC58CYG2S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 8Gb (2nd generation) */ + SPINAND_INFO("TH58CYG3S0HRAIJ", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), }; static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { From 49c64df880570034308e4a9a49c4bc95cf8cdb33 Mon Sep 17 00:00:00 2001 From: Wen Yang Date: Wed, 18 Mar 2020 23:31:56 +0800 Subject: [PATCH 098/103] mtd: phram: fix a double free issue in error path The variable 'name' is released multiple times in the error path, which may cause double free issues. This problem is avoided by adding a goto label to release the memory uniformly. And this change also makes the code a bit more cleaner. Fixes: 4f678a58d335 ("mtd: fix memory leaks in phram_setup") Signed-off-by: Wen Yang Cc: Joern Engel Cc: Miquel Raynal Cc: Richard Weinberger Cc: Vignesh Raghavendra Cc: linux-mtd@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200318153156.25612-1-wenyang@linux.alibaba.com --- drivers/mtd/devices/phram.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 931e5c2481b5..b50ec7ecd10c 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -243,22 +243,25 @@ static int phram_setup(const char *val) ret = parse_num64(&start, token[1]); if (ret) { - kfree(name); parse_err("illegal start address\n"); + goto error; } ret = parse_num64(&len, token[2]); if (ret) { - kfree(name); parse_err("illegal device length\n"); + goto error; } ret = register_device(name, start, len); - if (!ret) - pr_info("%s device: %#llx at %#llx\n", name, len, start); - else - kfree(name); + if (ret) + goto error; + pr_info("%s device: %#llx at %#llx\n", name, len, start); + return 0; + +error: + kfree(name); return ret; } From f1ffdbfad00a3c424cadfd307b5bb9c466638532 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 19 Mar 2020 17:42:00 -0500 Subject: [PATCH 099/103] mtd: maps: sa1100-flash: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200319224200.GA25162@embeddedor.com --- drivers/mtd/maps/sa1100-flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 47602af4ee34..bb1ef650ffd2 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -34,7 +34,7 @@ struct sa_subdev_info { struct sa_info { struct mtd_info *mtd; int num_subdev; - struct sa_subdev_info subdev[0]; + struct sa_subdev_info subdev[]; }; static DEFINE_SPINLOCK(sa1100_vpp_lock); From 4e4a9a828af29785fad12ecc11583769e1282024 Mon Sep 17 00:00:00 2001 From: Xiaoming Ni Date: Fri, 20 Mar 2020 11:15:11 +0800 Subject: [PATCH 100/103] mtd: Fix issue where write_cached_data() fails but write() still returns success The following sequence is problematic: mtdblock_flush() -->write_cached_data() --->erase_write() mtdblock: erase of region [0x40000, 0x20000] on "xxx" failed Problem is: mtdblock_flush() always returns 0. Indeed, even if write_cached_data() fails and data is not written to the device, syscall_write() still returns success. Avoid this situation by actually returning the error coming out of write_cached_data(). Signed-off-by: Xiaoming Ni Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1584674111-101462-1-git-send-email-nixiaoming@huawei.com --- drivers/mtd/mtdblock.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index c06b5322d470..078e0f67377d 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -294,12 +294,13 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd) static int mtdblock_flush(struct mtd_blktrans_dev *dev) { struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); + int ret; mutex_lock(&mtdblk->cache_mutex); - write_cached_data(mtdblk); + ret = write_cached_data(mtdblk); mutex_unlock(&mtdblk->cache_mutex); mtd_sync(dev->mtd); - return 0; + return ret; } static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) From 17872f51cb083ec537a611657e299285306890f5 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Wed, 25 Mar 2020 09:43:27 +0000 Subject: [PATCH 101/103] MAINTAINERS: Add the IRC channel to the MTD related subsystems The #mtd channel (on OFTC servers) is being used to discuss MTD related topics. Add it for better visibility to the HYPERBUS, NAND and SPI NOR entries. Signed-off-by: Tudor Ambarus Acked-by: Miquel Raynal Acked-by: Vignesh Raghavendra Signed-off-by: Miquel Raynal --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index abe66664d5f4..b171bf102a67 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7773,6 +7773,7 @@ M: Vignesh Raghavendra L: linux-mtd@lists.infradead.org Q: http://patchwork.ozlabs.org/project/linux-mtd/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git cfi/next +C: irc://irc.oftc.net/mtd S: Supported F: drivers/mtd/hyperbus/ F: include/linux/mtd/hyperbus.h @@ -11455,6 +11456,7 @@ L: linux-mtd@lists.infradead.org W: http://www.linux-mtd.infradead.org/ Q: http://patchwork.ozlabs.org/project/linux-mtd/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next +C: irc://irc.oftc.net/mtd S: Maintained F: drivers/mtd/nand/ F: include/linux/mtd/*nand*.h @@ -15693,6 +15695,7 @@ L: linux-mtd@lists.infradead.org W: http://www.linux-mtd.infradead.org/ Q: http://patchwork.ozlabs.org/project/linux-mtd/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git spi-nor/next +C: irc://irc.oftc.net/mtd S: Maintained F: drivers/mtd/spi-nor/ F: include/linux/mtd/spi-nor.h From fca88925d76978b7f20de42d8ead34fb91500003 Mon Sep 17 00:00:00 2001 From: Yoshio Furuyama Date: Wed, 25 Mar 2020 17:22:52 +0900 Subject: [PATCH 102/103] mtd: rawnand: toshiba: Support reading the number of bitflips for BENAND (Built-in ECC NAND) Add support vendor specific commands for KIOXIA CORPORATION BENAND. The actual bitflips number can be retrieved by this command. Signed-off-by: Yoshio Furuyama Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1585124572-4693-1-git-send-email-ytc-mb-yfuruyama7@kioxia.com --- drivers/mtd/nand/raw/nand_toshiba.c | 58 ++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c index 9c03fbb1f47d..f3dcd695b5db 100644 --- a/drivers/mtd/nand/raw/nand_toshiba.c +++ b/drivers/mtd/nand/raw/nand_toshiba.c @@ -14,14 +14,68 @@ /* Recommended to rewrite for BENAND */ #define TOSHIBA_NAND_STATUS_REWRITE_RECOMMENDED BIT(3) +/* ECC Status Read Command for BENAND */ +#define TOSHIBA_NAND_CMD_ECC_STATUS_READ 0x7A + +/* ECC Status Mask for BENAND */ +#define TOSHIBA_NAND_ECC_STATUS_MASK 0x0F + +/* Uncorrectable Error for BENAND */ +#define TOSHIBA_NAND_ECC_STATUS_UNCORR 0x0F + +/* Max ECC Steps for BENAND */ +#define TOSHIBA_NAND_MAX_ECC_STEPS 8 + +static int toshiba_nand_benand_read_eccstatus_op(struct nand_chip *chip, + u8 *buf) +{ + u8 *ecc_status = buf; + + if (nand_has_exec_op(chip)) { + const struct nand_sdr_timings *sdr = + nand_get_sdr_timings(&chip->data_interface); + struct nand_op_instr instrs[] = { + NAND_OP_CMD(TOSHIBA_NAND_CMD_ECC_STATUS_READ, + PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_8BIT_DATA_IN(chip->ecc.steps, ecc_status, 0), + }; + struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + + return nand_exec_op(chip, &op); + } + + return -ENOTSUPP; +} + static int toshiba_nand_benand_eccstatus(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); int ret; unsigned int max_bitflips = 0; - u8 status; + u8 status, ecc_status[TOSHIBA_NAND_MAX_ECC_STEPS]; /* Check Status */ + ret = toshiba_nand_benand_read_eccstatus_op(chip, ecc_status); + if (!ret) { + unsigned int i, bitflips = 0; + + for (i = 0; i < chip->ecc.steps; i++) { + bitflips = ecc_status[i] & TOSHIBA_NAND_ECC_STATUS_MASK; + if (bitflips == TOSHIBA_NAND_ECC_STATUS_UNCORR) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += bitflips; + max_bitflips = max(max_bitflips, bitflips); + } + } + + return max_bitflips; + } + + /* + * Fallback to regular status check if + * toshiba_nand_benand_read_eccstatus_op() failed. + */ ret = nand_status_op(chip, &status); if (ret) return ret; @@ -108,7 +162,7 @@ static void toshiba_nand_decode_id(struct nand_chip *chip) */ if (chip->id.len >= 6 && nand_is_slc(chip) && (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ && - !(chip->id.data[4] & 0x80) /* !BENAND */) { + !(chip->id.data[4] & TOSHIBA_NAND_ID4_IS_BENAND) /* !BENAND */) { memorg->oobsize = 32 * memorg->pagesize >> 9; mtd->oobsize = memorg->oobsize; } From 025a06c1104cd8995646b761d117816b5f28c873 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 25 Mar 2020 22:21:15 +0100 Subject: [PATCH 103/103] mtd: Convert fallthrough comments into statements Use Joe Perches cvt_fallthrough.pl script to convert /* fallthrough */ comments (and its derivatives) into a fallthrough; statement. This automatically drops useless ones. Do it MTD-wide. Signed-off-by: Miquel Raynal Acked-by: Vignesh Raghavendra Acked-by: Tudor Ambarus Acked-by: Richard Weinberger Link: https://lore.kernel.org/linux-mtd/20200325212115.14170-1-miquel.raynal@bootlin.com --- drivers/mtd/chips/cfi_cmdset_0001.c | 4 ++-- drivers/mtd/chips/cfi_cmdset_0002.c | 5 ++--- drivers/mtd/chips/cfi_cmdset_0020.c | 17 ++++++----------- drivers/mtd/chips/cfi_util.c | 12 ++++++------ drivers/mtd/devices/block2mtd.c | 4 ++-- drivers/mtd/devices/phram.c | 4 ++-- drivers/mtd/lpddr/lpddr_cmds.c | 3 +-- drivers/mtd/maps/sa1100-flash.c | 3 +-- drivers/mtd/nand/onenand/onenand_base.c | 2 +- drivers/mtd/nand/raw/diskonchip.c | 2 +- drivers/mtd/nand/raw/fsl_elbc_nand.c | 3 +-- drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c | 2 +- drivers/mtd/nand/raw/ingenic/jz4725b_bch.c | 4 ++-- drivers/mtd/nand/raw/ingenic/jz4780_bch.c | 4 ++-- drivers/mtd/nand/raw/nand_base.c | 9 +++------ drivers/mtd/nand/raw/nand_legacy.c | 6 ++---- drivers/mtd/nand/raw/nandsim.c | 4 ++-- drivers/mtd/nand/raw/omap_elm.c | 8 ++++---- drivers/mtd/spi-nor/controllers/aspeed-smc.c | 2 +- drivers/mtd/spi-nor/sfdp.c | 1 - drivers/mtd/ubi/attach.c | 2 +- drivers/mtd/ubi/build.c | 4 ++-- 22 files changed, 45 insertions(+), 60 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 00a79489067c..142c0f9485fe 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -834,7 +834,7 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long /* Someone else might have been playing with it. */ return -EAGAIN; } - /* Fall through */ + fallthrough; case FL_READY: case FL_CFI_QUERY: case FL_JEDEC_QUERY: @@ -907,7 +907,7 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) return 0; - /* Fall through */ + fallthrough; default: sleep: set_current_state(TASK_UNINTERRUPTIBLE); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 04b383bc3947..a1f3e1031c3d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -966,8 +966,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) return 0; - /* fall through */ - + fallthrough; default: sleep: set_current_state(TASK_UNINTERRUPTIBLE); @@ -2935,7 +2934,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) * as the whole point is that nobody can do anything * with the chip now anyway. */ - /* fall through */ + fallthrough; case FL_SYNCING: mutex_unlock(&chip->mutex); break; diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 54edae63b92d..270322bca221 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -324,8 +324,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof case FL_JEDEC_QUERY: map_write(map, CMD(0x70), cmd_addr); chip->state = FL_STATUS; - /* Fall through */ - + fallthrough; case FL_STATUS: status = map_read(map, cmd_addr); if (map_word_andequal(map, status, status_OK, status_OK)) { @@ -462,8 +461,7 @@ static int do_write_buffer(struct map_info *map, struct flchip *chip, #ifdef DEBUG_CFI_FEATURES printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr)); #endif - /* Fall through */ - + fallthrough; case FL_STATUS: status = map_read(map, cmd_adr); if (map_word_andequal(map, status, status_OK, status_OK)) @@ -756,8 +754,7 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u case FL_READY: map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - /* Fall through */ - + fallthrough; case FL_STATUS: status = map_read(map, adr); if (map_word_andequal(map, status, status_OK, status_OK)) @@ -998,7 +995,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) * as the whole point is that nobody can do anything * with the chip now anyway. */ - /* Fall through */ + fallthrough; case FL_SYNCING: mutex_unlock(&chip->mutex); break; @@ -1054,8 +1051,7 @@ static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, un case FL_READY: map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - /* Fall through */ - + fallthrough; case FL_STATUS: status = map_read(map, adr); if (map_word_andequal(map, status, status_OK, status_OK)) @@ -1201,8 +1197,7 @@ static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, case FL_READY: map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - /* Fall through */ - + fallthrough; case FL_STATUS: status = map_read(map, adr); if (map_word_andequal(map, status, status_OK, status_OK)) diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index e2d4db05aeb3..99b7986002f0 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -109,13 +109,13 @@ map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi case 8: onecmd |= (onecmd << (chip_mode * 32)); #endif - /* fall through */ + fallthrough; case 4: onecmd |= (onecmd << (chip_mode * 16)); - /* fall through */ + fallthrough; case 2: onecmd |= (onecmd << (chip_mode * 8)); - /* fall through */ + fallthrough; case 1: ; } @@ -165,13 +165,13 @@ unsigned long cfi_merge_status(map_word val, struct map_info *map, case 8: res |= (onestat >> (chip_mode * 32)); #endif - /* fall through */ + fallthrough; case 4: res |= (onestat >> (chip_mode * 16)); - /* fall through */ + fallthrough; case 2: res |= (onestat >> (chip_mode * 8)); - /* fall through */ + fallthrough; case 1: ; } diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 36aa082f6db0..c08721b11642 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -329,10 +329,10 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base) switch (**endp) { case 'G' : result *= 1024; - /* fall through */ + fallthrough; case 'M': result *= 1024; - /* fall through */ + fallthrough; case 'K': case 'k': result *= 1024; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index b50ec7ecd10c..087b5e86d1bf 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -148,10 +148,10 @@ static int parse_num64(uint64_t *num64, char *token) switch (token[len - 2]) { case 'G': shift += 10; - /* fall through */ + fallthrough; case 'M': shift += 10; - /* fall through */ + fallthrough; case 'k': shift += 10; token[len - 2] = 0; diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index 9341a8a592e8..fb1cbc9a2870 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -304,8 +304,7 @@ static int chip_ready(struct map_info *map, struct flchip *chip, int mode) /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) return 0; - /* fall through */ - + fallthrough; default: sleep: set_current_state(TASK_UNINTERRUPTIBLE); diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index bb1ef650ffd2..d3d4e987c163 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -81,8 +81,7 @@ static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *r default: printk(KERN_WARNING "SA1100 flash: unknown base address " "0x%08lx, assuming CS0\n", phys); - /* Fall through */ - + fallthrough; case SA1100_CS0_PHYS: subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; break; diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index d5326d19b136..ec18ade33262 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -3259,7 +3259,7 @@ static void onenand_check_features(struct mtd_info *mtd) switch (density) { case ONENAND_DEVICE_DENSITY_8Gb: this->options |= ONENAND_HAS_NOP_1; - /* fall through */ + fallthrough; case ONENAND_DEVICE_DENSITY_4Gb: if (ONENAND_IS_DDP(this)) this->options |= ONENAND_HAS_2PLANE; diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 2833c49c1378..c2a391ad2c35 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1482,7 +1482,7 @@ static int __init doc_probe(unsigned long physadr) break; case DOC_ChipID_DocMilPlus32: pr_err("DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); - /* fall through */ + fallthrough; default: ret = -ENODEV; goto notfound; diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index 634c550db13a..e1dc675b12bb 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -324,8 +324,7 @@ static void fsl_elbc_cmdfunc(struct nand_chip *chip, unsigned int command, /* READ0 and READ1 read the entire buffer to use hardware ECC. */ case NAND_CMD_READ1: column += 256; - - /* fall-through */ + fallthrough; case NAND_CMD_READ0: dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c index 49afebee50db..935c4902ada7 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -253,7 +253,7 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip) chip->ecc.hwctl = ingenic_nand_ecc_hwctl; chip->ecc.calculate = ingenic_nand_ecc_calculate; chip->ecc.correct = ingenic_nand_ecc_correct; - /* fall through */ + fallthrough; case NAND_ECC_SOFT: dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n", (nfc->ecc) ? "hardware ECC" : "software ECC", diff --git a/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c b/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c index 6c852eae09cf..2d0e0a2192ae 100644 --- a/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c +++ b/drivers/mtd/nand/raw/ingenic/jz4725b_bch.c @@ -145,10 +145,10 @@ static void jz4725b_bch_read_parity(struct ingenic_ecc *bch, u8 *buf, switch (size8) { case 3: dest8[2] = (val >> 16) & 0xff; - /* fall-through */ + fallthrough; case 2: dest8[1] = (val >> 8) & 0xff; - /* fall-through */ + fallthrough; case 1: dest8[0] = val & 0xff; break; diff --git a/drivers/mtd/nand/raw/ingenic/jz4780_bch.c b/drivers/mtd/nand/raw/ingenic/jz4780_bch.c index 079266a0d6cf..d67dbfff76cc 100644 --- a/drivers/mtd/nand/raw/ingenic/jz4780_bch.c +++ b/drivers/mtd/nand/raw/ingenic/jz4780_bch.c @@ -123,10 +123,10 @@ static void jz4780_bch_read_parity(struct ingenic_ecc *bch, void *buf, switch (size8) { case 3: dest8[2] = (val >> 16) & 0xff; - /* fall through */ + fallthrough; case 2: dest8[1] = (val >> 8) & 0xff; - /* fall through */ + fallthrough; case 1: dest8[0] = val & 0xff; break; diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 985a15a735af..c24e5e2ba130 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5637,8 +5637,7 @@ static int nand_scan_tail(struct nand_chip *chip) } if (!ecc->read_page) ecc->read_page = nand_read_page_hwecc_oob_first; - /* fall through */ - + fallthrough; case NAND_ECC_HW: /* Use standard hwecc read page function? */ if (!ecc->read_page) @@ -5657,8 +5656,7 @@ static int nand_scan_tail(struct nand_chip *chip) ecc->read_subpage = nand_read_subpage; if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) ecc->write_subpage = nand_write_subpage_hwecc; - /* fall through */ - + fallthrough; case NAND_ECC_HW_SYNDROME: if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && (!ecc->read_page || @@ -5695,8 +5693,7 @@ static int nand_scan_tail(struct nand_chip *chip) ecc->size, mtd->writesize); ecc->mode = NAND_ECC_SOFT; ecc->algo = NAND_ECC_HAMMING; - /* fall through */ - + fallthrough; case NAND_ECC_SOFT: ret = nand_set_ecc_soft_ops(chip); if (ret) { diff --git a/drivers/mtd/nand/raw/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c index f2526ec616a6..f91e92e1b972 100644 --- a/drivers/mtd/nand/raw/nand_legacy.c +++ b/drivers/mtd/nand/raw/nand_legacy.c @@ -331,8 +331,7 @@ static void nand_command(struct nand_chip *chip, unsigned int command, */ if (column == -1 && page_addr == -1) return; - /* fall through */ - + fallthrough; default: /* * If we don't have access to the busy pin, we apply the given @@ -483,8 +482,7 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); - - /* fall through - This applies to read commands */ + fallthrough; /* This applies to read commands */ default: /* * If we don't have access to the busy pin, we apply the given diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index 9a70754a61ef..1de03bb34e84 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -2251,10 +2251,10 @@ static int __init ns_init_module(void) switch (bbt) { case 2: chip->bbt_options |= NAND_BBT_NO_OOB; - /* fall through */ + fallthrough; case 1: chip->bbt_options |= NAND_BBT_USE_FLASH; - /* fall through */ + fallthrough; case 0: break; default: diff --git a/drivers/mtd/nand/raw/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c index 5502ffbdd1e6..3fa0e2cbbe53 100644 --- a/drivers/mtd/nand/raw/omap_elm.c +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -455,13 +455,13 @@ static int elm_context_save(struct elm_info *info) ELM_SYNDROME_FRAGMENT_5 + offset); regs->elm_syndrome_fragment_4[i] = elm_read_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset); - /* fall through */ + fallthrough; case BCH8_ECC: regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset); regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset); - /* fall through */ + fallthrough; case BCH4_ECC: regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset); @@ -503,13 +503,13 @@ static int elm_context_restore(struct elm_info *info) regs->elm_syndrome_fragment_5[i]); elm_write_reg(info, ELM_SYNDROME_FRAGMENT_4 + offset, regs->elm_syndrome_fragment_4[i]); - /* fall through */ + fallthrough; case BCH8_ECC: elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, regs->elm_syndrome_fragment_3[i]); elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, regs->elm_syndrome_fragment_2[i]); - /* fall through */ + fallthrough; case BCH4_ECC: elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, regs->elm_syndrome_fragment_1[i]); diff --git a/drivers/mtd/spi-nor/controllers/aspeed-smc.c b/drivers/mtd/spi-nor/controllers/aspeed-smc.c index e26a1897db0e..ae85e4c0e114 100644 --- a/drivers/mtd/spi-nor/controllers/aspeed-smc.c +++ b/drivers/mtd/spi-nor/controllers/aspeed-smc.c @@ -354,7 +354,7 @@ static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr) default: WARN_ONCE(1, "Unexpected address width %u, defaulting to 3\n", nor->addr_width); - /* FALLTHROUGH */ + fallthrough; case 3: cmdaddr = addr & 0xFFFFFF; cmdaddr |= cmd << 24; diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index df967f1f4951..f6038d3a3684 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -623,7 +623,6 @@ static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) case SMPT_CMD_ADDRESS_LEN_4: return 4; case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: - /* fall through */ default: return nor->addr_width; } diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index ea7440ac913b..ae5abe492b52 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -1059,7 +1059,7 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, * be a result of power cut during erasure. */ ai->maybe_bad_peb_count += 1; - /* fall through */ + fallthrough; case UBI_IO_BAD_HDR: /* * If we're facing a bad VID header we have to drop *all* diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 2f93c25bbaee..12c02342149c 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1342,10 +1342,10 @@ static int bytes_str_to_int(const char *str) switch (*endp) { case 'G': result *= 1024; - /* fall through */ + fallthrough; case 'M': result *= 1024; - /* fall through */ + fallthrough; case 'K': result *= 1024; if (endp[1] == 'i' && endp[2] == 'B')