From 215283a1a4833f441778580359aea768642c56af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Nov 2023 23:02:48 +0100 Subject: mtd: rawnand: brcmnand: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). By changing the function brcmnand_remove() to return void several drivers that use this function as remove callback can be converted to .remove_new(). Signed-off-by: Uwe Kleine-König Reviewed-by: Florian Fainelli Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231102220246.3336154-7-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c | 2 +- drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c | 2 +- drivers/mtd/nand/raw/brcmnand/bcma_nand.c | 2 +- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 +--- drivers/mtd/nand/raw/brcmnand/brcmnand.h | 2 +- drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c | 2 +- drivers/mtd/nand/raw/brcmnand/iproc_nand.c | 2 +- 7 files changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c index 9596629000f4..968c5b674b08 100644 --- a/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c @@ -85,7 +85,7 @@ MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match); static struct platform_driver bcm63138_nand_driver = { .probe = bcm63138_nand_probe, - .remove = brcmnand_remove, + .remove_new = brcmnand_remove, .driver = { .name = "bcm63138_nand", .pm = &brcmnand_pm_ops, diff --git a/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c index a06cd87f839a..05b7b653bdf3 100644 --- a/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/bcm6368_nand.c @@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, bcm6368_nand_of_match); static struct platform_driver bcm6368_nand_driver = { .probe = bcm6368_nand_probe, - .remove = brcmnand_remove, + .remove_new = brcmnand_remove, .driver = { .name = "bcm6368_nand", .pm = &brcmnand_pm_ops, diff --git a/drivers/mtd/nand/raw/brcmnand/bcma_nand.c b/drivers/mtd/nand/raw/brcmnand/bcma_nand.c index dd27977919fb..4e7e435ba339 100644 --- a/drivers/mtd/nand/raw/brcmnand/bcma_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/bcma_nand.c @@ -119,7 +119,7 @@ static int brcmnand_bcma_nand_probe(struct platform_device *pdev) static struct platform_driver brcmnand_bcma_nand_driver = { .probe = brcmnand_bcma_nand_probe, - .remove = brcmnand_remove, + .remove_new = brcmnand_remove, .driver = { .name = "bcma_brcmnand", .pm = &brcmnand_pm_ops, diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 440bef477930..30fc399f346e 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -3299,7 +3299,7 @@ err: } EXPORT_SYMBOL_GPL(brcmnand_probe); -int brcmnand_remove(struct platform_device *pdev) +void brcmnand_remove(struct platform_device *pdev) { struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev); struct brcmnand_host *host; @@ -3316,8 +3316,6 @@ int brcmnand_remove(struct platform_device *pdev) clk_disable_unprepare(ctrl->clk); dev_set_drvdata(&pdev->dev, NULL); - - return 0; } EXPORT_SYMBOL_GPL(brcmnand_remove); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.h b/drivers/mtd/nand/raw/brcmnand/brcmnand.h index f1f93d85f50d..928114c0be5e 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.h +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.h @@ -88,7 +88,7 @@ static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val, } int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc); -int brcmnand_remove(struct platform_device *pdev); +void brcmnand_remove(struct platform_device *pdev); extern const struct dev_pm_ops brcmnand_pm_ops; diff --git a/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c index 950923d977b7..558f083b92e9 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmstb_nand.c @@ -23,7 +23,7 @@ static int brcmstb_nand_probe(struct platform_device *pdev) static struct platform_driver brcmstb_nand_driver = { .probe = brcmstb_nand_probe, - .remove = brcmnand_remove, + .remove_new = brcmnand_remove, .driver = { .name = "brcmstb_nand", .pm = &brcmnand_pm_ops, diff --git a/drivers/mtd/nand/raw/brcmnand/iproc_nand.c b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c index 089c70fc6edf..bf46c8b85898 100644 --- a/drivers/mtd/nand/raw/brcmnand/iproc_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c @@ -134,7 +134,7 @@ MODULE_DEVICE_TABLE(of, iproc_nand_of_match); static struct platform_driver iproc_nand_driver = { .probe = iproc_nand_probe, - .remove = brcmnand_remove, + .remove_new = brcmnand_remove, .driver = { .name = "iproc_nand", .pm = &brcmnand_pm_ops, -- cgit v1.2.3 From 160c0b7f9a166d62a3aa32853883666eda896c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Nov 2023 23:02:49 +0100 Subject: mtd: rawnand: txx9ndfmc: Switch to module_platform_driver() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While module_platform_driver_probe() offers the possibility to discard .probe() and .remove() in some situations, the handling is difficult and in today's systems the few hundred bytes that can be saved have little importance. So convert the driver to be a normal driver that can be bound and unbound at runtime as most other drivers, too. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231102220246.3336154-8-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/txx9ndfmc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index eddcc0728a67..9d6c62f73bb7 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -276,7 +276,7 @@ static const struct nand_controller_ops txx9ndfmc_controller_ops = { .attach_chip = txx9ndfmc_attach_chip, }; -static int __init txx9ndfmc_probe(struct platform_device *dev) +static int txx9ndfmc_probe(struct platform_device *dev) { struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); int hold, spw; @@ -369,7 +369,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) return 0; } -static int __exit txx9ndfmc_remove(struct platform_device *dev) +static int txx9ndfmc_remove(struct platform_device *dev) { struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int ret, i; @@ -407,14 +407,14 @@ static int txx9ndfmc_resume(struct platform_device *dev) #endif static struct platform_driver txx9ndfmc_driver = { - .remove = __exit_p(txx9ndfmc_remove), + .probe = txx9ndfmc_probe, + .remove = txx9ndfmc_remove, .resume = txx9ndfmc_resume, .driver = { .name = "txx9ndfmc", }, }; - -module_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe); +module_platform_driver(txx9ndfmc_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); -- cgit v1.2.3 From 354dbdcbdd79ec417f6c35f55b1fe6310e7dd431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Nov 2023 23:02:50 +0100 Subject: mtd: rawnand: txx9ndfmc: Drop if block with always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit txx9ndfmc_remove() is only called after txx9ndfmc_probe() completed successfully. In this case platform_set_drvdata() was called with a non-NULL argument and so platform_get_drvdata() won't return NULL. Simplify by removing the if block with the always false condition. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231102220246.3336154-9-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/txx9ndfmc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 9d6c62f73bb7..fdcdfbea0cbd 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -374,8 +374,6 @@ static int txx9ndfmc_remove(struct platform_device *dev) struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int ret, i; - if (!drvdata) - return 0; for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { struct mtd_info *mtd = drvdata->mtds[i]; struct nand_chip *chip; -- cgit v1.2.3 From f52221d55d8dae7c4154116453d5fb6c544bae46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 2 Nov 2023 23:02:51 +0100 Subject: mtd: rawnand: txx9ndfmc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new(), which already returns void. Eventually after all drivers are converted, .remove_new() will be renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231102220246.3336154-10-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/txx9ndfmc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index fdcdfbea0cbd..37f79c019a72 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -369,7 +369,7 @@ static int txx9ndfmc_probe(struct platform_device *dev) return 0; } -static int txx9ndfmc_remove(struct platform_device *dev) +static void txx9ndfmc_remove(struct platform_device *dev) { struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int ret, i; @@ -390,7 +390,6 @@ static int txx9ndfmc_remove(struct platform_device *dev) kfree(txx9_priv->mtdname); kfree(txx9_priv); } - return 0; } #ifdef CONFIG_PM @@ -406,7 +405,7 @@ static int txx9ndfmc_resume(struct platform_device *dev) static struct platform_driver txx9ndfmc_driver = { .probe = txx9ndfmc_probe, - .remove = txx9ndfmc_remove, + .remove_new = txx9ndfmc_remove, .resume = txx9ndfmc_resume, .driver = { .name = "txx9ndfmc", -- cgit v1.2.3 From 075ede8d20f8f201900756af55c58994c6660659 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Wed, 1 Nov 2023 14:58:48 +0000 Subject: mtd: spi-nor: use kernel sized types instead of c99 types The kernel offers and prefers the kernel sized types instead of the c99 types when not in the uapi directory, use them. Link: https://lore.kernel.org/r/20231101145853.524045-2-tudor.ambarus@linaro.org Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/atmel.c | 16 +++++++--------- drivers/mtd/spi-nor/core.c | 5 ++--- drivers/mtd/spi-nor/core.h | 6 +++--- drivers/mtd/spi-nor/sst.c | 6 +++--- drivers/mtd/spi-nor/swp.c | 25 ++++++++++++------------- 5 files changed, 27 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index e13b8d2dd50a..45d1153a04a0 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -16,12 +16,12 @@ * is to unlock the whole flash array on startup. Therefore, we have to support * exactly this operation. */ -static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len) { return -EOPNOTSUPP; } -static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { int ret; @@ -37,7 +37,7 @@ static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) return ret; } -static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) { return -EOPNOTSUPP; } @@ -69,7 +69,7 @@ static const struct spi_nor_fixups at25fs_nor_fixups = { * Return: 0 on success, -error otherwise. */ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, - uint64_t len, bool is_protect) + u64 len, bool is_protect) { int ret; u8 sr; @@ -118,20 +118,18 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, return spi_nor_write_sr(nor, nor->bouncebuf, 1); } -static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, - uint64_t len) +static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len) { return atmel_nor_set_global_protection(nor, ofs, len, true); } -static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, - uint64_t len) +static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len) { return atmel_nor_set_global_protection(nor, ofs, len, false); } static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs, - uint64_t len) + u64 len) { int ret; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 1c443fe568cf..25a64c65717d 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1799,8 +1799,7 @@ destroy_erase_cmd_list: static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) { struct spi_nor *nor = mtd_to_spi_nor(mtd); - u32 addr, len; - uint32_t rem; + u32 addr, len, rem; int ret; dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, @@ -2146,7 +2145,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, if (is_power_of_2(page_size)) { page_offset = addr & (page_size - 1); } else { - uint64_t aux = addr; + u64 aux = addr; page_offset = do_div(aux, page_size); } diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 93cd2fc3606d..a456042379ee 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -293,9 +293,9 @@ struct spi_nor_erase_map { * @is_locked: check if a region of the SPI NOR is completely locked */ struct spi_nor_locking_ops { - int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); - int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); - int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); + int (*lock)(struct spi_nor *nor, loff_t ofs, u64 len); + int (*unlock)(struct spi_nor *nor, loff_t ofs, u64 len); + int (*is_locked)(struct spi_nor *nor, loff_t ofs, u64 len); }; /** diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index 44d2a546bf17..180b7390690c 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -13,12 +13,12 @@ #define SST26VF_CR_BPNV BIT(3) -static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len) { return -EOPNOTSUPP; } -static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { int ret; @@ -38,7 +38,7 @@ static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) return spi_nor_global_block_unlock(nor); } -static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) { return -EOPNOTSUPP; } diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 585813310ee1..e48c3cff247a 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -53,7 +53,7 @@ static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) } static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, - uint64_t *len) + u64 *len) { struct mtd_info *mtd = &nor->mtd; u64 min_prot_len; @@ -90,10 +90,10 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, * (if @locked is false); false otherwise. */ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, - uint64_t len, u8 sr, bool locked) + u64 len, u8 sr, bool locked) { loff_t lock_offs, lock_offs_max, offs_max; - uint64_t lock_len; + u64 lock_len; if (!len) return true; @@ -111,14 +111,13 @@ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, return (ofs >= lock_offs_max) || (offs_max <= lock_offs); } -static bool 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, u64 len, u8 sr) { return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); } -static bool 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, u64 len, + u8 sr) { return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); } @@ -156,7 +155,7 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, * * Returns negative on errors, 0 on success. */ -static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len) { struct mtd_info *mtd = &nor->mtd; u64 min_prot_len; @@ -246,7 +245,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) * * Returns negative on errors, 0 on success. */ -static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len) { struct mtd_info *mtd = &nor->mtd; u64 min_prot_len; @@ -331,7 +330,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) * 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) +static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, u64 len) { int ret; @@ -353,7 +352,7 @@ 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) +static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, u64 len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); int ret; @@ -368,7 +367,7 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return ret; } -static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, u64 len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); int ret; @@ -383,7 +382,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return ret; } -static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, u64 len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); int ret; -- cgit v1.2.3 From 923fb6238cb3ac529aa2bf13b3b1e53762186a8b Mon Sep 17 00:00:00 2001 From: Ronald Monthero Date: Sat, 18 Nov 2023 18:31:51 +1000 Subject: mtd: rawnand: Increment IFC_TIMEOUT_MSECS for nand controller response Under heavy load it is likely that the controller is done with its own task but the thread unlocking the wait is not scheduled in time. Increasing IFC_TIMEOUT_MSECS allows the controller to respond within allowable timeslice of 1 sec. fsl,ifc-nand 7e800000.nand: Controller is not responding [<804b2047>] (nand_get_device) from [<804b5335>] (nand_write_oob+0x1b/0x4a) [<804b5335>] (nand_write_oob) from [<804a3585>] (mtd_write+0x41/0x5c) [<804a3585>] (mtd_write) from [<804c1d47>] (ubi_io_write+0x17f/0x22c) [<804c1d47>] (ubi_io_write) from [<804c047b>] (ubi_eba_write_leb+0x5b/0x1d0) Fixes: 82771882d960 ("NAND Machine support for Integrated Flash Controller") Reviewed-by: Miquel Raynal Reviewed-by: Andy Shevchenko Signed-off-by: Ronald Monthero Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231118083156.776887-1-debug.penguin32@gmail.com --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 20bb1e0cb5eb..f0e2318ce088 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -21,7 +21,7 @@ #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ -#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait +#define IFC_TIMEOUT_MSECS 1000 /* Maximum timeout to wait for IFC NAND Machine */ struct fsl_ifc_ctrl; -- cgit v1.2.3 From acb1fd579efbcac26ce8f9c4fc8bd82f7eaa56e9 Mon Sep 17 00:00:00 2001 From: Arseniy Krasnov Date: Mon, 20 Nov 2023 09:42:39 +0300 Subject: mtd: rawnand: meson: initialize clock register Clock register must be also initialized during controller probing. If this is not performed (for example by bootloader before) - controller will not work. Signed-off-by: Arseniy Krasnov Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231120064239.3304108-1-avkrasnov@salutedevices.com --- drivers/mtd/nand/raw/meson_nand.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 71ec4052e52a..7e16a13fb438 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -90,6 +90,8 @@ /* eMMC clock register, misc control */ #define CLK_SELECT_NAND BIT(31) +#define CLK_ALWAYS_ON_NAND BIT(24) +#define CLK_SELECT_FIX_PLL2 BIT(6) #define NFC_CLK_CYCLE 6 @@ -1154,7 +1156,7 @@ static int meson_nfc_clk_init(struct meson_nfc *nfc) return PTR_ERR(nfc->nand_clk); /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ - writel(CLK_SELECT_NAND | readl(nfc->reg_clk), + writel(CLK_ALWAYS_ON_NAND | CLK_SELECT_NAND | CLK_SELECT_FIX_PLL2, nfc->reg_clk); ret = clk_prepare_enable(nfc->core_clk); -- cgit v1.2.3 From 2082b6956ce95b66b03983f3059744f559493d98 Mon Sep 17 00:00:00 2001 From: Arseniy Krasnov Date: Thu, 9 Nov 2023 08:39:53 +0300 Subject: mtd: rawnand: meson: handle OOB buffer according OOB layout In case of MTD_OPS_AUTO_OOB mode, MTD/NAND layer fills/reads OOB buffer according current OOB layout so we need to follow it in the driver. Signed-off-by: Arseniy Krasnov Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231109053953.3863664-1-avkrasnov@salutedevices.com --- drivers/mtd/nand/raw/meson_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 7e16a13fb438..cdb58aca59c0 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -511,7 +511,7 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf) __le64 *info; int i, count; - for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { + for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) { info = &meson_chip->info_buf[i]; *info |= oob_buf[count]; *info |= oob_buf[count + 1] << 8; @@ -524,7 +524,7 @@ static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf) __le64 *info; int i, count; - for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { + for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) { info = &meson_chip->info_buf[i]; oob_buf[count] = *info; oob_buf[count + 1] = *info >> 8; -- cgit v1.2.3 From 578dc962ff2000ba4bf52d50717aea0819615634 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 24 Nov 2023 17:24:35 -0800 Subject: mtd: rawnand: Add destructive operation Erase and program operations need the write protect (wp) pin to be de-asserted to take effect. Add the concept of destructive operation and pass the information to exec_op() so controllers know when they should de-assert this pin without having to decode the command opcode. Signed-off-by: Boris Brezillon Signed-off-by: David Regan Reviewed-by: Florian Fainelli Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231125012438.15191-1-dregan@broadcom.com --- drivers/mtd/nand/raw/nand_base.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 9e24bedffd89..7dd9be5d58c4 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1493,7 +1493,8 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, NAND_COMMON_TIMING_NS(conf, tWB_max)), NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), 0), }; - struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs, + instrs); int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); if (naddrs < 0) @@ -1916,7 +1917,8 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tBERS_max), 0), }; - struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs, + instrs); if (chip->options & NAND_ROW_ADDR_3) instrs[1].ctx.addr.naddrs++; -- cgit v1.2.3 From 68cce21e3cc5fea8d955a62394454149270c98bc Mon Sep 17 00:00:00 2001 From: David Regan Date: Fri, 24 Nov 2023 17:24:36 -0800 Subject: mtd: rawnand: NAND controller write protect Allow NAND controller to be responsible for write protect pin handling during fast path and exec_op destructive operation when controller_wp flag is set. Signed-off-by: David Regan Reviewed-by: Florian Fainelli Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231125012438.15191-2-dregan@broadcom.com --- drivers/mtd/nand/raw/nand_base.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 7dd9be5d58c4..0f342cd691a3 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -366,6 +366,10 @@ static int nand_check_wp(struct nand_chip *chip) if (chip->options & NAND_BROKEN_XD) return 0; + /* controller responsible for NAND write protect */ + if (chip->controller->controller_wp) + return 0; + /* Check the WP bit */ ret = nand_status_op(chip, &status); if (ret) -- cgit v1.2.3 From c86b63b82fde4f96ee94dde827a5f28ff5adeb57 Mon Sep 17 00:00:00 2001 From: David Regan Date: Fri, 24 Nov 2023 17:24:37 -0800 Subject: mtd: rawnand: brcmnand: pass host struct to bcmnand_ctrl_poll_status Pass host struct to bcmnand_ctrl_poll_status instead of ctrl struct since real time status requires host, and ctrl is a member of host. Real time status is required for low level commands vs cached status since the NAND controller will not do an automatic status read at the end of a low level command as it would with a high level command. Signed-off-by: David Regan Reviewed-by: Florian Fainelli Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231125012438.15191-3-dregan@broadcom.com --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 30fc399f346e..4867b4dad5e4 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -1061,10 +1061,11 @@ enum { CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30), }; -static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl, +static int bcmnand_ctrl_poll_status(struct brcmnand_host *host, u32 mask, u32 expected_val, unsigned long timeout_ms) { + struct brcmnand_controller *ctrl = host->ctrl; unsigned long limit; u32 val; @@ -1379,7 +1380,7 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp) * make sure ctrl/flash ready before and after * changing state of #WP pin */ - ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY | + ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY | NAND_STATUS_READY, NAND_CTRL_RDY | NAND_STATUS_READY, 0); @@ -1389,7 +1390,7 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp) brcmnand_set_wp(ctrl, wp); nand_status_op(chip, NULL); /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ - ret = bcmnand_ctrl_poll_status(ctrl, + ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY | NAND_STATUS_READY | NAND_STATUS_WP, @@ -1629,13 +1630,13 @@ static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) */ if (oops_in_progress) { if (ctrl->cmd_pending && - bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0)) + bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY, NAND_CTRL_RDY, 0)) return; } else BUG_ON(ctrl->cmd_pending != 0); ctrl->cmd_pending = cmd; - ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); + ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); WARN_ON(ret); mb(); /* flush previous writes */ @@ -1664,7 +1665,7 @@ static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip) if (mtd->oops_panic_write || ctrl->irq < 0) { /* switch to interrupt polling and PIO mode */ disable_ctrl_irqs(ctrl); - sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, + sts = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); err = sts < 0; } else { -- cgit v1.2.3 From 3c8260ce76634291aed877032a41e373884d69e4 Mon Sep 17 00:00:00 2001 From: David Regan Date: Fri, 24 Nov 2023 17:24:38 -0800 Subject: mtd: rawnand: brcmnand: exec_op implementation exec_op implementation for Broadcom STB, Broadband and iProc SoC This adds exec_op and removes the legacy interface. Based on changes proposed by Boris Brezillon. Link: https://github.com/bbrezillon/linux/commit/4ec6f8d8d83f5aaca5d1877f02d48da96d41fcba Link: https://github.com/bbrezillon/linux/commit/11b4acffd761c4928652d7028d19fcd6f45e4696 Signed-off-by: David Regan [Miquel Raynal: Misc style fixes] Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20231125012438.15191-4-dregan@broadcom.com --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 391 +++++++++++++------------------ 1 file changed, 168 insertions(+), 223 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 4867b4dad5e4..8faca43ae1ff 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -625,6 +625,8 @@ enum { /* Only for v7.2 */ #define ACC_CONTROL_ECC_EXT_SHIFT 13 +static u8 brcmnand_status(struct brcmnand_host *host); + static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl) { #if IS_ENABLED(CONFIG_MTD_NAND_BRCMNAND_BCMA) @@ -1022,19 +1024,6 @@ static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl) return -1; } -static int brcmnand_get_sector_size_1k(struct brcmnand_host *host) -{ - struct brcmnand_controller *ctrl = host->ctrl; - int shift = brcmnand_sector_1k_shift(ctrl); - u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs, - BRCMNAND_CS_ACC_CONTROL); - - if (shift < 0) - return 0; - - return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1; -} - static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val) { struct brcmnand_controller *ctrl = host->ctrl; @@ -1074,6 +1063,9 @@ static int bcmnand_ctrl_poll_status(struct brcmnand_host *host, limit = jiffies + msecs_to_jiffies(timeout_ms); do { + if (mask & INTFC_FLASH_STATUS) + brcmnand_status(host); + val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); if ((val & mask) == expected_val) return 0; @@ -1085,6 +1077,9 @@ static int bcmnand_ctrl_poll_status(struct brcmnand_host *host, * do a final check after time out in case the CPU was busy and the driver * did not get enough time to perform the polling to avoid false alarms */ + if (mask & INTFC_FLASH_STATUS) + brcmnand_status(host); + val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); if ((val & mask) == expected_val) return 0; @@ -1388,7 +1383,8 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp) return; brcmnand_set_wp(ctrl, wp); - nand_status_op(chip, NULL); + /* force controller operation to update internal copy of NAND chip status */ + brcmnand_status(host); /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY | @@ -1644,16 +1640,6 @@ static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) cmd << brcmnand_cmd_shift(ctrl)); } -/*********************************************************************** - * NAND MTD API: read/program/erase - ***********************************************************************/ - -static void brcmnand_cmd_ctrl(struct nand_chip *chip, int dat, - unsigned int ctrl) -{ - /* intentionally left blank */ -} - static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip) { struct brcmnand_host *host = nand_get_controller_data(chip); @@ -1704,6 +1690,26 @@ static int brcmnand_waitfunc(struct nand_chip *chip) INTFC_FLASH_STATUS; } +static u8 brcmnand_status(struct brcmnand_host *host) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + + brcmnand_set_cmd_addr(mtd, 0); + brcmnand_send_cmd(host, CMD_STATUS_READ); + + return brcmnand_waitfunc(chip); +} + +static u8 brcmnand_reset(struct brcmnand_host *host) +{ + struct nand_chip *chip = &host->chip; + + brcmnand_send_cmd(host, CMD_FLASH_RESET); + + return brcmnand_waitfunc(chip); +} + enum { LLOP_RE = BIT(16), LLOP_WE = BIT(17), @@ -1753,190 +1759,6 @@ static int brcmnand_low_level_op(struct brcmnand_host *host, return brcmnand_waitfunc(chip); } -static void brcmnand_cmdfunc(struct nand_chip *chip, unsigned command, - int column, int page_addr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - u64 addr = (u64)page_addr << chip->page_shift; - int native_cmd = 0; - - if (command == NAND_CMD_READID || command == NAND_CMD_PARAM || - command == NAND_CMD_RNDOUT) - addr = (u64)column; - /* Avoid propagating a negative, don't-care address */ - else if (page_addr < 0) - addr = 0; - - dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command, - (unsigned long long)addr); - - host->last_cmd = command; - host->last_byte = 0; - host->last_addr = addr; - - switch (command) { - case NAND_CMD_RESET: - native_cmd = CMD_FLASH_RESET; - break; - case NAND_CMD_STATUS: - native_cmd = CMD_STATUS_READ; - break; - case NAND_CMD_READID: - native_cmd = CMD_DEVICE_ID_READ; - break; - case NAND_CMD_READOOB: - native_cmd = CMD_SPARE_AREA_READ; - break; - case NAND_CMD_ERASE1: - native_cmd = CMD_BLOCK_ERASE; - brcmnand_wp(mtd, 0); - break; - case NAND_CMD_PARAM: - native_cmd = CMD_PARAMETER_READ; - break; - case NAND_CMD_SET_FEATURES: - case NAND_CMD_GET_FEATURES: - brcmnand_low_level_op(host, LL_OP_CMD, command, false); - brcmnand_low_level_op(host, LL_OP_ADDR, column, false); - break; - case NAND_CMD_RNDOUT: - native_cmd = CMD_PARAMETER_CHANGE_COL; - addr &= ~((u64)(FC_BYTES - 1)); - /* - * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0 - * NB: hwcfg.sector_size_1k may not be initialized yet - */ - if (brcmnand_get_sector_size_1k(host)) { - host->hwcfg.sector_size_1k = - brcmnand_get_sector_size_1k(host); - brcmnand_set_sector_size_1k(host, 0); - } - break; - } - - if (!native_cmd) - return; - - brcmnand_set_cmd_addr(mtd, addr); - brcmnand_send_cmd(host, native_cmd); - brcmnand_waitfunc(chip); - - if (native_cmd == CMD_PARAMETER_READ || - native_cmd == CMD_PARAMETER_CHANGE_COL) { - /* Copy flash cache word-wise */ - u32 *flash_cache = (u32 *)ctrl->flash_cache; - int i; - - brcmnand_soc_data_bus_prepare(ctrl->soc, true); - - /* - * Must cache the FLASH_CACHE now, since changes in - * SECTOR_SIZE_1K may invalidate it - */ - for (i = 0; i < FC_WORDS; i++) - /* - * Flash cache is big endian for parameter pages, at - * least on STB SoCs - */ - flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i)); - - brcmnand_soc_data_bus_unprepare(ctrl->soc, true); - - /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ - if (host->hwcfg.sector_size_1k) - brcmnand_set_sector_size_1k(host, - host->hwcfg.sector_size_1k); - } - - /* Re-enable protection is necessary only after erase */ - if (command == NAND_CMD_ERASE1) - brcmnand_wp(mtd, 1); -} - -static uint8_t brcmnand_read_byte(struct nand_chip *chip) -{ - struct brcmnand_host *host = nand_get_controller_data(chip); - struct brcmnand_controller *ctrl = host->ctrl; - uint8_t ret = 0; - int addr, offs; - - switch (host->last_cmd) { - case NAND_CMD_READID: - if (host->last_byte < 4) - ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >> - (24 - (host->last_byte << 3)); - else if (host->last_byte < 8) - ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >> - (56 - (host->last_byte << 3)); - break; - - case NAND_CMD_READOOB: - ret = oob_reg_read(ctrl, host->last_byte); - break; - - case NAND_CMD_STATUS: - ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & - INTFC_FLASH_STATUS; - if (wp_on) /* hide WP status */ - ret |= NAND_STATUS_WP; - break; - - case NAND_CMD_PARAM: - case NAND_CMD_RNDOUT: - addr = host->last_addr + host->last_byte; - offs = addr & (FC_BYTES - 1); - - /* At FC_BYTES boundary, switch to next column */ - if (host->last_byte > 0 && offs == 0) - nand_change_read_column_op(chip, addr, NULL, 0, false); - - ret = ctrl->flash_cache[offs]; - break; - case NAND_CMD_GET_FEATURES: - if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) { - ret = 0; - } else { - bool last = host->last_byte == - ONFI_SUBFEATURE_PARAM_LEN - 1; - brcmnand_low_level_op(host, LL_OP_RD, 0, last); - ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff; - } - } - - dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret); - host->last_byte++; - - return ret; -} - -static void brcmnand_read_buf(struct nand_chip *chip, uint8_t *buf, int len) -{ - int i; - - for (i = 0; i < len; i++, buf++) - *buf = brcmnand_read_byte(chip); -} - -static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf, - int len) -{ - int i; - struct brcmnand_host *host = nand_get_controller_data(chip); - - switch (host->last_cmd) { - case NAND_CMD_SET_FEATURES: - for (i = 0; i < len; i++) - brcmnand_low_level_op(host, LL_OP_WR, buf[i], - (i + 1) == len); - break; - default: - BUG(); - break; - } -} - /* * Kick EDU engine */ @@ -2346,8 +2168,9 @@ static int brcmnand_read_page(struct nand_chip *chip, uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct brcmnand_host *host = nand_get_controller_data(chip); u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; + u64 addr = (u64)page << chip->page_shift; - nand_read_page_op(chip, page, 0, NULL, 0); + host->last_addr = addr; return brcmnand_read(mtd, chip, host->last_addr, mtd->writesize >> FC_SHIFT, (u32 *)buf, oob); @@ -2360,8 +2183,9 @@ static int brcmnand_read_page_raw(struct nand_chip *chip, uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL; int ret; + u64 addr = (u64)page << chip->page_shift; - nand_read_page_op(chip, page, 0, NULL, 0); + host->last_addr = addr; brcmnand_set_ecc_enabled(host, 0); ret = brcmnand_read(mtd, chip, host->last_addr, @@ -2469,11 +2293,11 @@ static int brcmnand_write_page(struct nand_chip *chip, const uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct brcmnand_host *host = nand_get_controller_data(chip); void *oob = oob_required ? chip->oob_poi : NULL; + u64 addr = (u64)page << chip->page_shift; - nand_prog_page_begin_op(chip, page, 0, NULL, 0); - brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + host->last_addr = addr; - return nand_prog_page_end_op(chip); + return brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); } static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, @@ -2482,13 +2306,15 @@ static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); struct brcmnand_host *host = nand_get_controller_data(chip); void *oob = oob_required ? chip->oob_poi : NULL; + u64 addr = (u64)page << chip->page_shift; + int ret = 0; - nand_prog_page_begin_op(chip, page, 0, NULL, 0); + host->last_addr = addr; brcmnand_set_ecc_enabled(host, 0); - brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); + ret = brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob); brcmnand_set_ecc_enabled(host, 1); - return nand_prog_page_end_op(chip); + return ret; } static int brcmnand_write_oob(struct nand_chip *chip, int page) @@ -2512,6 +2338,130 @@ static int brcmnand_write_oob_raw(struct nand_chip *chip, int page) return ret; } +static int brcmnand_exec_instr(struct brcmnand_host *host, int i, + const struct nand_operation *op) +{ + const struct nand_op_instr *instr = &op->instrs[i]; + struct brcmnand_controller *ctrl = host->ctrl; + const u8 *out; + bool last_op; + int ret = 0; + u8 *in; + + /* + * The controller needs to be aware of the last command in the operation + * (WAITRDY excepted). + */ + last_op = ((i == (op->ninstrs - 1)) && (instr->type != NAND_OP_WAITRDY_INSTR)) || + ((i == (op->ninstrs - 2)) && (op->instrs[i+1].type == NAND_OP_WAITRDY_INSTR)); + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + brcmnand_low_level_op(host, LL_OP_CMD, instr->ctx.cmd.opcode, last_op); + break; + + case NAND_OP_ADDR_INSTR: + for (i = 0; i < instr->ctx.addr.naddrs; i++) + brcmnand_low_level_op(host, LL_OP_ADDR, instr->ctx.addr.addrs[i], + last_op && (i == (instr->ctx.addr.naddrs - 1))); + break; + + case NAND_OP_DATA_IN_INSTR: + in = instr->ctx.data.buf.in; + for (i = 0; i < instr->ctx.data.len; i++) { + brcmnand_low_level_op(host, LL_OP_RD, 0, + last_op && (i == (instr->ctx.data.len - 1))); + in[i] = brcmnand_read_reg(host->ctrl, BRCMNAND_LL_RDATA); + } + break; + + case NAND_OP_DATA_OUT_INSTR: + out = instr->ctx.data.buf.out; + for (i = 0; i < instr->ctx.data.len; i++) + brcmnand_low_level_op(host, LL_OP_WR, out[i], + last_op && (i == (instr->ctx.data.len - 1))); + break; + + case NAND_OP_WAITRDY_INSTR: + ret = bcmnand_ctrl_poll_status(host, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); + break; + + default: + dev_err(ctrl->dev, "unsupported instruction type: %d\n", + instr->type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int brcmnand_op_is_status(const struct nand_operation *op) +{ + if ((op->ninstrs == 2) && + (op->instrs[0].type == NAND_OP_CMD_INSTR) && + (op->instrs[0].ctx.cmd.opcode == NAND_CMD_STATUS) && + (op->instrs[1].type == NAND_OP_DATA_IN_INSTR)) + return 1; + + return 0; +} + +static int brcmnand_op_is_reset(const struct nand_operation *op) +{ + if ((op->ninstrs == 2) && + (op->instrs[0].type == NAND_OP_CMD_INSTR) && + (op->instrs[0].ctx.cmd.opcode == NAND_CMD_RESET) && + (op->instrs[1].type == NAND_OP_WAITRDY_INSTR)) + return 1; + + return 0; +} + +static int brcmnand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + struct brcmnand_host *host = nand_get_controller_data(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + u8 *status; + unsigned int i; + int ret = 0; + + if (check_only) + return 0; + + if (brcmnand_op_is_status(op)) { + status = op->instrs[1].ctx.data.buf.in; + *status = brcmnand_status(host); + + return 0; + } + else if (brcmnand_op_is_reset(op)) { + ret = brcmnand_reset(host); + if (ret < 0) + return ret; + + brcmnand_wp(mtd, 1); + + return 0; + } + + if (op->deassert_wp) + brcmnand_wp(mtd, 0); + + for (i = 0; i < op->ninstrs; i++) { + ret = brcmnand_exec_instr(host, i, op); + if (ret) + break; + } + + if (op->deassert_wp) + brcmnand_wp(mtd, 1); + + return ret; +} + /*********************************************************************** * Per-CS setup (1 NAND device) ***********************************************************************/ @@ -2822,6 +2772,7 @@ static int brcmnand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops brcmnand_controller_ops = { .attach_chip = brcmnand_attach_chip, + .exec_op = brcmnand_exec_op, }; static int brcmnand_init_cs(struct brcmnand_host *host, @@ -2846,13 +2797,6 @@ static int brcmnand_init_cs(struct brcmnand_host *host, mtd->owner = THIS_MODULE; mtd->dev.parent = dev; - chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl; - chip->legacy.cmdfunc = brcmnand_cmdfunc; - chip->legacy.waitfunc = brcmnand_waitfunc; - chip->legacy.read_byte = brcmnand_read_byte; - chip->legacy.read_buf = brcmnand_read_buf; - chip->legacy.write_buf = brcmnand_write_buf; - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.read_page = brcmnand_read_page; chip->ecc.write_page = brcmnand_write_page; @@ -2864,6 +2808,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, chip->ecc.write_oob = brcmnand_write_oob; chip->controller = &ctrl->controller; + ctrl->controller.controller_wp = 1; /* * The bootloader might have configured 16bit mode but -- cgit v1.2.3 From 9641423174d05da32543e96ced66bb30cebcce16 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 25 Nov 2023 14:35:25 +0200 Subject: mtd: spi-nor: add erase die (chip) capability JESD216 mentions die erase, but does not provide an opcode for it. Check BFPT dword 11, bits 30:24, "Chip Erase, Typical time", it says: "Typical time to erase one chip (die). User must poll device busy to determine if the operation has completed. For a device consisting of multiple dies, that are individually accessed, the time is for each die to which a chip erase command is applied." So when a flash consists of a single die, this is the erase time for the full chip (die) erase, and when it consists of multiple dies, it's the die erase time. Chip and die are the same thing. Add support for die erase. For now, benefit of the die erase when addr and len are aligned with die size. This could be improved however for the uniform and non-uniform erases cases to use the die erase when possible. For example if one requests that an erase of a 2 die device starting from the last 64KB of the first die to the end of the flash size, we could use just 2 commands, a 64KB erase and a die erase. This improvement is left as an exercise for the reader. Tested-by: Fabio Estevam Link: https://lore.kernel.org/r/20231125123529.55686-2-tudor.ambarus@linaro.org Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.c | 108 +++++++++++++++++++++++++++++------------- drivers/mtd/spi-nor/core.h | 8 ++-- drivers/mtd/spi-nor/debugfs.c | 2 +- 3 files changed, 81 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 25a64c65717d..479494cf00c9 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1060,24 +1060,32 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) } /** - * spi_nor_erase_chip() - Erase the entire flash memory. + * spi_nor_erase_die() - Erase the entire die. * @nor: pointer to 'struct spi_nor'. + * @addr: address of the die. + * @die_size: size of the die. * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_erase_chip(struct spi_nor *nor) +static int spi_nor_erase_die(struct spi_nor *nor, loff_t addr, size_t die_size) { + bool multi_die = nor->mtd.size != die_size; int ret; - dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); + dev_dbg(nor->dev, " %lldKiB\n", (long long)(die_size >> 10)); if (nor->spimem) { - struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP; + struct spi_mem_op op = + SPI_NOR_DIE_ERASE_OP(nor->params->die_erase_opcode, + nor->addr_nbytes, addr, multi_die); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { + if (multi_die) + return -EOPNOTSUPP; + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0); @@ -1792,6 +1800,51 @@ destroy_erase_cmd_list: return ret; } +static int spi_nor_erase_dice(struct spi_nor *nor, loff_t addr, + size_t len, size_t die_size) +{ + unsigned long timeout; + int ret; + + /* + * Scale the timeout linearly with the size of the flash, with + * a minimum calibrated to an old 2MB flash. We could try to + * pull these from CFI/SFDP, but these values should be good + * enough for now. + */ + timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, + CHIP_ERASE_2MB_READY_WAIT_JIFFIES * + (unsigned long)(nor->mtd.size / SZ_2M)); + + do { + ret = spi_nor_lock_device(nor); + if (ret) + return ret; + + ret = spi_nor_write_enable(nor); + if (ret) { + spi_nor_unlock_device(nor); + return ret; + } + + ret = spi_nor_erase_die(nor, addr, die_size); + + spi_nor_unlock_device(nor); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready_with_timeout(nor, timeout); + if (ret) + return ret; + + addr += die_size; + len -= die_size; + + } while (len); + + return 0; +} + /* * Erase an address range on the nor chip. The address range may extend * one or more erase sectors. Return an error if there is a problem erasing. @@ -1799,7 +1852,10 @@ destroy_erase_cmd_list: static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) { struct spi_nor *nor = mtd_to_spi_nor(mtd); + u8 n_dice = nor->params->n_dice; + bool multi_die_erase = false; u32 addr, len, rem; + size_t die_size; int ret; dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, @@ -1814,39 +1870,22 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) addr = instr->addr; len = instr->len; + if (n_dice) { + die_size = div_u64(mtd->size, n_dice); + if (!(len & (die_size - 1)) && !(addr & (die_size - 1))) + multi_die_erase = true; + } else { + die_size = mtd->size; + } + ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len); if (ret) return ret; - /* whole-chip erase? */ - if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { - unsigned long timeout; - - ret = spi_nor_lock_device(nor); - if (ret) - goto erase_err; - - ret = spi_nor_write_enable(nor); - if (ret) { - spi_nor_unlock_device(nor); - goto erase_err; - } - - ret = spi_nor_erase_chip(nor); - spi_nor_unlock_device(nor); - if (ret) - goto erase_err; - - /* - * Scale the timeout linearly with the size of the flash, with - * a minimum calibrated to an old 2MB flash. We could try to - * pull these from CFI/SFDP, but these values should be good - * enough for now. - */ - timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES, - CHIP_ERASE_2MB_READY_WAIT_JIFFIES * - (unsigned long)(mtd->size / SZ_2M)); - ret = spi_nor_wait_till_ready_with_timeout(nor, timeout); + /* chip (die) erase? */ + if ((len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) || + multi_die_erase) { + ret = spi_nor_erase_dice(nor, addr, len, die_size); if (ret) goto erase_err; @@ -2902,6 +2941,9 @@ static int spi_nor_late_init_params(struct spi_nor *nor) return ret; } + if (!nor->params->die_erase_opcode) + nor->params->die_erase_opcode = SPINOR_OP_CHIP_ERASE; + /* Default method kept for backward compatibility. */ if (!params->set_4byte_addr_mode) params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index a456042379ee..b43ea2d49e74 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -85,9 +85,9 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPI_NOR_CHIP_ERASE_OP \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), \ - SPI_MEM_OP_NO_ADDR, \ +#define SPI_NOR_DIE_ERASE_OP(opcode, addr_nbytes, addr, dice) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ + SPI_MEM_OP_ADDR(dice ? addr_nbytes : 0, addr, 0), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) @@ -362,6 +362,7 @@ struct spi_nor_otp { * command in octal DTR mode. * @n_banks: number of banks. * @n_dice: number of dice in the flash memory. + * @die_erase_opcode: die erase opcode. Defaults to SPINOR_OP_CHIP_ERASE. * @vreg_offset: volatile register offset for each die. * @hwcaps: describes the read and page program hardware * capabilities. @@ -399,6 +400,7 @@ struct spi_nor_flash_parameter { u8 rdsr_addr_nbytes; u8 n_banks; u8 n_dice; + u8 die_erase_opcode; u32 *vreg_offset; struct spi_nor_hwcaps hwcaps; diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index 6e163cb5b478..2dbda6b6938a 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -138,7 +138,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); - seq_printf(s, " %02x (%s)\n", SPINOR_OP_CHIP_ERASE, buf); + seq_printf(s, " %02x (%s)\n", nor->params->die_erase_opcode, buf); } seq_puts(s, "\nsector map\n"); -- cgit v1.2.3 From 461d0babb54462188c98818b472e6a3d5a91fd60 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 25 Nov 2023 14:35:26 +0200 Subject: mtd: spi-nor: spansion: enable die erase for multi die flashes Enable die erase for spansion multi die flashes. Tested-by: Takahiro Kuwano Link: https://lore.kernel.org/r/20231125123529.55686-3-tudor.ambarus@linaro.org Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spansion.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 12921344373d..6cc237c24e07 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -17,6 +17,7 @@ #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ #define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */ +#define SPINOR_OP_CYPRESS_DIE_ERASE 0x61 /* Chip (die) erase */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_REG_CYPRESS_VREG 0x00800000 @@ -644,6 +645,7 @@ static int s25hx_t_late_init(struct spi_nor *nor) params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); + params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE; return 0; } @@ -933,7 +935,6 @@ static const struct flash_info spansion_nor_parts[] = { .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), .name = "s25hl02gt", .mfr_flags = USE_CLPEF, - .flags = NO_CHIP_ERASE, .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), @@ -954,7 +955,6 @@ static const struct flash_info spansion_nor_parts[] = { .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), .name = "s25hs02gt", .mfr_flags = USE_CLPEF, - .flags = NO_CHIP_ERASE, .fixups = &s25hx_t_fixups }, { .id = SNOR_ID(0x34, 0x5a, 0x1a), -- cgit v1.2.3 From 53919a968b43648822f2d35b6cafacd3950238cc Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 25 Nov 2023 14:35:27 +0200 Subject: mtd: spi-nor: micron-st: enable die erase for multi die flashes Enable die erase for multi die flashes, it will speed the erase time. Unfortunately, Micron does not provide a 4-byte opcode equivalent for the die erase. The SFDP 4BAIT table fails to consider the die erase too, the standard can be improved. Thus we're forced to enter in the 4 byte address mode in order to benefit of the die erase. Tested on n25q00. This flash defines the 4BAIT SFDP table, thus it will use the 4BAIT opcodes for reads, page programs or erases, with the exception that it will use the die erase command in the 4 byte address mode. Link: https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/n25q/n25q_1gb_3v_65nm.pdf?rev=b6eba74759984f749f8c039bc5bc47b7 Link: https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/serial-nor/mt25q/die-rev-b/mt25q_qlkt_l_02g_cbb_0.pdf?rev=43f7f66fc8da4d7d901b35fa51284c8f Link: https://lore.kernel.org/r/20231125123529.55686-4-tudor.ambarus@linaro.org Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.c | 32 +++++++++++++++++--------------- drivers/mtd/spi-nor/micron-st.c | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 479494cf00c9..52e5b569ddfd 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2935,6 +2935,9 @@ static int spi_nor_late_init_params(struct spi_nor *nor) return ret; } + /* Needed by some flashes late_init hooks. */ + spi_nor_init_flags(nor); + if (nor->info->fixups && nor->info->fixups->late_init) { ret = nor->info->fixups->late_init(nor); if (ret) @@ -2948,7 +2951,6 @@ static int spi_nor_late_init_params(struct spi_nor *nor) if (!params->set_4byte_addr_mode) params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr; - spi_nor_init_flags(nor); spi_nor_init_fixup_flags(nor); /* @@ -3186,6 +3188,18 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) struct spi_nor_flash_parameter *params = nor->params; int ret; + if (enable) { + /* + * If the RESET# pin isn't hooked up properly, or the system + * otherwise doesn't perform a reset command in the boot + * sequence, it's impossible to 100% protect against unexpected + * reboots (e.g., crashes). Warn the user (or hopefully, system + * designer) that this is bad. + */ + WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, + "enabling reset hack; may not recover from unexpected reboots\n"); + } + ret = params->set_4byte_addr_mode(nor, enable); if (ret && ret != -ENOTSUPP) return ret; @@ -3234,20 +3248,8 @@ static int spi_nor_init(struct spi_nor *nor) if (nor->addr_nbytes == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && - !(nor->flags & SNOR_F_4B_OPCODES)) { - /* - * If the RESET# pin isn't hooked up properly, or the system - * otherwise doesn't perform a reset command in the boot - * sequence, it's impossible to 100% protect against unexpected - * reboots (e.g., crashes). Warn the user (or hopefully, system - * designer) that this is bad. - */ - WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, - "enabling reset hack; may not recover from unexpected reboots\n"); - err = spi_nor_set_4byte_addr_mode(nor, true); - if (err) - return err; - } + !(nor->flags & SNOR_F_4B_OPCODES)) + return spi_nor_set_4byte_addr_mode(nor, true); return 0; } diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 8920547c12bf..b63f1e9b97d0 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -11,6 +11,7 @@ /* flash_info mfr_flag. Used to read proprietary FSR register. */ #define USE_FSR BIT(0) +#define SPINOR_OP_MT_DIE_ERASE 0xc4 /* Chip (die) erase opcode */ #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ #define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ #define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */ @@ -192,6 +193,30 @@ static struct spi_nor_fixups mt25qu512a_fixups = { .post_bfpt = mt25qu512a_post_bfpt_fixup, }; +static int st_nor_four_die_late_init(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + + params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE; + params->n_dice = 4; + + /* + * Unfortunately the die erase opcode does not have a 4-byte opcode + * correspondent for these flashes. The SFDP 4BAIT table fails to + * consider the die erase too. We're forced to enter in the 4 byte + * address mode in order to benefit of the die erase. + */ + return spi_nor_set_4byte_addr_mode(nor, true); +} + +static struct spi_nor_fixups n25q00_fixups = { + .late_init = st_nor_four_die_late_init, +}; + +static struct spi_nor_fixups mt25q02_fixups = { + .late_init = st_nor_four_die_late_init, +}; + static const struct flash_info st_nor_parts[] = { { .name = "m25p05-nonjedec", @@ -366,16 +391,17 @@ static const struct flash_info st_nor_parts[] = { .name = "n25q00", .size = SZ_128M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | - SPI_NOR_BP3_SR_BIT6 | NO_CHIP_ERASE, + SPI_NOR_BP3_SR_BIT6, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, + .fixups = &n25q00_fixups, }, { .id = SNOR_ID(0x20, 0xba, 0x22), .name = "mt25ql02g", .size = SZ_256M, - .flags = NO_CHIP_ERASE, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, + .fixups = &mt25q02_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x15), .name = "n25q016a", @@ -433,16 +459,16 @@ static const struct flash_info st_nor_parts[] = { .id = SNOR_ID(0x20, 0xbb, 0x21), .name = "n25q00a", .size = SZ_128M, - .flags = NO_CHIP_ERASE, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, + .fixups = &n25q00_fixups, }, { .id = SNOR_ID(0x20, 0xbb, 0x22), .name = "mt25qu02g", .size = SZ_256M, - .flags = NO_CHIP_ERASE, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .mfr_flags = USE_FSR, + .fixups = &mt25q02_fixups, } }; -- cgit v1.2.3 From 06de1257aae787fe3af14d03b9ceb0b9f6af9e1f Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Sat, 25 Nov 2023 14:35:28