From b410f4eb01a1950ed73ae40859d0978b1a924380 Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Tue, 9 Jul 2019 11:41:45 +0200 Subject: mtd: rawnand: stm32_fmc2: avoid warnings when building with W=1 option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch solves warnings detected by setting W=1 when building. Warnings type detected: drivers/mtd/nand/raw/stm32_fmc2_nand.c: In function ‘stm32_fmc2_calc_timings’: drivers/mtd/nand/raw/stm32_fmc2_nand.c:1417:23: warning: comparison is always false due to limited range of data type [-Wtype-limits] else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK) Signed-off-by: Christophe Kerello Cc: stable@vger.kernel.org Fixes: 2cd457f328c1 ("mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver") Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/stm32_fmc2_nand.c | 90 +++++++++++----------------------- 1 file changed, 29 insertions(+), 61 deletions(-) (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 e63acc077c18..8cc852dc7d54 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1427,21 +1427,16 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, struct stm32_fmc2_timings *tims = &nand->timings; unsigned long hclk = clk_get_rate(fmc2->clk); unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); - int tar, tclr, thiz, twait, tset_mem, tset_att, thold_mem, thold_att; - - tar = hclkp; - if (tar < sdrt->tAR_min) - tar = sdrt->tAR_min; - tims->tar = DIV_ROUND_UP(tar, hclkp) - 1; - if (tims->tar > FMC2_PCR_TIMING_MASK) - tims->tar = FMC2_PCR_TIMING_MASK; - - tclr = hclkp; - if (tclr < sdrt->tCLR_min) - tclr = sdrt->tCLR_min; - tims->tclr = DIV_ROUND_UP(tclr, hclkp) - 1; - if (tims->tclr > FMC2_PCR_TIMING_MASK) - tims->tclr = FMC2_PCR_TIMING_MASK; + unsigned long timing, tar, tclr, thiz, twait; + unsigned long tset_mem, tset_att, thold_mem, thold_att; + + tar = max_t(unsigned long, hclkp, sdrt->tAR_min); + timing = DIV_ROUND_UP(tar, hclkp) - 1; + tims->tar = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK); + + tclr = max_t(unsigned long, hclkp, sdrt->tCLR_min); + timing = DIV_ROUND_UP(tclr, hclkp) - 1; + tims->tclr = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK); tims->thiz = FMC2_THIZ; thiz = (tims->thiz + 1) * hclkp; @@ -1451,18 +1446,11 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, * tWAIT > tWP * tWAIT > tREA + tIO */ - twait = hclkp; - if (twait < sdrt->tRP_min) - twait = sdrt->tRP_min; - if (twait < sdrt->tWP_min) - twait = sdrt->tWP_min; - if (twait < sdrt->tREA_max + FMC2_TIO) - twait = sdrt->tREA_max + FMC2_TIO; - tims->twait = DIV_ROUND_UP(twait, hclkp); - if (tims->twait == 0) - tims->twait = 1; - else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK) - tims->twait = FMC2_PMEM_PATT_TIMING_MASK; + twait = max_t(unsigned long, hclkp, sdrt->tRP_min); + twait = max_t(unsigned long, twait, sdrt->tWP_min); + twait = max_t(unsigned long, twait, sdrt->tREA_max + FMC2_TIO); + timing = DIV_ROUND_UP(twait, hclkp); + tims->twait = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tSETUP_MEM > tCS - tWAIT @@ -1477,20 +1465,15 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if (twait > thiz && (sdrt->tDS_min > twait - thiz) && (tset_mem < sdrt->tDS_min - (twait - thiz))) tset_mem = sdrt->tDS_min - (twait - thiz); - tims->tset_mem = DIV_ROUND_UP(tset_mem, hclkp); - if (tims->tset_mem == 0) - tims->tset_mem = 1; - else if (tims->tset_mem > FMC2_PMEM_PATT_TIMING_MASK) - tims->tset_mem = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(tset_mem, hclkp); + tims->tset_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tHOLD_MEM > tCH * tHOLD_MEM > tREH - tSETUP_MEM * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT) */ - thold_mem = hclkp; - if (thold_mem < sdrt->tCH_min) - thold_mem = sdrt->tCH_min; + thold_mem = max_t(unsigned long, hclkp, sdrt->tCH_min); if (sdrt->tREH_min > tset_mem && (thold_mem < sdrt->tREH_min - tset_mem)) thold_mem = sdrt->tREH_min - tset_mem; @@ -1500,11 +1483,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if ((sdrt->tWC_min > tset_mem + twait) && (thold_mem < sdrt->tWC_min - (tset_mem + twait))) thold_mem = sdrt->tWC_min - (tset_mem + twait); - tims->thold_mem = DIV_ROUND_UP(thold_mem, hclkp); - if (tims->thold_mem == 0) - tims->thold_mem = 1; - else if (tims->thold_mem > FMC2_PMEM_PATT_TIMING_MASK) - tims->thold_mem = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(thold_mem, hclkp); + tims->thold_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tSETUP_ATT > tCS - tWAIT @@ -1526,11 +1506,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if (twait > thiz && (sdrt->tDS_min > twait - thiz) && (tset_att < sdrt->tDS_min - (twait - thiz))) tset_att = sdrt->tDS_min - (twait - thiz); - tims->tset_att = DIV_ROUND_UP(tset_att, hclkp); - if (tims->tset_att == 0) - tims->tset_att = 1; - else if (tims->tset_att > FMC2_PMEM_PATT_TIMING_MASK) - tims->tset_att = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(tset_att, hclkp); + tims->tset_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); /* * tHOLD_ATT > tALH @@ -1545,17 +1522,11 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT) * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT) */ - thold_att = hclkp; - if (thold_att < sdrt->tALH_min) - thold_att = sdrt->tALH_min; - if (thold_att < sdrt->tCH_min) - thold_att = sdrt->tCH_min; - if (thold_att < sdrt->tCLH_min) - thold_att = sdrt->tCLH_min; - if (thold_att < sdrt->tCOH_min) - thold_att = sdrt->tCOH_min; - if (thold_att < sdrt->tDH_min) - thold_att = sdrt->tDH_min; + thold_att = max_t(unsigned long, hclkp, sdrt->tALH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCLH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCOH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tDH_min); if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) && (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem)) thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem; @@ -1574,11 +1545,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip, if ((sdrt->tWC_min > tset_att + twait) && (thold_att < sdrt->tWC_min - (tset_att + twait))) thold_att = sdrt->tWC_min - (tset_att + twait); - tims->thold_att = DIV_ROUND_UP(thold_att, hclkp); - if (tims->thold_att == 0) - tims->thold_att = 1; - else if (tims->thold_att > FMC2_PMEM_PATT_TIMING_MASK) - tims->thold_att = FMC2_PMEM_PATT_TIMING_MASK; + timing = DIV_ROUND_UP(thold_att, hclkp); + tims->thold_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); } static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr, -- cgit v1.2.3 From 4902e87faf26067d661e2a9285ce93457884da6d Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Tue, 9 Jul 2019 22:38:37 +0530 Subject: mtd: rawnand: vf610: Add of_node_put() before goto Each iteration of for_each_available_child_of_node puts the previous node, but in the case of a goto from the middle of the loop, there is no put, thus causing a memory leak. Hence add an of_node_put before the goto. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Reviewed-by: Stefan Agner Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/vf610_nfc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index e4fe8c4bc711..6b399a75f9ae 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -862,6 +862,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) dev_err(nfc->dev, "Only one NAND chip supported!\n"); err = -EINVAL; + of_node_put(child); goto err_disable_clk; } -- cgit v1.2.3 From c436f68beeb20f2f92937677db1d9069b0dd2a3d Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Tue, 9 Jul 2019 22:40:16 +0530 Subject: mtd: rawnand: oxnas: Add of_node_put() Each iteration of for_each_child_of_node puts the previous node, but in the case of a goto from the middle of the loop, there is no put, thus causing a memory leak. Hence add an of_node_put under a new goto to put the node at a loop exit. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/oxnas_nand.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c index 30c51f772de6..c43cb4d92d3d 100644 --- a/drivers/mtd/nand/raw/oxnas_nand.c +++ b/drivers/mtd/nand/raw/oxnas_nand.c @@ -116,7 +116,7 @@ static int oxnas_nand_probe(struct platform_device *pdev) GFP_KERNEL); if (!chip) { err = -ENOMEM; - goto err_clk_unprepare; + goto err_release_child; } chip->controller = &oxnas->base; @@ -137,12 +137,12 @@ static int oxnas_nand_probe(struct platform_device *pdev) /* Scan to find existence of the device */ err = nand_scan(chip, 1); if (err) - goto err_clk_unprepare; + goto err_release_child; err = mtd_device_register(mtd, NULL, 0); if (err) { nand_release(chip); - goto err_clk_unprepare; + goto err_release_child; } oxnas->chips[nchips] = chip; @@ -159,6 +159,8 @@ static int oxnas_nand_probe(struct platform_device *pdev) return 0; +err_release_child: + of_node_put(nand_np); err_clk_unprepare: clk_disable_unprepare(oxnas->clk); return err; -- cgit v1.2.3 From a08e42987118952c98707f0b29cf0e5f4fd2e00b Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Tue, 9 Jul 2019 22:43:16 +0530 Subject: mtd: rawnand: tango: Add of_node_put() before return Each iteration of for_each_child_of_node puts the previous node, but in the case of a return from the middle of the loop, there is no put, thus causing a memory leak. Hence add an of_node_put before the return. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Acked-by: Mans Rullgard Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/tango_nand.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index b3f2cabcc7c0..9acf2de37ee0 100644 --- a/drivers/mtd/nand/raw/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -659,6 +659,7 @@ static int tango_nand_probe(struct platform_device *pdev) err = chip_init(&pdev->dev, np); if (err) { tango_nand_remove(pdev); + of_node_put(np); return err; } } -- cgit v1.2.3 From 60be51f4be49b1b1a40f2d51a17ea54217d3dcde Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Tue, 9 Jul 2019 22:46:40 +0530 Subject: mtd: rawnand: meson: Add of_node_put() before return Each iteration of for_each_child_of_node puts the previous node, but in the case of a return from the middle of the loop, there is no put, thus causing a memory leak. Hence add an of_node_put before the return. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Acked-by: Liang Yang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/meson_nand.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index ea57ddcec41e..1b82b687e5a5 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1320,6 +1320,7 @@ static int meson_nfc_nand_chips_init(struct device *dev, ret = meson_nfc_nand_chip_init(dev, nfc, nand_np); if (ret) { meson_nfc_nand_chip_cleanup(nfc); + of_node_put(nand_np); return ret; } } -- cgit v1.2.3 From 1670e678a8a72e8b09774e21986d1d77d4610f96 Mon Sep 17 00:00:00 2001 From: Nishka Dasgupta Date: Tue, 9 Jul 2019 22:50:30 +0530 Subject: mtd: rawnand: ingenic: Add of_node_put() before return Each iteration of for_each_child_of_node puts the previous node, but in the case of a return from the middle of the loop, there is no put, thus causing a memory leak. Hence add an of_node_put before the return. Issue found with Coccinelle. Signed-off-by: Nishka Dasgupta Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c index d7b7c0f13909..df214e7ddec8 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -418,6 +418,7 @@ static int ingenic_nand_init_chips(struct ingenic_nfc *nfc, ret = ingenic_nand_init_chip(pdev, nfc, np, i); if (ret) { ingenic_nand_cleanup_chips(nfc); + of_node_put(np); return ret; } -- cgit v1.2.3 From 3194166052401e04ca6ca71446a1ca5dd9af2433 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 22 Jul 2019 18:39:34 -0500 Subject: mtd: rawnand: ingenic: fix devm_platform_ioremap_resource.cocci warnings drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c:330:1-9: WARNING: Use devm_platform_ioremap_resource for cs -> base Use devm_platform_ioremap_resource helper which wraps platform_get_resource() and devm_ioremap_resource() together. Generated by: scripts/coccinelle/api/devm_platform_ioremap_resource.cocci Fixes: c403ec33b613 ("mtd: rawnand: ingenic: Fix ingenic_ecc dependency") CC: Paul Cercueil Reported-by: kbuild test robot Signed-off-by: Julia Lawall Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c index df214e7ddec8..49afebee50db 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -310,7 +310,6 @@ static int ingenic_nand_init_chip(struct platform_device *pdev, struct device *dev = &pdev->dev; struct ingenic_nand *nand; struct ingenic_nand_cs *cs; - struct resource *res; struct nand_chip *chip; struct mtd_info *mtd; const __be32 *reg; @@ -326,8 +325,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev, jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); - res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); - cs->base = devm_ioremap_resource(dev, res); + cs->base = devm_platform_ioremap_resource(pdev, chipnr); if (IS_ERR(cs->base)) return PTR_ERR(cs->base); -- cgit v1.2.3 From 75de0eb28d03ebe3c9448fc04372df5c39b6cacc Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Tue, 23 Jul 2019 20:47:27 +0800 Subject: mtd: rawnand: r852: Use dev_get_drvdata Instead of using to_pci_dev + pci_get_drvdata, use dev_get_drvdata to make code simpler. Signed-off-by: Chuhong Yuan Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/r852.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c index dae0d235bb17..77774250fb11 100644 --- a/drivers/mtd/nand/raw/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -998,7 +998,7 @@ static void r852_shutdown(struct pci_dev *pci_dev) #ifdef CONFIG_PM_SLEEP static int r852_suspend(struct device *device) { - struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + struct r852_device *dev = dev_get_drvdata(device); if (dev->ctlreg & R852_CTL_CARDENABLE) return -EBUSY; @@ -1019,7 +1019,7 @@ static int r852_suspend(struct device *device) static int r852_resume(struct device *device) { - struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + struct r852_device *dev = dev_get_drvdata(device); r852_disable_irqs(dev); r852_card_update_present(dev); -- cgit v1.2.3 From 80107e764846a6f9b40fc2a78306329ed9052733 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 31 Jul 2019 09:52:08 +0100 Subject: mtd: rawnand: remove redundant assignment to variable ret Variable ret is being initialized with a value that is never read and ret is being re-assigned a little later on. The assignment is redundant and hence can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 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 91f046d4d452..5c2c30a7dffa 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4112,7 +4112,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd_to_nand(mtd); - int ret = -ENOTSUPP; + int ret; ops->retlen = 0; -- cgit v1.2.3 From d83aef09aaa50bdafbb32981859128299abf32eb Mon Sep 17 00:00:00 2001 From: Wenwen Wang Date: Sun, 18 Aug 2019 10:52:49 -0500 Subject: mtd: onenand_base: Fix a memory leak bug In onenand_scan(), if CONFIG_MTD_ONENAND_VERIFY_WRITE is defined, 'this->verify_buf' is allocated through kzalloc(). However, it is not deallocated in the following execution, if the allocation for 'this->oob_buf' fails, leading to a memory leak bug. To fix this issue, free 'this->verify_buf' before returning the error. Signed-off-by: Wenwen Wang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/onenand/onenand_base.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index a1f8fe1abb10..dd9fced3a283 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -3879,6 +3879,9 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->oob_buf) { if (this->options & ONENAND_PAGEBUF_ALLOC) { this->options &= ~ONENAND_PAGEBUF_ALLOC; +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + kfree(this->verify_buf); +#endif kfree(this->page_buf); } return -ENOMEM; -- cgit v1.2.3 From 86aa04f4c2215912fcca6728f2dcf174f7e31fc4 Mon Sep 17 00:00:00 2001 From: Wenwen Wang Date: Sun, 18 Aug 2019 21:46:04 -0500 Subject: mtd: rawnand: Fix a memory leak bug In nand_scan_bbt(), a temporary buffer 'buf' is allocated through vmalloc(). However, if check_create() fails, 'buf' is not deallocated, leading to a memory leak bug. To fix this issue, free 'buf' before returning the error. Signed-off-by: Wenwen Wang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_bbt.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index 2ef15ef94525..96045d60471e 100644 --- a/drivers/mtd/nand/raw/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -1232,7 +1232,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) if (!td) { if ((res = nand_memory_bbt(this, bd))) { pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); - goto err; + goto err_free_bbt; } return 0; } @@ -1245,7 +1245,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) buf = vmalloc(len); if (!buf) { res = -ENOMEM; - goto err; + goto err_free_bbt; } /* Is the bbt at a given page? */ @@ -1258,7 +1258,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) res = check_create(this, buf, bd); if (res) - goto err; + goto err_free_buf; /* Prevent the bbt regions from erasing / writing */ mark_bbt_region(this, td); @@ -1268,7 +1268,9 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd) vfree(buf); return 0; -err: +err_free_buf: + vfree(buf); +err_free_bbt: kfree(this->bbt); this->bbt = NULL; return res; -- cgit v1.2.3 From 738b0ca55f4f6ae1035262c2a2a605d2e9085031 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Mon, 19 Aug 2019 15:19:08 +0800 Subject: mtd: rawnand: Add Macronix raw NAND controller driver Add a driver for Macronix raw NAND controller. Signed-off-by: Mason Yang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 6 + drivers/mtd/nand/raw/Makefile | 1 + drivers/mtd/nand/raw/mxic_nand.c | 582 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 589 insertions(+) create mode 100644 drivers/mtd/nand/raw/mxic_nand.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 5a711d8beaca..9cff36a91864 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -407,6 +407,12 @@ config MTD_NAND_MTK Enables support for NAND controller on MTK SoCs. This controller is found on mt27xx, mt81xx, mt65xx SoCs. +config MTD_NAND_MXIC + tristate "Macronix raw NAND controller" + depends on HAS_IOMEM || COMPILE_TEST + help + This selects the Macronix raw NAND controller driver. + config MTD_NAND_TEGRA tristate "NVIDIA Tegra NAND controller" depends on ARCH_TEGRA || COMPILE_TEST diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index efaf5cd25edc..9b43fbff9742 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o +obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o diff --git a/drivers/mtd/nand/raw/mxic_nand.c b/drivers/mtd/nand/raw/mxic_nand.c new file mode 100644 index 000000000000..9d49e6c845e1 --- /dev/null +++ b/drivers/mtd/nand/raw/mxic_nand.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Macronix International Co., Ltd. + * + * Author: + * Mason Yang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internals.h" + +#define HC_CFG 0x0 +#define HC_CFG_IF_CFG(x) ((x) << 27) +#define HC_CFG_DUAL_SLAVE BIT(31) +#define HC_CFG_INDIVIDUAL BIT(30) +#define HC_CFG_NIO(x) (((x) / 4) << 27) +#define HC_CFG_TYPE(s, t) ((t) << (23 + ((s) * 2))) +#define HC_CFG_TYPE_SPI_NOR 0 +#define HC_CFG_TYPE_SPI_NAND 1 +#define HC_CFG_TYPE_SPI_RAM 2 +#define HC_CFG_TYPE_RAW_NAND 3 +#define HC_CFG_SLV_ACT(x) ((x) << 21) +#define HC_CFG_CLK_PH_EN BIT(20) +#define HC_CFG_CLK_POL_INV BIT(19) +#define HC_CFG_BIG_ENDIAN BIT(18) +#define HC_CFG_DATA_PASS BIT(17) +#define HC_CFG_IDLE_SIO_LVL(x) ((x) << 16) +#define HC_CFG_MAN_START_EN BIT(3) +#define HC_CFG_MAN_START BIT(2) +#define HC_CFG_MAN_CS_EN BIT(1) +#define HC_CFG_MAN_CS_ASSERT BIT(0) + +#define INT_STS 0x4 +#define INT_STS_EN 0x8 +#define INT_SIG_EN 0xc +#define INT_STS_ALL GENMASK(31, 0) +#define INT_RDY_PIN BIT(26) +#define INT_RDY_SR BIT(25) +#define INT_LNR_SUSP BIT(24) +#define INT_ECC_ERR BIT(17) +#define INT_CRC_ERR BIT(16) +#define INT_LWR_DIS BIT(12) +#define INT_LRD_DIS BIT(11) +#define INT_SDMA_INT BIT(10) +#define INT_DMA_FINISH BIT(9) +#define INT_RX_NOT_FULL BIT(3) +#define INT_RX_NOT_EMPTY BIT(2) +#define INT_TX_NOT_FULL BIT(1) +#define INT_TX_EMPTY BIT(0) + +#define HC_EN 0x10 +#define HC_EN_BIT BIT(0) + +#define TXD(x) (0x14 + ((x) * 4)) +#define RXD 0x24 + +#define SS_CTRL(s) (0x30 + ((s) * 4)) +#define LRD_CFG 0x44 +#define LWR_CFG 0x80 +#define RWW_CFG 0x70 +#define OP_READ BIT(23) +#define OP_DUMMY_CYC(x) ((x) << 17) +#define OP_ADDR_BYTES(x) ((x) << 14) +#define OP_CMD_BYTES(x) (((x) - 1) << 13) +#define OP_OCTA_CRC_EN BIT(12) +#define OP_DQS_EN BIT(11) +#define OP_ENHC_EN BIT(10) +#define OP_PREAMBLE_EN BIT(9) +#define OP_DATA_DDR BIT(8) +#define OP_DATA_BUSW(x) ((x) << 6) +#define OP_ADDR_DDR BIT(5) +#define OP_ADDR_BUSW(x) ((x) << 3) +#define OP_CMD_DDR BIT(2) +#define OP_CMD_BUSW(x) (x) +#define OP_BUSW_1 0 +#define OP_BUSW_2 1 +#define OP_BUSW_4 2 +#define OP_BUSW_8 3 + +#define OCTA_CRC 0x38 +#define OCTA_CRC_IN_EN(s) BIT(3 + ((s) * 16)) +#define OCTA_CRC_CHUNK(s, x) ((fls((x) / 32)) << (1 + ((s) * 16))) +#define OCTA_CRC_OUT_EN(s) BIT(0 + ((s) * 16)) + +#define ONFI_DIN_CNT(s) (0x3c + (s)) + +#define LRD_CTRL 0x48 +#define RWW_CTRL 0x74 +#define LWR_CTRL 0x84 +#define LMODE_EN BIT(31) +#define LMODE_SLV_ACT(x) ((x) << 21) +#define LMODE_CMD1(x) ((x) << 8) +#define LMODE_CMD0(x) (x) + +#define LRD_ADDR 0x4c +#define LWR_ADDR 0x88 +#define LRD_RANGE 0x50 +#define LWR_RANGE 0x8c + +#define AXI_SLV_ADDR 0x54 + +#define DMAC_RD_CFG 0x58 +#define DMAC_WR_CFG 0x94 +#define DMAC_CFG_PERIPH_EN BIT(31) +#define DMAC_CFG_ALLFLUSH_EN BIT(30) +#define DMAC_CFG_LASTFLUSH_EN BIT(29) +#define DMAC_CFG_QE(x) (((x) + 1) << 16) +#define DMAC_CFG_BURST_LEN(x) (((x) + 1) << 12) +#define DMAC_CFG_BURST_SZ(x) ((x) << 8) +#define DMAC_CFG_DIR_READ BIT(1) +#define DMAC_CFG_START BIT(0) + +#define DMAC_RD_CNT 0x5c +#define DMAC_WR_CNT 0x98 + +#define SDMA_ADDR 0x60 + +#define DMAM_CFG 0x64 +#define DMAM_CFG_START BIT(31) +#define DMAM_CFG_CONT BIT(30) +#define DMAM_CFG_SDMA_GAP(x) (fls((x) / 8192) << 2) +#define DMAM_CFG_DIR_READ BIT(1) +#define DMAM_CFG_EN BIT(0) + +#define DMAM_CNT 0x68 + +#define LNR_TIMER_TH 0x6c + +#define RDM_CFG0 0x78 +#define RDM_CFG0_POLY(x) (x) + +#define RDM_CFG1 0x7c +#define RDM_CFG1_RDM_EN BIT(31) +#define RDM_CFG1_SEED(x) (x) + +#define LWR_SUSP_CTRL 0x90 +#define LWR_SUSP_CTRL_EN BIT(31) + +#define DMAS_CTRL 0x9c +#define DMAS_CTRL_EN BIT(31) +#define DMAS_CTRL_DIR_READ BIT(30) + +#define DATA_STROB 0xa0 +#define DATA_STROB_EDO_EN BIT(2) +#define DATA_STROB_INV_POL BIT(1) +#define DATA_STROB_DELAY_2CYC BIT(0) + +#define IDLY_CODE(x) (0xa4 + ((x) * 4)) +#define IDLY_CODE_VAL(x, v) ((v) << (((x) % 4) * 8)) + +#define GPIO 0xc4 +#define GPIO_PT(x) BIT(3 + ((x) * 16)) +#define GPIO_RESET(x) BIT(2 + ((x) * 16)) +#define GPIO_HOLDB(x) BIT(1 + ((x) * 16)) +#define GPIO_WPB(x) BIT((x) * 16) + +#define HC_VER 0xd0 + +#define HW_TEST(x) (0xe0 + ((x) * 4)) + +#define MXIC_NFC_MAX_CLK_HZ 50000000 +#define IRQ_TIMEOUT 1000 + +struct mxic_nand_ctlr { + struct clk *ps_clk; + struct clk *send_clk; + struct clk *send_dly_clk; + struct completion complete; + void __iomem *regs; + struct nand_controller controller; + struct device *dev; + struct nand_chip chip; +}; + +static int mxic_nfc_clk_enable(struct mxic_nand_ctlr *nfc) +{ + int ret; + + ret = clk_prepare_enable(nfc->ps_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(nfc->send_clk); + if (ret) + goto err_ps_clk; + + ret = clk_prepare_enable(nfc->send_dly_clk); + if (ret) + goto err_send_dly_clk; + + return ret; + +err_send_dly_clk: + clk_disable_unprepare(nfc->send_clk); +err_ps_clk: + clk_disable_unprepare(nfc->ps_clk); + + return ret; +} + +static void mxic_nfc_clk_disable(struct mxic_nand_ctlr *nfc) +{ + clk_disable_unprepare(nfc->send_clk); + clk_disable_unprepare(nfc->send_dly_clk); + clk_disable_unprepare(nfc->ps_clk); +} + +static void mxic_nfc_set_input_delay(struct mxic_nand_ctlr *nfc, u8 idly_code) +{ + writel(IDLY_CODE_VAL(0, idly_code) | + IDLY_CODE_VAL(1, idly_code) | + IDLY_CODE_VAL(2, idly_code) | + IDLY_CODE_VAL(3, idly_code), + nfc->regs + IDLY_CODE(0)); + writel(IDLY_CODE_VAL(4, idly_code) | + IDLY_CODE_VAL(5, idly_code) | + IDLY_CODE_VAL(6, idly_code) | + IDLY_CODE_VAL(7, idly_code), + nfc->regs + IDLY_CODE(1)); +} + +static int mxic_nfc_clk_setup(struct mxic_nand_ctlr *nfc, unsigned long freq) +{ + int ret; + + ret = clk_set_rate(nfc->send_clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(nfc->send_dly_clk, freq); + if (ret) + return ret; + + /* + * A constant delay range from 0x0 ~ 0x1F for input delay, + * the unit is 78 ps, the max input delay is 2.418 ns. + */ + mxic_nfc_set_input_delay(nfc, 0xf); + + /* + * Phase degree = 360 * freq * output-delay + * where output-delay is a constant value 1 ns in FPGA. + * + * Get Phase degree = 360 * freq * 1 ns + * = 360 * freq * 1 sec / 1000000000 + * = 9 * freq / 25000000 + */ + ret = clk_set_phase(nfc->send_dly_clk, 9 * freq / 25000000); + if (ret) + return ret; + + return 0; +} + +static int mxic_nfc_set_freq(struct mxic_nand_ctlr *nfc, unsigned long freq) +{ + int ret; + + if (freq > MXIC_NFC_MAX_CLK_HZ) + freq = MXIC_NFC_MAX_CLK_HZ; + + mxic_nfc_clk_disable(nfc); + ret = mxic_nfc_clk_setup(nfc, freq); + if (ret) + return ret; + + ret = mxic_nfc_clk_enable(nfc); + if (ret) + return ret; + + return 0; +} + +static irqreturn_t mxic_nfc_isr(int irq, void *dev_id) +{ + struct mxic_nand_ctlr *nfc = dev_id; + u32 sts; + + sts = readl(nfc->regs + INT_STS); + if (sts & INT_RDY_PIN) + complete(&nfc->complete); + else + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static void mxic_nfc_hw_init(struct mxic_nand_ctlr *nfc) +{ + writel(HC_CFG_NIO(8) | HC_CFG_TYPE(1, HC_CFG_TYPE_RAW_NAND) | + HC_CFG_SLV_ACT(0) | HC_CFG_MAN_CS_EN | + HC_CFG_IDLE_SIO_LVL(1), nfc->regs + HC_CFG); + writel(INT_STS_ALL, nfc->regs + INT_STS_EN); + writel(INT_RDY_PIN, nfc->regs + INT_SIG_EN); + writel(0x0, nfc->regs + ONFI_DIN_CNT(0)); + writel(0, nfc->regs + LRD_CFG); + writel(0, nfc->regs + LRD_CTRL); + writel(0x0, nfc->regs + HC_EN); +} + +static void mxic_nfc_cs_enable(struct mxic_nand_ctlr *nfc) +{ + writel(readl(nfc->regs + HC_CFG) | HC_CFG_MAN_CS_EN, + nfc->regs + HC_CFG); + writel(HC_CFG_MAN_CS_ASSERT | readl(nfc->regs + HC_CFG), + nfc->regs + HC_CFG); +} + +static void mxic_nfc_cs_disable(struct mxic_nand_ctlr *nfc) +{ + writel(~HC_CFG_MAN_CS_ASSERT & readl(nfc->regs + HC_CFG), + nfc->regs + HC_CFG); +} + +static int mxic_nfc_wait_ready(struct nand_chip *chip) +{ + struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip); + int ret; + + ret = wait_for_completion_timeout(&nfc->complete, + msecs_to_jiffies(IRQ_TIMEOUT)); + if (!ret) { + dev_err(nfc->dev, "nand device timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int mxic_nfc_data_xfer(struct mxic_nand_ctlr *nfc, const void *txbuf, + void *rxbuf, unsigned int len) +{ + unsigned int pos = 0; + + while (pos < len) { + unsigned int nbytes = len - pos; + u32 data = 0xffffffff; + u32 sts; + int ret; + + if (nbytes > 4) + nbytes = 4; + + if (txbuf) + memcpy(&data, txbuf + pos, nbytes); + + ret = readl_poll_timeout(nfc->regs + INT_STS, sts, + sts & INT_TX_EMPTY, 0, USEC_PER_SEC); + if (ret) + return ret; + + writel(data, nfc->regs + TXD(nbytes % 4)); + + ret = readl_poll_timeout(nfc->regs + INT_STS, sts, + sts & INT_TX_EMPTY, 0, USEC_PER_SEC); + if (ret) + return ret; + + ret = readl_poll_timeout(nfc->regs + INT_STS, sts, + sts & INT_RX_NOT_EMPTY, 0, + USEC_PER_SEC); + if (ret) + return ret; + + data = readl(nfc->regs + RXD); + if (rxbuf) { + data >>= (8 * (4 - nbytes)); + memcpy(rxbuf + pos, &data, nbytes); + } + if (readl(nfc->regs + INT_STS) & INT_RX_NOT_EMPTY) + dev_warn(nfc->dev, "RX FIFO not empty\n"); + + pos += nbytes; + } + + return 0; +} + +static int mxic_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, bool check_only) +{ + struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip); + const struct nand_op_instr *instr = NULL; + int ret = 0; + unsigned int op_id; + + mxic_nfc_cs_enable(nfc); + init_completion(&nfc->complete); + for (op_id = 0; op_id < op->ninstrs; op_id++) { + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + writel(0, nfc->regs + HC_EN); + writel(HC_EN_BIT, nfc->regs + HC_EN); + writel(OP_CMD_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) | + OP_CMD_BYTES(0), nfc->regs + SS_CTRL(0)); + + ret = mxic_nfc_data_xfer(nfc, + &instr->ctx.cmd.opcode, + NULL, 1); + break; + + case NAND_OP_ADDR_INSTR: + writel(OP_ADDR_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) | + OP_ADDR_BYTES(instr->ctx.addr.naddrs), + nfc->regs + SS_CTRL(0)); + ret = mxic_nfc_data_xfer(nfc, + instr->ctx.addr.addrs, NULL, + instr->ctx.addr.naddrs); + break; + + case NAND_OP_DATA_IN_INSTR: + writel(0x0, nfc->regs + ONFI_DIN_CNT(0)); + writel(OP_DATA_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) | + OP_READ, nfc->regs + SS_CTRL(0)); + ret = mxic_nfc_data_xfer(nfc, NULL, + instr->ctx.data.buf.in, + instr->ctx.data.len); + break; + + case NAND_OP_DATA_OUT_INSTR: + writel(instr->ctx.data.len, + nfc->regs + ONFI_DIN_CNT(0)); + writel(OP_DATA_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F), + nfc->regs + SS_CTRL(0)); + ret = mxic_nfc_data_xfer(nfc, + instr->ctx.data.buf.out, NULL, + instr->ctx.data.len); + break; + + case NAND_OP_WAITRDY_INSTR: + ret = mxic_nfc_wait_ready(chip); + break; + } + } + mxic_nfc_cs_disable(nfc); + + return ret; +} + +static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr, + const struct nand_data_interface *conf) +{ + struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip); + const struct nand_sdr_timings *sdr; + unsigned long freq; + int ret; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + freq = NSEC_PER_SEC / (sdr->tRC_min / 1000); + + ret = mxic_nfc_set_freq(nfc, freq); + if (ret) + dev_err(nfc->dev, "set freq:%ld failed\n", freq); + + if (sdr->tRC_min < 30000) + writel(DATA_STROB_EDO_EN, nfc->regs + DATA_STROB); + + return 0; +} + +static const struct nand_controller_ops mxic_nand_controller_ops = { + .exec_op = mxic_nfc_exec_op, + .setup_data_interface = mxic_nfc_setup_data_interface, +}; + +static int mxic_nfc_probe(struct platform_device *pdev) +{ + struct device_node *nand_np, *np = pdev->dev.of_node; + struct mtd_info *mtd; + struct mxic_nand_ctlr *nfc; + struct nand_chip *nand_chip; + int err; + int irq; + + nfc = devm_kzalloc(&pdev->dev, sizeof(struct mxic_nand_ctlr), + GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->ps_clk = devm_clk_get(&pdev->dev, "ps"); + if (IS_ERR(nfc->ps_clk)) + return PTR_ERR(nfc->ps_clk); + + nfc->send_clk = devm_clk_get(&pdev->dev, "send"); + if (IS_ERR(nfc->send_clk)) + return PTR_ERR(nfc->send_clk); + + nfc->send_dly_clk = devm_clk_get(&pdev->dev, "send_dly"); + if (IS_ERR(nfc->send_dly_clk)) + return PTR_ERR(nfc->send_dly_clk); + + nfc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + nand_chip = &nfc->chip; + mtd = nand_to_mtd(nand_chip); + mtd->dev.parent = &pdev->dev; + + for_each_child_of_node(np, nand_np) + nand_set_flash_node(nand_chip, nand_np); + + nand_chip->priv = nfc; + nfc->dev = &pdev->dev; + nfc->controller.ops = &mxic_nand_controller_ops; + nand_controller_init(&nfc->controller); + nand_chip->controller = &nfc->controller; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to retrieve irq\n"); + return irq; + } + + mxic_nfc_hw_init(nfc); + + err = devm_request_irq(&pdev->dev, irq, mxic_nfc_isr, + 0, "mxic-nfc", nfc); + if (err) + goto fail; + + err = nand_scan(nand_chip, 1); + if (err) + goto fail; + + err = mtd_device_register(mtd, NULL, 0); + if (err) + goto fail; + + platform_set_drvdata(pdev, nfc); + return 0; + +fail: + mxic_nfc_clk_disable(nfc); + return err; +} + +static int mxic_nfc_remove(struct platform_device *pdev) +{ + struct mxic_nand_ctlr *nfc = platform_get_drvdata(pdev); + + nand_release(&nfc->chip); + mxic_nfc_clk_disable(nfc); + return 0; +} + +static const struct of_device_id mxic_nfc_of_ids[] = { + { .compatible = "mxic,multi-itfc-v009-nand-controller", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mxic_nfc_of_ids); + +static struct platform_driver mxic_nfc_driver = { + .probe = mxic_nfc_probe, + .remove = mxic_nfc_remove, + .driver = { + .name = "mxic-nfc", + .of_match_table = mxic_nfc_of_ids, + }, +}; +module_platform_driver(mxic_nfc_driver); + +MODULE_AUTHOR("Mason Yang "); +MODULE_DESCRIPTION("Macronix raw NAND controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 419a7a1f167176d60d036d8002b6e3661fde9707 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 9 Aug 2019 22:27:40 +0200 Subject: mtd: rawnand: remove w90x900 driver The ARM w90x900 platform is getting removed, so this driver is obsolete. Signed-off-by: Arnd Bergmann Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 8 - drivers/mtd/nand/raw/Makefile | 1 - drivers/mtd/nand/raw/nuc900_nand.c | 304 ------------------------------------- 3 files changed, 313 deletions(-) delete mode 100644 drivers/mtd/nand/raw/nuc900_nand.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 9cff36a91864..e59de3f60cf6 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -351,14 +351,6 @@ config MTD_NAND_SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. -config MTD_NAND_NUC900 - tristate "Nuvoton NUC9xx/w90p910 NAND controller" - depends on ARCH_W90X900 || COMPILE_TEST - depends on HAS_IOMEM - help - This enables the driver for the NAND Flash on evaluation board based - on w90p910 / NUC9xx. - source "drivers/mtd/nand/raw/ingenic/Kconfig" config MTD_NAND_FSMC diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 9b43fbff9742..a98721988e61 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -41,7 +41,6 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o -obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o diff --git a/drivers/mtd/nand/raw/nuc900_nand.c b/drivers/mtd/nand/raw/nuc900_nand.c deleted file mode 100644 index 13bf7b2894d3..000000000000 --- a/drivers/mtd/nand/raw/nuc900_nand.c +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright © 2009 Nuvoton technology corporation. - * - * Wan ZongShun - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define REG_FMICSR 0x00 -#define REG_SMCSR 0xa0 -#define REG_SMISR 0xac -#define REG_SMCMD 0xb0 -#define REG_SMADDR 0xb4 -#define REG_SMDATA 0xb8 - -#define RESET_FMI 0x01 -#define NAND_EN 0x08 -#define READYBUSY (0x01 << 18) - -#define SWRST 0x01 -#define PSIZE (0x01 << 3) -#define DMARWEN (0x03 << 1) -#define BUSWID (0x01 << 4) -#define ECC4EN (0x01 << 5) -#define WP (0x01 << 24) -#define NANDCS (0x01 << 25) -#define ENDADDR (0x01 << 31) - -#define read_data_reg(dev) \ - __raw_readl((dev)->reg + REG_SMDATA) - -#define write_data_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMDATA) - -#define write_cmd_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMCMD) - -#define write_addr_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMADDR) - -struct nuc900_nand { - struct nand_chip chip; - void __iomem *reg; - struct clk *clk; - spinlock_t lock; -}; - -static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip); -} - -static const struct mtd_partition partitions[] = { - { - .name = "NAND FS 0", - .offset = 0, - .size = 8 * 1024 * 1024 - }, - { - .name = "NAND FS 1", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL - } -}; - -static unsigned char nuc900_nand_read_byte(struct nand_chip *chip) -{ - unsigned char ret; - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - - ret = (unsigned char)read_data_reg(nand); - - return ret; -} - -static void nuc900_nand_read_buf(struct nand_chip *chip, - unsigned char *buf, int len) -{ - int i; - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - - for (i = 0; i < len; i++) - buf[i] = (unsigned char)read_data_reg(nand); -} - -static void nuc900_nand_write_buf(struct nand_chip *chip, - const unsigned char *buf, int len) -{ - int i; - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - - for (i = 0; i < len; i++) - write_data_reg(nand, buf[i]); -} - -static int nuc900_check_rb(struct nuc900_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - val = __raw_readl(nand->reg + REG_SMISR); - val &= READYBUSY; - spin_unlock(&nand->lock); - - return val; -} - -static int nuc900_nand_devready(struct nand_chip *chip) -{ - struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip)); - int ready; - - ready = (nuc900_check_rb(nand)) ? 1 : 0; - return ready; -} - -static void nuc900_nand_command_lp(struct nand_chip *chip, - unsigned int command, - int column, int page_addr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct nuc900_nand *nand = mtd_to_nuc900(mtd); - - if (command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - write_cmd_reg(nand, command & 0xff); - - if (column != -1 || page_addr != -1) { - - if (column != -1) { - if (chip->options & NAND_BUSWIDTH_16 && - !nand_opcode_8bits(command)) - column >>= 1; - write_addr_reg(nand, column); - write_addr_reg(nand, column >> 8 | ENDADDR); - } - if (page_addr != -1) { - write_addr_reg(nand, page_addr); - - if (chip->options & NAND_ROW_ADDR_3) { - write_addr_reg(nand, page_addr >> 8); - write_addr_reg(nand, page_addr >> 16 | ENDADDR); - } else { - write_addr_reg(nand, page_addr >> 8 | ENDADDR); - } - } - } - - switch (command) { - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - if (chip->legacy.dev_ready) - break; - udelay(chip->legacy.chip_delay); - - write_cmd_reg(nand, NAND_CMD_STATUS); - write_cmd_reg(nand, command); - - while (!nuc900_check_rb(nand)) - ; - - return; - - case NAND_CMD_RNDOUT: - write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); - return; - - case NAND_CMD_READ0: - write_cmd_reg(nand, NAND_CMD_READSTART); - /* fall through */ - - default: - - if (!chip->legacy.dev_ready) { - udelay(chip->legacy.chip_delay); - return; - } - } - - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - - while (!chip->legacy.dev_ready(chip)) - ; -} - - -static void nuc900_nand_enable(struct nuc900_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); - - val = __raw_readl(nand->reg + REG_FMICSR); - - if (!(val & NAND_EN)) - __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); - - val = __raw_readl(nand->reg + REG_SMCSR); - - val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); - val |= WP; - - __raw_writel(val, nand->reg + REG_SMCSR); - - spin_unlock(&nand->lock); -} - -static int nuc900_nand_probe(struct platform_device *pdev) -{ - struct nuc900_nand *nuc900_nand; - struct nand_chip *chip; - struct mtd_info *mtd; - struct resource *res; - - nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand), - GFP_KERNEL); - if (!nuc900_nand) - return -ENOMEM; - chip = &(nuc900_nand->chip); - mtd = nand_to_mtd(chip); - - mtd->dev.parent = &pdev->dev; - spin_lock_init(&nuc900_nand->lock); - - nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nuc900_nand->clk)) - return -ENOENT; - clk_enable(nuc900_nand->clk); - - chip->legacy.cmdfunc = nuc900_nand_command_lp; - chip->legacy.dev_ready = nuc900_nand_devready; - chip->legacy.read_byte = nuc900_nand_read_byte; - chip->legacy.write_buf = nuc900_nand_write_buf; - chip->legacy.read_buf = nuc900_nand_read_buf; - chip->legacy.chip_delay = 50; - chip->options = 0; - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(nuc900_nand->reg)) - return PTR_ERR(nuc900_nand->reg); - - nuc900_nand_enable(nuc900_nand); - - if (nand_scan(chip, 1)) - return -ENXIO; - - mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions)); - - platform_set_drvdata(pdev, nuc900_nand); - - return 0; -} - -static int nuc900_nand_remove(struct platform_device *pdev) -{ - struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); - - nand_release(&nuc900_nand->chip); - clk_disable(nuc900_nand->clk); - - return 0; -} - -static struct platform_driver nuc900_nand_driver = { - .probe = nuc900_nand_probe, - .remove = nuc900_nand_remove, - .driver = { - .name = "nuc900-fmi", - }, -}; - -module_platform_driver(nuc900_nand_driver); - -MODULE_AUTHOR("Wan ZongShun "); -MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-fmi"); -- cgit v1.2.3 From 7f852cc1579297fd763789f8cd370639d0c654b6 Mon Sep 17 00:00:00 2001 From: Claire Lin Date: Mon, 26 Aug 2019 15:57:56 -0400 Subject: mtd: rawnand: brcmnand: Fix ecc chunk calculation for erased page bitfips In brcmstb_nand_verify_erased_page(), the ECC chunk pointer calculation while correcting erased page bitflips is wrong, fix it. Fixes: 02b88eea9f9c ("mtd: brcmnand: Add check for erased page bitflips") Signed-off-by: Claire Lin Reviewed-by: Ray Jui Signed-off-by: Kamal Dasu Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 33310b8a6eb8..15ef30b368a5 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -1792,6 +1792,7 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, int bitflips = 0; int page = addr >> chip->page_shift; int ret; + void *ecc_chunk; if (!buf) buf = nand_get_data_buf(chip); @@ -1804,7 +1805,9 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, return ret; for (i = 0; i < chip->ecc.steps; i++, oob += sas) { - ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, + ecc_chunk = buf + chip->ecc.size * i; + ret = nand_check_erased_ecc_chunk(ecc_chunk, + chip->ecc.size, oob, sas, NULL, 0, chip->ecc.strength); if (ret < 0) -- cgit v1.2.3 From f480b969448ee3896f55ef6d037ad4a2df3914bf Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 27 Aug 2019 12:36:53 +0200 Subject: mtd: rawnand: omap2: Fix number of bitflips reporting with ELM omap_elm_correct_data() returns the number of bitflips for the whole page. This is wrong, it should return the maximum number of bitflips found in each ECC step. In my case with a 4k page size NAND mtcdore reported -EUCLEAN with only 12 bitflips on a page where we could correct up to 128 bits per page (provided they are distributed equally on the 8 ECC steps) Signed-off-by: Sascha Hauer Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/omap2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 8d881a28140e..6ec65f48501c 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -1501,7 +1501,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data, } /* Update number of correctable errors */ - stat += err_vec[i].error_count; + stat = max_t(unsigned int, stat, err_vec[i].error_count); /* Update page data with sector size */ data += ecc->size; -- cgit v1.2.3