From ff013330fbdb2782e9001787db6c0b6415cdad77 Mon Sep 17 00:00:00 2001 From: Shuhao Mai Date: Mon, 8 Feb 2021 15:53:03 +0800 Subject: mtd: spi-nor: winbond: Add support for w25q512jvq Add support for w25q512jvq. This is of the same series chip with w25q256jv, which is already supported, but with size doubled and different JEDEC ID. Tested on Intel whitley platform with dd from/to the flash for read/write respectly, and flash_erase for erasing the flash. Signed-off-by: Shuhao Mai [ta: put flash_info flags in order, first SPI_NOR_DUAL_READ, then SPI_NOR_QUAD_READ] Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20210208075303.4200-1-shuhao.mai.1990@gmail.com --- drivers/mtd/spi-nor/winbond.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index e5dfa786f190..e24bcb928be7 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -97,6 +97,8 @@ static const struct flash_info winbond_parts[] = { 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) }, + { "w25q512jvq", INFO(0xef4020, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, }; /** -- cgit From 04fc298c7d0877695847e8662d1b5b29a19c2723 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Sat, 6 Mar 2021 00:45:52 +0100 Subject: mtd: spi-nor: use is_power_of_2() There is already a function to check if an integer is a power of 2. Use it. Signed-off-by: Michael Walle Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20210305234552.19204-1-michael@walle.cc --- drivers/mtd/spi-nor/core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 0522304f52fa..4a315cb1c4db 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2336,11 +2336,8 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, * If page_size is a power of two, the offset can be quickly * calculated with an AND operation. On the other cases we * need to do a modulus operation (more expensive). - * Power of two numbers have only one bit set and we can use - * the instruction hweight32 to detect if we need to do a - * modulus (do_div()) or not. */ - if (hweight32(nor->page_size) == 1) { + if (is_power_of_2(nor->page_size)) { page_offset = addr & (nor->page_size - 1); } else { uint64_t aux = addr; -- cgit From ae2177cf318d169e349319b24a26881ba0e5248f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 4 Mar 2021 16:08:20 +0200 Subject: mtd: spi-nor: intel-spi: Move platform data header to x86 subfolder In order to group x86 related platform data move intel-spi.h to x86 folder. While at it, remove duplicate inclusion in C file. Signed-off-by: Andy Shevchenko [ta: s/x85/x86] Signed-off-by: Tudor Ambarus Reviewed-by: Vignesh Raghavendra Link: https://lore.kernel.org/r/20210304140820.56692-1-andriy.shevchenko@linux.intel.com --- drivers/mtd/spi-nor/controllers/intel-spi.c | 1 - drivers/mtd/spi-nor/controllers/intel-spi.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/controllers/intel-spi.c b/drivers/mtd/spi-nor/controllers/intel-spi.c index b54a56a68100..a413892ff449 100644 --- a/drivers/mtd/spi-nor/controllers/intel-spi.c +++ b/drivers/mtd/spi-nor/controllers/intel-spi.c @@ -15,7 +15,6 @@ #include #include #include -#include #include "intel-spi.h" diff --git a/drivers/mtd/spi-nor/controllers/intel-spi.h b/drivers/mtd/spi-nor/controllers/intel-spi.h index e2f41b8827bf..f2871179fd34 100644 --- a/drivers/mtd/spi-nor/controllers/intel-spi.h +++ b/drivers/mtd/spi-nor/controllers/intel-spi.h @@ -9,7 +9,7 @@ #ifndef INTEL_SPI_H #define INTEL_SPI_H -#include +#include struct intel_spi; struct resource; -- cgit From 7cd37e7e958bbc1038b90c126162532a4afaa9f8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Jan 2021 21:30:13 +0100 Subject: mtd: nand: ecc-bch: Populate the public nsteps field Advertize the actual number of steps that will actually be used by the driver by populating the public field. Signed-off-by: Miquel Raynal Tested-by: Adam Ford #logicpd Torpedo Link: https://lore.kernel.org/linux-mtd/20210127203020.9574-3-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-bch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c index 0a0ac11d5725..5018bc0db626 100644 --- a/drivers/mtd/nand/ecc-sw-bch.c +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -245,6 +245,7 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand) } nand->ecc.ctx.priv = engine_conf; + nand->ecc.ctx.nsteps = nsteps; nand->ecc.ctx.total = nsteps * code_size; ret = nand_ecc_sw_bch_init(nand); -- cgit From 12e0df0c6f60baf10f6ecb78a086bf1048ad9d8b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Jan 2021 21:30:14 +0100 Subject: mtd: nand: ecc-hamming: Populate the public nsteps field Advertize the actual number of steps that will actually be used by the driver by populating the public field. Signed-off-by: Miquel Raynal Tested-by: Adam Ford #logicpd Torpedo Link: https://lore.kernel.org/linux-mtd/20210127203020.9574-4-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index 6334d1d7735d..5144775e5a59 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -513,6 +513,7 @@ int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) } nand->ecc.ctx.priv = engine_conf; + nand->ecc.ctx.nsteps = mtd->writesize / conf->step_size; nand->ecc.ctx.total = engine_conf->nsteps * engine_conf->code_size; return 0; -- cgit From 5b9215acb5182355a8a3c9f0a930e6b51c6eea57 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Jan 2021 21:30:17 +0100 Subject: mtd: rawnand: Try not to use the ECC private structures Most of the time, there is no need to use the software ECC Hamming and BCH algorithms private context to know their configuration. All the data has been stored by their ->init_ctx() hook in the generic NAND ECC engine structure, so use this one when possible. Signed-off-by: Miquel Raynal Tested-by: Adam Ford #logicpd Torpedo Link: https://lore.kernel.org/linux-mtd/20210127203020.9574-7-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/nand_base.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c33fa1b1847f..4e1bd1e5d474 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5162,8 +5162,8 @@ int rawnand_sw_hamming_init(struct nand_chip *chip) chip->ecc.size = base->ecc.ctx.conf.step_size; chip->ecc.strength = base->ecc.ctx.conf.strength; chip->ecc.total = base->ecc.ctx.total; - chip->ecc.steps = engine_conf->nsteps; - chip->ecc.bytes = engine_conf->code_size; + chip->ecc.steps = nanddev_get_ecc_nsteps(base); + chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base); return 0; } @@ -5201,7 +5201,7 @@ EXPORT_SYMBOL(rawnand_sw_hamming_cleanup); int rawnand_sw_bch_init(struct nand_chip *chip) { struct nand_device *base = &chip->base; - struct nand_ecc_sw_bch_conf *engine_conf; + const struct nand_ecc_props *ecc_conf = nanddev_get_ecc_conf(base); int ret; base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; @@ -5213,13 +5213,11 @@ int rawnand_sw_bch_init(struct nand_chip *chip) if (ret) return ret; - engine_conf = base->ecc.ctx.priv; - - chip->ecc.size = base->ecc.ctx.conf.step_size; - chip->ecc.strength = base->ecc.ctx.conf.strength; + chip->ecc.size = ecc_conf->step_size; + chip->ecc.strength = ecc_conf->strength; chip->ecc.total = base->ecc.ctx.total; - chip->ecc.steps = engine_conf->nsteps; - chip->ecc.bytes = engine_conf->code_size; + chip->ecc.steps = nanddev_get_ecc_nsteps(base); + chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base); return 0; } -- cgit From 49894937fc11662491a675a03f3f3c5b308763d4 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Jan 2021 21:30:18 +0100 Subject: mtd: rawnand: omap: Use ECC information from the generic structures As part of a previous fix, we imported the BCH internal structure in order to get information about the BCH engine configuration. It is best not to access private structure so instead, a small rework has been done to export more information from the ECC engines. Now, let's use these. Signed-off-by: Miquel Raynal Tested-by: Adam Ford #logicpd Torpedo Link: https://lore.kernel.org/linux-mtd/20210127203020.9574-8-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/omap2.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 2c3e65cb68f3..c75e7a0b101f 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -1868,18 +1868,19 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { struct nand_device *nand = mtd_to_nanddev(mtd); - const struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int nsteps = nanddev_get_ecc_nsteps(nand); + unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); int off = BADBLOCK_MARKER_LENGTH; - if (section >= engine_conf->nsteps) + if (section >= nsteps) return -ERANGE; /* * When SW correction is employed, one OMAP specific marker byte is * reserved after each ECC step. */ - oobregion->offset = off + (section * (engine_conf->code_size + 1)); - oobregion->length = engine_conf->code_size; + oobregion->offset = off + (section * (ecc_bytes + 1)); + oobregion->length = ecc_bytes; return 0; } @@ -1888,7 +1889,8 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion) { struct nand_device *nand = mtd_to_nanddev(mtd); - const struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int nsteps = nanddev_get_ecc_nsteps(nand); + unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); int off = BADBLOCK_MARKER_LENGTH; if (section) @@ -1898,7 +1900,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, * When SW correction is employed, one OMAP specific marker byte is * reserved after each ECC step. */ - off += ((engine_conf->code_size + 1) * engine_conf->nsteps); + off += ((ecc_bytes + 1) * nsteps); if (off >= mtd->oobsize) return -ERANGE; -- cgit From 3e66843c74289b294b91547edd364c5a6fdef45b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Jan 2021 21:30:19 +0100 Subject: mtd: nand: ecc-bch: Use the public nsteps field The software BCH ECC engine stores the nsteps variable in its own private structure while it is also exported as a public ECC field. Let's get rid of the redundant private one and let's use the nand_ecc_context structure when possible. Signed-off-by: Miquel Raynal Tested-by: Adam Ford #logicpd Torpedo Link: https://lore.kernel.org/linux-mtd/20210127203020.9574-9-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-bch.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c index 5018bc0db626..405552d014a8 100644 --- a/drivers/mtd/nand/ecc-sw-bch.c +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -236,7 +236,6 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand) goto free_engine_conf; engine_conf->code_size = code_size; - engine_conf->nsteps = nsteps; engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL); engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL); if (!engine_conf->calc_buf || !engine_conf->code_buf) { @@ -254,7 +253,7 @@ int nand_ecc_sw_bch_init_ctx(struct nand_device *nand) /* Verify the layout validity */ if (mtd_ooblayout_count_eccbytes(mtd) != - engine_conf->nsteps * engine_conf->code_size) { + nand->ecc.ctx.nsteps * engine_conf->code_size) { pr_err("Invalid ECC layout\n"); ret = -EINVAL; goto cleanup_bch_ctx; @@ -296,7 +295,7 @@ static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand, struct mtd_info *mtd = nanddev_to_mtd(nand); int eccsize = nand->ecc.ctx.conf.step_size; int eccbytes = engine_conf->code_size; - int eccsteps = engine_conf->nsteps; + int eccsteps = nand->ecc.ctx.nsteps; int total = nand->ecc.ctx.total; u8 *ecccalc = engine_conf->calc_buf; const u8 *data; @@ -334,7 +333,7 @@ static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand, int eccsize = nand->ecc.ctx.conf.step_size; int total = nand->ecc.ctx.total; int eccbytes = engine_conf->code_size; - int eccsteps = engine_conf->nsteps; + int eccsteps = nand->ecc.ctx.nsteps; u8 *ecccalc = engine_conf->calc_buf; u8 *ecccode = engine_conf->code_buf; unsigned int max_bitflips = 0; @@ -366,7 +365,7 @@ static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand, nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]); /* Finish a page read: compare and correct */ - for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in; + for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in; eccsteps; eccsteps--, i += eccbytes, data += eccsize) { int stat = nand_ecc_sw_bch_correct(nand, data, -- cgit From bf3816d28f0778de0d3d00a2a65525e19e5dbad2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 27 Jan 2021 21:30:20 +0100 Subject: mtd: nand: ecc-hamming: Use the public nsteps field The software Hamming ECC engine stores the nsteps variable in its own private structure while it is also exported as a public ECC field. Let's get rid of the redundant private one and let's use the nand_ecc_context structure when possible. Signed-off-by: Miquel Raynal Tested-by: Adam Ford #logicpd Torpedo Link: https://lore.kernel.org/linux-mtd/20210127203020.9574-10-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index 5144775e5a59..a7655b668f32 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -504,7 +504,6 @@ int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) goto free_engine_conf; engine_conf->code_size = 3; - engine_conf->nsteps = mtd->writesize / conf->step_size; engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL); engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL); if (!engine_conf->calc_buf || !engine_conf->code_buf) { @@ -514,7 +513,7 @@ int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) nand->ecc.ctx.priv = engine_conf; nand->ecc.ctx.nsteps = mtd->writesize / conf->step_size; - nand->ecc.ctx.total = engine_conf->nsteps * engine_conf->code_size; + nand->ecc.ctx.total = nand->ecc.ctx.nsteps * engine_conf->code_size; return 0; @@ -549,7 +548,7 @@ static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand, struct mtd_info *mtd = nanddev_to_mtd(nand); int eccsize = nand->ecc.ctx.conf.step_size; int eccbytes = engine_conf->code_size; - int eccsteps = engine_conf->nsteps; + int eccsteps = nand->ecc.ctx.nsteps; int total = nand->ecc.ctx.total; u8 *ecccalc = engine_conf->calc_buf; const u8 *data; @@ -587,7 +586,7 @@ static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand, int eccsize = nand->ecc.ctx.conf.step_size; int total = nand->ecc.ctx.total; int eccbytes = engine_conf->code_size; - int eccsteps = engine_conf->nsteps; + int eccsteps = nand->ecc.ctx.nsteps; u8 *ecccalc = engine_conf->calc_buf; u8 *ecccode = engine_conf->code_buf; unsigned int max_bitflips = 0; @@ -619,7 +618,7 @@ static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand, nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]); /* Finish a page read: compare and correct */ - for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in; + for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in; eccsteps; eccsteps--, i += eccbytes, data += eccsize) { int stat = nand_ecc_sw_hamming_correct(nand, data, -- cgit From 0646493edd02e4c872b37258e4e248eb9df4f25b Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Sun, 31 Jan 2021 01:37:16 +0530 Subject: mtd: rawnand: qcom: Update register macro name for 0x2c offset This change will remove unused register name macro NAND_DEV1_ECC_CFG. Since this register was only available in QPIC version 1.4.20 ipq40xx and it was not used. In QPIC version 1.5 on wards this register got removed.In QPIC version 2.0 0x2c offset is updated with register NAND_AUTO_STATUS_EN So adding this register macro NAND_AUTO_STATUS_EN with offset 0x2c. Signed-off-by: Md Sadre Alam Reviewed-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1612037236-7954-1-git-send-email-mdalam@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index fd4c318b520f..c0d3f9204223 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -27,7 +27,7 @@ #define NAND_DEV0_CFG0 0x20 #define NAND_DEV0_CFG1 0x24 #define NAND_DEV0_ECC_CFG 0x28 -#define NAND_DEV1_ECC_CFG 0x2c +#define NAND_AUTO_STATUS_EN 0x2c #define NAND_DEV1_CFG0 0x30 #define NAND_DEV1_CFG1 0x34 #define NAND_READ_ID 0x40 -- cgit From ec9e0203a3598d979d6151703e673c8cb186304d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 5 Feb 2021 15:27:24 +0100 Subject: mtd: nand: fix error handling in nand_prog_page_op() #1 On success chip->legacy.waitfunc() returns the NAND status byte, but on failure it returns a negative error code. This was never tested for and instead the return value was interpreted as NAND status without error checking. Add the missing error check. Signed-off-by: Sascha Hauer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210205142725.13225-1-s.hauer@pengutronix.de --- drivers/mtd/nand/raw/nand_base.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 4e1bd1e5d474..878492fbb52a 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1466,6 +1466,8 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, chip->legacy.write_buf(chip, buf, len); chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1); status = chip->legacy.waitfunc(chip); + if (status < 0) + return status; } if (status & NAND_STATUS_FAIL) -- cgit From 8ffbec7df4d64446cb0479261524c490a2c7529e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 5 Feb 2021 15:27:25 +0100 Subject: mtd: nand: fix error handling in nand_prog_page_op() #2 On success nand_exec_prog_page_op() returns the NAND status byte, but on failure it returns a negative error code. nand_prog_page_op() interprets the return value as NAND status byte without error checking. This means a failure in nand_exec_prog_page_op() can go through unnoticed. The straight forward fix would be to add the missing error checking. To clean the code a bit we can move the nand status check to nand_prog_page_op(). This way we can get rid of the overloaded return value from nand_exec_prog_page_op() and return a plain error code which is less error prone. nand_exec_prog_page_op() is only called from one other place and in this call the 'prog' parameter is false in which case the nand status check is skipped, so it's correct to not add the NAND status check there. Signed-off-by: Sascha Hauer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210205142725.13225-2-s.hauer@pengutronix.de --- drivers/mtd/nand/raw/nand_base.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 878492fbb52a..a984cda86e2d 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1294,8 +1294,6 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); - int ret; - u8 status; if (naddrs < 0) return naddrs; @@ -1335,15 +1333,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, op.ninstrs--; } - ret = nand_exec_op(chip, &op); - if (!prog || ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - - return status; + return nand_exec_op(chip, &op); } /** @@ -1449,7 +1439,8 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, unsigned int len) { struct mtd_info *mtd = nand_to_mtd(chip); - int status; + u8 status; + int ret; if (!len || !buf) return -EINVAL; @@ -1458,16 +1449,24 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, return -EINVAL; if (nand_has_exec_op(chip)) { - status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, + ret = nand_exec_prog_page_op(chip, page, offset_in_page, buf, len, true); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; } else { chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page, page); chip->legacy.write_buf(chip, buf, len); chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1); - status = chip->legacy.waitfunc(chip); - if (status < 0) - return status; + ret = chip->legacy.waitfunc(chip); + if (ret < 0) + return ret; + + status = ret; } if (status & NAND_STATUS_FAIL) -- cgit From 469b992489852b500d39048aa0013639dfe9f2e6 Mon Sep 17 00:00:00 2001 From: Reto Schneider Date: Thu, 11 Feb 2021 12:36:19 +0100 Subject: mtd: spinand: gigadevice: Support GD5F1GQ5UExxG The relevant changes to the already existing GD5F1GQ4UExxG support has been determined by consulting the GigaDevice product change notice AN-0392-10, version 1.0 from November 30, 2020. As the overlaps are huge, variable names have been generalized accordingly. Apart from the lowered ECC strength (4 instead of 8 bits per 512 bytes), the new device ID, and the extra quad IO dummy byte, no changes had to be taken into account. New hardware features are not supported, namely: - Power on reset - Unique ID - Double transfer rate (DTR) - Parameter page - Random data quad IO The inverted semantic of the "driver strength" register bits, defaulting to 100% instead of 50% for the Q5 devices, got ignored as the driver has never touched them anyway. The no longer supported "read from cache during block erase" functionality is not reflected as the current SPI NAND core does not support it anyway. Implementation has been tested on MediaTek MT7688 based GARDENA smart Gateways using both, GigaDevice GD5F1GQ5UEYIG and GD5F1GQ4UBYIG. Signed-off-by: Reto Schneider Reviewed-by: Frieder Schrempf Reviewed-by: Stefan Roese Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210211113619.3502-1-code@reto-schneider.ch --- drivers/mtd/nand/spi/gigadevice.c | 69 ++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 9 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index 33c67403c4aa..1dd1c5898093 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -13,7 +13,10 @@ #define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS (1 << 4) #define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS (3 << 4) -#define GD5FXGQ4UEXXG_REG_STATUS2 0xf0 +#define GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS (1 << 4) +#define GD5FXGQ5XE_STATUS_ECC_4_BITFLIPS (3 << 4) + +#define GD5FXGQXXEXXG_REG_STATUS2 0xf0 #define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) #define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) @@ -102,7 +105,7 @@ static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } -static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, +static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -114,7 +117,7 @@ static int gd5fxgq4_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, return 0; } -static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, +static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { if (section) @@ -127,9 +130,10 @@ static int gd5fxgq4_variant2_ooblayout_free(struct mtd_info *mtd, int section, return 0; } -static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = { - .ecc = gd5fxgq4_variant2_ooblayout_ecc, - .free = gd5fxgq4_variant2_ooblayout_free, +/* Valid for Q4/Q5 and Q6 (untested) devices */ +static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = { + .ecc = gd5fxgqx_variant2_ooblayout_ecc, + .free = gd5fxgqx_variant2_ooblayout_free, }; static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section, @@ -165,7 +169,7 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { u8 status2; - struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQ4UEXXG_REG_STATUS2, + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, &status2); int ret; @@ -203,6 +207,43 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } +static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ + u8 status2; + struct spi_mem_op op = SPINAND_GET_FEATURE_OP(GD5FXGQXXEXXG_REG_STATUS2, + &status2); + int ret; + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case GD5FXGQ5XE_STATUS_ECC_1_4_BITFLIPS: + /* + * Read status2 register to determine a more fine grained + * bit error status + */ + ret = spi_mem_exec_op(spinand->spimem, &op); + if (ret) + return ret; + + /* + * 1 ... 4 bits are flipped (and corrected) + */ + /* bits sorted this way (1...0): ECCSE1, ECCSE0 */ + return ((status2 & STATUS_ECC_MASK) >> 4) + 1; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, u8 status) { @@ -282,7 +323,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), SPINAND_INFO("GD5F1GQ4UFxxG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), @@ -292,8 +333,18 @@ static const struct spinand_info gigadevice_spinand_table[] = { &write_cache_variants, &update_cache_variants), SPINAND_HAS_QE_BIT, - SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq4ufxxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ5UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), }; static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { -- cgit From e7a97528e3c787802d8c643d6ab2f428511bb047 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 15 Feb 2021 18:58:49 +0300 Subject: mtd: rawnand: fsmc: Fix error code in fsmc_nand_probe() If dma_request_channel() fails then the probe fails and it should return a negative error code, but currently it returns success. fixes: 4774fb0a48aa ("mtd: nand/fsmc: Add DMA support") Signed-off-by: Dan Carpenter Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/YCqaOZ83OvPOzLwh@mwanda --- drivers/mtd/nand/raw/fsmc_nand.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 0101c0fab50a..a24e2f57fa68 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1077,11 +1077,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->read_dma_chan = dma_request_channel(mask, filter, NULL); if (!host->read_dma_chan) { dev_err(&pdev->dev, "Unable to get read dma channel\n"); + ret = -ENODEV; goto disable_clk; } host->write_dma_chan = dma_request_channel(mask, filter, NULL); if (!host->write_dma_chan) { dev_err(&pdev->dev, "Unable to get write dma channel\n"); + ret = -ENODEV; goto release_dma_read_chan; } } -- cgit From 9a7c39e23d7059ba6ef231d941dd820643bd5ce2 Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Wed, 24 Feb 2021 01:08:57 +0530 Subject: mtd: rawnand: qcom: Convert nandc to chip in Read/Write helper This change will convert nandc to chip in Read/Write helper, this change is needed because if we wnated to access number of steps in Read/Write helper then we need to get the chip->ecc.steps, currentlly its not possible.After this change we can directly acces chip->ecc.steps in Read/Write helper. Signed-off-by: Md Sadre Alam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1614109141-7531-1-git-send-email-mdalam@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 153 ++++++++++++++++++++------------------ 1 file changed, 80 insertions(+), 73 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index c0d3f9204223..41d4ad8ede1a 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -181,8 +181,8 @@ #define ECC_BCH_4BIT BIT(2) #define ECC_BCH_8BIT BIT(3) -#define nandc_set_read_loc(nandc, reg, offset, size, is_last) \ -nandc_set_reg(nandc, NAND_READ_LOCATION_##reg, \ +#define nandc_set_read_loc(chip, reg, offset, size, is_last) \ +nandc_set_reg(chip, NAND_READ_LOCATION_##reg, \ ((offset) << READ_LOCATION_OFFSET) | \ ((size) << READ_LOCATION_SIZE) | \ ((is_last) << READ_LOCATION_LAST)) @@ -649,9 +649,10 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) } } -static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset, +static void nandc_set_reg(struct nand_chip *chip, int offset, u32 val) { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nandc_regs *regs = nandc->regs; __le32 *reg; @@ -665,13 +666,12 @@ static void nandc_set_reg(struct qcom_nand_controller *nandc, int offset, static void set_address(struct qcom_nand_host *host, u16 column, int page) { struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); if (chip->options & NAND_BUSWIDTH_16) column >>= 1; - nandc_set_reg(nandc, NAND_ADDR0, page << 16 | column); - nandc_set_reg(nandc, NAND_ADDR1, page >> 16 & 0xff); + nandc_set_reg(chip, NAND_ADDR0, page << 16 | column); + nandc_set_reg(chip, NAND_ADDR1, page >> 16 & 0xff); } /* @@ -684,7 +684,6 @@ static void set_address(struct qcom_nand_host *host, u16 column, int page) static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) { struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); u32 cmd, cfg0, cfg1, ecc_bch_cfg; if (read) { @@ -710,17 +709,17 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) ecc_bch_cfg = 1 << ECC_CFG_ECC_DISABLE; } - nandc_set_reg(nandc, NAND_FLASH_CMD, cmd); - nandc_set_reg(nandc, NAND_DEV0_CFG0, cfg0); - nandc_set_reg(nandc, NAND_DEV0_CFG1, cfg1); - nandc_set_reg(nandc, NAND_DEV0_ECC_CFG, ecc_bch_cfg); - nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); - nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); - nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + nandc_set_reg(chip, NAND_FLASH_CMD, cmd); + nandc_set_reg(chip, NAND_DEV0_CFG0, cfg0); + nandc_set_reg(chip, NAND_DEV0_CFG1, cfg1); + nandc_set_reg(chip, NAND_DEV0_ECC_CFG, ecc_bch_cfg); + nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, host->ecc_buf_cfg); + nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus); + nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); if (read) - nandc_set_read_loc(nandc, 0, 0, host->use_ecc ? + nandc_set_read_loc(chip, 0, 0, host->use_ecc ? host->cw_data : host->cw_size, 1); } @@ -1079,8 +1078,10 @@ static int write_data_dma(struct qcom_nand_controller *nandc, int reg_off, * Helper to prepare DMA descriptors for configuring registers * before reading a NAND page. */ -static void config_nand_page_read(struct qcom_nand_controller *nandc) +static void config_nand_page_read(struct nand_chip *chip) { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + write_reg_dma(nandc, NAND_ADDR0, 2, 0); write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, 0); @@ -1094,8 +1095,10 @@ static void config_nand_page_read(struct qcom_nand_controller *nandc) * before reading each codeword in NAND page. */ static void -config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc) +config_nand_cw_read(struct nand_chip *chip, bool use_ecc) { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + if (nandc->props->is_bam) write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL); @@ -1117,19 +1120,21 @@ config_nand_cw_read(struct qcom_nand_controller *nandc, bool use_ecc) * single codeword in page */ static void -config_nand_single_cw_page_read(struct qcom_nand_controller *nandc, +config_nand_single_cw_page_read(struct nand_chip *chip, bool use_ecc) { - config_nand_page_read(nandc); - config_nand_cw_read(nandc, use_ecc); + config_nand_page_read(chip); + config_nand_cw_read(chip, use_ecc); } /* * Helper to prepare DMA descriptors used to configure registers needed for * before writing a NAND page. */ -static void config_nand_page_write(struct qcom_nand_controller *nandc) +static void config_nand_page_write(struct nand_chip *chip) { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + write_reg_dma(nandc, NAND_ADDR0, 2, 0); write_reg_dma(nandc, NAND_DEV0_CFG0, 3, 0); write_reg_dma(nandc, NAND_EBI2_ECC_BUF_CFG, 1, @@ -1140,8 +1145,10 @@ static void config_nand_page_write(struct qcom_nand_controller *nandc) * Helper to prepare DMA descriptors for configuring registers * before writing each codeword in NAND page. */ -static void config_nand_cw_write(struct qcom_nand_controller *nandc) +static void config_nand_cw_write(struct nand_chip *chip) { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -1168,44 +1175,44 @@ static int nandc_param(struct qcom_nand_host *host) * bytes to read onfi params */ if (nandc->props->qpic_v2) - nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ_ONFI_READ | + nandc_set_reg(chip, NAND_FLASH_CMD, OP_PAGE_READ_ONFI_READ | PAGE_ACC | LAST_PAGE); else - nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | + nandc_set_reg(chip, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE); - nandc_set_reg(nandc, NAND_ADDR0, 0); - nandc_set_reg(nandc, NAND_ADDR1, 0); - nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE + nandc_set_reg(chip, NAND_ADDR0, 0); + nandc_set_reg(chip, NAND_ADDR1, 0); + nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE | 512 << UD_SIZE_BYTES | 5 << NUM_ADDR_CYCLES | 0 << SPARE_SIZE_BYTES); - nandc_set_reg(nandc, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES + nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES | 0 << CS_ACTIVE_BSY | 17 << BAD_BLOCK_BYTE_NUM | 1 << BAD_BLOCK_IN_SPARE_AREA | 2 << WR_RD_BSY_GAP | 0 << WIDE_FLASH | 1 << DEV0_CFG1_ECC_DISABLE); - nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); + nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ if (!nandc->props->qpic_v2) { - nandc_set_reg(nandc, NAND_DEV_CMD_VLD, + nandc_set_reg(chip, NAND_DEV_CMD_VLD, (nandc->vld & ~READ_START_VLD)); - nandc_set_reg(nandc, NAND_DEV_CMD1, + nandc_set_reg(chip, NAND_DEV_CMD1, (nandc->cmd1 & ~(0xFF << READ_ADDR)) | NAND_CMD_PARAM << READ_ADDR); } - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); if (!nandc->props->qpic_v2) { - nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); - nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); + nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1); + nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); } - nandc_set_read_loc(nandc, 0, 0, 512, 1); + nandc_set_read_loc(chip, 0, 0, 512, 1); if (!nandc->props->qpic_v2) { write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); @@ -1215,7 +1222,7 @@ static int nandc_param(struct qcom_nand_host *host) nandc->buf_count = 512; memset(nandc->data_buffer, 0xff, nandc->buf_count); - config_nand_single_cw_page_read(nandc, false); + config_nand_single_cw_page_read(chip, false); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, nandc->buf_count, 0); @@ -1235,16 +1242,16 @@ static int erase_block(struct qcom_nand_host *host, int page_addr) struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - nandc_set_reg(nandc, NAND_FLASH_CMD, + nandc_set_reg(chip, NAND_FLASH_CMD, OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE); - nandc_set_reg(nandc, NAND_ADDR0, page_addr); - nandc_set_reg(nandc, NAND_ADDR1, 0); - nandc_set_reg(nandc, NAND_DEV0_CFG0, + nandc_set_reg(chip, NAND_ADDR0, page_addr); + nandc_set_reg(chip, NAND_ADDR1, 0); + nandc_set_reg(chip, NAND_DEV0_CFG0, host->cfg0_raw & ~(7 << CW_PER_PAGE)); - nandc_set_reg(nandc, NAND_DEV0_CFG1, host->cfg1_raw); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - nandc_set_reg(nandc, NAND_FLASH_STATUS, host->clrflashstatus); - nandc_set_reg(nandc, NAND_READ_STATUS, host->clrreadstatus); + nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus); + nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus); write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); @@ -1267,12 +1274,12 @@ static int read_id(struct qcom_nand_host *host, int column) if (column == -1) return 0; - nandc_set_reg(nandc, NAND_FLASH_CMD, OP_FETCH_ID); - nandc_set_reg(nandc, NAND_ADDR0, column); - nandc_set_reg(nandc, NAND_ADDR1, 0); - nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, + nandc_set_reg(chip, NAND_FLASH_CMD, OP_FETCH_ID); + nandc_set_reg(chip, NAND_ADDR0, column); + nandc_set_reg(chip, NAND_ADDR1, 0); + nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, nandc->props->is_bam ? 0 : DM_EN); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -1288,8 +1295,8 @@ static int reset(struct qcom_nand_host *host) struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - nandc_set_reg(nandc, NAND_FLASH_CMD, OP_RESET_DEVICE); - nandc_set_reg(nandc, NAND_EXEC_CMD, 1); + nandc_set_reg(chip, NAND_FLASH_CMD, OP_RESET_DEVICE); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -1617,7 +1624,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, clear_bam_transaction(nandc); set_address(host, host->cw_size * cw, page); update_rw_regs(host, 1, true); - config_nand_page_read(nandc); + config_nand_page_read(chip); data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); oob_size1 = host->bbm_size; @@ -1633,19 +1640,19 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, } if (nandc->props->is_bam) { - nandc_set_read_loc(nandc, 0, read_loc, data_size1, 0); + nandc_set_read_loc(chip, 0, read_loc, data_size1, 0); read_loc += data_size1; - nandc_set_read_loc(nandc, 1, read_loc, oob_size1, 0); + nandc_set_read_loc(chip, 1, read_loc, oob_size1, 0); read_loc += oob_size1; - nandc_set_read_loc(nandc, 2, read_loc, data_size2, 0); + nandc_set_read_loc(chip, 2, read_loc, data_size2, 0); read_loc += data_size2; - nandc_set_read_loc(nandc, 3, read_loc, oob_size2, 1); + nandc_set_read_loc(chip, 3, read_loc, oob_size2, 1); } - config_nand_cw_read(nandc, false); + config_nand_cw_read(chip, false); read_data_dma(nandc, reg_off, data_buf, data_size1, 0); reg_off += data_size1; @@ -1856,7 +1863,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf; int i, ret; - config_nand_page_read(nandc); + config_nand_page_read(chip); /* queue cmd descs for each codeword */ for (i = 0; i < ecc->steps; i++) { @@ -1873,18 +1880,18 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, if (nandc->props->is_bam) { if (data_buf && oob_buf) { - nandc_set_read_loc(nandc, 0, 0, data_size, 0); - nandc_set_read_loc(nandc, 1, data_size, + nandc_set_read_loc(chip, 0, 0, data_size, 0); + nandc_set_read_loc(chip, 1, data_size, oob_size, 1); } else if (data_buf) { - nandc_set_read_loc(nandc, 0, 0, data_size, 1); + nandc_set_read_loc(chip, 0, 0, data_size, 1); } else { - nandc_set_read_loc(nandc, 0, data_size, + nandc_set_read_loc(chip, 0, data_size, oob_size, 1); } } - config_nand_cw_read(nandc, true); + config_nand_cw_read(chip, true); if (data_buf) read_data_dma(nandc, FLASH_BUF_ACC, data_buf, @@ -1946,7 +1953,7 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, true); - config_nand_single_cw_page_read(nandc, host->use_ecc); + config_nand_single_cw_page_read(chip, host->use_ecc); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); @@ -2036,7 +2043,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, host->use_ecc = true; update_rw_regs(host, ecc->steps, false); - config_nand_page_write(nandc); + config_nand_page_write(chip); for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; @@ -2068,7 +2075,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, oob_buf, oob_size, 0); } - config_nand_cw_write(nandc); + config_nand_cw_write(chip); data_buf += data_size; oob_buf += oob_size; @@ -2107,7 +2114,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, host->use_ecc = false; update_rw_regs(host, ecc->steps, false); - config_nand_page_write(nandc); + config_nand_page_write(chip); for (i = 0; i < ecc->steps; i++) { int data_size1, data_size2, oob_size1, oob_size2; @@ -2144,7 +2151,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0); oob_buf += oob_size2; - config_nand_cw_write(nandc); + config_nand_cw_write(chip); } ret = submit_descs(nandc); @@ -2191,10 +2198,10 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false); - config_nand_page_write(nandc); + config_nand_page_write(chip); write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, data_size + oob_size, 0); - config_nand_cw_write(nandc); + config_nand_cw_write(chip); ret = submit_descs(nandc); @@ -2270,10 +2277,10 @@ static int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs) set_address(host, host->cw_size * (ecc->steps - 1), page); update_rw_regs(host, 1, false); - config_nand_page_write(nandc); + config_nand_page_write(chip); write_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, host->cw_size, 0); - config_nand_cw_write(nandc); + config_nand_cw_write(chip); ret = submit_descs(nandc); -- cgit From b057e498fdaff82092710b0d7c553d3375b2e314 Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Wed, 24 Feb 2021 01:08:58 +0530 Subject: mtd: rawnand: qcom: Add helper to check last code word Add the qcom_nandc_is_last_cw() helper which checks if the input cw index is the last one or not. Signed-off-by: Md Sadre Alam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1614109141-7531-2-git-send-email-mdalam@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 41d4ad8ede1a..d5cf68f22dac 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -662,6 +662,12 @@ static void nandc_set_reg(struct nand_chip *chip, int offset, *reg = cpu_to_le32(val); } +/* Helper to check the code word, whether it is last cw or not */ +static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) +{ + return cw == (ecc->steps - 1); +} + /* helper to configure address register values */ static void set_address(struct qcom_nand_host *host, u16 column, int page) { @@ -1629,7 +1635,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); oob_size1 = host->bbm_size; - if (cw == (ecc->steps - 1)) { + if (qcom_nandc_is_last_cw(ecc, cw)) { data_size2 = ecc->size - data_size1 - ((ecc->steps - 1) * 4); oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + @@ -1710,7 +1716,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, } for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) { - if (cw == (ecc->steps - 1)) { + if (qcom_nandc_is_last_cw(ecc, cw)) { data_size = ecc->size - ((ecc->steps - 1) * 4); oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; } else { @@ -1770,7 +1776,7 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf, u32 flash, buffer, erased_cw; int data_len, oob_len; - if (i == (ecc->steps - 1)) { + if (qcom_nandc_is_last_cw(ecc, i)) { data_len = ecc->size - ((ecc->steps - 1) << 2); oob_len = ecc->steps << 2; } else { @@ -1869,7 +1875,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; - if (i == (ecc->steps - 1)) { + if (qcom_nandc_is_last_cw(ecc, i)) { data_size = ecc->size - ((ecc->steps - 1) << 2); oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + host->spare_bytes; @@ -2048,7 +2054,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; - if (i == (ecc->steps - 1)) { + if (qcom_nandc_is_last_cw(ecc, i)) { data_size = ecc->size - ((ecc->steps - 1) << 2); oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + host->spare_bytes; @@ -2068,7 +2074,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, * itself. For the last codeword, we skip the bbm positions and * write to the free oob area. */ - if (i == (ecc->steps - 1)) { + if (qcom_nandc_is_last_cw(ecc, i)) { oob_buf += host->bbm_size; write_data_dma(nandc, FLASH_BUF_ACC + data_size, @@ -2123,7 +2129,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); oob_size1 = host->bbm_size; - if (i == (ecc->steps - 1)) { + if (qcom_nandc_is_last_cw(ecc, i)) { data_size2 = ecc->size - data_size1 - ((ecc->steps - 1) << 2); oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + -- cgit From 622d3fc8de7d88def6e2b7cb00432e9c7c595c0b Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Wed, 24 Feb 2021 01:08:59 +0530 Subject: mtd: rawnand: qcom: Rename parameter name in macro Rename the parameters of the nandc_set_read_loc() macro to avoid the confusion between is_last_read_loc which is last location in a read code word and last_cw which is last code word of a page data. Signed-off-by: Md Sadre Alam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1614109141-7531-3-git-send-email-mdalam@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index d5cf68f22dac..cc4163453bb9 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -181,11 +181,11 @@ #define ECC_BCH_4BIT BIT(2) #define ECC_BCH_8BIT BIT(3) -#define nandc_set_read_loc(chip, reg, offset, size, is_last) \ +#define nandc_set_read_loc(chip, reg, cw_offset, read_size, is_last_read_loc) \ nandc_set_reg(chip, NAND_READ_LOCATION_##reg, \ - ((offset) << READ_LOCATION_OFFSET) | \ - ((size) << READ_LOCATION_SIZE) | \ - ((is_last) << READ_LOCATION_LAST)) + ((cw_offset) << READ_LOCATION_OFFSET) | \ + ((read_size) << READ_LOCATION_SIZE) | \ + ((is_last_read_loc) << READ_LOCATION_LAST)) /* * Returns the actual register address for all NAND_DEV_ registers -- cgit From e7a307f21a514ce7aacee492011589999456d820 Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Wed, 24 Feb 2021 01:09:00 +0530 Subject: mtd: rawnand: qcom: Add helper to configure location register Create a nandc_set_read_loc() helper to abstract the configuration of the location register. QPIC v2 onwards features a separate location register for the last codeword, so introducing this extra helper which will simplify the addition of QPIC v2 support. Signed-off-by: Md Sadre Alam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1614109141-7531-4-git-send-email-mdalam@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index cc4163453bb9..88e1cba6260b 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -181,8 +181,8 @@ #define ECC_BCH_4BIT BIT(2) #define ECC_BCH_8BIT BIT(3) -#define nandc_set_read_loc(chip, reg, cw_offset, read_size, is_last_read_loc) \ -nandc_set_reg(chip, NAND_READ_LOCATION_##reg, \ +#define nandc_set_read_loc_first(chip, reg, cw_offset, read_size, is_last_read_loc) \ +nandc_set_reg(chip, reg, \ ((cw_offset) << READ_LOCATION_OFFSET) | \ ((read_size) << READ_LOCATION_SIZE) | \ ((is_last_read_loc) << READ_LOCATION_LAST)) @@ -668,6 +668,18 @@ static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) return cw == (ecc->steps - 1); } +/* helper to configure location register values */ +static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, + int cw_offset, int read_size, int is_last_read_loc) +{ + int reg_base = NAND_READ_LOCATION_0; + + reg_base += reg * 4; + + return nandc_set_read_loc_first(chip, reg_base, cw_offset, + read_size, is_last_read_loc); +} + /* helper to configure address register values */ static void set_address(struct qcom_nand_host *host, u16 column, int page) { @@ -725,7 +737,7 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) nandc_set_reg(chip, NAND_EXEC_CMD, 1); if (read) - nandc_set_read_loc(chip, 0, 0, host->use_ecc ? + nandc_set_read_loc(chip, 0, 0, 0, host->use_ecc ? host->cw_data : host->cw_size, 1); } @@ -1218,7 +1230,7 @@ static int nandc_param(struct qcom_nand_host *host) nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); } - nandc_set_read_loc(chip, 0, 0, 512, 1); + nandc_set_read_loc(chip, 0, 0, 0, 512, 1); if (!nandc->props->qpic_v2) { write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); @@ -1646,16 +1658,16 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, } if (nandc->props->is_bam) { - nandc_set_read_loc(chip, 0, read_loc, data_size1, 0); + nandc_set_read_loc(chip, cw, 0, read_loc, data_size1, 0); read_loc += data_size1; - nandc_set_read_loc(chip, 1, read_loc, oob_size1, 0); + nandc_set_read_loc(chip, cw, 1, read_loc, oob_size1, 0); read_loc += oob_size1; - nandc_set_read_loc(chip, 2, read_loc, data_size2, 0); + nandc_set_read_loc(chip, cw, 2, read_loc, data_size2, 0); read_loc += data_size2; - nandc_set_read_loc(chip, 3, read_loc, oob_size2, 1); + nandc_set_read_loc(chip, cw, 3, read_loc, oob_size2, 1); } config_nand_cw_read(chip, false); @@ -1886,13 +1898,13 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, if (nandc->props->is_bam) { if (data_buf && oob_buf) { - nandc_set_read_loc(chip, 0, 0, data_size, 0); - nandc_set_read_loc(chip, 1, data_size, + nandc_set_read_loc(chip, i, 0, 0, data_size, 0); + nandc_set_read_loc(chip, i, 1, data_size, oob_size, 1); } else if (data_buf) { - nandc_set_read_loc(chip, 0, 0, data_size, 1); + nandc_set_read_loc(chip, i, 0, 0, data_size, 1); } else { - nandc_set_read_loc(chip, 0, data_size, + nandc_set_read_loc(chip, i, 0, data_size, oob_size, 1); } } -- cgit From 503ee5aad43054a26cfd5cc592a31270c05539cd Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Wed, 24 Feb 2021 01:09:01 +0530 Subject: mtd: rawnand: qcom: update last code word register From QPIC v2 onwards a new register got added to read last code word.Add support for this READ_LOCATION_LAST_CW_n register. In the case of QPIC v2, codewords 0, 1 and 2 will be accessed through READ_LOCATION_n, while codeword 3 will be accessed through READ_LOCATION_LAST_CW_n. Signed-off-by: Md Sadre Alam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1614109141-7531-5-git-send-email-mdalam@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 78 ++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 21 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 88e1cba6260b..34d31c7c2dcf 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -48,6 +48,10 @@ #define NAND_READ_LOCATION_1 0xf24 #define NAND_READ_LOCATION_2 0xf28 #define NAND_READ_LOCATION_3 0xf2c +#define NAND_READ_LOCATION_LAST_CW_0 0xf40 +#define NAND_READ_LOCATION_LAST_CW_1 0xf44 +#define NAND_READ_LOCATION_LAST_CW_2 0xf48 +#define NAND_READ_LOCATION_LAST_CW_3 0xf4c /* dummy register offsets, used by write_reg_dma */ #define NAND_DEV_CMD1_RESTORE 0xdead @@ -187,6 +191,11 @@ nandc_set_reg(chip, reg, \ ((read_size) << READ_LOCATION_SIZE) | \ ((is_last_read_loc) << READ_LOCATION_LAST)) +#define nandc_set_read_loc_last(chip, reg, cw_offset, read_size, is_last_read_loc) \ +nandc_set_reg(chip, reg, \ + ((cw_offset) << READ_LOCATION_OFFSET) | \ + ((read_size) << READ_LOCATION_SIZE) | \ + ((is_last_read_loc) << READ_LOCATION_LAST)) /* * Returns the actual register address for all NAND_DEV_ registers * (i.e. NAND_DEV_CMD0, NAND_DEV_CMD1, NAND_DEV_CMD2 and NAND_DEV_CMD_VLD) @@ -316,6 +325,10 @@ struct nandc_regs { __le32 read_location1; __le32 read_location2; __le32 read_location3; + __le32 read_location_last0; + __le32 read_location_last1; + __le32 read_location_last2; + __le32 read_location_last3; __le32 erased_cw_detect_cfg_clr; __le32 erased_cw_detect_cfg_set; @@ -644,6 +657,14 @@ static __le32 *offset_to_nandc_reg(struct nandc_regs *regs, int offset) return ®s->read_location2; case NAND_READ_LOCATION_3: return ®s->read_location3; + case NAND_READ_LOCATION_LAST_CW_0: + return ®s->read_location_last0; + case NAND_READ_LOCATION_LAST_CW_1: + return ®s->read_location_last1; + case NAND_READ_LOCATION_LAST_CW_2: + return ®s->read_location_last2; + case NAND_READ_LOCATION_LAST_CW_3: + return ®s->read_location_last3; default: return NULL; } @@ -672,12 +693,21 @@ static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw) static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg, int cw_offset, int read_size, int is_last_read_loc) { + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; int reg_base = NAND_READ_LOCATION_0; + if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) + reg_base = NAND_READ_LOCATION_LAST_CW_0; + reg_base += reg * 4; - return nandc_set_read_loc_first(chip, reg_base, cw_offset, - read_size, is_last_read_loc); + if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) + return nandc_set_read_loc_last(chip, reg_base, cw_offset, + read_size, is_last_read_loc); + else + return nandc_set_read_loc_first(chip, reg_base, cw_offset, + read_size, is_last_read_loc); } /* helper to configure address register values */ @@ -698,8 +728,9 @@ static void set_address(struct qcom_nand_host *host, u16 column, int page) * * @num_cw: number of steps for the read/write operation * @read: read or write operation + * @cw : which code word */ -static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) +static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, int cw) { struct nand_chip *chip = &host->chip; u32 cmd, cfg0, cfg1, ecc_bch_cfg; @@ -737,7 +768,7 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) nandc_set_reg(chip, NAND_EXEC_CMD, 1); if (read) - nandc_set_read_loc(chip, 0, 0, 0, host->use_ecc ? + nandc_set_read_loc(chip, cw, 0, 0, host->use_ecc ? host->cw_data : host->cw_size, 1); } @@ -1113,13 +1144,18 @@ static void config_nand_page_read(struct nand_chip *chip) * before reading each codeword in NAND page. */ static void -config_nand_cw_read(struct nand_chip *chip, bool use_ecc) +config_nand_cw_read(struct nand_chip *chip, bool use_ecc, int cw) { struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + + int reg = NAND_READ_LOCATION_0; + + if (nandc->props->qpic_v2 && qcom_nandc_is_last_cw(ecc, cw)) + reg = NAND_READ_LOCATION_LAST_CW_0; if (nandc->props->is_bam) - write_reg_dma(nandc, NAND_READ_LOCATION_0, 4, - NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, reg, 4, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -1139,10 +1175,10 @@ config_nand_cw_read(struct nand_chip *chip, bool use_ecc) */ static void config_nand_single_cw_page_read(struct nand_chip *chip, - bool use_ecc) + bool use_ecc, int cw) { config_nand_page_read(chip); - config_nand_cw_read(chip, use_ecc); + config_nand_cw_read(chip, use_ecc, cw); } /* @@ -1240,7 +1276,7 @@ static int nandc_param(struct qcom_nand_host *host) nandc->buf_count = 512; memset(nandc->data_buffer, 0xff, nandc->buf_count); - config_nand_single_cw_page_read(chip, false); + config_nand_single_cw_page_read(chip, false, 0); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, nandc->buf_count, 0); @@ -1517,7 +1553,7 @@ static void qcom_nandc_command(struct nand_chip *chip, unsigned int command, host->use_ecc = true; set_address(host, 0, page_addr); - update_rw_regs(host, ecc->steps, true); + update_rw_regs(host, ecc->steps, true, 0); break; case NAND_CMD_SEQIN: @@ -1641,7 +1677,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, clear_bam_transaction(nandc); set_address(host, host->cw_size * cw, page); - update_rw_regs(host, 1, true); + update_rw_regs(host, 1, true, cw); config_nand_page_read(chip); data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); @@ -1670,7 +1706,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, nandc_set_read_loc(chip, cw, 3, read_loc, oob_size2, 1); } - config_nand_cw_read(chip, false); + config_nand_cw_read(chip, false, cw); read_data_dma(nandc, reg_off, data_buf, data_size1, 0); reg_off += data_size1; @@ -1909,7 +1945,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } } - config_nand_cw_read(chip, true); + config_nand_cw_read(chip, true, i); if (data_buf) read_data_dma(nandc, FLASH_BUF_ACC, data_buf, @@ -1969,9 +2005,9 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) memset(nandc->data_buffer, 0xff, size); set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, true); + update_rw_regs(host, 1, true, ecc->steps - 1); - config_nand_single_cw_page_read(chip, host->use_ecc); + config_nand_single_cw_page_read(chip, host->use_ecc, ecc->steps - 1); read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, size, 0); @@ -2036,7 +2072,7 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page) host->use_ecc = true; set_address(host, 0, page); - update_rw_regs(host, ecc->steps, true); + update_rw_regs(host, ecc->steps, true, 0); return read_page_ecc(host, NULL, chip->oob_poi, page); } @@ -2060,7 +2096,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, oob_buf = chip->oob_poi; host->use_ecc = true; - update_rw_regs(host, ecc->steps, false); + update_rw_regs(host, ecc->steps, false, 0); config_nand_page_write(chip); for (i = 0; i < ecc->steps; i++) { @@ -2131,7 +2167,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, oob_buf = chip->oob_poi; host->use_ecc = false; - update_rw_regs(host, ecc->steps, false); + update_rw_regs(host, ecc->steps, false, 0); config_nand_page_write(chip); for (i = 0; i < ecc->steps; i++) { @@ -2214,7 +2250,7 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page) 0, mtd->oobavail); set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, false); + update_rw_regs(host, 1, false, 0); config_nand_page_write(chip); write_data_dma(nandc, FLASH_BUF_ACC, @@ -2293,7 +2329,7 @@ static int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs) /* prepare write */ host->use_ecc = false; set_address(host, host->cw_size * (ecc->steps - 1), page); - update_rw_regs(host, 1, false); + update_rw_regs(host, 1, false, ecc->steps - 1); config_nand_page_write(chip); write_data_dma(nandc, FLASH_BUF_ACC, -- cgit From f5200c14242fb8fa4a9b93f7fd4064d237e58785 Mon Sep 17 00:00:00 2001 From: Álvaro Fernández Rojas Date: Wed, 24 Feb 2021 09:02:10 +0100 Subject: mtd: rawnand: brcmnand: fix OOB R/W with Hamming ECC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hamming ECC doesn't cover the OOB data, so reading or writing OOB shall always be done without ECC enabled. This is a problem when adding JFFS2 cleanmarkers to erased blocks. If JFFS2 clenmarkers are added to the OOB with ECC enabled, OOB bytes will be changed from ff ff ff to 00 00 00, reporting incorrect ECC errors. Fixes: 27c5b17cd1b1 ("mtd: nand: add NAND driver "library" for Broadcom STB NAND controller") Signed-off-by: Álvaro Fernández Rojas Acked-by: Brian Norris Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210224080210.23686-1-noltari@gmail.com --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 659eaa6f0980..5ff4291380c5 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -2688,6 +2688,12 @@ static int brcmnand_attach_chip(struct nand_chip *chip) ret = brcmstb_choose_ecc_layout(host); + /* If OOB is written with ECC enabled it will cause ECC errors */ + if (is_hamming_ecc(host->ctrl, &host->hwcfg)) { + chip->ecc.write_oob = brcmnand_write_oob_raw; + chip->ecc.read_oob = brcmnand_read_oob_raw; + } + return ret; } -- cgit From 08608adb520e51403be7592c2214846fa440a23a Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 2 Mar 2021 18:57:56 +0530 Subject: mtd: Handle possible -EPROBE_DEFER from parse_mtd_partitions() There are chances that the parse_mtd_partitions() function will return -EPROBE_DEFER in mtd_device_parse_register(). This might happen when the dependency is not available for the parser. For instance, on SDX55 the MTD_QCOMSMEM_PARTS parser depends on the QCOM_SMEM driver to parse the partitions defined in the shared memory region. With the current flow, the error returned from parse_mtd_partitions() will be discarded in favor of trying to add the fallback partition. This will prevent the driver to end up in probe deferred pool and the partitions won't be parsed even after the QCOM_SMEM driver is available. Fix this issue by bailing out of mtd_device_parse_register() when -EPROBE_DEFER error is returned from parse_mtd_partitions() function and propagate the error code to the driver core for probing later. Fixes: 5ac67ce36cfe ("mtd: move code adding (registering) partitions to the parse_mtd_partitions()") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal --- drivers/mtd/mtdcore.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 2d6423d89a17..d97ddc65b5d4 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -820,6 +820,9 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, /* Prefer parsed partitions over driver-provided fallback */ ret = parse_mtd_partitions(mtd, types, parser_data); + if (ret == -EPROBE_DEFER) + goto out; + if (ret > 0) ret = 0; else if (nr_parts) -- cgit From 55fbb9ba4f06cb6aff32daca1e1910173c13ec51 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 2 Mar 2021 18:57:57 +0530 Subject: mtd: rawnand: qcom: Return actual error code instead of -ENODEV In qcom_probe_nand_devices() function, the error code returned by qcom_nand_host_init_and_register() is converted to -ENODEV in the case of failure. This poses issue if -EPROBE_DEFER is returned when the dependency is not available for a component like parser. So let's restructure the error handling logic a bit and return the actual error code in case of qcom_nand_host_init_and_register() failure. Fixes: c76b78d8ec05 ("mtd: nand: Qualcomm NAND controller driver") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/qcom_nandc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 34d31c7c2dcf..b9194680cd3c 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2959,7 +2959,7 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) struct device *dev = nandc->dev; struct device_node *dn = dev->of_node, *child; struct qcom_nand_host *host; - int ret; + int ret = -ENODEV; for_each_available_child_of_node(dn, child) { host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); @@ -2977,10 +2977,7 @@ static int qcom_probe_nand_devices(struct qcom_nand_controller *nandc) list_add_tail(&host->node, &nandc->host_list); } - if (list_empty(&nandc->host_list)) - return -ENODEV; - - return 0; + return ret; } /* parse custom DT properties here */ -- cgit From 683313993dbe1651c7aa00bb42a041d70e914925 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 12 Feb 2021 04:40:22 -0600 Subject: mtd: physmap: physmap-bt1-rom: Fix unintentional stack access Cast &data to (char *) in order to avoid unintentionally accessing the stack. Notice that data is of type u32, so any increment to &data will be in the order of 4-byte chunks, and this piece of code is actually intended to be a byte offset. Fixes: b3e79e7682e0 ("mtd: physmap: Add Baikal-T1 physically mapped ROM support") Addresses-Coverity-ID: 1497765 ("Out-of-bounds access") Cc: stable@vger.kernel.org Signed-off-by: Gustavo A. R. Silva Acked-by: Serge Semin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210212104022.GA242669@embeddedor --- drivers/mtd/maps/physmap-bt1-rom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/physmap-bt1-rom.c b/drivers/mtd/maps/physmap-bt1-rom.c index a35450002284..58782cfaf71c 100644 --- a/drivers/mtd/maps/physmap-bt1-rom.c +++ b/drivers/mtd/maps/physmap-bt1-rom.c @@ -79,7 +79,7 @@ static void __xipram bt1_rom_map_copy_from(struct map_info *map, if (shift) { chunk = min_t(ssize_t, 4 - shift, len); data = readl_relaxed(src - shift); - memcpy(to, &data + shift, chunk); + memcpy(to, (char *)&data + shift, chunk); src += chunk; to += chunk; len -= chunk; -- cgit From c45f07399db2f69f428fa7c7a4177968ae53c736 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sun, 14 Feb 2021 00:45:54 +0800 Subject: mtd: ftl: Use module_mtd_blktrans to register driver Removing some boilerplate by using module_mtd_blktrans instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Dejin Zheng Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210213164600.409061-3-zhengdejin5@gmail.com --- drivers/mtd/ftl.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 2578f27914ef..9b33c082179d 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1056,19 +1056,7 @@ static struct mtd_blktrans_ops ftl_tr = { .owner = THIS_MODULE, }; -static int __init init_ftl(void) -{ - return register_mtd_blktrans(&ftl_tr); -} - -static void __exit cleanup_ftl(void) -{ - deregister_mtd_blktrans(&ftl_tr); -} - -module_init(init_ftl); -module_exit(cleanup_ftl); - +module_mtd_blktrans(ftl_tr); MODULE_LICENSE("Dual MPL/GPL"); MODULE_AUTHOR("David Hinds "); -- cgit From f7e39bb7f8ee0bb14778f597e90740e6eea4e011 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sun, 14 Feb 2021 00:45:55 +0800 Subject: mtd: inftlcore: Use module_mtd_blktrans to register driver Removing some boilerplate by using module_mtd_blktrans instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Dejin Zheng Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210213164600.409061-4-zhengdejin5@gmail.com --- drivers/mtd/inftlcore.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index a0d6c00e7b85..6b48397c750c 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -937,18 +937,7 @@ static struct mtd_blktrans_ops inftl_tr = { .owner = THIS_MODULE, }; -static int __init init_inftl(void) -{ - return register_mtd_blktrans(&inftl_tr); -} - -static void __exit cleanup_inftl(void) -{ - deregister_mtd_blktrans(&inftl_tr); -} - -module_init(init_inftl); -module_exit(cleanup_inftl); +module_mtd_blktrans(inftl_tr); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Greg Ungerer , David Woodhouse , Fabrice Bellard et al."); -- cgit From 27b08bf3c3387b064dceedb34e8add7bb159386d Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sun, 14 Feb 2021 00:45:56 +0800 Subject: mtd: mtdblock: Use module_mtd_blktrans to register driver Removing some boilerplate by using module_mtd_blktrans instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Dejin Zheng Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210213164600.409061-5-zhengdejin5@gmail.com --- drivers/mtd/mtdblock.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 32e52d83b961..a80809543793 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -346,19 +346,7 @@ static struct mtd_blktrans_ops mtdblock_tr = { .owner = THIS_MODULE, }; -static int __init init_mtdblock(void) -{ - return register_mtd_blktrans(&mtdblock_tr); -} - -static void __exit cleanup_mtdblock(void) -{ - deregister_mtd_blktrans(&mtdblock_tr); -} - -module_init(init_mtdblock); -module_exit(cleanup_mtdblock); - +module_mtd_blktrans(mtdblock_tr); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Nicolas Pitre et al."); -- cgit From b1f9604f4d2aa4ca211e48b00feb5b831bd46982 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sun, 14 Feb 2021 00:45:57 +0800 Subject: mtd: mtdblock_ro: Use module_mtd_blktrans to register driver Removing some boilerplate by using module_mtd_blktrans instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Dejin Zheng Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210213164600.409061-6-zhengdejin5@gmail.com --- drivers/mtd/mtdblock_ro.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 7fcf29ef2bdc..d92914f73d52 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -67,18 +67,7 @@ static struct mtd_blktrans_ops mtdblock_tr = { .owner = THIS_MODULE, }; -static int __init mtdblock_init(void) -{ - return register_mtd_blktrans(&mtdblock_tr); -} - -static void __exit mtdblock_exit(void) -{ - deregister_mtd_blktrans(&mtdblock_tr); -} - -module_init(mtdblock_init); -module_exit(mtdblock_exit); +module_mtd_blktrans(mtdblock_tr); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse "); -- cgit From 1d5b7d479e7e180425ab2f02049c031ffe3b2c43 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sun, 14 Feb 2021 00:45:58 +0800 Subject: mtd: mtdswap: Use module_mtd_blktrans to register driver Removing some boilerplate by using module_mtd_blktrans instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Dejin Zheng Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210213164600.409061-7-zhengdejin5@gmail.com --- drivers/mtd/mtdswap.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index 795dec4483c2..7e309270ddd4 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -1484,19 +1484,7 @@ static struct mtd_blktrans_ops mtdswap_ops = { .owner = THIS_MODULE, }; -static int __init mtdswap_modinit(void) -{ - return register_mtd_blktrans(&mtdswap_ops); -} - -static void __exit mtdswap_modexit(void) -{ - deregister_mtd_blktrans(&mtdswap_ops); -} - -module_init(mtdswap_modinit); -module_exit(mtdswap_modexit); - +module_mtd_blktrans(mtdswap_ops); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jarkko Lavinen "); -- cgit From 2dd8b55e2c28caec3cbaefd0480d276f30e152cd Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sun, 14 Feb 2021 00:45:59 +0800 Subject: mtd: nftlcore: Use module_mtd_blktrans to register driver Removing some boilerplate by using module_mtd_blktrans instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Dejin Zheng Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210213164600.409061-8-zhengdejin5@gmail.com --- drivers/mtd/nftlcore.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index d44641129cdb..bcd0094f172d 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -797,18 +797,7 @@ static struct mtd_blktrans_ops nftl_tr = { .owner = THIS_MODULE, }; -static int __init init_nftl(void) -{ - return register_mtd_blktrans(&nftl_tr); -} - -static void __exit cleanup_nftl(void) -{ - deregister_mtd_blktrans(&nftl_tr); -} - -module_init(init_nftl); -module_exit(cleanup_nftl); +module_mtd_blktrans(nftl_tr); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse , Fabrice Bellard et al."); -- cgit From d38c2b93258a0e19dee3b04c2156f6c9077bd498 Mon Sep 17 00:00:00 2001 From: Dejin Zheng Date: Sun, 14 Feb 2021 00:46:00 +0800 Subject: mtd: rfd_ftl: Use module_mtd_blktrans to register driver Removing some boilerplate by using module_mtd_blktrans instead of calling register and unregister in the otherwise empty init/exit functions. Signed-off-by: Dejin Zheng Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210213164600.409061-9-zhengdejin5@gmail.com --- drivers/mtd/rfd_ftl.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 3d1df82fa105..cce3bf6f99b4 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -794,18 +794,7 @@ static struct mtd_blktrans_ops rfd_ftl_tr = { .owner = THIS_MODULE, }; -static int __init init_rfd_ftl(void) -{ - return register_mtd_blktrans(&rfd_ftl_tr); -} - -static void __exit cleanup_rfd_ftl(void) -{ - deregister_mtd_blktrans(&rfd_ftl_tr); -} - -module_init(init_rfd_ftl); -module_exit(cleanup_rfd_ftl); +module_mtd_blktrans(rfd_ftl_tr); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sean Young "); -- cgit From cb4543054c5c4fd33df960b41d7b483ebca8e786 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Wed, 17 Feb 2021 20:53:20 +0100 Subject: mtd: don't lock when recursively deleting partitions When recursively deleting partitions, don't acquire the masters partition lock twice. Otherwise the process ends up in a deadlocked state. Fixes: 46b5889cc2c5 ("mtd: implement proper partition handling") Signed-off-by: David Bauer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210217195320.893253-1-mail@david-bauer.net --- drivers/mtd/mtdpart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 12ca4f19cb14..665fd9020b76 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -331,7 +331,7 @@ static int __del_mtd_partitions(struct mtd_info *mtd) list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { if (mtd_has_partitions(child)) - del_mtd_partitions(child); + __del_mtd_partitions(child); pr_info("Deleting %s MTD partition\n", child->name); ret = del_mtd_device(child); -- cgit From ecd400ce5f97e03aed6bef9bc4efb1befdbf779b Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Wed, 17 Feb 2021 22:18:44 +0100 Subject: mtd: char: Drop mtd_mutex usage from mtdchar_open() It looks unnecessary in the function, remove it from the function having in mind to remove it completely. Signed-off-by: Alexander Sverdlin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210217211845.43364-1-alexander.sverdlin@nokia.com --- drivers/mtd/mtdchar.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 323035d4f2d0..f31390d186ca 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -58,13 +58,10 @@ static int mtdchar_open(struct inode *inode, struct file *file) if ((file->f_mode & FMODE_WRITE) && (minor & 1)) return -EACCES; - mutex_lock(&mtd_mutex); mtd = get_mtd_device(NULL, devnum); - if (IS_ERR(mtd)) { - ret = PTR_ERR(mtd); - goto out; - } + if (IS_ERR(mtd)) + return PTR_ERR(mtd); if (mtd->type == MTD_ABSENT) { ret = -ENODEV; @@ -84,13 +81,10 @@ static int mtdchar_open(struct inode *inode, struct file *file) } mfi->mtd = mtd; file->private_data = mfi; - mutex_unlock(&mtd_mutex); return 0; out1: put_mtd_device(mtd); -out: - mutex_unlock(&mtd_mutex); return ret; } /* mtdchar_open */ -- cgit From 1ad55288829c78e85bfe7d0c86d75415adf5f305 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Wed, 17 Feb 2021 22:18:45 +0100 Subject: mtd: char: Get rid of Big MTD Lock Get rid of central chrdev MTD lock, which prevents simultaneous operations on completely independent physical MTD chips. Replace it with newly introduced per-master mutex. Signed-off-by: Alexander Sverdlin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210217211845.43364-2-alexander.sverdlin@nokia.com --- drivers/mtd/mtdchar.c | 14 ++++++++------ drivers/mtd/mtdcore.c | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index f31390d186ca..57c4a2f0b703 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -27,8 +27,6 @@ #include "mtdcore.h" -static DEFINE_MUTEX(mtd_mutex); - /* * Data structure to hold the pointer to the mtd device as well * as mode information of various use cases. @@ -1020,11 +1018,14 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) static long mtdchar_unlocked_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); int ret; - mutex_lock(&mtd_mutex); + mutex_lock(&master->master.chrdev_lock); ret = mtdchar_ioctl(file, cmd, arg); - mutex_unlock(&mtd_mutex); + mutex_unlock(&master->master.chrdev_lock); return ret; } @@ -1045,10 +1046,11 @@ static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd, { 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 = compat_ptr(arg); int ret = 0; - mutex_lock(&mtd_mutex); + mutex_lock(&master->master.chrdev_lock); switch (cmd) { case MEMWRITEOOB32: @@ -1111,7 +1113,7 @@ static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd, ret = mtdchar_ioctl(file, cmd, (unsigned long)argp); } - mutex_unlock(&mtd_mutex); + mutex_unlock(&master->master.chrdev_lock); return ret; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 2d6423d89a17..0b095975895e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -773,6 +773,7 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd) INIT_LIST_HEAD(&mtd->partitions); mutex_init(&mtd->master.partitions_lock); + mutex_init(&mtd->master.chrdev_lock); } /** -- cgit From 1ca890d325c8ec11e553c706b339bf07a6450696 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Tue, 23 Feb 2021 13:47:04 +0200 Subject: mtd: parsers: extend Qcom SMEM parser to SPI flash The Qcom MIBIB partition might be stored on SPI flash devices, and the parser works in this case just as well: [ 1.404229] spi_qup 78b5000.spi: IN:block:16, fifo:64, OUT:block:16, fifo:64 [ 1.408078] spi-nor spi0.0: found mx25u6435f, expected n25q128a11 [ 1.415016] spi-nor spi0.0: mx25u6435f (8192 Kbytes) [ 1.420756] 12 qcomsmem partitions found on MTD device spi0.0 [ 1.425739] Creating 12 MTD partitions on "spi0.0": [ 1.431381] 0x000000000000-0x00000000c000 : "0:sbl1" [ 1.437058] 0x00000000c000-0x00000000d000 : "0:mibib" [ 1.442143] 0x00000000d000-0x000000027000 : "0:qsee" [ 1.447057] 0x000000027000-0x000000028000 : "0:devcfg" [ 1.452088] 0x000000028000-0x00000002a000 : "0:rpm" [ 1.457065] 0x00000002a000-0x00000002b000 : "0:cdt" [ 1.461832] 0x00000002b000-0x00000002c000 : "0:appsblenv" [ 1.466736] 0x00000002c000-0x000000036000 : "0:appsbl" [ 1.472248] 0x000000036000-0x00000003a000 : "0:art" [ 1.477297] 0x00000003a000-0x00000003e000 : "config" [ 1.482047] 0x00000003e000-0x00000004e000 : "data" [ 1.487257] 0x00000004e000-0x000000200000 : "0:hlos" Remove dependency on MTD_NAND_QCOM. Update the Kconfig prompt and help text accordingly. Cc: Manivannan Sadhasivam Signed-off-by: Baruch Siach Reviewed-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/7f558e888a41c5b1fd4ca0427b3976531c51a876.1614080824.git.baruch@tkos.co.il --- drivers/mtd/parsers/Kconfig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index d90c30229052..96d5716db544 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -162,9 +162,8 @@ config MTD_REDBOOT_PARTS_READONLY endif # MTD_REDBOOT_PARTS config MTD_QCOMSMEM_PARTS - tristate "Qualcomm SMEM NAND flash partition parser" - depends on MTD_NAND_QCOM || COMPILE_TEST + tristate "Qualcomm SMEM flash partition parser" depends on QCOM_SMEM help This provides support for parsing partitions from Shared Memory (SMEM) - for NAND flash on Qualcomm platforms. + for NAND and SPI flash on Qualcomm platforms. -- cgit From 462d69a2dc3d6d8289d99479961632411b9d30ad Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 25 Feb 2021 16:33:29 +0200 Subject: mtd: mtdcore: constify name param in mtd_bdi_init The bdi name is not modified by the function, it should be const. Signed-off-by: Tomas Winkler Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210225143329.430012-1-tomas.winkler@intel.com --- drivers/mtd/mtdcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 0b095975895e..38782ceea1f6 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -2173,7 +2173,7 @@ static int mtd_proc_show(struct seq_file *m, void *v) /*====================================================================*/ /* Init code */ -static struct backing_dev_info * __init mtd_bdi_init(char *name) +static struct backing_dev_info * __init mtd_bdi_init(const char *name) { struct backing_dev_info *bdi; int ret; -- cgit From bb17230c61a6424b622e92006ec52ba23aa5a967 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 1 Mar 2021 11:58:23 +0100 Subject: mtd: parsers: ofpart: support BCM4908 fixed partitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices use fixed partitioning with some partitions requiring some extra logic. E.g. BCM4908 may have multiple firmware partitions but detecting currently used one requires checking bootloader parameters. To support such cases without duplicating a lot of code (without copying most of the ofpart.c code) support for post-parsing callback was added. BCM4908 support in ofpart can be enabled using config option and results in compiling & executing a specific callback. It simply reads offset of currently used firmware partition from the DT. Bootloader specifies it using the "brcm_blparms" property. Signed-off-by: Rafał Miłecki Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210301105823.31032-1-zajec5@gmail.com --- drivers/mtd/parsers/Kconfig | 9 ++ drivers/mtd/parsers/Makefile | 2 + drivers/mtd/parsers/ofpart.c | 239 ------------------------------- drivers/mtd/parsers/ofpart_bcm4908.c | 64 +++++++++ drivers/mtd/parsers/ofpart_bcm4908.h | 15 ++ drivers/mtd/parsers/ofpart_core.c | 263 +++++++++++++++++++++++++++++++++++ 6 files changed, 353 insertions(+), 239 deletions(-) delete mode 100644 drivers/mtd/parsers/ofpart.c create mode 100644 drivers/mtd/parsers/ofpart_bcm4908.c create mode 100644 drivers/mtd/parsers/ofpart_bcm4908.h create mode 100644 drivers/mtd/parsers/ofpart_core.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index 96d5716db544..0528855cf6c1 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -67,6 +67,15 @@ config MTD_OF_PARTS flash memory node, as described in Documentation/devicetree/bindings/mtd/partition.txt. +config MTD_OF_PARTS_BCM4908 + bool "BCM4908 partitioning support" + depends on MTD_OF_PARTS && (ARCH_BCM4908 || COMPILE_TEST) + default ARCH_BCM4908 + help + This provides partitions parser for BCM4908 family devices + that can have multiple "firmware" partitions. It takes care of + finding currently used one and backup ones. + config MTD_PARSER_IMAGETAG tristate "Parser for BCM963XX Image Tag format partitions" depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile index 50eb0b0a2210..2dfe9fb602de 100644 --- a/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile @@ -4,6 +4,8 @@ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o +ofpart-y += ofpart_core.o +ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o diff --git a/drivers/mtd/parsers/ofpart.c b/drivers/mtd/parsers/ofpart.c deleted file mode 100644 index daf507c123e6..000000000000 --- a/drivers/mtd/parsers/ofpart.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Flash partitions described by the OF (or flattened) device tree - * - * Copyright © 2006 MontaVista Software Inc. - * Author: Vitaly Wool - * - * Revised to handle newer style flash binding by: - * Copyright © 2007 David Gibson, IBM Corporation. - */ - -#include -#include -#include -#include -#include -#include - -static bool node_has_compatible(struct device_node *pp) -{ - return of_get_property(pp, "compatible", NULL); -} - -static int parse_fixed_partitions(struct mtd_info *master, - const struct mtd_partition **pparts, - struct mtd_part_parser_data *data) -{ - struct mtd_partition *parts; - struct device_node *mtd_node; - struct device_node *ofpart_node; - const char *partname; - struct device_node *pp; - int nr_parts, i, ret = 0; - bool dedicated = true; - - - /* Pull of_node from the master device node */ - mtd_node = mtd_get_of_node(master); - if (!mtd_node) - return 0; - - ofpart_node = of_get_child_by_name(mtd_node, "partitions"); - if (!ofpart_node) { - /* - * We might get here even when ofpart isn't used at all (e.g., - * when using another parser), so don't be louder than - * KERN_DEBUG - */ - pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", - master->name, mtd_node); - ofpart_node = mtd_node; - dedicated = false; - } else if (!of_device_is_compatible(ofpart_node, "fixed-partitions")) { - /* The 'partitions' subnode might be used by another parser */ - return 0; - } - - /* First count the subnodes */ - nr_parts = 0; - for_each_child_of_node(ofpart_node, pp) { - if (!dedicated && node_has_compatible(pp)) - continue; - - nr_parts++; - } - - if (nr_parts == 0) - return 0; - - parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); - if (!parts) - return -ENOMEM; - - i = 0; - for_each_child_of_node(ofpart_node, pp) { - const __be32 *reg; - int len; - int a_cells, s_cells; - - if (!dedicated && node_has_compatible(pp)) - continue; - - reg = of_get_property(pp, "reg", &len); - if (!reg) { - if (dedicated) { - pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", - master->name, pp, - mtd_node); - goto ofpart_fail; - } else { - nr_parts--; - continue; - } - } - - a_cells = of_n_addr_cells(pp); - s_cells = of_n_size_cells(pp); - if (len / 4 != a_cells + s_cells) { - pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", - master->name, pp, - mtd_node); - goto ofpart_fail; - } - - parts[i].offset = of_read_number(reg, a_cells); - parts[i].size = of_read_number(reg + a_cells, s_cells); - parts[i].of_node = pp; - - partname = of_get_property(pp, "label", &len); - if (!partname) - partname = of_get_property(pp, "name", &len); - parts[i].name = partname; - - if (of_get_property(pp, "read-only", &len)) - parts[i].mask_flags |= MTD_WRITEABLE; - - if (of_get_property(pp, "lock", &len)) - parts[i].mask_flags |= MTD_POWERUP_LOCK; - - if (of_property_read_bool(pp, "slc-mode")) - parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; - - i++; - } - - if (!nr_parts) - goto ofpart_none; - - *pparts = parts; - return nr_parts; - -ofpart_fail: - pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", - master->name, pp, mtd_node); - ret = -EINVAL; -ofpart_none: - of_node_put(pp); - kfree(parts); - return ret; -} - -static const struct of_device_id parse_ofpart_match_table[] = { - { .compatible = "fixed-partitions" }, - {}, -}; -MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); - -static struct mtd_part_parser ofpart_parser = { - .parse_fn = parse_fixed_partitions, - .name = "fixed-partitions", - .of_match_table = parse_ofpart_match_table, -}; - -static int parse_ofoldpart_partitions(struct mtd_info *master, - const struct mtd_partition **pparts, - struct mtd_part_parser_data *data) -{ - struct mtd_partition *parts; - struct device_node *dp; - int i, plen, nr_parts; - const struct { - __be32 offset, len; - } *part; - const char *names; - - /* Pull of_node from the master device node */ - dp = mtd_get_of_node(master); - if (!dp) - return 0; - - part = of_get_property(dp, "partitions", &plen); - if (!part) - return 0; /* No partitions found */ - - pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); - - nr_parts = plen / sizeof(part[0]); - - parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); - if (!parts) - return -ENOMEM; - - names = of_get_property(dp, "partition-names", &plen); - - for (i = 0; i < nr_parts; i++) { - parts[i].offset = be32_to_cpu(part->offset); - parts[i].size = be32_to_cpu(part->len) & ~1; - /* bit 0 set signifies read only partition */ - if (be32_to_cpu(part->len) & 1) - parts[i].mask_flags = MTD_WRITEABLE; - - if (names && (plen > 0)) { - int len = strlen(names) + 1; - - parts[i].name = names; - plen -= len; - names += len; - } else { - parts[i].name = "unnamed"; - } - - part++; - } - - *pparts = parts; - return nr_parts; -} - -static struct mtd_part_parser ofoldpart_parser = { - .parse_fn = parse_ofoldpart_partitions, - .name = "ofoldpart", -}; - -static int __init ofpart_parser_init(void) -{ - register_mtd_parser(&ofpart_parser); - register_mtd_parser(&ofoldpart_parser); - return 0; -} - -static void __exit ofpart_parser_exit(void) -{ - deregister_mtd_parser(&ofpart_parser); - deregister_mtd_parser(&ofoldpart_parser); -} - -module_init(ofpart_parser_init); -module_exit(ofpart_parser_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); -MODULE_AUTHOR("Vitaly Wool, David Gibson"); -/* - * When MTD core cannot find the requested parser, it tries to load the module - * with the same name. Since we provide the ofoldpart parser, we should have - * the corresponding alias. - */ -MODULE_ALIAS("fixed-partitions"); -MODULE_ALIAS("ofoldpart"); diff --git a/drivers/mtd/parsers/ofpart_bcm4908.c b/drivers/mtd/parsers/ofpart_bcm4908.c new file mode 100644 index 000000000000..0eddef4c198e --- /dev/null +++ b/drivers/mtd/parsers/ofpart_bcm4908.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Rafał Miłecki + */ + +#include +#include +#include +#include +#include +#include + +#include "ofpart_bcm4908.h" + +#define BLPARAMS_FW_OFFSET "NAND_RFS_OFS" + +static long long bcm4908_partitions_fw_offset(void) +{ + struct device_node *root; + struct property *prop; + const char *s; + + root = of_find_node_by_path("/"); + if (!root) + return -ENOENT; + + of_property_for_each_string(root, "brcm_blparms", prop, s) { + size_t len = strlen(BLPARAMS_FW_OFFSET); + unsigned long offset; + int err; + + if (strncmp(s, BLPARAMS_FW_OFFSET, len) || s[len] != '=') + continue; + + err = kstrtoul(s + len + 1, 0, &offset); + if (err) { + pr_err("failed to parse %s\n", s + len + 1); + return err; + } + + return offset << 10; + } + + return -ENOENT; +} + +int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts) +{ + long long fw_offset; + int i; + + fw_offset = bcm4908_partitions_fw_offset(); + + for (i = 0; i < nr_parts; i++) { + if (of_device_is_compatible(parts[i].of_node, "brcm,bcm4908-firmware")) { + if (fw_offset < 0 || parts[i].offset == fw_offset) + parts[i].name = "firmware"; + else + parts[i].name = "backup"; + } + } + + return 0; +} diff --git a/drivers/mtd/parsers/ofpart_bcm4908.h b/drivers/mtd/parsers/ofpart_bcm4908.h new file mode 100644 index 000000000000..80f8c086641f --- /dev/null +++ b/drivers/mtd/parsers/ofpart_bcm4908.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BCM4908_PARTITIONS_H +#define __BCM4908_PARTITIONS_H + +#ifdef CONFIG_MTD_OF_PARTS_BCM4908 +int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts); +#else +static inline int bcm4908_partitions_post_parse(struct mtd_info *mtd, struct mtd_partition *parts, + int nr_parts) +{ + return -EOPNOTSUPP; +} +#endif + +#endif diff --git a/drivers/mtd/parsers/ofpart_core.c b/drivers/mtd/parsers/ofpart_core.c new file mode 100644 index 000000000000..258c06a42283 --- /dev/null +++ b/drivers/mtd/parsers/ofpart_core.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Flash partitions described by the OF (or flattened) device tree + * + * Copyright © 2006 MontaVista Software Inc. + * Author: Vitaly Wool + * + * Revised to handle newer style flash binding by: + * Copyright © 2007 David Gibson, IBM Corporation. + */ + +#include +#include +#include +#include +#include +#include + +#include "ofpart_bcm4908.h" + +struct fixed_partitions_quirks { + int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts); +}; + +struct fixed_partitions_quirks bcm4908_partitions_quirks = { + .post_parse = bcm4908_partitions_post_parse, +}; + +static const struct of_device_id parse_ofpart_match_table[]; + +static bool node_has_compatible(struct device_node *pp) +{ + return of_get_property(pp, "compatible", NULL); +} + +static int parse_fixed_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + const struct fixed_partitions_quirks *quirks; + const struct of_device_id *of_id; + struct mtd_partition *parts; + struct device_node *mtd_node; + struct device_node *ofpart_node; + const char *partname; + struct device_node *pp; + int nr_parts, i, ret = 0; + bool dedicated = true; + + /* Pull of_node from the master device node */ + mtd_node = mtd_get_of_node(master); + if (!mtd_node) + return 0; + + ofpart_node = of_get_child_by_name(mtd_node, "partitions"); + if (!ofpart_node) { + /* + * We might get here even when ofpart isn't used at all (e.g., + * when using another parser), so don't be louder than + * KERN_DEBUG + */ + pr_debug("%s: 'partitions' subnode not found on %pOF. Trying to parse direct subnodes as partitions.\n", + master->name, mtd_node); + ofpart_node = mtd_node; + dedicated = false; + } + + of_id = of_match_node(parse_ofpart_match_table, ofpart_node); + if (dedicated && !of_id) { + /* The 'partitions' subnode might be used by another parser */ + return 0; + } + + quirks = of_id ? of_id->data : NULL; + + /* First count the subnodes */ + nr_parts = 0; + for_each_child_of_node(ofpart_node, pp) { + if (!dedicated && node_has_compatible(pp)) + continue; + + nr_parts++; + } + + if (nr_parts == 0) + return 0; + + parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + i = 0; + for_each_child_of_node(ofpart_node, pp) { + const __be32 *reg; + int len; + int a_cells, s_cells; + + if (!dedicated && node_has_compatible(pp)) + continue; + + reg = of_get_property(pp, "reg", &len); + if (!reg) { + if (dedicated) { + pr_debug("%s: ofpart partition %pOF (%pOF) missing reg property.\n", + master->name, pp, + mtd_node); + goto ofpart_fail; + } else { + nr_parts--; + continue; + } + } + + a_cells = of_n_addr_cells(pp); + s_cells = of_n_size_cells(pp); + if (len / 4 != a_cells + s_cells) { + pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", + master->name, pp, + mtd_node); + goto ofpart_fail; + } + + parts[i].offset = of_read_number(reg, a_cells); + parts[i].size = of_read_number(reg + a_cells, s_cells); + parts[i].of_node = pp; + + partname = of_get_property(pp, "label", &len); + if (!partname) + partname = of_get_property(pp, "name", &len); + parts[i].name = partname; + + if (of_get_property(pp, "read-only", &len)) + parts[i].mask_flags |= MTD_WRITEABLE; + + if (of_get_property(pp, "lock", &len)) + parts[i].mask_flags |= MTD_POWERUP_LOCK; + + if (of_property_read_bool(pp, "slc-mode")) + parts[i].add_flags |= MTD_SLC_ON_MLC_EMULATION; + + i++; + } + + if (!nr_parts) + goto ofpart_none; + + if (quirks && quirks->post_parse) + quirks->post_parse(master, parts, nr_parts); + + *pparts = parts; + return nr_parts; + +ofpart_fail: + pr_err("%s: error parsing ofpart partition %pOF (%pOF)\n", + master->name, pp, mtd_node); + ret = -EINVAL; +ofpart_none: + of_node_put(pp); + kfree(parts); + return ret; +} + +static const struct of_device_id parse_ofpart_match_table[] = { + /* Generic */ + { .compatible = "fixed-partitions" }, + /* Customized */ + { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, }, + {}, +}; +MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); + +static struct mtd_part_parser ofpart_parser = { + .parse_fn = parse_fixed_partitions, + .name = "fixed-partitions", + .of_match_table = parse_ofpart_match_table, +}; + +static int parse_ofoldpart_partitions(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + struct mtd_partition *parts; + struct device_node *dp; + int i, plen, nr_parts; + const struct { + __be32 offset, len; + } *part; + const char *names; + + /* Pull of_node from the master device node */ + dp = mtd_get_of_node(master); + if (!dp) + return 0; + + part = of_get_property(dp, "partitions", &plen); + if (!part) + return 0; /* No partitions found */ + + pr_warn("Device tree uses obsolete partition map binding: %pOF\n", dp); + + nr_parts = plen / sizeof(part[0]); + + parts = kcalloc(nr_parts, sizeof(*parts), GFP_KERNEL); + if (!parts) + return -ENOMEM; + + names = of_get_property(dp, "partition-names", &plen); + + for (i = 0; i < nr_parts; i++) { + parts[i].offset = be32_to_cpu(part->offset); + parts[i].size = be32_to_cpu(part->len) & ~1; + /* bit 0 set signifies read only partition */ + if (be32_to_cpu(part->len) & 1) + parts[i].mask_flags = MTD_WRITEABLE; + + if (names && (plen > 0)) { + int len = strlen(names) + 1; + + parts[i].name = names; + plen -= len; + names += len; + } else { + parts[i].name = "unnamed"; + } + + part++; + } + + *pparts = parts; + return nr_parts; +} + +static struct mtd_part_parser ofoldpart_parser = { + .parse_fn = parse_ofoldpart_partitions, + .name = "ofoldpart", +}; + +static int __init ofpart_parser_init(void) +{ + register_mtd_parser(&ofpart_parser); + register_mtd_parser(&ofoldpart_parser); + return 0; +} + +static void __exit ofpart_parser_exit(void) +{ + deregister_mtd_parser(&ofpart_parser); + deregister_mtd_parser(&ofoldpart_parser); +} + +module_init(ofpart_parser_init); +module_exit(ofpart_parser_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parser for MTD partitioning information in device tree"); +MODULE_AUTHOR("Vitaly Wool, David Gibson"); +/* + * When MTD core cannot find the requested parser, it tries to load the module + * with the same name. Since we provide the ofoldpart parser, we should have + * the corresponding alias. + */ +MODULE_ALIAS("fixed-partitions"); +MODULE_ALIAS("ofoldpart"); -- cgit From 2d751203aacf86a1b301a188d8551c7da91043ab Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 2 Mar 2021 20:00:12 +0100 Subject: mtd: parsers: ofpart: limit parsing of deprecated DT syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For backward compatibility ofpart still supports the old syntax like: spi-flash@0 { compatible = "jedec,spi-nor"; reg = <0x0>; partition@0 { label = "bootloader"; reg = <0x0 0x100000>; }; }; (without "partitions" subnode). There is no reason however to support nested partitions without a clear "compatible" string like: partitions { compatible = "fixed-partitions"; #address-cells = <1>; #size-cells = <1>; partition@0 { label = "bootloader"; reg = <0x0 0x100000>; partition@0 { label = "config"; reg = <0x80000 0x80000>; }; }; }; (we never officially supported or documented that). Make sure ofpart doesn't attempt to parse above. Cc: Ansuel Smith Signed-off-by: Rafał Miłecki Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210302190012.1255-1-zajec5@gmail.com --- drivers/mtd/parsers/ofpart_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/parsers/ofpart_core.c b/drivers/mtd/parsers/ofpart_core.c index 258c06a42283..02658298d99a 100644 --- a/drivers/mtd/parsers/ofpart_core.c +++ b/drivers/mtd/parsers/ofpart_core.c @@ -53,7 +53,7 @@ static int parse_fixed_partitions(struct mtd_info *master, return 0; ofpart_node = of_get_child_by_name(mtd_node, "partitions"); - if (!ofpart_node) { + if (!ofpart_node && !master->parent) { /* * We might get here even when ofpart isn't used at all (e.g., * when using another parser), so don't be louder than @@ -64,6 +64,8 @@ static int parse_fixed_partitions(struct mtd_info *master, ofpart_node = mtd_node; dedicated = false; } + if (!ofpart_node) + return 0; of_id = of_match_node(parse_ofpart_match_table, ofpart_node); if (dedicated && !of_id) { -- cgit From c95310e1b33eae9767af9698aa976d5301f37203 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 3 Mar 2021 09:46:34 +0100 Subject: mtd: parsers: qcom: Fix error condition qcom_smem_get() does not return NULL, and even if it did, the NULL condition is usually not an error but a success condition and should not trigger an error trace. Let's replace IS_ERR_OR_NULL() by IS_ERR(). This fixes the following smatch warning: drivers/mtd/parsers/qcomsmempart.c:109 parse_qcomsmem_part() warn: passing zero to 'PTR_ERR' Reported-by: kernel test robot Reported-by: Dan Carpenter Fixes: 803eb124e1a6 ("mtd: parsers: Add Qcom SMEM parser") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210303084634.12796-1-miquel.raynal@bootlin.com --- drivers/mtd/parsers/qcomsmempart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/parsers/qcomsmempart.c b/drivers/mtd/parsers/qcomsmempart.c index 808cb33d71f8..1c8a44d0d6e4 100644 --- a/drivers/mtd/parsers/qcomsmempart.c +++ b/drivers/mtd/parsers/qcomsmempart.c @@ -104,7 +104,7 @@ static int parse_qcomsmem_part(struct mtd_info *mtd, * complete partition table */ ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); - if (IS_ERR_OR_NULL(ptable)) { + if (IS_ERR(ptable)) { pr_err("Error reading partition table\n"); return PTR_ERR(ptable); } -- cgit From 8f62f59f83c3bc902af91c80732cfcd17e0d7069 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 3 Mar 2021 18:48:16 +0200 Subject: mtd: parsers: qcom: incompatible with spi-nor 4k sectors Partition size and offset value are in block size units, which is the same as 'erasesize'. But when 4K sectors are enabled erasesize is set to 4K. Bail out in that case. Fixes: 803eb124e1a64 ("mtd: parsers: Add Qcom SMEM parser") Reviewed-by: Manivannan Sadhasivam Signed-off-by: Baruch Siach Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/0a2611f885b894274436ded3ca78bc0440fca74a.1614790096.git.baruch@tkos.co.il --- drivers/mtd/parsers/qcomsmempart.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/parsers/qcomsmempart.c b/drivers/mtd/parsers/qcomsmempart.c index 1c8a44d0d6e4..d9083308f6ba 100644 --- a/drivers/mtd/parsers/qcomsmempart.c +++ b/drivers/mtd/parsers/qcomsmempart.c @@ -65,6 +65,13 @@ static int parse_qcomsmem_part(struct mtd_info *mtd, int ret, i, numparts; char *name, *c; + if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_4K_SECTORS) + && mtd->type == MTD_NORFLASH) { + pr_err("%s: SMEM partition parser is incompatible with 4K sectors\n", + mtd->name); + return -EINVAL; + } + pr_debug("Parsing partition table info from SMEM\n"); ptable = qcom_smem_get(SMEM_APPS, SMEM_AARM_PARTITION_TABLE, &len); if (IS_ERR(ptable)) { -- cgit From b87b6d2d6f540e29c3f98e1572d64e560d73d6c1 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 4 Mar 2021 06:46:00 +0000 Subject: mtd: parsers: ofpart: make symbol 'bcm4908_partitions_quirks' static The sparse tool complains as follows: drivers/mtd/parsers/ofpart_core.c:25:32: warning: symbol 'bcm4908_partitions_quirks' was not declared. Should it be static? This symbol is not used outside of ofpart_core.c, so this commit marks it static. Fixes: 457da931b608 ("mtd: parsers: ofpart: support BCM4908 fixed partitions") Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210304064600.3279138-1-weiyongjun1@huawei.com --- drivers/mtd/parsers/ofpart_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/parsers/ofpart_core.c b/drivers/mtd/parsers/ofpart_core.c index 02658298d99a..2cef527dd976 100644 --- a/drivers/mtd/parsers/ofpart_core.c +++ b/drivers/mtd/parsers/ofpart_core.c @@ -22,7 +22,7 @@ struct fixed_partitions_quirks { int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts); }; -struct fixed_partitions_quirks bcm4908_partitions_quirks = { +static struct fixed_partitions_quirks bcm4908_partitions_quirks = { .post_parse = bcm4908_partitions_post_parse, }; -- cgit From 620b90d30c08684dc6ebee07c72755d997f9d1f6 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Sun, 7 Mar 2021 19:44:46 -0800 Subject: mtd: maps: fix error return code of physmap_flash_remove() When platform_get_drvdata() returns NULL to info, no error return code of physmap_flash_remove() is assigned. To fix this bug, err is assigned with -EINVAL in this case Fixes: 73566edf9b91 ("[MTD] Convert physmap to platform driver") Reported-by: TOTE Robot Signed-off-by: Jia-Ju Bai Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210308034446.3052-1-baijiaju1990@gmail.com --- drivers/mtd/maps/physmap-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index 001ed5deb622..4f63b8430c71 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -69,8 +69,10 @@ static int physmap_flash_remove(struct platform_device *dev) int i, err = 0; info = platform_get_drvdata(dev); - if (!info) + if (!info) { + err = -EINVAL; goto out; + } if (info->cmtd) { err = mtd_device_unregister(info->cmtd); -- cgit From c6f51f1f5527946a83da930b43da2e37dc741dc2 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 5 Mar 2021 02:19:33 -0600 Subject: mtd: cfi: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix multiple warnings by explicitly adding multiple break statements and a return instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Acked-by: Vignesh Raghavendra Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210305081933.GA137147@embeddedor --- drivers/mtd/chips/cfi_cmdset_0001.c | 1 + drivers/mtd/chips/cfi_cmdset_0002.c | 2 ++ drivers/mtd/chips/cfi_cmdset_0020.c | 2 ++ 3 files changed, 5 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 42001c49833b..b7f5e7977dcd 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -2549,6 +2549,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) anyway? The latter for now. */ printk(KERN_NOTICE "Flash device refused suspend due to active operation (state %d)\n", chip->state); ret = -EAGAIN; + break; case FL_PM_SUSPENDED: break; } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index a1f3e1031c3d..6f6b0265c22d 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -902,6 +902,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr /* Someone else might have been playing with it. */ goto retry; } + return 0; case FL_READY: case FL_CFI_QUERY: @@ -2994,6 +2995,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) * as the whole point is that nobody can do anything * with the chip now anyway. */ + break; case FL_PM_SUSPENDED: break; diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 270322bca221..d35df526e0a6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -1332,6 +1332,8 @@ static int cfi_staa_suspend(struct mtd_info *mtd) * as the whole point is that nobody can do anything * with the chip now anyway. */ + break; + case FL_PM_SUSPENDED: break; -- cgit From 0975b633871cb77c3be7f1deb9feb885e3e86b9c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 5 Mar 2021 02:22:24 -0600 Subject: mtd: mtdchar: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210305082224.GA137360@embeddedor --- drivers/mtd/mtdchar.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 57c4a2f0b703..998d86f46488 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -983,6 +983,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) if (!mtd_has_oob(mtd)) return -EOPNOTSUPP; mfi->mode = arg; + break; case MTD_FILE_MODE_NORMAL: break; -- cgit From 36a016a572cad7e5626b13c98b414302d2b10929 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 5 Mar 2021 02:23:56 -0600 Subject: mtd: onenand: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210305082356.GA137489@embeddedor --- drivers/mtd/nand/onenand/onenand_samsung.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c index 87b28e397d67..b64895573515 100644 --- a/drivers/mtd/nand/onenand/onenand_samsung.c +++ b/drivers/mtd/nand/onenand/onenand_samsung.c @@ -396,6 +396,7 @@ static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, case ONENAND_CMD_READOOB: case ONENAND_CMD_BUFFERRAM: ONENAND_SET_NEXT_BUFFERRAM(this); + break; default: break; } -- cgit From fe1bc21f447289670c331d4826aea8f3ff38ae6e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 5 Mar 2021 02:25:59 -0600 Subject: mtd: rawnand: fsmc: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210305082559.GA137646@embeddedor --- drivers/mtd/nand/raw/fsmc_nand.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 0101c0fab50a..a5d1bd0ff135 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -930,6 +930,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) "Using 4-bit SW BCH ECC scheme\n"); break; } + break; case NAND_ECC_ENGINE_TYPE_ON_DIE: break; -- cgit From 3ba6d1ff041f07cfbbe1fdf0f25094590d7e543e Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 5 Mar 2021 02:29:53 -0600 Subject: mtd: rawnand: stm32_fmc2: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a couple of warnings by explicitly adding a couple of break statements instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210305082953.GA137771@embeddedor --- drivers/mtd/nand/raw/stm32_fmc2_nand.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 550bda4d1415..1c277fbb91f2 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -531,6 +531,7 @@ static int stm32_fmc2_nfc_ham_correct(struct nand_chip *chip, u8 *dat, switch (b % 4) { case 2: bit_position += shifting; + break; case 1: break; default: @@ -546,6 +547,7 @@ static int stm32_fmc2_nfc_ham_correct(struct nand_chip *chip, u8 *dat, switch (b % 4) { case 2: byte_addr += shifting; + break; case 1: break; default: -- cgit From 786a0a75d0f34a8fd6863a5c7d47ed0d2c3169fa Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 6 Mar 2021 11:49:58 +0200 Subject: mtd: spi-nor: core: Advance erase after the erase cmd has been completed addr and len were gratuitously updated even when spi_nor_wait_till_ready() failed. Wait for the erase cmd to complete and then advance the erase. Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20210306095002.22983-2-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 4a315cb1c4db..d996e57c6d82 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1618,12 +1618,12 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len) if (ret) goto destroy_erase_cmd_list; - addr += cmd->size; - cmd->count--; - ret = spi_nor_wait_till_ready(nor); if (ret) goto destroy_erase_cmd_list; + + addr += cmd->size; + cmd->count--; } list_del(&cmd->list); kfree(cmd); @@ -1704,12 +1704,12 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) if (ret) goto erase_err; - addr += mtd->erasesize; - len -= mtd->erasesize; - ret = spi_nor_wait_till_ready(nor); if (ret) goto erase_err; + + addr += mtd->erasesize; + len -= mtd->erasesize; } /* erase multiple sectors */ -- cgit From 8758888c3d7873004b4ebf516430cba70bbcf39a Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 6 Mar 2021 11:49:59 +0200 Subject: mtd: spi-nor: core: Add vdbg msg for spi_nor_erase_multi_sectors() Useful when debugging non-uniform erase. Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20210306095002.22983-3-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index d996e57c6d82..dd35ec778339 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1610,6 +1610,9 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len) list_for_each_entry_safe(cmd, next, &erase_list, list) { nor->erase_opcode = cmd->opcode; while (cmd->count) { + dev_vdbg(nor->dev, "erase_cmd->size = 0x%08x, erase_cmd->opcode = 0x%02x, erase_cmd->count = %u\n", + cmd->size, cmd->opcode, cmd->count); + ret = spi_nor_write_enable(nor); if (ret) goto destroy_erase_cmd_list; -- cgit From a580293a19fc49b2745019075f9fa8561a6a0b32 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 6 Mar 2021 11:50:00 +0200 Subject: mtd: spi-nor: Get rid of duplicated argument in spi_nor_parse_sfdp() spi_nor_parse_sfdp(nor, nor->params); passes for the second argument a member within the first argument. Drop the second argument and obtain it directly from the first, and do it across all the children functions. This is a follow up for 'commit 69a8eed58cc0 ("mtd: spi-nor: Don't copy self-pointing struct around")' Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20210306095002.22983-4-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 10 +++--- drivers/mtd/spi-nor/core.h | 6 ++-- drivers/mtd/spi-nor/issi.c | 3 +- drivers/mtd/spi-nor/macronix.c | 3 +- drivers/mtd/spi-nor/sfdp.c | 72 ++++++++++++++++-------------------------- drivers/mtd/spi-nor/sfdp.h | 3 +- drivers/mtd/spi-nor/spansion.c | 12 +++---- drivers/mtd/spi-nor/winbond.c | 3 +- 8 files changed, 42 insertions(+), 70 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index dd35ec778339..d5dbbad8bd32 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2626,22 +2626,20 @@ void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, 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) + const struct sfdp_bfpt *bfpt) { int ret; if (nor->manufacturer && nor->manufacturer->fixups && nor->manufacturer->fixups->post_bfpt) { ret = nor->manufacturer->fixups->post_bfpt(nor, bfpt_header, - bfpt, params); + bfpt); if (ret) return ret; } if (nor->info->fixups && nor->info->fixups->post_bfpt) - return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt, - params); + return nor->info->fixups->post_bfpt(nor, bfpt_header, bfpt); return 0; } @@ -2896,7 +2894,7 @@ static void spi_nor_sfdp_init_params(struct spi_nor *nor) memcpy(&sfdp_params, nor->params, sizeof(sfdp_params)); - if (spi_nor_parse_sfdp(nor, nor->params)) { + if (spi_nor_parse_sfdp(nor)) { memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); nor->addr_width = 0; nor->flags &= ~SNOR_F_4B_OPCODES; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 4a3f7f150b5d..db07832ee66c 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -261,8 +261,7 @@ 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); + const struct sfdp_bfpt *bfpt); void (*post_sfdp)(struct spi_nor *nor); }; @@ -470,8 +469,7 @@ void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, 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); + const struct sfdp_bfpt *bfpt); static struct spi_nor __maybe_unused *mtd_to_spi_nor(struct mtd_info *mtd) { diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index ffcb60e54a80..1e5bb5408b68 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -11,8 +11,7 @@ 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) + const struct sfdp_bfpt *bfpt) { /* * IS25LP256 supports 4B opcodes, but the BFPT advertises a diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index 9203abaac229..6c2680b4cdad 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -11,8 +11,7 @@ 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) + const struct sfdp_bfpt *bfpt) { /* * MX25L25635F supports 4B opcodes but MX25L25635E does not. diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 25142ec4737b..23c28e91f698 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -405,8 +405,6 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) * @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. @@ -431,9 +429,9 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) * 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) + const struct sfdp_parameter_header *bfpt_header) { + struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_erase_map *map = ¶ms->erase_map; struct spi_nor_erase_type *erase_type = map->erase_type; struct sfdp_bfpt bfpt; @@ -552,8 +550,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* Stop here if not JESD216 rev A or later. */ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216) - return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, - params); + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ val = bfpt.dwords[BFPT_DWORD(11)]; @@ -614,8 +611,8 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, /* Stop here if not JESD216 rev C or later. */ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) - return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, - params); + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); + /* 8D-8D-8D command extension. */ switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { case BFPT_DWORD18_CMD_EXT_REP: @@ -635,7 +632,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, return -EOPNOTSUPP; } - return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); } /** @@ -800,18 +797,14 @@ spi_nor_region_check_overlay(struct spi_nor_erase_region *region, /** * 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) +static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, + const u32 *smpt) { - struct spi_nor_erase_map *map = ¶ms->erase_map; + struct spi_nor_erase_map *map = &nor->params->erase_map; struct spi_nor_erase_type *erase = map->erase_type; struct spi_nor_erase_region *region; u64 offset; @@ -889,8 +882,6 @@ spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, * 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 @@ -899,8 +890,7 @@ spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, * 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 struct sfdp_parameter_header *smpt_header) { const u32 *sector_map; u32 *smpt; @@ -928,11 +918,11 @@ static int spi_nor_parse_smpt(struct spi_nor *nor, goto out; } - ret = spi_nor_init_non_uniform_erase_map(nor, params, sector_map); + ret = spi_nor_init_non_uniform_erase_map(nor, sector_map); if (ret) goto out; - spi_nor_regions_sort_erase_types(¶ms->erase_map); + spi_nor_regions_sort_erase_types(&nor->params->erase_map); /* fall through */ out: kfree(smpt); @@ -944,13 +934,11 @@ out: * @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) + const struct sfdp_parameter_header *param_header) { static const struct sfdp_4bait reads[] = { { SNOR_HWCAPS_READ, BIT(0) }, @@ -974,6 +962,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, { 0u /* not used */, BIT(11) }, { 0u /* not used */, BIT(12) }, }; + struct spi_nor_flash_parameter *params = nor->params; 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; @@ -1130,13 +1119,11 @@ out: * @nor: pointer to a 'struct spi_nor' * @profile1_header: pointer to the 'struct sfdp_parameter_header' describing * the Profile 1.0 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_profile1(struct spi_nor *nor, - const struct sfdp_parameter_header *profile1_header, - struct spi_nor_flash_parameter *params) + const struct sfdp_parameter_header *profile1_header) { u32 *dwords, addr; size_t len; @@ -1160,14 +1147,14 @@ static int spi_nor_parse_profile1(struct spi_nor *nor, /* Set the Read Status Register dummy cycles and dummy address bytes. */ if (dwords[0] & PROFILE1_DWORD1_RDSR_DUMMY) - params->rdsr_dummy = 8; + nor->params->rdsr_dummy = 8; else - params->rdsr_dummy = 4; + nor->params->rdsr_dummy = 4; if (dwords[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES) - params->rdsr_addr_nbytes = 4; + nor->params->rdsr_addr_nbytes = 4; else - params->rdsr_addr_nbytes = 0; + nor->params->rdsr_addr_nbytes = 0; /* * We don't know what speed the controller is running at. Find the @@ -1193,7 +1180,7 @@ static int spi_nor_parse_profile1(struct spi_nor *nor, dummy = round_up(dummy, 2); /* Update the fast read settings. */ - spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], + spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR], 0, dummy, opcode, SNOR_PROTO_8_8_8_DTR); @@ -1210,13 +1197,11 @@ out: * @nor: pointer to a 'struct spi_nor' * @sccr_header: pointer to the 'struct sfdp_parameter_header' describing * the SCCR Map 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_sccr(struct spi_nor *nor, - const struct sfdp_parameter_header *sccr_header, - struct spi_nor_flash_parameter *params) + const struct sfdp_parameter_header *sccr_header) { u32 *dwords, addr; size_t len; @@ -1245,8 +1230,6 @@ out: /** * 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 @@ -1256,8 +1239,7 @@ out: * * Return: 0 on success, -errno otherwise. */ -int spi_nor_parse_sfdp(struct spi_nor *nor, - struct spi_nor_flash_parameter *params) +int spi_nor_parse_sfdp(struct spi_nor *nor) { const struct sfdp_parameter_header *param_header, *bfpt_header; struct sfdp_parameter_header *param_headers = NULL; @@ -1326,7 +1308,7 @@ int spi_nor_parse_sfdp(struct spi_nor *nor, bfpt_header = param_header; } - err = spi_nor_parse_bfpt(nor, bfpt_header, params); + err = spi_nor_parse_bfpt(nor, bfpt_header); if (err) goto exit; @@ -1336,19 +1318,19 @@ int spi_nor_parse_sfdp(struct spi_nor *nor, switch (SFDP_PARAM_HEADER_ID(param_header)) { case SFDP_SECTOR_MAP_ID: - err = spi_nor_parse_smpt(nor, param_header, params); + err = spi_nor_parse_smpt(nor, param_header); break; case SFDP_4BAIT_ID: - err = spi_nor_parse_4bait(nor, param_header, params); + err = spi_nor_parse_4bait(nor, param_header); break; case SFDP_PROFILE1_ID: - err = spi_nor_parse_profile1(nor, param_header, params); + err = spi_nor_parse_profile1(nor, param_header); break; case SFDP_SCCR_MAP_ID: - err = spi_nor_parse_sccr(nor, param_header, params); + err = spi_nor_parse_sccr(nor, param_header); break; default: diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index 89152ae1cf3e..bbf80d2990ab 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -107,7 +107,6 @@ struct sfdp_parameter_header { u8 id_msb; }; -int spi_nor_parse_sfdp(struct spi_nor *nor, - struct spi_nor_flash_parameter *params); +int spi_nor_parse_sfdp(struct spi_nor *nor); #endif /* __LINUX_MTD_SFDP_H */ diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index b0c5521c1e27..ee82dcd75310 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -142,8 +142,7 @@ static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor) static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, - const struct sfdp_bfpt *bfpt, - struct spi_nor_flash_parameter *params) + const struct sfdp_bfpt *bfpt) { /* * The BFPT table advertises a 512B page size but the page size is @@ -162,9 +161,9 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, return ret; if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) - params->page_size = 512; + nor->params->page_size = 512; else - params->page_size = 256; + nor->params->page_size = 256; return 0; } @@ -178,8 +177,7 @@ static struct spi_nor_fixups s28hs512t_fixups = { static int s25fs_s_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) + const struct sfdp_bfpt *bfpt) { /* * The S25FS-S chip family reports 512-byte pages in BFPT but @@ -187,7 +185,7 @@ s25fs_s_post_bfpt_fixups(struct spi_nor *nor, * of 256 bytes. Overwrite the page size advertised by BFPT * to get the writes working. */ - params->page_size = 256; + nor->params->page_size = 256; return 0; } diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index e24bcb928be7..20b2c6f19d6f 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -11,8 +11,7 @@ static int w25q256_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) + const struct sfdp_bfpt *bfpt) { /* * W25Q256JV supports 4B opcodes but W25Q256FV does not. -- cgit From 79321e752aff36569fb2b5b121f6e1dca9fc5f74 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 15 Mar 2021 07:56:34 +0200 Subject: mtd: spi-nor: core: Update comment about the default flash parameters s/legacy/default. spi_nor_info_init_params initializes some default flash parameters and settings that can be overwritten when parsing SFDP, or by fixup hooks. There's nothing legacy about them, they are just some default settings, if not otherwise discovered or specified. Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20210315055634.17332-1-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index d5dbbad8bd32..fbc34158a883 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2914,7 +2914,7 @@ static void spi_nor_info_init_params(struct spi_nor *nor) struct device_node *np = spi_nor_get_flash_node(nor); u8 i, erase_mask; - /* Initialize legacy flash parameters and settings. */ + /* Initialize default flash parameters and settings. */ params->quad_enable = spi_nor_sr2_bit1_quad_enable; params->set_4byte_addr_mode = spansion_set_4byte_addr_mode; params->setup = spi_nor_default_setup; -- cgit From a071912636cc3420f54e2a6312c1625ac763cf03 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Thu, 11 Mar 2021 12:09:08 -0500 Subject: mtd: rawnand: brcmnand: read/write oob during EDU transfer Added support to read/write oob during EDU transfers. Signed-off-by: Kamal Dasu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210311170909.9031-1-kdasu.kdev@gmail.com --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 59 ++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 5ff4291380c5..31ef2f13ab64 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -242,6 +242,9 @@ struct brcmnand_controller { u32 edu_ext_addr; u32 edu_cmd; u32 edu_config; + int sas; /* spare area size, per flash cache */ + int sector_size_1k; + u8 *oob; /* flash_dma reg */ const u16 *flash_dma_offsets; @@ -249,7 +252,7 @@ struct brcmnand_controller { dma_addr_t dma_pa; int (*dma_trans)(struct brcmnand_host *host, u64 addr, u32 *buf, - u32 len, u8 dma_cmd); + u8 *oob, u32 len, u8 dma_cmd); /* in-memory cache of the FLASH_CACHE, used only for some commands */ u8 flash_cache[FC_BYTES]; @@ -1479,6 +1482,23 @@ static irqreturn_t brcmnand_edu_irq(int irq, void *data) edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr); edu_readl(ctrl, EDU_EXT_ADDR); + if (ctrl->oob) { + if (ctrl->edu_cmd == EDU_CMD_READ) { + ctrl->oob += read_oob_from_regs(ctrl, + ctrl->edu_count + 1, + ctrl->oob, ctrl->sas, + ctrl->sector_size_1k); + } else { + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + ctrl->edu_ext_addr); + brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + ctrl->oob += write_oob_to_regs(ctrl, + ctrl->edu_count, + ctrl->oob, ctrl->sas, + ctrl->sector_size_1k); + } + } + mb(); /* flush previous writes */ edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd); edu_readl(ctrl, EDU_CMD); @@ -1850,9 +1870,10 @@ 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) + u8 *oob, u32 len, u8 cmd) { struct brcmnand_controller *ctrl = host->ctrl; + struct brcmnand_cfg *cfg = &host->hwcfg; unsigned long timeo = msecs_to_jiffies(200); int ret = 0; int dir = (cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); @@ -1860,6 +1881,9 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf, unsigned int trans = len >> FC_SHIFT; dma_addr_t pa; + dev_dbg(ctrl->dev, "EDU %s %p:%p\n", ((edu_cmd == EDU_CMD_READ) ? + "read" : "write"), buf, oob); + 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"); @@ -1871,6 +1895,8 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf, ctrl->edu_ext_addr = addr; ctrl->edu_cmd = edu_cmd; ctrl->edu_count = trans; + ctrl->sas = cfg->spare_area_size; + ctrl->oob = oob; edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr); edu_readl(ctrl, EDU_DRAM_ADDR); @@ -1879,6 +1905,16 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf, edu_writel(ctrl, EDU_LENGTH, FC_BYTES); edu_readl(ctrl, EDU_LENGTH); + if (ctrl->oob && (ctrl->edu_cmd == EDU_CMD_WRITE)) { + brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, + ctrl->edu_ext_addr); + brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); + ctrl->oob += write_oob_to_regs(ctrl, + 1, + ctrl->oob, ctrl->sas, + ctrl->sector_size_1k); + } + /* Start edu engine */ mb(); /* flush previous writes */ edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd); @@ -1893,6 +1929,14 @@ static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf, dma_unmap_single(ctrl->dev, pa, len, dir); + /* read last subpage oob */ + if (ctrl->oob && (ctrl->edu_cmd == EDU_CMD_READ)) { + ctrl->oob += read_oob_from_regs(ctrl, + 1, + ctrl->oob, ctrl->sas, + ctrl->sector_size_1k); + } + /* for program page check NAND status */ if (((brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & INTFC_FLASH_STATUS) & NAND_STATUS_FAIL) && @@ -2002,7 +2046,7 @@ static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc) } static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, - u32 len, u8 dma_cmd) + u8 *oob, u32 len, u8 dma_cmd) { struct brcmnand_controller *ctrl = host->ctrl; dma_addr_t buf_pa; @@ -2147,8 +2191,9 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, try_dmaread: brcmnand_clear_ecc_addr(ctrl); - if (ctrl->dma_trans && !oob && flash_dma_buf_ok(buf)) { - err = ctrl->dma_trans(host, addr, buf, + if (ctrl->dma_trans && (has_edu(ctrl) || !oob) && + flash_dma_buf_ok(buf)) { + err = ctrl->dma_trans(host, addr, buf, oob, trans * FC_BYTES, CMD_PAGE_READ); @@ -2296,8 +2341,8 @@ 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 (use_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) { - if (ctrl->dma_trans(host, addr, (u32 *)buf, mtd->writesize, + if (use_dma(ctrl) && (has_edu(ctrl) || !oob) && flash_dma_buf_ok(buf)) { + if (ctrl->dma_trans(host, addr, (u32 *)buf, oob, mtd->writesize, CMD_PROGRAM_PAGE)) ret = -EIO; -- cgit From 22ca05b82d3e3abc2b116a11ee41b6b692b95530 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Thu, 11 Mar 2021 12:09:09 -0500 Subject: mtd: rawnand: brcmnand: move to polling in pio mode on oops write This change makes sure that Broadcom NAND driver moves to interrupt polling on the first brcmnand_write() call. Signed-off-by: Kamal Dasu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210311170909.9031-2-kdasu.kdev@gmail.com --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 31ef2f13ab64..f75929783b94 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -2341,6 +2341,10 @@ 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 (mtd->oops_panic_write) + /* switch to interrupt polling and PIO mode */ + disable_ctrl_irqs(ctrl); + if (use_dma(ctrl) && (has_edu(ctrl) || !oob) && flash_dma_buf_ok(buf)) { if (ctrl->dma_trans(host, addr, (u32 *)buf, oob, mtd->writesize, CMD_PROGRAM_PAGE)) -- cgit From 4682dd19a6686dccf1871479d12dd1a4a3df0b70 Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 15 Mar 2021 09:08:15 +0800 Subject: mtd: rawnand: r852: replace spin_lock_irqsave by spin_lock in hard IRQ The code has been in a irq-disabled context since it is hard IRQ. There is no necessity to do it again. Signed-off-by: Tian Tao Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1615770495-31939-1-git-send-email-tiantao6@hisilicon.com --- drivers/mtd/nand/raw/r852.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c index c742354c1b0b..ebe859ca49cb 100644 --- a/drivers/mtd/nand/raw/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -724,10 +724,9 @@ static irqreturn_t r852_irq(int irq, void *data) struct r852_device *dev = (struct r852_device *)data; uint8_t card_status, dma_status; - unsigned long flags; irqreturn_t ret = IRQ_NONE; - spin_lock_irqsave(&dev->irqlock, flags); + spin_lock(&dev->irqlock); /* handle card detection interrupts first */ card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); @@ -813,7 +812,7 @@ static irqreturn_t r852_irq(int irq, void *data) dbg("strange card status = %x", card_status); out: - spin_unlock_irqrestore(&dev->irqlock, flags); + spin_unlock(&dev->irqlock); return ret; } -- cgit From 1200c7f834ae7060c61f098db305ef2b92181226 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 15 Mar 2021 21:00:42 -0300 Subject: mtd: rawnand: mxc: Remove unneeded of_match_ptr() i.MX is a DT-only platform, so of_match_ptr() can be safely removed. Remove the unneeded of_match_ptr(). Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210316000042.200392-1-festevam@gmail.com --- drivers/mtd/nand/raw/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index f78302e16b84..f6c96341b896 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1849,7 +1849,7 @@ static int mxcnd_remove(struct platform_device *pdev) static struct platform_driver mxcnd_driver = { .driver = { .name = DRIVER_NAME, - .of_match_table = of_match_ptr(mxcnd_dt_ids), + .of_match_table = mxcnd_dt_ids, }, .probe = mxcnd_probe, .remove = mxcnd_remove, -- cgit From 33cebf701e98dd12b01d39d1c644387b27c1a627 Mon Sep 17 00:00:00 2001 From: "Kai Stuhlemmer (ebee Engineering)" Date: Mon, 22 Mar 2021 17:07:14 +0200 Subject: mtd: rawnand: atmel: Update ecc_stats.corrected counter Update MTD ECC statistics with the number of corrected bits. Fixes: f88fc122cc34 ("mtd: nand: Cleanup/rework the atmel_nand driver") Cc: stable@vger.kernel.org Signed-off-by: Kai Stuhlemmer (ebee Engineering) Signed-off-by: Tudor Ambarus Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210322150714.101585-1-tudor.ambarus@microchip.com --- drivers/mtd/nand/raw/atmel/nand-controller.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index e6ceec8f50dc..8aab1017b460 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -883,10 +883,12 @@ static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf, NULL, 0, chip->ecc.strength); - if (ret >= 0) + if (ret >= 0) { + mtd->ecc_stats.corrected += ret; max_bitflips = max(ret, max_bitflips); - else + } else { mtd->ecc_stats.failed++; + } databuf += chip->ecc.size; eccbuf += chip->ecc.bytes; -- cgit From 5c8a620ab22b05eae7e480cb83ff599047b8aff4 Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Tue, 23 Mar 2021 13:11:37 +0000 Subject: mtd: rawnand: rockchip: Use flexible-array member instead of zero-length array Suppresses the following coccinelle warning: drivers/mtd/nand/raw/rockchip-nand-controller.c:162:4-8: WARNING use flexible-array member instead Signed-off-by: Zou Wei Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210323131137.45552-1-zou_wei@huawei.com --- drivers/mtd/nand/raw/rockchip-nand-controller.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c index 796b678cb108..b5405bc7ca3a 100644 --- a/drivers/mtd/nand/raw/rockchip-nand-controller.c +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -159,7 +159,7 @@ struct rk_nfc_nand_chip { u32 timing; u8 nsels; - u8 sels[0]; + u8 sels[]; /* Nothing after this field. */ }; -- cgit From 25fefc88c71f47db0466570335e3f75f10952e7a Mon Sep 17 00:00:00 2001 From: Alexander Lobakin Date: Tue, 23 Mar 2021 17:37:19 +0000 Subject: mtd: spinand: core: add missing MODULE_DEVICE_TABLE() The module misses MODULE_DEVICE_TABLE() for both SPI and OF ID tables and thus never autoloads on ID matches. Add the missing declarations. Present since day-0 of spinand framework introduction. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Cc: stable@vger.kernel.org # 4.19+ Signed-off-by: Alexander Lobakin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210323173714.317884-1-alobakin@pm.me --- drivers/mtd/nand/spi/core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 61d932c1b718..17f63f95f4a2 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1263,12 +1263,14 @@ static const struct spi_device_id spinand_ids[] = { { .name = "spi-nand" }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(spi, spinand_ids); #ifdef CONFIG_OF static const struct of_device_id spinand_of_ids[] = { { .compatible = "spi-nand" }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, spinand_of_ids); #endif static struct spi_mem_driver spinand_drv = { -- cgit From 1e97743fd180981bef5f01402342bb54bf1c6366 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 3 Mar 2021 16:57:35 +0100 Subject: mtd: require write permissions for locking and badblock ioctls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MEMLOCK, MEMUNLOCK and OTPLOCK modify protection bits. Thus require write permission. Depending on the hardware MEMLOCK might even be write-once, e.g. for SPI-NOR flashes with their WP# tied to GND. OTPLOCK is always write-once. MEMSETBADBLOCK modifies the bad block table. Fixes: f7e6b19bc764 ("mtd: properly check all write ioctls for permissions") Signed-off-by: Michael Walle Reviewed-by: Greg Kroah-Hartman Acked-by: Rafał Miłecki Acked-by: Richard Weinberger Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210303155735.25887-1-michael@walle.cc --- drivers/mtd/mtdchar.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 998d86f46488..870f7a19ad9d 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -643,16 +643,12 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) case MEMGETINFO: case MEMREADOOB: case MEMREADOOB64: - case MEMLOCK: - case MEMUNLOCK: case MEMISLOCKED: case MEMGETOOBSEL: case MEMGETBADBLOCK: - case MEMSETBADBLOCK: case OTPSELECT: case OTPGETREGIONCOUNT: case OTPGETREGIONINFO: - case OTPLOCK: case ECCGETLAYOUT: case ECCGETSTATS: case MTDFILEMODE: @@ -663,9 +659,13 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) /* "dangerous" commands */ case MEMERASE: case MEMERASE64: + case MEMLOCK: + case MEMUNLOCK: + case MEMSETBADBLOCK: case MEMWRITEOOB: case MEMWRITEOOB64: case MEMWRITE: + case OTPLOCK: if (!(file->f_mode & FMODE_WRITE)) return -EPERM; break; -- cgit From e3c1f1c92d6ede3cfa09d6a103d3d1c1ef645e35 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 3 Mar 2021 21:18:19 +0100 Subject: mtd: add OTP (one-time-programmable) erase ioctl This may sound like a contradiction but some SPI-NOR flashes really support erasing their OTP region until it is finally locked. Having the possibility to erase an OTP region might come in handy during development. The ioctl argument follows the OTPLOCK style. Signed-off-by: Michael Walle Acked-by: Vignesh Raghavendra Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210303201819.2752-1-michael@walle.cc --- drivers/mtd/mtdchar.c | 7 ++++++- drivers/mtd/mtdcore.c | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 870f7a19ad9d..155e991d9d75 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -666,6 +666,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) case MEMWRITEOOB64: case MEMWRITE: case OTPLOCK: + case OTPERASE: if (!(file->f_mode & FMODE_WRITE)) return -EPERM; break; @@ -930,6 +931,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) } case OTPLOCK: + case OTPERASE: { struct otp_info oinfo; @@ -937,7 +939,10 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) return -EINVAL; if (copy_from_user(&oinfo, argp, sizeof(oinfo))) return -EFAULT; - ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length); + if (cmd == OTPLOCK) + ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length); + else + ret = mtd_erase_user_prot_reg(mtd, oinfo.start, oinfo.length); break; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 38782ceea1f6..aea58366a94e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1919,6 +1919,18 @@ int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) } EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg); +int mtd_erase_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) +{ + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_erase_user_prot_reg) + return -EOPNOTSUPP; + if (!len) + return 0; + return master->_erase_user_prot_reg(master, from, len); +} +EXPORT_SYMBOL_GPL(mtd_erase_user_prot_reg); + /* Chip-supported device locking */ int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { -- cgit From 658c4448bbbf02a143abf1b89d09a3337ebd3ba6 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Fri, 12 Mar 2021 07:28:19 +0100 Subject: mtd: core: add nvmem-cells compatible to parse mtd as nvmem cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Partitions that contains the nvmem-cells compatible will register their direct subonodes as nvmem cells and the node will be treated as a nvmem provider. Signed-off-by: Ansuel Smith Tested-by: Rafał Miłecki Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210312062830.20548-1-ansuelsmth@gmail.com --- drivers/mtd/mtdcore.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index aea58366a94e..dab169d597c9 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -531,6 +531,7 @@ static int mtd_nvmem_reg_read(void *priv, unsigned int offset, static int mtd_nvmem_add(struct mtd_info *mtd) { + struct device_node *node = mtd_get_of_node(mtd); struct nvmem_config config = {}; config.id = -1; @@ -543,7 +544,7 @@ static int mtd_nvmem_add(struct mtd_info *mtd) config.stride = 1; config.read_only = true; config.root_only = true; - config.no_of_node = true; + config.no_of_node = !of_device_is_compatible(node, "nvmem-cells"); config.priv = mtd; mtd->nvmem = nvmem_register(&config); -- cgit From 7134a2d026d942210b4d26d6059c9d979ca7866e Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 12 Mar 2021 14:49:19 +0100 Subject: mtd: parsers: ofpart: support Linksys Northstar partitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows extending ofpart parser with support for Linksys Northstar devices. That support uses recently added quirks mechanism. Signed-off-by: Rafał Miłecki Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210312134919.7767-2-zajec5@gmail.com --- drivers/mtd/parsers/Kconfig | 10 +++++++ drivers/mtd/parsers/Makefile | 1 + drivers/mtd/parsers/ofpart_core.c | 6 ++++ drivers/mtd/parsers/ofpart_linksys_ns.c | 50 +++++++++++++++++++++++++++++++++ drivers/mtd/parsers/ofpart_linksys_ns.h | 18 ++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 drivers/mtd/parsers/ofpart_linksys_ns.c create mode 100644 drivers/mtd/parsers/ofpart_linksys_ns.h (limited to 'drivers/mtd') diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index 0528855cf6c1..9babe678c41b 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -76,6 +76,16 @@ config MTD_OF_PARTS_BCM4908 that can have multiple "firmware" partitions. It takes care of finding currently used one and backup ones. +config MTD_OF_PARTS_LINKSYS_NS + bool "Linksys Northstar partitioning support" + depends on MTD_OF_PARTS && (ARCH_BCM_5301X || ARCH_BCM4908 || COMPILE_TEST) + default ARCH_BCM_5301X + help + This provides partitions parser for Linksys devices based on Broadcom + Northstar architecture. Linksys commonly uses fixed flash layout with + two "firmware" partitions. Currently used firmware has to be detected + using CFE environment variable. + config MTD_PARSER_IMAGETAG tristate "Parser for BCM963XX Image Tag format partitions" depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile index 2dfe9fb602de..2e98aa048278 100644 --- a/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o ofpart-y += ofpart_core.o ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o +ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o diff --git a/drivers/mtd/parsers/ofpart_core.c b/drivers/mtd/parsers/ofpart_core.c index 2cef527dd976..0fd8d2a0db97 100644 --- a/drivers/mtd/parsers/ofpart_core.c +++ b/drivers/mtd/parsers/ofpart_core.c @@ -17,6 +17,7 @@ #include #include "ofpart_bcm4908.h" +#include "ofpart_linksys_ns.h" struct fixed_partitions_quirks { int (*post_parse)(struct mtd_info *mtd, struct mtd_partition *parts, int nr_parts); @@ -26,6 +27,10 @@ static struct fixed_partitions_quirks bcm4908_partitions_quirks = { .post_parse = bcm4908_partitions_post_parse, }; +static struct fixed_partitions_quirks linksys_ns_partitions_quirks = { + .post_parse = linksys_ns_partitions_post_parse, +}; + static const struct of_device_id parse_ofpart_match_table[]; static bool node_has_compatible(struct device_node *pp) @@ -167,6 +172,7 @@ static const struct of_device_id parse_ofpart_match_table[] = { { .compatible = "fixed-partitions" }, /* Customized */ { .compatible = "brcm,bcm4908-partitions", .data = &bcm4908_partitions_quirks, }, + { .compatible = "linksys,ns-partitions", .data = &linksys_ns_partitions_quirks, }, {}, }; MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); diff --git a/drivers/mtd/parsers/ofpart_linksys_ns.c b/drivers/mtd/parsers/ofpart_linksys_ns.c new file mode 100644 index 000000000000..318c42d0256b --- /dev/null +++ b/drivers/mtd/parsers/ofpart_linksys_ns.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Rafał Miłecki + */ + +#include +#include +#include + +#include "ofpart_linksys_ns.h" + +#define NVRAM_BOOT_PART "bootpartition" + +static int ofpart_linksys_ns_bootpartition(void) +{ + char buf[4]; + int bootpartition; + + /* Check CFE environment variable */ + if (bcm47xx_nvram_getenv(NVRAM_BOOT_PART, buf, sizeof(buf)) > 0) { + if (!kstrtoint(buf, 0, &bootpartition)) + return bootpartition; + pr_warn("Failed to parse %s value \"%s\"\n", NVRAM_BOOT_PART, + buf); + } else { + pr_warn("Failed to get NVRAM \"%s\"\n", NVRAM_BOOT_PART); + } + + return 0; +} + +int linksys_ns_partitions_post_parse(struct mtd_info *mtd, + struct mtd_partition *parts, + int nr_parts) +{ + int bootpartition = ofpart_linksys_ns_bootpartition(); + int trx_idx = 0; + int i; + + for (i = 0; i < nr_parts; i++) { + if (of_device_is_compatible(parts[i].of_node, "linksys,ns-firmware")) { + if (trx_idx++ == bootpartition) + parts[i].name = "firmware"; + else + parts[i].name = "backup"; + } + } + + return 0; +} diff --git a/drivers/mtd/parsers/ofpart_linksys_ns.h b/drivers/mtd/parsers/ofpart_linksys_ns.h new file mode 100644 index 000000000000..730c46812ebf --- /dev/null +++ b/drivers/mtd/parsers/ofpart_linksys_ns.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __OFPART_LINKSYS_NS_H +#define __OFPART_LINKSYS_NS_H + +#ifdef CONFIG_MTD_OF_PARTS_LINKSYS_NS +int linksys_ns_partitions_post_parse(struct mtd_info *mtd, + struct mtd_partition *parts, + int nr_parts); +#else +static inline int linksys_ns_partitions_post_parse(struct mtd_info *mtd, + struct mtd_partition *parts, + int nr_parts) +{ + return -EOPNOTSUPP; +} +#endif + +#endif -- cgit From bd9c9fe2ad04546940f4a9979d679e62cae6aa51 Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Thu, 25 Mar 2021 11:23:37 +0100 Subject: mtd: rawnand: bbt: Skip bad blocks when searching for the BBT in NAND The blocks containing the bad block table can become bad as well. So make sure to skip any blocks that are marked bad when searching for the bad block table. Otherwise in very rare cases where two BBT blocks wear out it might happen that an obsolete BBT is used instead of a newer available version. Signed-off-by: Stefan Riedmueller Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210325102337.481172-1-s.riedmueller@phytec.de --- drivers/mtd/nand/raw/nand_bbt.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index dced32a126d9..6e25a5ce5ba9 100644 --- a/drivers/mtd/nand/raw/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -525,6 +525,7 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf, { u64 targetsize = nanddev_target_size(&this->base); struct mtd_info *mtd = nand_to_mtd(this); + struct nand_bbt_descr *bd = this->badblock_pattern; int i, chips; int startblock, block, dir; int scanlen = mtd->writesize + mtd->oobsize; @@ -560,6 +561,10 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf, int actblock = startblock + dir * block; loff_t offs = (loff_t)actblock << this->bbt_erase_shift; + /* Check if block is marked bad */ + if (scan_block_fast(this, bd, offs, buf)) + continue; + /* Read first page */ scan_read(this, buf, offs, mtd->writesize, td); if (!check_pattern(buf, scanlen, mtd->writesize, td)) { -- cgit From c4c795105f2924c80752c30ffd3c7029a8e0ef28 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 22 Mar 2021 09:51:30 +0200 Subject: mtd: spi-nor: Move Software Write Protection logic out of the core It makes the core file a bit smaller and provides better separation between the Software Write Protection features and the core logic. All the next generic software write protection features (e.g. Individual Block Protection) will reside in swp.c. Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Acked-by: Pratyush Yadav Link: https://lore.kernel.org/r/20210322075131.45093-2-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/Makefile | 2 +- drivers/mtd/spi-nor/core.c | 406 +---------------------------------------- drivers/mtd/spi-nor/core.h | 4 + drivers/mtd/spi-nor/swp.c | 424 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 432 insertions(+), 404 deletions(-) create mode 100644 drivers/mtd/spi-nor/swp.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 653923896205..da05b03f28d2 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -spi-nor-objs := core.o sfdp.o +spi-nor-objs := core.o sfdp.o swp.o spi-nor-objs += atmel.o spi-nor-objs += catalyst.o spi-nor-objs += eon.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index fbc34158a883..5bc7ecfe7740 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1730,376 +1730,6 @@ erase_err: 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 = spi_nor_get_sr_bp_mask(nor); - - /* Reserved one for "protect none" and one for "protect all". */ - bp_slots = (1 << hweight8(mask)) - 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 = 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_BP3_BIT6 && val & SR_BP3_BIT6) - val = (val & ~SR_BP3_BIT6) | SR_BP3; - - bp = val >> SR_BP_SHIFT; - - if (!bp) { - /* No protection */ - *ofs = 0; - *len = 0; - 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; -} - -/* - * Return 1 if the entire region is locked (if @locked is true) or unlocked (if - * @locked is false); 0 otherwise - */ -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; - - if (!len) - return 1; - - spi_nor_get_locked_range_sr(nor, sr, &lock_offs, &lock_len); - - if (locked) - /* Requested range is a sub-range of locked range */ - return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); - else - /* Requested range does not overlap with locked range */ - return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs); -} - -static int spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, - u8 sr) -{ - return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); -} - -static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, - u8 sr) -{ - return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); -} - -/* - * Lock a region of the flash. Compatible with ST Micro and similar flash. - * 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) - * - * Support for the following is provided conditionally for some flash: - * - TB: top/bottom protect - * - * Sample table portion for 8MB flash (Winbond w25q64fw): - * - * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion - * -------------------------------------------------------------------------- - * X | X | 0 | 0 | 0 | NONE | NONE - * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64 - * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32 - * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16 - * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8 - * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 - * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 - * X | X | 1 | 1 | 1 | 8 MB | ALL - * ------|-------|-------|-------|-------|---------------|------------------- - * 0 | 1 | 0 | 0 | 1 | 128 KB | Lower 1/64 - * 0 | 1 | 0 | 1 | 0 | 256 KB | Lower 1/32 - * 0 | 1 | 0 | 1 | 1 | 512 KB | Lower 1/16 - * 0 | 1 | 1 | 0 | 0 | 1 MB | Lower 1/8 - * 0 | 1 | 1 | 0 | 1 | 2 MB | Lower 1/4 - * 0 | 1 | 1 | 1 | 0 | 4 MB | Lower 1/2 - * - * Returns negative on errors, 0 on success. - */ -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 = 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; - bool use_top; - - ret = spi_nor_read_sr(nor, nor->bouncebuf); - if (ret) - return ret; - - status_old = nor->bouncebuf[0]; - - /* If nothing in our range is unlocked, we don't need to do anything */ - 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 (!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 (!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) - return -EINVAL; - - /* Prefer top, if both are valid */ - use_top = can_be_top; - - /* lock_len: length of region that should end up locked */ - if (use_top) - lock_len = mtd->size - ofs; - else - lock_len = ofs + len; - - if (lock_len == mtd->size) { - val = mask; - } else { - 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 (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) - val = (val & ~SR_BP3) | SR_BP3_BIT6; - - if (val & ~mask) - return -EINVAL; - - /* Don't "lock" with no region! */ - if (!(val & mask)) - return -EINVAL; - } - - status_new = (status_old & ~mask & ~tb_mask) | val; - - /* Disallow further writes if WP pin is asserted */ - status_new |= SR_SRWD; - - if (!use_top) - status_new |= tb_mask; - - /* Don't bother if they're the same */ - if (status_new == status_old) - return 0; - - /* Only modify protection if it will not unlock other areas */ - if ((status_new & mask) < (status_old & mask)) - return -EINVAL; - - return spi_nor_write_sr_and_check(nor, status_new); -} - -/* - * Unlock a region of the flash. See spi_nor_sr_lock() for more info - * - * Returns negative on errors, 0 on success. - */ -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 = 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; - bool use_top; - - ret = spi_nor_read_sr(nor, nor->bouncebuf); - if (ret) - return ret; - - status_old = nor->bouncebuf[0]; - - /* If nothing in our range is locked, we don't need to do anything */ - 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 (!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 (!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) - return -EINVAL; - - /* Prefer top, if both are valid */ - use_top = can_be_top; - - /* lock_len: length of region that should remain locked */ - if (use_top) - lock_len = mtd->size - (ofs + len); - else - lock_len = ofs; - - if (lock_len == 0) { - val = 0; /* fully unlocked */ - } else { - 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 (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; - } - - status_new = (status_old & ~mask & ~tb_mask) | val; - - /* Don't protect status register if we're fully unlocked */ - if (lock_len == 0) - status_new &= ~SR_SRWD; - - if (!use_top) - status_new |= tb_mask; - - /* Don't bother if they're the same */ - if (status_new == status_old) - return 0; - - /* Only modify protection if it will not lock other areas */ - if ((status_new & mask) > (status_old & mask)) - return -EINVAL; - - return spi_nor_write_sr_and_check(nor, status_new); -} - -/* - * 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 spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) -{ - int ret; - - ret = spi_nor_read_sr(nor, nor->bouncebuf); - if (ret) - return ret; - - return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]); -} - -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) -{ - struct spi_nor *nor = mtd_to_spi_nor(mtd); - int ret; - - ret = spi_nor_lock_and_prep(nor); - if (ret) - return ret; - - ret = nor->params->locking_ops->lock(nor, ofs, len); - - spi_nor_unlock_and_unprep(nor); - return ret; -} - -static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct spi_nor *nor = mtd_to_spi_nor(mtd); - int ret; - - ret = spi_nor_lock_and_prep(nor); - if (ret) - return ret; - - ret = nor->params->locking_ops->unlock(nor, ofs, len); - - spi_nor_unlock_and_unprep(nor); - return ret; -} - -static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct spi_nor *nor = mtd_to_spi_nor(mtd); - int ret; - - ret = spi_nor_lock_and_prep(nor); - if (ret) - return ret; - - ret = nor->params->locking_ops->is_locked(nor, ofs, len); - - spi_nor_unlock_and_unprep(nor); - return ret; -} - /** * spi_nor_sr1_bit6_quad_enable() - Set the Quad Enable BIT(6) in the Status * Register 1. @@ -3046,7 +2676,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 = &spi_nor_sr_locking_ops; + spi_nor_init_default_locking_ops(nor); } /** @@ -3158,32 +2788,6 @@ static int spi_nor_quad_enable(struct spi_nor *nor) return nor->params->quad_enable(nor); } -/** - * spi_nor_try_unlock_all() - Tries to unlock the entire flash memory array. - * @nor: pointer to a 'struct spi_nor'. - * - * Some SPI NOR flashes are write protected by default after a power-on reset - * cycle, in order to avoid inadvertent writes during power-up. Backward - * compatibility imposes to unlock the entire flash memory array at power-up - * by default. - * - * Unprotecting the entire flash array will fail for boards which are hardware - * write-protected. Thus any errors are ignored. - */ -static void spi_nor_try_unlock_all(struct spi_nor *nor) -{ - int ret; - - if (!(nor->flags & SNOR_F_HAS_LOCK)) - return; - - dev_dbg(nor->dev, "Unprotecting entire flash array\n"); - - ret = spi_nor_unlock(&nor->mtd, 0, nor->params->size); - if (ret) - dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n"); -} - static int spi_nor_init(struct spi_nor *nor) { int err; @@ -3494,12 +3098,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->_suspend = spi_nor_suspend; mtd->_resume = spi_nor_resume; - if (nor->params->locking_ops) { - mtd->_lock = spi_nor_lock; - mtd->_unlock = spi_nor_unlock; - mtd->_is_locked = spi_nor_is_locked; - } - if (info->flags & USE_FSR) nor->flags |= SNOR_F_USE_FSR; if (info->flags & SPI_NOR_HAS_TB) { @@ -3551,6 +3149,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (ret) return ret; + spi_nor_register_locking_ops(nor); + /* Send all the required SPI flash commands to initialize device */ ret = spi_nor_init(nor); if (ret) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index db07832ee66c..74f6026b7335 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -471,6 +471,10 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt); +void spi_nor_init_default_locking_ops(struct spi_nor *nor); +void spi_nor_try_unlock_all(struct spi_nor *nor); +void spi_nor_register_locking_ops(struct spi_nor *nor); + static struct spi_nor __maybe_unused *mtd_to_spi_nor(struct mtd_info *mtd) { return mtd->priv; diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c new file mode 100644 index 000000000000..5b236db6bb56 --- /dev/null +++ b/drivers/mtd/spi-nor/swp.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SPI NOR Software Write Protection logic. + * + * Copyright (C) 2005, Intec Automation Inc. + * Copyright (C) 2014, Freescale Semiconductor, Inc. + */ +#include +#include + +#include "core.h" + +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 = spi_nor_get_sr_bp_mask(nor); + + /* Reserved one for "protect none" and one for "protect all". */ + bp_slots = (1 << hweight8(mask)) - 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 = 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_BP3_BIT6 && val & SR_BP3_BIT6) + val = (val & ~SR_BP3_BIT6) | SR_BP3; + + bp = val >> SR_BP_SHIFT; + + if (!bp) { + /* No protection */ + *ofs = 0; + *len = 0; + 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; +} + +/* + * Return 1 if the entire region is locked (if @locked is true) or unlocked (if + * @locked is false); 0 otherwise + */ +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; + + if (!len) + return 1; + + spi_nor_get_locked_range_sr(nor, sr, &lock_offs, &lock_len); + + if (locked) + /* Requested range is a sub-range of locked range */ + return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); + else + /* Requested range does not overlap with locked range */ + return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs); +} + +static int spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + u8 sr) +{ + return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); +} + +static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + u8 sr) +{ + return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); +} + +/* + * Lock a region of the flash. Compatible with ST Micro and similar flash. + * 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) + * + * Support for the following is provided conditionally for some flash: + * - TB: top/bottom protect + * + * Sample table portion for 8MB flash (Winbond w25q64fw): + * + * SEC | TB | BP2 | BP1 | BP0 | Prot Length | Protected Portion + * -------------------------------------------------------------------------- + * X | X | 0 | 0 | 0 | NONE | NONE + * 0 | 0 | 0 | 0 | 1 | 128 KB | Upper 1/64 + * 0 | 0 | 0 | 1 | 0 | 256 KB | Upper 1/32 + * 0 | 0 | 0 | 1 | 1 | 512 KB | Upper 1/16 + * 0 | 0 | 1 | 0 | 0 | 1 MB | Upper 1/8 + * 0 | 0 | 1 | 0 | 1 | 2 MB | Upper 1/4 + * 0 | 0 | 1 | 1 | 0 | 4 MB | Upper 1/2 + * X | X | 1 | 1 | 1 | 8 MB | ALL + * ------|-------|-------|-------|-------|---------------|------------------- + * 0 | 1 | 0 | 0 | 1 | 128 KB | Lower 1/64 + * 0 | 1 | 0 | 1 | 0 | 256 KB | Lower 1/32 + * 0 | 1 | 0 | 1 | 1 | 512 KB | Lower 1/16 + * 0 | 1 | 1 | 0 | 0 | 1 MB | Lower 1/8 + * 0 | 1 | 1 | 0 | 1 | 2 MB | Lower 1/4 + * 0 | 1 | 1 | 1 | 0 | 4 MB | Lower 1/2 + * + * Returns negative on errors, 0 on success. + */ +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 = 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; + bool use_top; + + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + status_old = nor->bouncebuf[0]; + + /* If nothing in our range is unlocked, we don't need to do anything */ + 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 (!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 (!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) + return -EINVAL; + + /* Prefer top, if both are valid */ + use_top = can_be_top; + + /* lock_len: length of region that should end up locked */ + if (use_top) + lock_len = mtd->size - ofs; + else + lock_len = ofs + len; + + if (lock_len == mtd->size) { + val = mask; + } else { + 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 (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3) + val = (val & ~SR_BP3) | SR_BP3_BIT6; + + if (val & ~mask) + return -EINVAL; + + /* Don't "lock" with no region! */ + if (!(val & mask)) + return -EINVAL; + } + + status_new = (status_old & ~mask & ~tb_mask) | val; + + /* Disallow further writes if WP pin is asserted */ + status_new |= SR_SRWD; + + if (!use_top) + status_new |= tb_mask; + + /* Don't bother if they're the same */ + if (status_new == status_old) + return 0; + + /* Only modify protection if it will not unlock other areas */ + if ((status_new & mask) < (status_old & mask)) + return -EINVAL; + + return spi_nor_write_sr_and_check(nor, status_new); +} + +/* + * Unlock a region of the flash. See spi_nor_sr_lock() for more info + * + * Returns negative on errors, 0 on success. + */ +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 = 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; + bool use_top; + + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + status_old = nor->bouncebuf[0]; + + /* If nothing in our range is locked, we don't need to do anything */ + 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 (!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 (!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) + return -EINVAL; + + /* Prefer top, if both are valid */ + use_top = can_be_top; + + /* lock_len: length of region that should remain locked */ + if (use_top) + lock_len = mtd->size - (ofs + len); + else + lock_len = ofs; + + if (lock_len == 0) { + val = 0; /* fully unlocked */ + } else { + 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 (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; + } + + status_new = (status_old & ~mask & ~tb_mask) | val; + + /* Don't protect status register if we're fully unlocked */ + if (lock_len == 0) + status_new &= ~SR_SRWD; + + if (!use_top) + status_new |= tb_mask; + + /* Don't bother if they're the same */ + if (status_new == status_old) + return 0; + + /* Only modify protection if it will not lock other areas */ + if ((status_new & mask) > (status_old & mask)) + return -EINVAL; + + return spi_nor_write_sr_and_check(nor, status_new); +} + +/* + * 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 spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + int ret; + + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + return spi_nor_is_locked_sr(nor, ofs, len, nor->bouncebuf[0]); +} + +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, +}; + +void spi_nor_init_default_locking_ops(struct spi_nor *nor) +{ + nor->params->locking_ops = &spi_nor_sr_locking_ops; +} + +static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->locking_ops->lock(nor, ofs, len); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->locking_ops->unlock(nor, ofs, len); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = nor->params->locking_ops->is_locked(nor, ofs, len); + + spi_nor_unlock_and_unprep(nor); + return ret; +} + +/** + * spi_nor_try_unlock_all() - Tries to unlock the entire flash memory array. + * @nor: pointer to a 'struct spi_nor'. + * + * Some SPI NOR flashes are write protected by default after a power-on reset + * cycle, in order to avoid inadvertent writes during power-up. Backward + * compatibility imposes to unlock the entire flash memory array at power-up + * by default. + * + * Unprotecting the entire flash array will fail for boards which are hardware + * write-protected. Thus any errors are ignored. + */ +void spi_nor_try_unlock_all(struct spi_nor *nor) +{ + int ret; + + if (!(nor->flags & SNOR_F_HAS_LOCK)) + return; + + dev_dbg(nor->dev, "Unprotecting entire flash array\n"); + + ret = spi_nor_unlock(&nor->mtd, 0, nor->params->size); + if (ret) + dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n"); +} + +void spi_nor_register_locking_ops(struct spi_nor *nor) +{ + struct mtd_info *mtd = &nor->mtd; + + if (!nor->params->locking_ops) + return; + + mtd->_lock = spi_nor_lock; + mtd->_unlock = spi_nor_unlock; + mtd->_is_locked = spi_nor_is_locked; +} -- cgit From b6cbd9167d442389614c079b7a8816d952114b90 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 22 Mar 2021 09:51:31 +0200 Subject: mtd: spi-nor: swp: Improve code around spi_nor_check_lock_status_sr() - bool return value for spi_nor_check_lock_status_sr(), gets rid of the return 1, - introduce temporary variables for better readability. Suggested-by: Joe Perches Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20210322075131.45093-3-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/swp.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 5b236db6bb56..8594bcbb7dbe 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -81,36 +81,39 @@ static void spi_nor_get_locked_range_sr(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 + * Return true if the entire region is locked (if @locked is true) or unlocked + * (if @locked is false); false otherwise. */ -static int spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, - uint64_t len, u8 sr, bool locked) +static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, + uint64_t len, u8 sr, bool locked) { - loff_t lock_offs; + loff_t lock_offs, lock_offs_max, offs_max; uint64_t lock_len; if (!len) - return 1; + return true; spi_nor_get_locked_range_sr(nor, sr, &lock_offs, &lock_len); + lock_offs_max = lock_offs + lock_len; + offs_max = ofs + len; + if (locked) /* Requested range is a sub-range of locked range */ - return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs); + return (offs_max <= lock_offs_max) && (ofs >= lock_offs); else /* Requested range does not overlap with locked range */ - return (ofs >= lock_offs + lock_len) || (ofs + len <= lock_offs); + return (ofs >= lock_offs_max) || (offs_max <= lock_offs); } -static int spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, - u8 sr) +static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + u8 sr) { return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); } -static int spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, - u8 sr) +static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, + uint64_t len, u8 sr) { return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); } -- cgit From 069089acf88b2216b667c1e5994e08b4d2e1ea12 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 22 Mar 2021 00:51:38 +0100 Subject: mtd: spi-nor: add OTP support SPI flashes sometimes have a special OTP area, which can (and is) used to store immutable properties like board serial number or vendor assigned network hardware addresses. The MTD subsystem already supports accessing such areas and some (non SPI NOR) flashes already implement support for it. It differentiates between user and factory areas. User areas can be written by the user and factory ones are pre-programmed and locked down by the vendor, usually containing an "electrical serial number". This patch will only add support for the user areas. Lay the foundation and implement the MTD callbacks for the SPI NOR and add necessary parameters to the flash_info structure. If a flash supports OTP it can be added by the convenience macro OTP_INFO(). Sometimes there are individual regions, which might have individual offsets. Therefore, it is possible to specify the starting address of the first regions as well as the distance between two regions (e.g. Winbond devices uses this method). Additionally, the regions might be locked down. Once locked, no further write access is possible. For SPI NOR flashes the OTP area is accessed like the normal memory, e.g. by offset addressing; except that you either have to use special read/write commands (Winbond) or you have to enter (and exit) a specific OTP mode (Macronix, Micron). Thus we introduce four operations to which the MTD callbacks will be mapped: .read(), .write(), .lock() and .is_locked(). The read and the write ops will be given an address offset to operate on while the locking ops use regions because locking always affects a whole region. It is up to the flash driver to implement these ops. Signed-off-by: Michael Walle [ta: use div64_u64(), IS_ALIGNED, params->otp.org. unsigned int region, drop comment, add rlen local variable in spi_nor_mtd_otp_lock()] Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20210321235140.8308-2-michael@walle.cc --- drivers/mtd/spi-nor/Makefile | 2 +- drivers/mtd/spi-nor/core.c | 5 + drivers/mtd/spi-nor/core.h | 53 +++++++++++ drivers/mtd/spi-nor/otp.c | 212 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/spi-nor/otp.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index da05b03f28d2..136f245c91dc 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -spi-nor-objs := core.o sfdp.o swp.o +spi-nor-objs := core.o sfdp.o swp.o otp.o spi-nor-objs += atmel.o spi-nor-objs += catalyst.o spi-nor-objs += eon.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 5bc7ecfe7740..9e27ea727628 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2548,6 +2548,8 @@ static void spi_nor_info_init_params(struct spi_nor *nor) params->quad_enable = spi_nor_sr2_bit1_quad_enable; params->set_4byte_addr_mode = spansion_set_4byte_addr_mode; params->setup = spi_nor_default_setup; + params->otp.org = &info->otp_org; + /* Default to 16-bit Write Status (01h) Command */ nor->flags |= SNOR_F_HAS_16BIT_SR; @@ -3156,6 +3158,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (ret) return ret; + /* Configure OTP parameters and ops */ + spi_nor_otp_init(nor); + dev_info(dev, "%s (%lld Kbytes)\n", info->name, (long long)mtd->size >> 10); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 74f6026b7335..cfbc43c5cc57 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -187,6 +187,45 @@ struct spi_nor_locking_ops { int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); }; +/** + * struct spi_nor_otp_organization - Structure to describe the SPI NOR OTP regions + * @len: size of one OTP region in bytes. + * @base: start address of the OTP area. + * @offset: offset between consecutive OTP regions if there are more + * than one. + * @n_regions: number of individual OTP regions. + */ +struct spi_nor_otp_organization { + size_t len; + loff_t base; + loff_t offset; + unsigned int n_regions; +}; + +/** + * struct spi_nor_otp_ops - SPI NOR OTP methods + * @read: read from the SPI NOR OTP area. + * @write: write to the SPI NOR OTP area. + * @lock: lock an OTP region. + * @is_locked: check if an OTP region of the SPI NOR is locked. + */ +struct spi_nor_otp_ops { + int (*read)(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); + int (*write)(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); + int (*lock)(struct spi_nor *nor, unsigned int region); + int (*is_locked)(struct spi_nor *nor, unsigned int region); +}; + +/** + * struct spi_nor_otp - SPI NOR OTP grouping structure + * @org: OTP region organization + * @ops: OTP access ops + */ +struct spi_nor_otp { + const struct spi_nor_otp_organization *org; + const struct spi_nor_otp_ops *ops; +}; + /** * struct spi_nor_flash_parameter - SPI NOR flash parameters and settings. * Includes legacy flash parameters and settings that can be overwritten @@ -208,6 +247,7 @@ struct spi_nor_locking_ops { * higher index in the array, the higher priority. * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. + * @otp_info: describes the OTP regions. * @octal_dtr_enable: enables SPI NOR octal DTR mode. * @quad_enable: enables SPI NOR quad mode. * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. @@ -219,6 +259,7 @@ struct spi_nor_locking_ops { * e.g. different opcodes, specific address calculation, * page size, etc. * @locking_ops: SPI NOR locking methods. + * @otp: SPI NOR OTP methods. */ struct spi_nor_flash_parameter { u64 size; @@ -232,6 +273,7 @@ struct spi_nor_flash_parameter { struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX]; struct spi_nor_erase_map erase_map; + struct spi_nor_otp otp; int (*octal_dtr_enable)(struct spi_nor *nor, bool enable); int (*quad_enable)(struct spi_nor *nor); @@ -338,6 +380,8 @@ struct flash_info { * power-up in a write-protected state. */ + const struct spi_nor_otp_organization otp_org; + /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; }; @@ -392,6 +436,14 @@ struct flash_info { .addr_width = 3, \ .flags = SPI_NOR_NO_FR | SPI_NOR_XSR_RDY, +#define OTP_INFO(_len, _n_regions, _base, _offset) \ + .otp_org = { \ + .len = (_len), \ + .base = (_base), \ + .offset = (_offset), \ + .n_regions = (_n_regions), \ + }, + /** * struct spi_nor_manufacturer - SPI NOR manufacturer object * @name: manufacturer name @@ -474,6 +526,7 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor, void spi_nor_init_default_locking_ops(struct spi_nor *nor); void spi_nor_try_unlock_all(struct spi_nor *nor); void spi_nor_register_locking_ops(struct spi_nor *nor); +void spi_nor_otp_init(struct spi_nor *nor); static struct spi_nor __maybe_unused *mtd_to_spi_nor(struct mtd_info *mtd) { diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c new file mode 100644 index 000000000000..075b7290a95d --- /dev/null +++ b/drivers/mtd/spi-nor/otp.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OTP support for SPI NOR flashes + * + * Copyright (C) 2021 Michael Walle + */ + +#include +#include +#include + +#include "core.h" + +#define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len) +#define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions) + +static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, unsigned int region) +{ + const struct spi_nor_otp_organization *org = nor->params->otp.org; + + return org->base + region * org->offset; +} + +static size_t spi_nor_otp_size(struct spi_nor *nor) +{ + return spi_nor_otp_n_regions(nor) * spi_nor_otp_region_len(nor); +} + +/* Translate the file offsets from and to OTP regions. */ +static loff_t spi_nor_otp_region_to_offset(struct spi_nor *nor, unsigned int region) +{ + return region * spi_nor_otp_region_len(nor); +} + +static unsigned int spi_nor_otp_offset_to_region(struct spi_nor *nor, loff_t ofs) +{ + return div64_u64(ofs, spi_nor_otp_region_len(nor)); +} + +static int spi_nor_mtd_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + const struct spi_nor_otp_ops *ops = nor->params->otp.ops; + unsigned int n_regions = spi_nor_otp_n_regions(nor); + unsigned int i; + int ret, locked; + + if (len < n_regions * sizeof(*buf)) + return -ENOSPC; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + for (i = 0; i < n_regions; i++) { + buf->start = spi_nor_otp_region_to_offset(nor, i); + buf->length = spi_nor_otp_region_len(nor); + + locked = ops->is_locked(nor, i); + if (locked < 0) { + ret = locked; + goto out; + } + + buf->locked = !!locked; + buf++; + } + + *retlen = n_regions * sizeof(*buf); + +out: + spi_nor_unlock_and_unprep(nor); + + return ret; +} + +static int spi_nor_mtd_otp_read_write(struct mtd_info *mtd, loff_t ofs, + size_t total_len, size_t *retlen, + u8 *buf, bool is_write) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + const struct spi_nor_otp_ops *ops = nor->params->otp.ops; + const size_t rlen = spi_nor_otp_region_len(nor); + loff_t rstart, rofs; + unsigned int region; + size_t len; + int ret; + + if (ofs < 0 || ofs >= spi_nor_otp_size(nor)) + return 0; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + /* don't access beyond the end */ + total_len = min_t(size_t, total_len, spi_nor_otp_size(nor) - ofs); + + *retlen = 0; + while (total_len) { + /* + * The OTP regions are mapped into a contiguous area starting + * at 0 as expected by the MTD layer. This will map the MTD + * file offsets to the address of an OTP region as used in the + * actual SPI commands. + */ + region = spi_nor_otp_offset_to_region(nor, ofs); + rstart = spi_nor_otp_region_start(nor, region); + + /* + * The size of a OTP region is expected to be a power of two, + * thus we can just mask the lower bits and get the offset into + * a region. + */ + rofs = ofs & (rlen - 1); + + /* don't access beyond one OTP region */ + len = min_t(size_t, total_len, rlen - rofs); + + if (is_write) + ret = ops->write(nor, rstart + rofs, len, buf); + else + ret = ops->read(nor, rstart + rofs, len, buf); + if (ret == 0) + ret = -EIO; + if (ret < 0) + goto out; + + *retlen += ret; + ofs += ret; + buf += ret; + total_len -= ret; + } + ret = 0; + +out: + spi_nor_unlock_and_unprep(nor); + return ret; +} + +static int spi_nor_mtd_otp_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u8 *buf) +{ + return spi_nor_mtd_otp_read_write(mtd, from, len, retlen, buf, false); +} + +static int spi_nor_mtd_otp_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, u8 *buf) +{ + return spi_nor_mtd_otp_read_write(mtd, to, len, retlen, buf, true); +} + +static int spi_nor_mtd_otp_lock(struct mtd_info *mtd, loff_t from, size_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + const struct spi_nor_otp_ops *ops = nor->params->otp.ops; + const size_t rlen = spi_nor_otp_region_len(nor); + unsigned int region; + int ret; + + if (from < 0 || (from + len) > spi_nor_otp_size(nor)) + return -EINVAL; + + /* the user has to explicitly ask for whole regions */ + if (!IS_ALIGNED(len, rlen) || !IS_ALIGNED(from, rlen)) + return -EINVAL; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + while (len) { + region = spi_nor_otp_offset_to_region(nor, from); + ret = ops->lock(nor, region); + if (ret) + goto out; + + len -= rlen; + from += rlen; + } + +out: + spi_nor_unlock_and_unprep(nor); + + return ret; +} + +void spi_nor_otp_init(struct spi_nor *nor) +{ + struct mtd_info *mtd = &nor->mtd; + + if (!nor->params->otp.ops) + return; + + if (WARN_ON(!is_power_of_2(spi_nor_otp_region_len(nor)))) + return; + + /* + * We only support user_prot callbacks (yet). + * + * Some SPI NOR flashes like Macronix ones can be ordered in two + * different variants. One with a factory locked OTP area and one where + * it is left to the user to write to it. The factory locked OTP is + * usually preprogrammed with an "electrical serial number". We don't + * support these for now. + */ + mtd->_get_user_prot_info = spi_nor_mtd_otp_info; + mtd->_read_user_prot_reg = spi_nor_mtd_otp_read; + mtd->_write_user_prot_reg = spi_nor_mtd_otp_write; + mtd->_lock_user_prot_reg = spi_nor_mtd_otp_lock; +} -- cgit From cad3193fe9d1f0af4d05ed86693f99984409b188 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 22 Mar 2021 00:51:39 +0100 Subject: mtd: spi-nor: implement OTP support for Winbond and similar flashes Use the new OTP ops to implement OTP access on Winbond flashes. Most Winbond flashes provides up to four different OTP regions ("Security Registers"). Winbond devices use a special opcode to read and write to the OTP regions, just like the RDSFDP opcode. In fact, it seems that the (undocumented) first OTP area of the newer flashes is the actual SFDP table. On a side note, Winbond devices also allow erasing the OTP regions as long as the area isn't locked down. Signed-off-by: Michael Walle Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20210321235140.8308-3-michael@walle.cc --- drivers/mtd/spi-nor/core.c | 2 +- drivers/mtd/spi-nor/core.h | 6 ++ drivers/mtd/spi-nor/otp.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 9e27ea727628..8cf3cf92129e 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1034,7 +1034,7 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) +int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) { int ret; u8 *sr_cr = nor->bouncebuf; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index cfbc43c5cc57..e9b6b2e76cdb 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -495,6 +495,7 @@ int spi_nor_read_sr(struct spi_nor *nor, u8 *sr); int spi_nor_read_cr(struct spi_nor *nor, u8 *cr); int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len); int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1); +int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr); 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, @@ -502,6 +503,11 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, const u8 *buf); +int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); +int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region); +int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region); + int spi_nor_hwcaps_read2cmd(u32 hwcaps); u8 spi_nor_convert_3to4_read(u8 opcode); void spi_nor_set_read_settings(struct spi_nor_read_command *read, diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 075b7290a95d..5021d40dffbf 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -14,6 +14,170 @@ #define spi_nor_otp_region_len(nor) ((nor)->params->otp.org->len) #define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions) +/** + * spi_nor_otp_read_secr() - read OTP data + * @nor: pointer to 'struct spi_nor' + * @from: offset to read from + * @len: number of bytes to read + * @buf: pointer to dst buffer + * + * Read OTP data from one region by using the SPINOR_OP_RSECR commands. This + * method is used on GigaDevice and Winbond flashes. + * + * Return: number of bytes read successfully, -errno otherwise + */ +int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) +{ + u8 addr_width, read_opcode, read_dummy; + struct spi_mem_dirmap_desc *rdesc; + enum spi_nor_protocol read_proto; + int ret; + + read_opcode = nor->read_opcode; + addr_width = nor->addr_width; + read_dummy = nor->read_dummy; + read_proto = nor->read_proto; + rdesc = nor->dirmap.rdesc; + + nor->read_opcode = SPINOR_OP_RSECR; + nor->addr_width = 3; + nor->read_dummy = 8; + nor->read_proto = SNOR_PROTO_1_1_1; + nor->dirmap.rdesc = NULL; + + ret = spi_nor_read_data(nor, addr, len, buf); + + nor->read_opcode = read_opcode; + nor->addr_width = addr_width; + nor->read_dummy = read_dummy; + nor->read_proto = read_proto; + nor->dirmap.rdesc = rdesc; + + return ret; +} + +/** + * spi_nor_otp_write_secr() - write OTP data + * @nor: pointer to 'struct spi_nor' + * @to: offset to write to + * @len: number of bytes to write + * @buf: pointer to src buffer + * + * Write OTP data to one region by using the SPINOR_OP_PSECR commands. This + * method is used on GigaDevice and Winbond flashes. + * + * Please note, the write must not span multiple OTP regions. + * + * Return: number of bytes written successfully, -errno otherwise + */ +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) +{ + enum spi_nor_protocol write_proto; + struct spi_mem_dirmap_desc *wdesc; + u8 addr_width, program_opcode; + int ret, written; + + program_opcode = nor->program_opcode; + addr_width = nor->addr_width; + write_proto = nor->write_proto; + wdesc = nor->dirmap.wdesc; + + nor->program_opcode = SPINOR_OP_PSECR; + nor->addr_width = 3; + nor->write_proto = SNOR_PROTO_1_1_1; + nor->dirmap.wdesc = NULL; + + /* + * We only support a write to one single page. For now all winbond + * flashes only have one page per OTP region. + */ + ret = spi_nor_write_enable(nor); + if (ret) + goto out; + + written = spi_nor_write_data(nor, addr, len, buf); + if (written < 0) + goto out; + + ret = spi_nor_wait_till_ready(nor); + +out: + nor->program_opcode = program_opcode; + nor->addr_width = addr_width; + nor->write_proto = write_proto; + nor->dirmap.wdesc = wdesc; + + return ret ?: written; +} + +static int spi_nor_otp_lock_bit_cr(unsigned int region) +{ + static const int lock_bits[] = { SR2_LB1, SR2_LB2, SR2_LB3 }; + + if (region >= ARRAY_SIZE(lock_bits)) + return -EINVAL; + + return lock_bits[region]; +} + +/** + * spi_nor_otp_lock_sr2() - lock the OTP region + * @nor: pointer to 'struct spi_nor' + * @region: OTP region + * + * Lock the OTP region by writing the status register-2. This method is used on + * GigaDevice and Winbond flashes. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region) +{ + u8 *cr = nor->bouncebuf; + int ret, lock_bit; + + lock_bit = spi_nor_otp_lock_bit_cr(region); + if (lock_bit < 0) + return lock_bit; + + ret = spi_nor_read_cr(nor, cr); + if (ret) + return ret; + + /* no need to write the register if region is already locked */ + if (cr[0] & lock_bit) + return 0; + + cr[0] |= lock_bit; + + return spi_nor_write_16bit_cr_and_check(nor, cr[0]); +} + +/** + * spi_nor_otp_is_locked_sr2() - get the OTP region lock status + * @nor: pointer to 'struct spi_nor' + * @region: OTP region + * + * Retrieve the OTP region lock bit by reading the status register-2. This + * method is used on GigaDevice and Winbond flashes. + * + * Return: 0 on success, -errno otherwise. + */ +int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region) +{ + u8 *cr = nor->bouncebuf; + int ret, lock_bit; + + lock_bit = spi_nor_otp_lock_bit_cr(region); + if (lock_bit < 0) + return lock_bit; + + ret = spi_nor_read_cr(nor, cr); + if (ret) + return ret; + + return cr[0] & lock_bit; +} + static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, unsigned int region) { const struct spi_nor_otp_organization *org = nor->params->otp.org; -- cgit From b206b82d1726f6f878891791069ab0aea2e31113 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 22 Mar 2021 00:51:40 +0100 Subject: mtd: spi-nor: winbond: add OTP support to w25q32fw/jw With all the helper functions in place, add OTP support for the Winbond W25Q32JW and W25Q32FW. Both were tested on a LS1028A SoC with a NXP FSPI controller. Signed-off-by: Michael Walle Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20210321235140.8308-4-michael@walle.cc --- drivers/mtd/spi-nor/winbond.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 20b2c6f19d6f..9a81c67a60c6 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -54,14 +54,18 @@ static const struct flash_info winbond_parts[] = { { "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) }, + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + OTP_INFO(256, 3, 0x1000, 0x1000) + }, + { "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) }, + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + OTP_INFO(256, 3, 0x1000, 0x1000) }, { "w25q64jwm", INFO(0xef8017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, @@ -132,9 +136,18 @@ static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) return spi_nor_write_disable(nor); } +static const struct spi_nor_otp_ops winbond_otp_ops = { + .read = spi_nor_otp_read_secr, + .write = spi_nor_otp_write_secr, + .lock = spi_nor_otp_lock_sr2, + .is_locked = spi_nor_otp_is_locked_sr2, +}; + static void winbond_default_init(struct spi_nor *nor) { nor->params->set_4byte_addr_mode = winbond_set_4byte_addr_mode; + if (nor->params->otp.org->n_regions) + nor->params->otp.ops = &winbond_otp_ops; } static const struct spi_nor_fixups winbond_fixups = { -- cgit From 13b89768275d6ca9764bf91449e4cafe46ba706b Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Apr 2021 20:31:27 +0530 Subject: mtd: rawnand: Add support for secure regions in NAND memory On a typical end product, a vendor may choose to secure some regions in the NAND memory which are supposed to stay intact between FW upgrades. The access to those regions will be blocked by a secure element like Trustzone. So the normal world software like Linux kernel should not touch these regions (including reading). The regions are declared using a NAND chip DT property, "secure-regions". So let's make use of this property in the raw NAND core and skip access to the secure regions present in a system. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210402150128.29128-4-manivannan.sadhasivam@linaro.org --- drivers/mtd/nand/raw/nand_base.c | 100 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a984cda86e2d..fb072c444495 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -278,11 +278,48 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs) return 0; } +/** + * nand_region_is_secured() - Check if the region is secured + * @chip: NAND chip object + * @offset: Offset of the region to check + * @size: Size of the region to check + * + * Checks if the region is secured by comparing the offset and size with the + * list of secure regions obtained from DT. Returns true if the region is + * secured else false. + */ +static bool nand_region_is_secured(struct nand_chip *chip, loff_t offset, u64 size) +{ + int i; + + /* Skip touching the secure regions if present */ + for (i = 0; i < chip->nr_secure_regions; i++) { + const struct nand_secure_region *region = &chip->secure_regions[i]; + + if (offset + size <= region->offset || + offset >= region->offset + region->size) + continue; + + pr_debug("%s: Region 0x%llx - 0x%llx is secured!", + __func__, offset, offset + size); + + return true; + } + + return false; +} + static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs) { + struct mtd_info *mtd = nand_to_mtd(chip); + if (chip->options & NAND_NO_BBM_QUIRK) return 0; + /* Check if the region is secured */ + if (nand_region_is_secured(chip, ofs, mtd->erasesize)) + return -EIO; + if (chip->legacy.block_bad) return chip->legacy.block_bad(chip, ofs); @@ -397,6 +434,10 @@ static int nand_do_write_oob(struct nand_chip *chip, loff_t to, return -EINVAL; } + /* Check if the region is secured */ + if (nand_region_is_secured(chip, to, ops->ooblen)) + return -EIO; + chipnr = (int)(to >> chip->chip_shift); /* @@ -3128,6 +3169,10 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from, int retry_mode = 0; bool ecc_fail = false; + /* Check if the region is secured */ + if (nand_region_is_secured(chip, from, readlen)) + return -EIO; + chipnr = (int)(from >> chip->chip_shift); nand_select_target(chip, chipnr); @@ -3459,6 +3504,10 @@ static int nand_do_read_oob(struct nand_chip *chip, loff_t from, pr_debug("%s: from = 0x%08Lx, len = %i\n", __func__, (unsigned long long)from, readlen); + /* Check if the region is secured */ + if (nand_region_is_secured(chip, from, readlen)) + return -EIO; + stats = mtd->ecc_stats; len = mtd_oobavail(mtd, ops); @@ -3980,6 +4029,10 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to, return -EINVAL; } + /* Check if the region is secured */ + if (nand_region_is_secured(chip, to, writelen)) + return -EIO; + column = to & (mtd->writesize - 1); chipnr = (int)(to >> chip->chip_shift); @@ -4181,6 +4234,10 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, if (check_offs_len(chip, instr->addr, instr->len)) return -EINVAL; + /* Check if the region is secured */ + if (nand_region_is_secured(chip, instr->addr, instr->len)) + return -EIO; + /* Grab the lock and see if the device is available */ ret = nand_get_device(chip); if (ret) @@ -4996,6 +5053,31 @@ static bool of_get_nand_on_flash_bbt(struct device_node *np) return of_property_read_bool(np, "nand-on-flash-bbt"); } +static int of_get_nand_secure_regions(struct nand_chip *chip) +{ + struct device_node *dn = nand_get_flash_node(chip); + int nr_elem, i, j; + + nr_elem = of_property_count_elems_of_size(dn, "secure-regions", sizeof(u64)); + if (!nr_elem) + return 0; + + chip->nr_secure_regions = nr_elem / 2; + chip->secure_regions = kcalloc(chip->nr_secure_regions, sizeof(*chip->secure_regions), + GFP_KERNEL); + if (!chip->secure_regions) + return -ENOMEM; + + for (i = 0, j = 0; i < chip->nr_secure_regions; i++, j += 2) { + of_property_read_u64_index(dn, "secure-regions", j, + &chip->secure_regions[i].offset); + of_property_read_u64_index(dn, "secure-regions", j + 1, + &chip->secure_regions[i].size); + } + + return 0; +} + static int rawnand_dt_init(struct nand_chip *chip) { struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip)); @@ -5952,6 +6034,16 @@ static int nand_scan_tail(struct nand_chip *chip) goto err_free_interface_config; } + /* + * Look for secure regions in the NAND chip. These regions are supposed + * to be protected by a secure element like Trustzone. So the read/write + * accesses to these regions will be blocked in the runtime by this + * driver. + */ + ret = of_get_nand_secure_regions(chip); + if (ret) + goto err_free_interface_config; + /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) return 0; @@ -5959,10 +6051,13 @@ static int nand_scan_tail(struct nand_chip *chip) /* Build bad block table */ ret = nand_create_bbt(chip); if (ret) - goto err_free_interface_config; + goto err_free_secure_regions; return 0; +err_free_secure_regions: + kfree(chip->secure_regions); + err_free_interface_config: kfree(chip->best_interface_config); @@ -6050,6 +6145,9 @@ void nand_cleanup(struct nand_chip *chip) nanddev_cleanup(&chip->base); + /* Free secure regions data */ + kfree(chip->secure_regions); + /* Free bad block table memory */ kfree(chip->bbt); kfree(chip->data_buf); -- cgit From ab2c8d3ef9b828a1eb1a7d448185bf4242bb0afd Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Apr 2021 20:31:28 +0530 Subject: mtd: rawnand: qcom: Add missing nand_cleanup() in error path Add missing nand_cleanup() in the alloc_bam_transaction() error path to cleanup the resources properly. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210402150128.29128-5-manivannan.sadhasivam@linaro.org --- drivers/mtd/nand/raw/qcom_nandc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index b9194680cd3c..1fc5ec1482cb 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2943,6 +2943,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, if (!nandc->bam_txn) { dev_err(nandc->dev, "failed to allocate bam transaction\n"); + nand_cleanup(chip); return -ENOMEM; } } -- cgit From 076de75de1e53160e9b099f75872c1f9adf41a0b Mon Sep 17 00:00:00 2001 From: Lv Yunlong Date: Fri, 2 Apr 2021 23:09:05 -0700 Subject: mtd: rawnand: gpmi: Fix a double free in gpmi_nand_init If the callee gpmi_alloc_dma_buffer() failed to alloc memory for this->raw_buffer, gpmi_free_dma_buffer() will be called to free this->auxiliary_virt. But this->auxiliary_virt is still a non-NULL and valid ptr. Then gpmi_alloc_dma_buffer() returns err and gpmi_free_dma_buffer() is called again to free this->auxiliary_virt in err_out. This causes a double free. As gpmi_free_dma_buffer() has already called in gpmi_alloc_dma_buffer's error path, so it should return err directly instead of releasing the dma buffer again. Fixes: 4d02423e9afe6 ("mtd: nand: gpmi: Fix gpmi_nand_init() error path") Signed-off-by: Lv Yunlong Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210403060905.5251-1-lyl2019@mail.ustc.edu.cn --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 3fa8c22d3f36..4d08e4ab5c1b 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -2449,7 +2449,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this) this->bch_geometry.auxiliary_size = 128; ret = gpmi_alloc_dma_buffer(this); if (ret) - goto err_out; + return ret; nand_controller_init(&this->base); this->base.ops = &gpmi_nand_controller_ops; -- cgit From 32cbc7cb70b07041e82f897f96b3035358470b14 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 5 Apr 2021 10:39:12 +0530 Subject: mtd: rawnand: qcom: Use dma_mapping_error() for error check dma_mapping_error() should be used for checking the error value of dma_map_resource() API. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210405050912.115591-1-manivannan.sadhasivam@linaro.org --- drivers/mtd/nand/raw/qcom_nandc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 1fc5ec1482cb..a64fb6ce915d 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -3051,7 +3051,7 @@ static int qcom_nandc_probe(struct platform_device *pdev) nandc->base_dma = dma_map_resource(dev, res->start, resource_size(res), DMA_BIDIRECTIONAL, 0); - if (!nandc->base_dma) + if (dma_mapping_error(dev, nandc->base_dma)) return -ENXIO; ret = qcom_nandc_alloc(nandc); -- cgit From 7e4404113686868858a34210c28ae122e967aa64 Mon Sep 17 00:00:00 2001 From: Mauri Sandberg Date: Tue, 9 Mar 2021 19:48:59 +0200 Subject: mtd: cfi_cmdset_0002: Disable buffered writes for AMD chip 0x2201 Buffer writes do not work with AMD chip 0x2201. The chip in question is a AMD/Spansion/Cypress Semiconductor S29GL256N and datasheet [1] talks about writing buffers being possible. While waiting for a neater solution resort to writing word-sized chunks only. Without the patch kernel logs will be flooded with entries like below: jffs2_scan_eraseblock(): End of filesystem marker found at 0x0 jffs2_build_filesystem(): unlocking the mtd device... done. jffs2_build_filesystem(): erasing all blocks after the end marker... MTD do_write_buffer_wait(): software timeout, address:0x01ec000a. jffs2: Write clean marker to block at 0x01920000 failed: -5 MTD do_write_buffer_wait(): software timeout, address:0x01e2000a. jffs2: Write clean marker to block at 0x01880000 failed: -5 MTD do_write_buffer_wait(): software timeout, address:0x01e0000a. jffs2: Write clean marker to block at 0x01860000 failed: -5 MTD do_write_buffer_wait(): software timeout, address:0x01dc000a. jffs2: Write clean marker to block at 0x01820000 failed: -5 MTD do_write_buffer_wait(): software timeout, address:0x01da000a. jffs2: Write clean marker to block at 0x01800000 failed: -5 ... Tested on a Buffalo wzr-hp-g300nh running kernel 5.10.16. [1] https://www.cypress.com/file/219941/download or https://datasheetspdf.com/pdf-file/565708/SPANSION/S29GL256N/1 Signed-off-by: Mauri Sandberg Signed-off-by: Vignesh Raghavendra Link: https://lore.kernel.org/r/20210309174859.362060-1-sandberg@mailfence.com --- drivers/mtd/chips/cfi_cmdset_0002.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index a1f3e1031c3d..0bd8b5af28c6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -272,6 +272,10 @@ static void fixup_use_write_buffers(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + + if (cfi->mfr == CFI_MFR_AMD && cfi->id == 0x2201) + return; + if (cfi->cfiq->BufWriteTimeoutTyp) { pr_debug("Using buffer write method\n"); mtd->_write = cfi_amdstd_write_buffers; -- cgit From f3907773d60229afa8e6c0a3ee5085715192a9cb Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 25 Mar 2021 17:45:14 +0000 Subject: mtd: cfi_cmdset_0002: remove redundant assignment to variable timeo The variable timeo is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Signed-off-by: Colin Ian King Signed-off-by: Vignesh Raghavendra Link: https://lore.kernel.org/r/20210325174514.486272-1-colin.king@canonical.com Addresses-Coverity: ("Unused value") --- drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 0bd8b5af28c6..627adc320913 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1653,7 +1653,7 @@ static int __xipram do_write_oneword_once(struct map_info *map, unsigned long adr, map_word datum, int mode, struct cfi_private *cfi) { - unsigned long timeo = jiffies + HZ; + unsigned long timeo; /* * We use a 1ms + 1 jiffies generic timeout for writes (most devices * have a max write time of a few hundreds usec). However, we should -- cgit From be94215be1ab19e5d38f50962f611c88d4bfc83a Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Thu, 1 Apr 2021 15:34:46 +0800 Subject: mtd: spi-nor: core: Fix an issue of releasing resources during read/write If rmmod the driver during read or write, the driver will release the resources which are used during read or write, so it is possible to refer to NULL pointer. Use the testcase "mtd_debug read /dev/mtd0 0xc00000 0x400000 dest_file & sleep 0.5;rmmod spi_hisi_sfc_v3xx.ko", the issue can be reproduced in hisi_sfc_v3xx driver. To avoid the issue, fill the interface _get_device and _put_device of mtd_info to grab the reference to the spi controller driver module, so the request of rmmod the driver is rejected before read/write is finished. Fixes: b199489d37b2 ("mtd: spi-nor: add the framework for SPI NOR") Signed-off-by: Xiang Chen Signed-off-by: Yicong Yang Signed-off-by: Tudor Ambarus Tested-by: Michael Walle Tested-by: Tudor Ambarus Reviewed-by: Michael Walle Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/1617262486-4223-1-git-send-email-yangyicong@hisilicon.com --- drivers/mtd/spi-nor/core.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 8cf3cf92129e..bd2c7717eb10 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2905,6 +2905,37 @@ static void spi_nor_resume(struct mtd_info *mtd) dev_err(dev, "resume() failed\n"); } +static int spi_nor_get_device(struct mtd_info *mtd) +{ + struct mtd_info *master = mtd_get_master(mtd); + struct spi_nor *nor = mtd_to_spi_nor(master); + struct device *dev; + + if (nor->spimem) + dev = nor->spimem->spi->controller->dev.parent; + else + dev = nor->dev; + + if (!try_module_get(dev->driver->owner)) + return -ENODEV; + + return 0; +} + +static void spi_nor_put_device(struct mtd_info *mtd) +{ + struct mtd_info *master = mtd_get_master(mtd); + struct spi_nor *nor = mtd_to_spi_nor(master); + struct device *dev; + + if (nor->spimem) + dev = nor->spimem->spi->controller->dev.parent; + else + dev = nor->dev; + + module_put(dev->driver->owner); +} + void spi_nor_restore(struct spi_nor *nor) { /* restore the addressing mode */ @@ -3099,6 +3130,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->_read = spi_nor_read; mtd->_suspend = spi_nor_suspend; mtd->_resume = spi_nor_resume; + mtd->_get_device = spi_nor_get_device; + mtd->_put_device = spi_nor_put_device; if (info->flags & USE_FSR) nor->flags |= SNOR_F_USE_FSR; -- cgit From 46094049a49be777f12a9589798f7c70b90cd03f Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 2 Apr 2021 11:20:30 +0300 Subject: Revert "mtd: spi-nor: macronix: Add support for mx25l51245g" This reverts commit 04b8edad262eec0d153005973dfbdd83423c0dcb. mx25l51245g and mx66l51235l have the same flash ID. The flash detection returns the first entry in the flash_info array that matches the flash ID that was read, thus for the 0xc2201a ID, mx25l51245g was always hit, introducing a regression for mx66l51235l. If one wants to differentiate the flash names, a better fix would be to differentiate between the two at run-time, depending on SFDP, and choose the correct name from a list of flash names, depending on the SFDP differentiator. Fixes: 04b8edad262e ("mtd: spi-nor: macronix: Add support for mx25l51245g") Signed-off-by: Tudor Ambarus Acked-by: Pratyush Yadav Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20210402082031.19055-2-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/macronix.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index 6c2680b4cdad..42c2cf31702e 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -72,9 +72,6 @@ static const struct flash_info macronix_parts[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx25l51245g", INFO(0xc2201a, 0, 64 * 1024, 1024, - SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES) }, { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, -- cgit From 1df1fc8c62f7527d953c7f3869930067bf5b3f29 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 3 Apr 2021 09:09:31 +0300 Subject: mtd: core: Constify buf in mtd_write_user_prot_reg() The write buffer comes from user and should be const. Constify write buffer in mtd core and across all _write_user_prot_reg() users. cfi_cmdset_{0001, 0002} and onenand_base will pay the cost of an explicit cast to discard the const qualifier since the beginning, since they are using an otp_op_t function prototype that is used for both reads and writes. mtd_dataflash and SPI NOR will benefit of the const buffer because they are using different paths for writes and reads. Signed-off-by: Tudor Ambarus Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210403060931.7119-1-tudor.ambarus@microchip.com --- drivers/mtd/chips/cfi_cmdset_0001.c | 7 ++++--- drivers/mtd/chips/cfi_cmdset_0002.c | 6 +++--- drivers/mtd/devices/mtd_dataflash.c | 2 +- drivers/mtd/mtdcore.c | 2 +- drivers/mtd/nand/onenand/onenand_base.c | 5 +++-- drivers/mtd/spi-nor/core.h | 6 ++++-- drivers/mtd/spi-nor/otp.c | 9 +++++---- 7 files changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index b7f5e7977dcd..54f92d09d9cf 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -72,7 +72,8 @@ static int cfi_intelext_is_locked(struct mtd_info *mtd, loff_t ofs, #ifdef CONFIG_MTD_OTP static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_write_user_prot_reg(struct mtd_info *, loff_t, size_t, + size_t *, const u_char *); static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); static int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t, size_t *, struct otp_info *); @@ -2447,10 +2448,10 @@ static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, - u_char *buf) + const u_char *buf) { return cfi_intelext_otp_walk(mtd, from, len, retlen, - buf, do_otp_write, 1); + (u_char *)buf, do_otp_write, 1); } static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index c5c9a4c3b027..3097e93787f7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -80,7 +80,7 @@ static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t, static int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t, - size_t *, u_char *); + size_t *, const u_char *); static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *, loff_t, size_t); static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, @@ -1635,9 +1635,9 @@ static int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, static int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, - u_char *buf) + const u_char *buf) { - return cfi_amdstd_otp_walk(mtd, from, len, retlen, buf, + return cfi_amdstd_otp_walk(mtd, from, len, retlen, (u_char *)buf, do_otp_write, 1); } diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 6d1eefe94106..9802e265fca8 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -527,7 +527,7 @@ static int dataflash_read_user_otp(struct mtd_info *mtd, } static int dataflash_write_user_otp(struct mtd_info *mtd, - loff_t from, size_t len, size_t *retlen, u_char *buf) + loff_t from, size_t len, size_t *retlen, const u_char *buf) { struct spi_message m; const size_t l = 4 + 64; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 0bc6871c3863..9aaeadd53eb4 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1889,7 +1889,7 @@ int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, 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) + size_t *retlen, const u_char *buf) { struct mtd_info *master = mtd_get_master(mtd); int ret; diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index a9fdea26ea46..958bac54b190 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -3167,9 +3167,10 @@ static int onenand_read_user_prot_reg(struct mtd_info *mtd, loff_t from, * Write user OTP area. */ static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) + size_t len, size_t *retlen, const u_char *buf) { - return onenand_otp_walk(mtd, from, len, retlen, buf, do_otp_write, MTD_OTP_USER); + return onenand_otp_walk(mtd, from, len, retlen, (u_char *)buf, + do_otp_write, MTD_OTP_USER); } /** diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index e9b6b2e76cdb..28a2e0be97a3 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -211,7 +211,8 @@ struct spi_nor_otp_organization { */ struct spi_nor_otp_ops { int (*read)(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); - int (*write)(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); + int (*write)(struct spi_nor *nor, loff_t addr, size_t len, + const u8 *buf); int (*lock)(struct spi_nor *nor, unsigned int region); int (*is_locked)(struct spi_nor *nor, unsigned int region); }; @@ -504,7 +505,8 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, const u8 *buf); int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); -int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, + const u8 *buf); int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region); int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region); diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 5021d40dffbf..fcf38d260345 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -70,7 +70,8 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) * * Return: number of bytes written successfully, -errno otherwise */ -int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) +int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, + const u8 *buf) { enum spi_nor_protocol write_proto; struct spi_mem_dirmap_desc *wdesc; @@ -241,7 +242,7 @@ out: static int spi_nor_mtd_otp_read_write(struct mtd_info *mtd, loff_t ofs, size_t total_len, size_t *retlen, - u8 *buf, bool is_write) + const u8 *buf, bool is_write) { struct spi_nor *nor = mtd_to_spi_nor(mtd); const struct spi_nor_otp_ops *ops = nor->params->otp.ops; @@ -285,7 +286,7 @@ static int spi_nor_mtd_otp_read_write(struct mtd_info *mtd, loff_t ofs, if (is_write) ret = ops->write(nor, rstart + rofs, len, buf); else - ret = ops->read(nor, rstart + rofs, len, buf); + ret = ops->read(nor, rstart + rofs, len, (u8 *)buf); if (ret == 0) ret = -EIO; if (ret < 0) @@ -310,7 +311,7 @@ static int spi_nor_mtd_otp_read(struct mtd_info *mtd, loff_t from, size_t len, } static int spi_nor_mtd_otp_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, u8 *buf) + size_t *retlen, const u8 *buf) { return spi_nor_mtd_otp_read_write(mtd, to, len, retlen, buf, true); } -- cgit From a881537dfaf281bfcb94313d69dcf9ef8fc89afe Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 19 Apr 2021 11:03:50 -0300 Subject: Revert "mtd: rawnand: bbt: Skip bad blocks when searching for the BBT in NAND" This reverts commit bd9c9fe2ad04546940f4a9979d679e62cae6aa51. Since commit bd9c9fe2ad04 ("mtd: rawnand: bbt: Skip bad blocks when searching for the BBT in NAND") the bad block table cannot be found on a imx27-phytec-phycard-s-rdk board: Bad block table not found for chip 0 Bad block table not found for chip 0 Revert it for now, until a better solution can be found. Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20210419140350.809853-1-festevam@gmail.com --- drivers/mtd/nand/raw/nand_bbt.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index 6e25a5ce5ba9..dced32a126d9 100644 --- a/drivers/mtd/nand/raw/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -525,7 +525,6 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf, { u64 targetsize = nanddev_target_size(&this->base); struct mtd_info *mtd = nand_to_mtd(this); - struct nand_bbt_descr *bd = this->badblock_pattern; int i, chips; int startblock, block, dir; int scanlen = mtd->writesize + mtd->oobsize; @@ -561,10 +560,6 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf, int actblock = startblock + dir * block; loff_t offs = (loff_t)actblock << this->bbt_erase_shift; - /* Check if block is marked bad */ - if (scan_block_fast(this, bd, offs, buf)) - continue; - /* Read first page */ scan_read(this, buf, offs, mtd->writesize, td); if (!check_pattern(buf, scanlen, mtd->writesize, td)) { -- cgit