From a8e3923ab57192547ffad01d78939c5c0d5d0c30 Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Fri, 30 Mar 2018 20:00:51 +0530 Subject: [PATCH 01/32] mtd: rawnand: davinci: don't acquire and enable clock NAND itself is an asynchronous interface, it does not have any clock input. DaVinci NAND driver acquires clock for AEMIF (asynchronous external memory interface) which is an on-chip IP to which NAND is connected. The same clock is also enabled in AEMIF driver (either present drivers/memory or from machine code for some older platforms). AEMIF timing must be initialized before NAND can be accessed. This ensures that AEMIF clock is enabled too. Remove the superfluous clock acquisition and enable in DaVinci NAND driver. Tested on K2L, K2HK, K2E, DA850 EVM, DA850 LCDK in device-tree boot and DM644x EVM in legacy boot. Signed-off-by: Sekhar Nori Tested-by: Bartosz Golaszewski Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/davinci_nand.c | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 0f09518d980f..7255a0d94374 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -55,7 +54,6 @@ struct davinci_nand_info { struct nand_chip chip; struct device *dev; - struct clk *clk; bool is_readmode; @@ -703,22 +701,6 @@ static int nand_davinci_probe(struct platform_device *pdev) /* Use board-specific ECC config */ info->chip.ecc.mode = pdata->ecc_mode; - ret = -EINVAL; - - info->clk = devm_clk_get(&pdev->dev, "aemif"); - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(info->clk); - if (ret < 0) { - dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n", - ret); - goto err_clk_enable; - } - spin_lock_irq(&davinci_nand_lock); /* put CSxNAND into NAND mode */ @@ -732,7 +714,7 @@ static int nand_davinci_probe(struct platform_device *pdev) ret = nand_scan_ident(mtd, pdata->mask_chipsel ? 2 : 1, NULL); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); - goto err; + return ret; } switch (info->chip.ecc.mode) { @@ -838,9 +820,6 @@ err_cleanup_nand: nand_cleanup(&info->chip); err: - clk_disable_unprepare(info->clk); - -err_clk_enable: spin_lock_irq(&davinci_nand_lock); if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) ecc4_busy = false; @@ -859,8 +838,6 @@ static int nand_davinci_remove(struct platform_device *pdev) nand_release(nand_to_mtd(&info->chip)); - clk_disable_unprepare(info->clk); - return 0; } From 6bf6ec522e050359b92600f7f820c32181615f87 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 4 Mar 2018 21:06:01 +0100 Subject: [PATCH 02/32] mtd: rawnand: gpmi: add support for specific ECC strength Add support for specified ECC strength/size using device tree properties nand-ecc-strength/nand-ecc-step-size. Signed-off-by: Stefan Agner Acked-by: Han Xu Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 30 ++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index c2597c8107a0..abd5146931fa 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -198,17 +198,16 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) * * We may have available oob space in this case. */ -static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) +static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, + unsigned int ecc_strength, + unsigned int ecc_step) { struct bch_geometry *geo = &this->bch_geometry; struct nand_chip *chip = &this->nand; struct mtd_info *mtd = nand_to_mtd(chip); unsigned int block_mark_bit_offset; - if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) - return -EINVAL; - - switch (chip->ecc_step_ds) { + switch (ecc_step) { case SZ_512: geo->gf_len = 13; break; @@ -221,8 +220,8 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) chip->ecc_strength_ds, chip->ecc_step_ds); return -EINVAL; } - geo->ecc_chunk_size = chip->ecc_step_ds; - geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + geo->ecc_chunk_size = ecc_step; + geo->ecc_strength = round_up(ecc_strength, 2); if (!gpmi_check_ecc(this)) return -EINVAL; @@ -230,7 +229,7 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) if (geo->ecc_chunk_size < mtd->oobsize) { dev_err(this->dev, "unsupported nand chip. ecc size: %d, oob size : %d\n", - chip->ecc_step_ds, mtd->oobsize); + ecc_step, mtd->oobsize); return -EINVAL; } @@ -423,9 +422,20 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) int common_nfc_set_geometry(struct gpmi_nand_data *this) { + struct nand_chip *chip = &this->nand; + + if (chip->ecc.strength > 0 && chip->ecc.size > 0) + return set_geometry_by_ecc_info(this, chip->ecc.strength, + chip->ecc.size); + if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) - || legacy_set_geometry(this)) - return set_geometry_by_ecc_info(this); + || legacy_set_geometry(this)) { + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -EINVAL; + + return set_geometry_by_ecc_info(this, chip->ecc_strength_ds, + chip->ecc_step_ds); + } return 0; } From 290450dddb318e28befbbb004f0c1fa7dbcfad69 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sun, 4 Mar 2018 21:06:02 +0100 Subject: [PATCH 03/32] dt-bindings: mtd: rawnand: gpmi: document specific ECC strength Document newly supported device tree properties nand-ecc-strength/ nand-ecc-step-size to specify ECC strength/size. Signed-off-by: Stefan Agner Acked-by: Han Xu Signed-off-by: Boris Brezillon --- Documentation/devicetree/bindings/mtd/gpmi-nand.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt index b289ef3c1b7e..393588385c6e 100644 --- a/Documentation/devicetree/bindings/mtd/gpmi-nand.txt +++ b/Documentation/devicetree/bindings/mtd/gpmi-nand.txt @@ -47,6 +47,11 @@ Optional properties: partitions written from Linux with this feature turned on may not be accessible by the BootROM code. + - nand-ecc-strength: integer representing the number of bits to correct + per ECC step. Needs to be a multiple of 2. + - nand-ecc-step-size: integer representing the number of data bytes + that are covered by a single ECC step. The driver + supports 512 and 1024. The device tree may optionally contain sub-nodes describing partitions of the address space. See partition.txt for more detail. From ddd5ed3a90e797e2015cd83e9697c6ed867a3891 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 27 Mar 2018 09:06:14 +0200 Subject: [PATCH 04/32] mtd: rawnand: sunxi: Remove support for GPIO-based Ready/Busy polling None of the existing platforms connect the R/B pin to a GPIO (they all use one of the dedicated R/B pin). Anyway, if we ever get short of native R/B pins, it's probably better to fallback to STATUS reg polling than trying to poll a GPIO. Signed-off-by: Boris Brezillon Reviewed-by: Rob Herring --- .../devicetree/bindings/mtd/sunxi-nand.txt | 2 - drivers/mtd/nand/raw/sunxi_nand.c | 91 +++---------------- 2 files changed, 15 insertions(+), 78 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt index 0734f03bf3d3..dcd5a5d80dc0 100644 --- a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt +++ b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt @@ -22,8 +22,6 @@ Optional properties: - reset : phandle + reset specifier pair - reset-names : must contain "ahb" - allwinner,rb : shall contain the native Ready/Busy ids. - or -- rb-gpios : shall contain the gpios used as R/B pins. - nand-ecc-mode : one of the supported ECC modes ("hw", "soft", "soft_bch" or "none") diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index aad42812a353..d831a141a196 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -165,49 +165,16 @@ #define NFC_MAX_CS 7 -/* - * Ready/Busy detection type: describes the Ready/Busy detection modes - * - * @RB_NONE: no external detection available, rely on STATUS command - * and software timeouts - * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy - * pin of the NAND flash chip must be connected to one of the - * native NAND R/B pins (those which can be muxed to the NAND - * Controller) - * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy - * pin of the NAND flash chip must be connected to a GPIO capable - * pin. - */ -enum sunxi_nand_rb_type { - RB_NONE, - RB_NATIVE, - RB_GPIO, -}; - -/* - * Ready/Busy structure: stores information related to Ready/Busy detection - * - * @type: the Ready/Busy detection mode - * @info: information related to the R/B detection mode. Either a gpio - * id or a native R/B id (those supported by the NAND controller). - */ -struct sunxi_nand_rb { - enum sunxi_nand_rb_type type; - union { - int gpio; - int nativeid; - } info; -}; - /* * Chip Select structure: stores information related to NAND Chip Select * * @cs: the NAND CS id used to communicate with a NAND Chip - * @rb: the Ready/Busy description + * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the + * NFC */ struct sunxi_nand_chip_sel { u8 cs; - struct sunxi_nand_rb rb; + s8 rb; }; /* @@ -440,30 +407,19 @@ static int sunxi_nfc_dev_ready(struct mtd_info *mtd) struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); - struct sunxi_nand_rb *rb; - int ret; + u32 mask; if (sunxi_nand->selected < 0) return 0; - rb = &sunxi_nand->sels[sunxi_nand->selected].rb; - - switch (rb->type) { - case RB_NATIVE: - ret = !!(readl(nfc->regs + NFC_REG_ST) & - NFC_RB_STATE(rb->info.nativeid)); - break; - case RB_GPIO: - ret = gpio_get_value(rb->info.gpio); - break; - case RB_NONE: - default: - ret = 0; + if (sunxi_nand->sels[sunxi_nand->selected].rb < 0) { dev_err(nfc->dev, "cannot check R/B NAND status!\n"); - break; + return 0; } - return ret; + mask = NFC_RB_STATE(sunxi_nand->sels[sunxi_nand->selected].rb); + + return !!(readl(nfc->regs + NFC_REG_ST) & mask); } static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) @@ -488,12 +444,11 @@ static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift); - if (sel->rb.type == RB_NONE) { + if (sel->rb < 0) { nand->dev_ready = NULL; } else { nand->dev_ready = sunxi_nfc_dev_ready; - if (sel->rb.type == RB_NATIVE) - ctl |= NFC_RB_SEL(sel->rb.info.nativeid); + ctl |= NFC_RB_SEL(sel->rb); } writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); @@ -1946,26 +1901,10 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, chip->sels[i].cs = tmp; if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && - tmp < 2) { - chip->sels[i].rb.type = RB_NATIVE; - chip->sels[i].rb.info.nativeid = tmp; - } else { - ret = of_get_named_gpio(np, "rb-gpios", i); - if (ret >= 0) { - tmp = ret; - chip->sels[i].rb.type = RB_GPIO; - chip->sels[i].rb.info.gpio = tmp; - ret = devm_gpio_request(dev, tmp, "nand-rb"); - if (ret) - return ret; - - ret = gpio_direction_input(tmp); - if (ret) - return ret; - } else { - chip->sels[i].rb.type = RB_NONE; - } - } + tmp < 2) + chip->sels[i].rb = tmp; + else + chip->sels[i].rb = -1; } nand = &chip->nand; From 7b4b199459f31541141c4510e542bd4533a60585 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Wed, 11 Apr 2018 10:57:57 +0800 Subject: [PATCH 05/32] mtd: rawnand: diskonchip: Replace mdelay with usleep_range in doc_probe doc_probe() is never called in atomic context. doc_probe() is only called by init_nanddoc(), which is only set as a parameter of module_init(). This function is not called in atomic context. Despite never getting called from atomic context, doc_probe() calls mdelay() to busily wait. This is not necessary and can be replaced with usleep_range() to avoid busy waiting. This is found by a static analysis tool named DCNS written by myself. And I also manually check it. Signed-off-by: Jia-Ju Bai Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/diskonchip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 86a258de0b75..8d0fb2daad71 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1481,12 +1481,12 @@ static int __init doc_probe(unsigned long physadr) WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); - mdelay(1); + usleep_range(1000, 2000); /* Enable the Millennium Plus ASIC */ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); - mdelay(1); + usleep_range(1000, 2000); ChipID = ReadDOC(virtadr, ChipID); From 36bf2eb90de03d37e05169be29efd2e959ae27a9 Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Mon, 16 Apr 2018 10:33:54 +0800 Subject: [PATCH 06/32] mtd: rawnand: mtk: use of_device_get_match_data() The usage of of_device_get_match_data() reduce the code size a bit. Also, the only way to call .probe() is to match an entry in .of_match_table[], so of_device_id cannot be NULL. Signed-off-by: Ryder Lee Reviewed-by: Xiaolei Li Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/mtk_ecc.c | 7 +------ drivers/mtd/nand/raw/mtk_nand.c | 10 +--------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c index 40d86a861a70..6432bd70c3b3 100644 --- a/drivers/mtd/nand/raw/mtk_ecc.c +++ b/drivers/mtd/nand/raw/mtk_ecc.c @@ -500,7 +500,6 @@ static int mtk_ecc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mtk_ecc *ecc; struct resource *res; - const struct of_device_id *of_ecc_id = NULL; u32 max_eccdata_size; int irq, ret; @@ -508,11 +507,7 @@ static int mtk_ecc_probe(struct platform_device *pdev) if (!ecc) return -ENOMEM; - of_ecc_id = of_match_device(mtk_ecc_dt_match, &pdev->dev); - if (!of_ecc_id) - return -ENODEV; - - ecc->caps = of_ecc_id->data; + ecc->caps = of_device_get_match_data(dev); max_eccdata_size = ecc->caps->num_ecc_strength - 1; max_eccdata_size = ecc->caps->ecc_strength[max_eccdata_size]; diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 6977da3a26aa..75c845adb050 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1434,7 +1434,6 @@ static int mtk_nfc_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct mtk_nfc *nfc; struct resource *res; - const struct of_device_id *of_nfc_id = NULL; int ret, irq; nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); @@ -1452,6 +1451,7 @@ static int mtk_nfc_probe(struct platform_device *pdev) else if (!nfc->ecc) return -ENODEV; + nfc->caps = of_device_get_match_data(dev); nfc->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1498,14 +1498,6 @@ static int mtk_nfc_probe(struct platform_device *pdev) goto clk_disable; } - of_nfc_id = of_match_device(mtk_nfc_id_table, &pdev->dev); - if (!of_nfc_id) { - ret = -ENODEV; - goto clk_disable; - } - - nfc->caps = of_nfc_id->data; - platform_set_drvdata(pdev, nfc); ret = mtk_nfc_nand_chips_init(dev, nfc); From 3855f66c8fdb30e0e9cc67003279af2c52451a7f Mon Sep 17 00:00:00 2001 From: Xiaolei Li Date: Mon, 16 Apr 2018 15:41:02 +0800 Subject: [PATCH 07/32] MAINTAINERS: Add entry for Mediatek NAND controller driver Add entry for Mediatek NAND controller driver and its bindings. Signed-off-by: Xiaolei Li Signed-off-by: Boris Brezillon --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0a1410d5a621..0eff9eba4d28 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8941,6 +8941,13 @@ L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/mediatek/mt7601u/ +MEDIATEK NAND CONTROLLER DRIVER +M: Xiaolei Li +L: linux-mtd@lists.infradead.org +S: Maintained +F: drivers/mtd/nand/raw/mtk_* +F: Documentation/devicetree/bindings/mtd/mtk-nand.txt + MEDIATEK RANDOM NUMBER GENERATOR SUPPORT M: Sean Wang S: Maintained From 87ed67ba65225fa0e23d39f2eaec55d1b2eaa2aa Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 19 Apr 2018 16:06:00 +0200 Subject: [PATCH 08/32] mtd: onenand: simplify getting .drvdata We should get drvdata from struct device directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Signed-off-by: Boris Brezillon --- drivers/mtd/nand/onenand/samsung.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/onenand/samsung.c b/drivers/mtd/nand/onenand/samsung.c index 2e9d076e445a..4cce4c0311ca 100644 --- a/drivers/mtd/nand/onenand/samsung.c +++ b/drivers/mtd/nand/onenand/samsung.c @@ -958,8 +958,7 @@ static int s3c_onenand_remove(struct platform_device *pdev) static int s3c_pm_ops_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mtd_info *mtd = platform_get_drvdata(pdev); + struct mtd_info *mtd = dev_get_drvdata(dev); struct onenand_chip *this = mtd->priv; this->wait(mtd, FL_PM_SUSPENDED); @@ -968,8 +967,7 @@ static int s3c_pm_ops_suspend(struct device *dev) static int s3c_pm_ops_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mtd_info *mtd = platform_get_drvdata(pdev); + struct mtd_info *mtd = dev_get_drvdata(dev); struct onenand_chip *this = mtd->priv; this->unlock_all(mtd); From 39b77c586e179db03d8cdbc448d4a0fc22f8b13a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:34 +0200 Subject: [PATCH 09/32] mtd: rawnand: fsl_elbc: fix probe function error path An error after nand_scan_tail() should trigger a nand_cleanup(). The helper mtd_device_parse_register() returns an error code that should be checked and nand_cleanup() called accordingly. However, in this driver, fsl_elbc_chip_remove() which is called upon error already triggers a nand_release() which is wrong, because a nand_release() should be triggered only if an mtd_register() succeeded. Move the nand_release() call out of the fsl_elbc_chip_remove() and put it back in the *_remove() hook. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index d28df991c73c..51f0b340bc0d 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -813,8 +813,6 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; struct mtd_info *mtd = nand_to_mtd(&priv->chip); - nand_release(mtd); - kfree(mtd->name); if (priv->vbase) @@ -926,15 +924,20 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev) /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ - mtd_device_parse_register(mtd, part_probe_types, NULL, - NULL, 0); + ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); + if (ret) + goto cleanup_nand; pr_info("eLBC NAND device at 0x%llx, bank %d\n", (unsigned long long)res.start, priv->bank); + return 0; +cleanup_nand: + nand_cleanup(&priv->chip); err: fsl_elbc_chip_remove(priv); + return ret; } @@ -942,7 +945,9 @@ static int fsl_elbc_nand_remove(struct platform_device *pdev) { struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev); + struct mtd_info *mtd = nand_to_mtd(&priv->chip); + nand_release(mtd); fsl_elbc_chip_remove(priv); mutex_lock(&fsl_elbc_nand_mutex); From acfc33091f7a5118a7c6d899162e9d9cee72b17a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:35 +0200 Subject: [PATCH 10/32] mtd: rawnand: fsl_ifc: fix probe function error path An error after nand_scan_tail() should trigger a nand_cleanup(). The helper mtd_device_parse_register() returns an error code that should be checked and nand_cleanup() called accordingly. However, in this driver, fsl_ifc_chip_remove() which is called upon error already triggers a nand_release() which is wrong, because a nand_release() should be triggered only if an mtd_register() succeeded. Move the nand_release() call out of the fsl_ifc_chip_remove() and put it back in the *_remove() hook. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 61aae0224078..00a609d4473e 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -924,8 +924,6 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) { struct mtd_info *mtd = nand_to_mtd(&priv->chip); - nand_release(mtd); - kfree(mtd->name); if (priv->vbase) @@ -1059,21 +1057,29 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ - mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); + ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); + if (ret) + goto cleanup_nand; dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", (unsigned long long)res.start, priv->bank); + return 0; +cleanup_nand: + nand_cleanup(&priv->chip); err: fsl_ifc_chip_remove(priv); + return ret; } static int fsl_ifc_nand_remove(struct platform_device *dev) { struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); + struct mtd_info *mtd = nand_to_mtd(&priv->chip); + nand_release(mtd); fsl_ifc_chip_remove(priv); mutex_lock(&fsl_ifc_nand_mutex); From 43fab011e93fea0699c2e79a7e561b8f095906f3 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:36 +0200 Subject: [PATCH 11/32] mtd: rawnand: fsmc: clean the probe function style Before fixing the error path of the probe function, fix the style of the probe function and mostly the goto labels: they should indicate what the next cleanup is, not the point from which they can be accessed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsmc_nand.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 28c48dcc514e..35af3890ddba 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1022,12 +1022,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->read_dma_chan = dma_request_channel(mask, filter, NULL); if (!host->read_dma_chan) { dev_err(&pdev->dev, "Unable to get read dma channel\n"); - goto err_req_read_chnl; + goto disable_clk; } host->write_dma_chan = dma_request_channel(mask, filter, NULL); if (!host->write_dma_chan) { dev_err(&pdev->dev, "Unable to get write dma channel\n"); - goto err_req_write_chnl; + goto release_dma_read_chan; } } @@ -1050,7 +1050,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) ret = nand_scan_ident(mtd, 1, NULL); if (ret) { dev_err(&pdev->dev, "No NAND Device found!\n"); - goto err_scan_ident; + goto release_dma_write_chan; } if (AMBA_REV_BITS(host->pid) >= 8) { @@ -1065,7 +1065,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "No oob scheme defined for oobsize %d\n", mtd->oobsize); ret = -EINVAL; - goto err_probe; + goto release_dma_write_chan; } mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); @@ -1090,7 +1090,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "Unsupported ECC mode!\n"); - goto err_probe; + goto release_dma_write_chan; } /* @@ -1110,7 +1110,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) "No oob scheme defined for oobsize %d\n", mtd->oobsize); ret = -EINVAL; - goto err_probe; + goto release_dma_write_chan; } } } @@ -1118,26 +1118,27 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) /* Second stage of scan to fill MTD data-structures */ ret = nand_scan_tail(mtd); if (ret) - goto err_probe; + goto release_dma_write_chan; mtd->name = "nand"; ret = mtd_device_register(mtd, NULL, 0); if (ret) - goto err_probe; + goto release_dma_write_chan; platform_set_drvdata(pdev, host); dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); + return 0; -err_probe: -err_scan_ident: +release_dma_write_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->write_dma_chan); -err_req_write_chnl: +release_dma_read_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->read_dma_chan); -err_req_read_chnl: +disable_clk: clk_disable_unprepare(host->clk); + return ret; } From 682cae27b1da0554496983a356b82b4d87540c48 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:37 +0200 Subject: [PATCH 12/32] mtd: rawnand: fsmc: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup(). Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsmc_nand.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 35af3890ddba..f4a5a317d4ae 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1123,13 +1123,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) mtd->name = "nand"; ret = mtd_device_register(mtd, NULL, 0); if (ret) - goto release_dma_write_chan; + goto cleanup_nand; platform_set_drvdata(pdev, host); dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); return 0; +cleanup_nand: + nand_cleanup(nand); release_dma_write_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->write_dma_chan); From 9326dc754c64996ccdc354413674f42486590574 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:38 +0200 Subject: [PATCH 13/32] mtd: rawnand: hisi504: clean the probe function error path There is not need for a goto statement when the only action to take is to return. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/hisi504_nand.c | 35 ++++++++++------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index 27558a67fa41..3c64a6281b54 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -731,23 +731,19 @@ static int hisi_nfc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no IRQ resource defined\n"); - ret = -ENXIO; - goto err_res; + return -ENXIO; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->iobase = devm_ioremap_resource(dev, res); - if (IS_ERR(host->iobase)) { - ret = PTR_ERR(host->iobase); - goto err_res; - } + if (IS_ERR(host->iobase)) + return PTR_ERR(host->iobase); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); host->mmio = devm_ioremap_resource(dev, res); if (IS_ERR(host->mmio)) { - ret = PTR_ERR(host->mmio); dev_err(dev, "devm_ioremap_resource[1] fail\n"); - goto err_res; + return PTR_ERR(host->mmio); } mtd->name = "hisi_nand"; @@ -770,19 +766,17 @@ static int hisi_nfc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host); if (ret) { dev_err(dev, "failed to request IRQ\n"); - goto err_res; + return ret; } ret = nand_scan_ident(mtd, max_chips, NULL); if (ret) - goto err_res; + return ret; host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize, &host->dma_buffer, GFP_KERNEL); - if (!host->buffer) { - ret = -ENOMEM; - goto err_res; - } + if (!host->buffer) + return -ENOMEM; host->dma_oob = host->dma_buffer + mtd->writesize; memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize); @@ -798,8 +792,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) */ default: dev_err(dev, "NON-2KB page size nand flash\n"); - ret = -EINVAL; - goto err_res; + return -EINVAL; } hinfc_write(host, flag, HINFC504_CON); @@ -809,21 +802,17 @@ static int hisi_nfc_probe(struct platform_device *pdev) ret = nand_scan_tail(mtd); if (ret) { dev_err(dev, "nand_scan_tail failed: %d\n", ret); - goto err_res; + return ret; } ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(dev, "Err MTD partition=%d\n", ret); - goto err_mtd; + nand_release(mtd); + return ret; } return 0; - -err_mtd: - nand_release(mtd); -err_res: - return ret; } static int hisi_nfc_remove(struct platform_device *pdev) From 6f533c463105472b71587b9bf8532f34f9d60deb Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:39 +0200 Subject: [PATCH 14/32] mtd: rawnand: hisi504: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup() and not a nand_release(). The latter doing an mtd_device_unregister() which is not needed if mtd_device_register() failed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/hisi504_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index 3c64a6281b54..a1e009c8e556 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -808,7 +808,7 @@ static int hisi_nfc_probe(struct platform_device *pdev) ret = mtd_device_register(mtd, NULL, 0); if (ret) { dev_err(dev, "Err MTD partition=%d\n", ret); - nand_release(mtd); + nand_cleanup(chip); return ret; } From ed64cb1df48152743a927a1187413de35b940441 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:40 +0200 Subject: [PATCH 15/32] mtd: rawnand: lpc32xx_mlc: clean the probe function Before fixing the error path of the probe function, fix the style of the entire function and particularly the goto labels: they should indicate what the next cleanup to do is. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index e357948a7505..6e31faf2f07f 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -673,7 +673,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) host->io_base = devm_ioremap_resource(&pdev->dev, rc); if (IS_ERR(host->io_base)) return PTR_ERR(host->io_base); - + host->io_base_phy = rc->start; nand_chip = &host->nand_chip; @@ -706,11 +706,11 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "Clock initialization failure\n"); res = -ENOENT; - goto err_exit1; + goto free_gpio; } res = clk_prepare_enable(host->clk); if (res) - goto err_put_clk; + goto put_clk; nand_chip->cmd_ctrl = lpc32xx_nand_cmd_ctrl; nand_chip->dev_ready = lpc32xx_nand_device_ready; @@ -744,7 +744,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = lpc32xx_dma_setup(host); if (res) { res = -EIO; - goto err_exit2; + goto unprepare_clk; } } @@ -754,18 +754,18 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) */ res = nand_scan_ident(mtd, 1, NULL); if (res) - goto err_exit3; + goto release_dma_chan; host->dma_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); if (!host->dma_buf) { res = -ENOMEM; - goto err_exit3; + goto release_dma_chan; } host->dummy_buf = devm_kzalloc(&pdev->dev, mtd->writesize, GFP_KERNEL); if (!host->dummy_buf) { res = -ENOMEM; - goto err_exit3; + goto release_dma_chan; } nand_chip->ecc.mode = NAND_ECC_HW; @@ -783,14 +783,14 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (host->irq < 0) { dev_err(&pdev->dev, "failed to get platform irq\n"); res = -EINVAL; - goto err_exit3; + goto release_dma_chan; } if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq, IRQF_TRIGGER_HIGH, DRV_NAME, host)) { dev_err(&pdev->dev, "Error requesting NAND IRQ\n"); res = -ENXIO; - goto err_exit3; + goto release_dma_chan; } /* @@ -799,27 +799,29 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) */ res = nand_scan_tail(mtd); if (res) - goto err_exit4; + goto free_irq; mtd->name = DRV_NAME; res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); - if (!res) - return res; + if (res) + goto release_nand; + return 0; + +release_nand: nand_release(mtd); - -err_exit4: +free_irq: free_irq(host->irq, host); -err_exit3: +release_dma_chan: if (use_dma) dma_release_channel(host->dma_chan); -err_exit2: +unprepare_clk: clk_disable_unprepare(host->clk); -err_put_clk: +put_clk: clk_put(host->clk); -err_exit1: +free_gpio: lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); From 838c07b05b369b3ee6bb18cdc1aec1241a1ef951 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:41 +0200 Subject: [PATCH 16/32] mtd: rawnand: lpc32xx_mlc: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup() and not a nand_release(). The latter doing an mtd_device_unregister() which is not needed if mtd_device_register() failed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 6e31faf2f07f..052d123a8304 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -806,12 +806,12 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); if (res) - goto release_nand; + goto cleanup_nand; return 0; -release_nand: - nand_release(mtd); +cleanup_nand: + nand_cleanup(nand_chip); free_irq: free_irq(host->irq, host); release_dma_chan: From e0ea20bfb46529c9e98165c8135ea6d2c64a2bac Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:42 +0200 Subject: [PATCH 17/32] mtd: rawnand: lpc32xx_slc: clean the probe function Before fixing the error path of the probe function, fix the style of the entire function and particularly the goto labels: they should indicate what the next cleanup to do is. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_slc.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 5f7cc6da0a7f..064c9389ee0c 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -831,11 +831,11 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "Clock failure\n"); res = -ENOENT; - goto err_exit1; + goto enable_wp; } res = clk_prepare_enable(host->clk); if (res) - goto err_exit1; + goto enable_wp; /* Set NAND IO addresses and command/ready functions */ chip->IO_ADDR_R = SLC_DATA(host->io_base); @@ -874,19 +874,19 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) GFP_KERNEL); if (host->data_buf == NULL) { res = -ENOMEM; - goto err_exit2; + goto unprepare_clk; } res = lpc32xx_nand_dma_setup(host); if (res) { res = -EIO; - goto err_exit2; + goto unprepare_clk; } /* Find NAND device */ res = nand_scan_ident(mtd, 1, NULL); if (res) - goto err_exit3; + goto release_dma; /* OOB and ECC CPU and DMA work areas */ host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); @@ -920,21 +920,23 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) */ res = nand_scan_tail(mtd); if (res) - goto err_exit3; + goto release_dma; mtd->name = "nxp_lpc3220_slc"; res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); - if (!res) - return res; + if (res) + goto release_nand; + return 0; + +release_nand: nand_release(mtd); - -err_exit3: +release_dma: dma_release_channel(host->dma_chan); -err_exit2: +unprepare_clk: clk_disable_unprepare(host->clk); -err_exit1: +enable_wp: lpc32xx_wp_enable(host); return res; From 553b0c6416e4426d0c8e552ff809c0a590dc3989 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 21 Apr 2018 20:00:43 +0200 Subject: [PATCH 18/32] mtd: rawnand: lpc32xx_slc: fix the probe function error path An error after nand_scan_tail() should trigger a nand_cleanup() and not a nand_release(). The latter doing an mtd_device_unregister() which is not needed if mtd_device_register() failed. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/lpc32xx_slc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 064c9389ee0c..42820aa1abab 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -926,12 +926,12 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) res = mtd_device_register(mtd, host->ncfg->parts, host->ncfg->num_parts); if (res) - goto release_nand; + goto cleanup_nand; return 0; -release_nand: - nand_release(mtd); +cleanup_nand: + nand_cleanup(chip); release_dma: dma_release_channel(host->dma_chan); unprepare_clk: From 256c4fc76a80a69a5108069d8a09b3836bbf6542 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sun, 22 Apr 2018 18:02:30 +0200 Subject: [PATCH 19/32] mtd: rawnand: add a way to pass an ID table with nand_scan() As part of the work of migrating all the drivers to nand_scan(), and because nand_scan() does not provide a way to pass an ID table, rename the function nand_scan_with_ids() and add a third parameter to give a flash ID table (like what was done with nand_scan_ident()). Create a nand_scan() helper that is just a wrapper of nand_scan_with_ids(), passing NULL as the ID table. This way a controller drivers can continue using nand_scan() transparently. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 10 ++++++---- include/linux/mtd/rawnand.h | 9 ++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 72f3a89da513..414a2a3a91d0 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6630,24 +6630,26 @@ EXPORT_SYMBOL(nand_scan_tail); #endif /** - * nand_scan - [NAND Interface] Scan for the NAND device + * nand_scan_with_ids - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: number of chips to scan for + * @ids: optional flash IDs table * * This fills out all the uninitialized function pointers with the defaults. * The flash ID is read and the mtd/chip structures are filled with the * appropriate values. */ -int nand_scan(struct mtd_info *mtd, int maxchips) +int nand_scan_with_ids(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *ids) { int ret; - ret = nand_scan_ident(mtd, maxchips, NULL); + ret = nand_scan_ident(mtd, maxchips, ids); if (!ret) ret = nand_scan_tail(mtd); return ret; } -EXPORT_SYMBOL(nand_scan); +EXPORT_SYMBOL(nand_scan_with_ids); /** * nand_cleanup - [NAND Interface] Free resources held by the NAND device diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 5dad59b31244..ba8d908f5cc7 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -28,7 +28,14 @@ struct nand_flash_dev; struct device_node; /* Scan and identify a NAND device */ -int nand_scan(struct mtd_info *mtd, int max_chips); +int nand_scan_with_ids(struct mtd_info *mtd, int max_chips, + struct nand_flash_dev *ids); + +static inline int nand_scan(struct mtd_info *mtd, int max_chips) +{ + return nand_scan_with_ids(mtd, max_chips, NULL); +} + /* * Separate phases of nand_scan(), allowing board driver to intervene * and override command or ECC setup according to flash type. From c3ee3f3db1facb2b83bba003aacb17c73e710764 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:22 +0200 Subject: [PATCH 20/32] mtd: rawnand: gpmi: drop dma_ops_type The GPMI nand driver puts dma_ops_type in its private data struct. Based on the ops type the DMA callback handler unmaps previously mapped buffers. Instead of unmapping the buffers in the DMA callback handler, do this in the caller directly which waits for the DMA transfer to finish. This makes the whole dma_ops_type mechanism unnecessary. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 36 +++++++++++++--------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 31 ++----------------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 11 ------- 3 files changed, 23 insertions(+), 55 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index e94556705dc7..d479358758a0 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -544,19 +544,13 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) return reg & mask; } -static inline void set_dma_type(struct gpmi_nand_data *this, - enum dma_ops_type type) -{ - this->last_dma_type = this->dma_type; - this->dma_type = type; -} - int gpmi_send_command(struct gpmi_nand_data *this) { struct dma_chan *channel = get_dma_chan(this); struct dma_async_tx_descriptor *desc; struct scatterlist *sgl; int chip = this->current_chip; + int ret; u32 pio[3]; /* [1] send out the PIO words */ @@ -586,8 +580,11 @@ int gpmi_send_command(struct gpmi_nand_data *this) return -EINVAL; /* [3] submit the DMA */ - set_dma_type(this, DMA_FOR_COMMAND); - return start_dma_without_bch_irq(this, desc); + ret = start_dma_without_bch_irq(this, desc); + + dma_unmap_sg(this->dev, sgl, 1, DMA_TO_DEVICE); + + return ret; } int gpmi_send_data(struct gpmi_nand_data *this) @@ -595,6 +592,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); int chip = this->current_chip; + int ret; uint32_t command_mode; uint32_t address; u32 pio[2]; @@ -624,8 +622,11 @@ int gpmi_send_data(struct gpmi_nand_data *this) return -EINVAL; /* [3] submit the DMA */ - set_dma_type(this, DMA_FOR_WRITE_DATA); - return start_dma_without_bch_irq(this, desc); + ret = start_dma_without_bch_irq(this, desc); + + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); + + return ret; } int gpmi_read_data(struct gpmi_nand_data *this) @@ -633,6 +634,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); int chip = this->current_chip; + int ret; u32 pio[2]; /* [1] : send PIO */ @@ -658,8 +660,14 @@ int gpmi_read_data(struct gpmi_nand_data *this) return -EINVAL; /* [3] : submit the DMA */ - set_dma_type(this, DMA_FOR_READ_DATA); - return start_dma_without_bch_irq(this, desc); + + ret = start_dma_without_bch_irq(this, desc); + + dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); + if (this->direct_dma_map_ok == false) + memcpy(this->upper_buf, this->data_buffer_dma, this->upper_len); + + return ret; } int gpmi_send_page(struct gpmi_nand_data *this, @@ -703,7 +711,6 @@ int gpmi_send_page(struct gpmi_nand_data *this, if (!desc) return -EINVAL; - set_dma_type(this, DMA_FOR_WRITE_ECC_PAGE); return start_dma_with_bch_irq(this, desc); } @@ -785,7 +792,6 @@ int gpmi_read_page(struct gpmi_nand_data *this, return -EINVAL; /* [4] submit the DMA */ - set_dma_type(this, DMA_FOR_READ_ECC_PAGE); return start_dma_with_bch_irq(this, desc); } diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index abd5146931fa..6252776b32ca 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -482,31 +482,6 @@ static void dma_irq_callback(void *param) struct gpmi_nand_data *this = param; struct completion *dma_c = &this->dma_done; - switch (this->dma_type) { - case DMA_FOR_COMMAND: - dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); - if (this->direct_dma_map_ok == false) - memcpy(this->upper_buf, this->data_buffer_dma, - this->upper_len); - break; - - case DMA_FOR_WRITE_DATA: - dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_TO_DEVICE); - break; - - case DMA_FOR_READ_ECC_PAGE: - case DMA_FOR_WRITE_ECC_PAGE: - /* We have to wait the BCH interrupt to finish. */ - break; - - default: - dev_err(this->dev, "in wrong DMA operation.\n"); - } - complete(dma_c); } @@ -526,8 +501,7 @@ int start_dma_without_bch_irq(struct gpmi_nand_data *this, /* Wait for the interrupt from the DMA block. */ timeout = wait_for_completion_timeout(dma_c, msecs_to_jiffies(1000)); if (!timeout) { - dev_err(this->dev, "DMA timeout, last DMA :%d\n", - this->last_dma_type); + dev_err(this->dev, "DMA timeout, last DMA\n"); gpmi_dump_info(this); return -ETIMEDOUT; } @@ -556,8 +530,7 @@ int start_dma_with_bch_irq(struct gpmi_nand_data *this, /* Wait for the interrupt from the BCH block. */ timeout = wait_for_completion_timeout(bch_c, msecs_to_jiffies(1000)); if (!timeout) { - dev_err(this->dev, "BCH timeout, last DMA :%d\n", - this->last_dma_type); + dev_err(this->dev, "BCH timeout\n"); gpmi_dump_info(this); return -ETIMEDOUT; } diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 62fde59b995f..2397010a8963 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -77,15 +77,6 @@ struct boot_rom_geometry { unsigned int search_area_stride_exponent; }; -/* DMA operations types */ -enum dma_ops_type { - DMA_FOR_COMMAND = 1, - DMA_FOR_READ_DATA, - DMA_FOR_WRITE_DATA, - DMA_FOR_READ_ECC_PAGE, - DMA_FOR_WRITE_ECC_PAGE -}; - enum gpmi_type { IS_MX23, IS_MX28, @@ -178,8 +169,6 @@ struct gpmi_nand_data { /* DMA channels */ #define DMA_CHANS 8 struct dma_chan *dma_chans[DMA_CHANS]; - enum dma_ops_type last_dma_type; - enum dma_ops_type dma_type; struct completion dma_done; /* private */ From ba3900e6fac59978d379982e843876183298f28a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:23 +0200 Subject: [PATCH 21/32] mtd: rawnand: gpmi: pass buffer and len around Instead of putting the buffer and len passed in from the mtd core into the private data struct, just pass it around in the GPMI drivers functions. This makes the lifetime of the variables more clear and the code easier to follow. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 14 +++++++------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 20 ++++++++------------ drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 11 ++++------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index d479358758a0..447961b798b4 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -587,7 +587,7 @@ int gpmi_send_command(struct gpmi_nand_data *this) return ret; } -int gpmi_send_data(struct gpmi_nand_data *this) +int gpmi_send_data(struct gpmi_nand_data *this, const void *buf, int len) { struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); @@ -606,7 +606,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(address) - | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + | BF_GPMI_CTRL0_XFER_COUNT(len); pio[1] = 0; desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); @@ -614,7 +614,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) return -EINVAL; /* [2] send DMA request */ - prepare_data_dma(this, DMA_TO_DEVICE); + prepare_data_dma(this, buf, len, DMA_TO_DEVICE); desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -629,7 +629,7 @@ int gpmi_send_data(struct gpmi_nand_data *this) return ret; } -int gpmi_read_data(struct gpmi_nand_data *this) +int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) { struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); @@ -643,7 +643,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) | BF_GPMI_CTRL0_CS(chip, this) | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) - | BF_GPMI_CTRL0_XFER_COUNT(this->upper_len); + | BF_GPMI_CTRL0_XFER_COUNT(len); pio[1] = 0; desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, @@ -652,7 +652,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) return -EINVAL; /* [2] : send DMA request */ - prepare_data_dma(this, DMA_FROM_DEVICE); + prepare_data_dma(this, buf, len, DMA_FROM_DEVICE); desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -665,7 +665,7 @@ int gpmi_read_data(struct gpmi_nand_data *this) dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); if (this->direct_dma_map_ok == false) - memcpy(this->upper_buf, this->data_buffer_dma, this->upper_len); + memcpy(buf, this->data_buffer_dma, len); return ret; } diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 6252776b32ca..c66712e594f7 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -447,15 +447,15 @@ struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) } /* Can we use the upper's buffer directly for DMA? */ -void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) +void prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, + enum dma_data_direction dr) { struct scatterlist *sgl = &this->data_sgl; int ret; /* first try to map the upper buffer directly */ - if (virt_addr_valid(this->upper_buf) && - !object_is_on_stack(this->upper_buf)) { - sg_init_one(sgl, this->upper_buf, this->upper_len); + if (virt_addr_valid(buf) && !object_is_on_stack(buf)) { + sg_init_one(sgl, buf, len); ret = dma_map_sg(this->dev, sgl, 1, dr); if (ret == 0) goto map_fail; @@ -466,10 +466,10 @@ void prepare_data_dma(struct gpmi_nand_data *this, enum dma_data_direction dr) map_fail: /* We have to use our own DMA buffer. */ - sg_init_one(sgl, this->data_buffer_dma, this->upper_len); + sg_init_one(sgl, this->data_buffer_dma, len); if (dr == DMA_TO_DEVICE) - memcpy(this->data_buffer_dma, this->upper_buf, this->upper_len); + memcpy(this->data_buffer_dma, buf, len); dma_map_sg(this->dev, sgl, 1, dr); @@ -929,10 +929,8 @@ static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) struct gpmi_nand_data *this = nand_get_controller_data(chip); dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = buf; - this->upper_len = len; - gpmi_read_data(this); + gpmi_read_data(this, buf, len); } static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) @@ -941,10 +939,8 @@ static void gpmi_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) struct gpmi_nand_data *this = nand_get_controller_data(chip); dev_dbg(this->dev, "len is %d\n", len); - this->upper_buf = (uint8_t *)buf; - this->upper_len = len; - gpmi_send_data(this); + gpmi_send_data(this, buf, len); } static uint8_t gpmi_read_byte(struct mtd_info *mtd) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index 2397010a8963..fba72ad28263 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -141,10 +141,6 @@ struct gpmi_nand_data { int current_chip; unsigned int command_length; - /* passed from upper layer */ - uint8_t *upper_buf; - int upper_len; - /* for DMA operations */ bool direct_dma_map_ok; @@ -178,7 +174,7 @@ struct gpmi_nand_data { /* Common Services */ int common_nfc_set_geometry(struct gpmi_nand_data *); struct dma_chan *get_dma_chan(struct gpmi_nand_data *); -void prepare_data_dma(struct gpmi_nand_data *, +void prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len, enum dma_data_direction dr); int start_dma_without_bch_irq(struct gpmi_nand_data *, struct dma_async_tx_descriptor *); @@ -197,8 +193,9 @@ int gpmi_disable_clk(struct gpmi_nand_data *this); int gpmi_setup_data_interface(struct mtd_info *mtd, int chipnr, const struct nand_data_interface *conf); void gpmi_nfc_apply_timings(struct gpmi_nand_data *this); -int gpmi_read_data(struct gpmi_nand_data *); -int gpmi_send_data(struct gpmi_nand_data *); +int gpmi_read_data(struct gpmi_nand_data *, void *buf, int len); +int gpmi_send_data(struct gpmi_nand_data *, const void *buf, int len); + int gpmi_send_page(struct gpmi_nand_data *, dma_addr_t payload, dma_addr_t auxiliary); int gpmi_read_page(struct gpmi_nand_data *, From 118c3f9b01629bdfb13c07efdcb34f8818cce3fa Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:24 +0200 Subject: [PATCH 22/32] mtd: rawnand: gpmi: put only once used functions inline read_page_prepare(), read_page_end() and read_page_swap_end() are trivial functions that are used only once and take 8 arguments each. De-obfuscate the code by open coding these functions in gpmi_ecc_read_page() Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 89 ++++++---------------- 1 file changed, 23 insertions(+), 66 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index c66712e594f7..82782e4d8eab 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -678,56 +678,6 @@ static void release_resources(struct gpmi_nand_data *this) release_dma_channels(this); } -static int read_page_prepare(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void **use_virt, dma_addr_t *use_phys) -{ - struct device *dev = this->dev; - - if (virt_addr_valid(destination)) { - dma_addr_t dest_phys; - - dest_phys = dma_map_single(dev, destination, - length, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dest_phys)) { - if (alt_size < length) { - dev_err(dev, "Alternate buffer is too small\n"); - return -ENOMEM; - } - goto map_failed; - } - *use_virt = destination; - *use_phys = dest_phys; - this->direct_dma_map_ok = true; - return 0; - } - -map_failed: - *use_virt = alt_virt; - *use_phys = alt_phys; - this->direct_dma_map_ok = false; - return 0; -} - -static inline void read_page_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (this->direct_dma_map_ok) - dma_unmap_single(this->dev, used_phys, length, DMA_FROM_DEVICE); -} - -static inline void read_page_swap_end(struct gpmi_nand_data *this, - void *destination, unsigned length, - void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, - void *used_virt, dma_addr_t used_phys) -{ - if (!this->direct_dma_map_ok) - memcpy(destination, alt_virt, length); -} - static int send_page_prepare(struct gpmi_nand_data *this, const void *source, unsigned length, void *alt_virt, dma_addr_t alt_phys, unsigned alt_size, @@ -1018,24 +968,33 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, int ret; dev_dbg(this->dev, "page number is : %d\n", page); - ret = read_page_prepare(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - &payload_virt, &payload_phys); - if (ret) { - dev_err(this->dev, "Inadequate DMA buffer\n"); - ret = -ENOMEM; - return ret; + + payload_virt = this->payload_virt; + payload_phys = this->payload_phys; + this->direct_dma_map_ok = false; + + if (virt_addr_valid(buf)) { + dma_addr_t dest_phys; + + dest_phys = dma_map_single(this->dev, buf, nfc_geo->payload_size, + DMA_FROM_DEVICE); + if (!dma_mapping_error(this->dev, dest_phys)) { + payload_virt = buf; + payload_phys = dest_phys; + this->direct_dma_map_ok = true; + } } + auxiliary_virt = this->auxiliary_virt; auxiliary_phys = this->auxiliary_phys; /* go! */ ret = gpmi_read_page(this, payload_phys, auxiliary_phys); - read_page_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + + if (this->direct_dma_map_ok) + dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, + DMA_FROM_DEVICE); + if (ret) { dev_err(this->dev, "Error in ECC-based read: %d\n", ret); return ret; @@ -1044,10 +1003,8 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, /* Loop over status bytes, accumulating ECC status. */ status = auxiliary_virt + nfc_geo->auxiliary_status_offset; - read_page_swap_end(this, buf, nfc_geo->payload_size, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); + if (!this->direct_dma_map_ok) + memcpy(buf, this->payload_virt, nfc_geo->payload_size); for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) From 111bfed4f30ff16e1d47e4ce3a1f427882f365d3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:25 +0200 Subject: [PATCH 23/32] mtd: rawnand: gpmi: remove direct_dma_map_ok from driver data struct Instead of putting direct_dma_map_ok into driver struct pass it around between functions to make the code more readable. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 5 +++-- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 15 +++++++-------- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h | 5 +---- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 447961b798b4..39834bedf460 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -636,6 +636,7 @@ int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) int chip = this->current_chip; int ret; u32 pio[2]; + bool direct; /* [1] : send PIO */ pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) @@ -652,7 +653,7 @@ int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) return -EINVAL; /* [2] : send DMA request */ - prepare_data_dma(this, buf, len, DMA_FROM_DEVICE); + direct = prepare_data_dma(this, buf, len, DMA_FROM_DEVICE); desc = dmaengine_prep_slave_sg(channel, &this->data_sgl, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); @@ -664,7 +665,7 @@ int gpmi_read_data(struct gpmi_nand_data *this, void *buf, int len) ret = start_dma_without_bch_irq(this, desc); dma_unmap_sg(this->dev, &this->data_sgl, 1, DMA_FROM_DEVICE); - if (this->direct_dma_map_ok == false) + if (!direct) memcpy(buf, this->data_buffer_dma, len); return ret; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 82782e4d8eab..148faee6a543 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -447,7 +447,7 @@ struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) } /* Can we use the upper's buffer directly for DMA? */ -void prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, +bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, enum dma_data_direction dr) { struct scatterlist *sgl = &this->data_sgl; @@ -460,8 +460,7 @@ void prepare_data_dma(struct gpmi_nand_data *this, const void *buf, int len, if (ret == 0) goto map_fail; - this->direct_dma_map_ok = true; - return; + return true; } map_fail: @@ -473,7 +472,7 @@ map_fail: dma_map_sg(this->dev, sgl, 1, dr); - this->direct_dma_map_ok = false; + return false; } /* This will be called after the DMA operation is finished. */ @@ -966,12 +965,12 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, unsigned char *status; unsigned int max_bitflips = 0; int ret; + bool direct = false; dev_dbg(this->dev, "page number is : %d\n", page); payload_virt = this->payload_virt; payload_phys = this->payload_phys; - this->direct_dma_map_ok = false; if (virt_addr_valid(buf)) { dma_addr_t dest_phys; @@ -981,7 +980,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, if (!dma_mapping_error(this->dev, dest_phys)) { payload_virt = buf; payload_phys = dest_phys; - this->direct_dma_map_ok = true; + direct = true; } } @@ -991,7 +990,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, /* go! */ ret = gpmi_read_page(this, payload_phys, auxiliary_phys); - if (this->direct_dma_map_ok) + if (direct) dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, DMA_FROM_DEVICE); @@ -1003,7 +1002,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, /* Loop over status bytes, accumulating ECC status. */ status = auxiliary_virt + nfc_geo->auxiliary_status_offset; - if (!this->direct_dma_map_ok) + if (!direct) memcpy(buf, this->payload_virt, nfc_geo->payload_size); for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) { diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h index fba72ad28263..6aa10d6962d6 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.h @@ -141,9 +141,6 @@ struct gpmi_nand_data { int current_chip; unsigned int command_length; - /* for DMA operations */ - bool direct_dma_map_ok; - struct scatterlist cmd_sgl; char *cmd_buffer; @@ -174,7 +171,7 @@ struct gpmi_nand_data { /* Common Services */ int common_nfc_set_geometry(struct gpmi_nand_data *); struct dma_chan *get_dma_chan(struct gpmi_nand_data *); -void prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len, +bool prepare_data_dma(struct gpmi_nand_data *, const void *buf, int len, enum dma_data_direction dr); int start_dma_without_bch_irq(struct gpmi_nand_data *, struct dma_async_tx_descriptor *); From e637f5fe8e0cbee455b75afbc0cf2a0cc76997f4 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:26 +0200 Subject: [PATCH 24/32] mtd: rawnand: gpmi: return valid value from bch_set_geometry() The caller of bch_set_geometry() expects the return value to be an error code, so !0 is not valid. return the error from the just called function instead. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c index 39834bedf460..83697b8df871 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-lib.c @@ -258,8 +258,9 @@ int bch_set_geometry(struct gpmi_nand_data *this) unsigned int gf_len; int ret; - if (common_nfc_set_geometry(this)) - return !0; + ret = common_nfc_set_geometry(this); + if (ret) + return ret; block_count = bch_geo->ecc_chunk_count - 1; block_size = bch_geo->ecc_chunk_size; From f6b74db84cd1ab7c447e412a4b83f9933a68589b Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 26 Apr 2018 17:41:27 +0200 Subject: [PATCH 25/32] mtd: rawnand: gpmi: remove unnecessary variables Use this->auxiliary_virt and this->auxiliary_phys directly rather than creating extra local variables for them. Signed-off-by: Sascha Hauer Signed-off-by: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 148faee6a543..f6aa358a3452 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -959,8 +959,6 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, struct mtd_info *mtd = nand_to_mtd(chip); void *payload_virt; dma_addr_t payload_phys; - void *auxiliary_virt; - dma_addr_t auxiliary_phys; unsigned int i; unsigned char *status; unsigned int max_bitflips = 0; @@ -984,11 +982,8 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, } } - auxiliary_virt = this->auxiliary_virt; - auxiliary_phys = this->auxiliary_phys; - /* go! */ - ret = gpmi_read_page(this, payload_phys, auxiliary_phys); + ret = gpmi_read_page(this, payload_phys, this->auxiliary_phys); if (direct) dma_unmap_single(this->dev, payload_phys, nfc_geo->payload_size, @@ -1000,7 +995,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, } /* Loop over status bytes, accumulating ECC status. */ - status = auxiliary_virt + nfc_geo->auxiliary_status_offset; + status = this->auxiliary_virt + nfc_geo->auxiliary_status_offset; if (!direct) memcpy(buf, this->payload_virt, nfc_geo->payload_size); @@ -1058,7 +1053,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, buf + i * nfc_geo->ecc_chunk_size, nfc_geo->ecc_chunk_size, eccbuf, eccbytes, - auxiliary_virt, + this->auxiliary_virt, nfc_geo->metadata_size, nfc_geo->ecc_strength); } else { @@ -1086,7 +1081,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, } /* handle the block mark swapping */ - block_mark_swapping(this, buf, auxiliary_virt); + block_mark_swapping(this, buf, this->auxiliary_virt); if (oob_required) { /* @@ -1100,7 +1095,7 @@ static int gpmi_ecc_read_page_data(struct nand_chip *chip, * the block mark. */ memset(chip->oob_poi, ~0, mtd->oobsize); - chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; + chip->oob_poi[0] = ((uint8_t *)this->auxiliary_virt)[0]; } return max_bitflips; From 4baf6819111a3e29d13456c0e7816beec0e457f2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sun, 22 Apr 2018 19:55:53 +0200 Subject: [PATCH 26/32] mtd: nand: add myself as NAND co-maintainer I have been actively reviewing and contributing improvements to the NAND subsystem for more than 6 months now and am willing to continue doing so in the future. Formalize my new role. Signed-off-by: Miquel Raynal Acked-by: Richard Weinberger Signed-off-by: Boris Brezillon --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0eff9eba4d28..634abf4569c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9581,6 +9581,7 @@ F: drivers/net/ethernet/myricom/myri10ge/ NAND FLASH SUBSYSTEM M: Boris Brezillon +M: Miquel Raynal R: Richard Weinberger L: linux-mtd@lists.infradead.org W: http://www.linux-mtd.infradead.org/ From bb415dff3ab73b0e97f031dbc7ca708f76739643 Mon Sep 17 00:00:00 2001 From: Xiaolei Li Date: Tue, 24 Apr 2018 10:11:07 +0800 Subject: [PATCH 27/32] dt-bindings: mtd: mtk-nand: Update properties description Update ecc step size, ecc strength, and parity bits supported on each MTK NAND controller. Signed-off-by: Xiaolei Li Signed-off-by: Boris Brezillon --- .../devicetree/bindings/mtd/mtk-nand.txt | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/mtk-nand.txt b/Documentation/devicetree/bindings/mtd/mtk-nand.txt index 1c88526dedfc..f20ab4abd214 100644 --- a/Documentation/devicetree/bindings/mtd/mtk-nand.txt +++ b/Documentation/devicetree/bindings/mtd/mtk-nand.txt @@ -50,14 +50,19 @@ Optional: - nand-on-flash-bbt: Store BBT on NAND Flash. - nand-ecc-mode: the NAND ecc mode (check driver for supported modes) - nand-ecc-step-size: Number of data bytes covered by a single ECC step. - valid values: 512 and 1024. + valid values: + 512 and 1024 on mt2701 and mt2712. + 512 only on mt7622. 1024 is recommended for large page NANDs. - nand-ecc-strength: Number of bits to correct per ECC step. - The valid values that the controller supports are: 4, 6, - 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44, - 48, 52, 56, 60. + The valid values that each controller supports: + mt2701: 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60. + mt2712: 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, + 32, 36, 40, 44, 48, 52, 56, 60, 68, 72, 80. + mt7622: 4, 6, 8, 10, 12, 14, 16. The strength should be calculated as follows: - E = (S - F) * 8 / 14 + E = (S - F) * 8 / B S = O / (P / Q) E : nand-ecc-strength. S : spare size per sector. @@ -66,6 +71,15 @@ Optional: O : oob size. P : page size. Q : nand-ecc-step-size. + B : number of parity bits needed to correct + 1 bitflip. + According to MTK NAND controller design, + this number depends on max ecc step size + that MTK NAND controller supports. + If max ecc step size supported is 1024, + then it should be always 14. And if max + ecc step size is 512, then it should be + always 13. If the result does not match any one of the listed choices above, please select the smaller valid value from the list. From a75bbe71a27875fdc61cde1af6d799037cef6bed Mon Sep 17 00:00:00 2001 From: Jane Wan Date: Tue, 8 May 2018 14:19:53 -0700 Subject: [PATCH 28/32] mtd: rawnand: fsl_ifc: fix FSL NAND driver to read all ONFI parameter pages Per ONFI specification (Rev. 4.0), if the CRC of the first parameter page read is not valid, the host should read redundant parameter page copies. Fix FSL NAND driver to read the two redundant copies which are mandatory in the specification. Signed-off-by: Jane Wan Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 00a609d4473e..382b67e97174 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -342,9 +342,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, case NAND_CMD_READID: case NAND_CMD_PARAM: { + /* + * For READID, read 8 bytes that are currently used. + * For PARAM, read all 3 copies of 256-bytes pages. + */ + int len = 8; int timing = IFC_FIR_OP_RB; - if (command == NAND_CMD_PARAM) + if (command == NAND_CMD_PARAM) { timing = IFC_FIR_OP_RBCD; + len = 256 * 3; + } ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | @@ -354,12 +361,8 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, &ifc->ifc_nand.nand_fcr0); ifc_out32(column, &ifc->ifc_nand.row3); - /* - * although currently it's 8 bytes for READID, we always read - * the maximum 256 bytes(for PARAM) - */ - ifc_out32(256, &ifc->ifc_nand.nand_fbcr); - ifc_nand_ctrl->read_bytes = 256; + ifc_out32(len, &ifc->ifc_nand.nand_fbcr); + ifc_nand_ctrl->read_bytes = len; set_addr(mtd, 0, 0, 0); fsl_ifc_run_command(mtd); From a676688032e819c4bb1999a422553a1a1f52ce45 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 May 2018 06:35:52 -0300 Subject: [PATCH 29/32] mtd: rawnand.h: use nested union kernel-doc markups Gets rid of those warnings and better document the parameters. ./include/linux/mtd/rawnand.h:752: warning: Function parameter or member 'timings.sdr' not described in 'nand_data_interface' ./include/linux/mtd/rawnand.h:817: warning: Function parameter or member 'buf' not described in 'nand_op_data_instr' ./include/linux/mtd/rawnand.h:817: warning: Function parameter or member 'buf.in' not described in 'nand_op_data_instr' ./include/linux/mtd/rawnand.h:817: warning: Function parameter or member 'buf.out' not described in 'nand_op_data_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.cmd' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.addr' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.data' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.waitrdy' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:1010: warning: Function parameter or member 'ctx' not described in 'nand_op_parser_pattern_elem' ./include/linux/mtd/rawnand.h:1010: warning: Function parameter or member 'ctx.addr' not described in 'nand_op_parser_pattern_elem' ./include/linux/mtd/rawnand.h:1010: warning: Function parameter or member 'ctx.data' not described in 'nand_op_parser_pattern_elem' ./include/linux/mtd/rawnand.h:1313: warning: Function parameter or member 'manufacturer.desc' not described in 'nand_chip' ./include/linux/mtd/rawnand.h:1313: warning: Function parameter or member 'manufacturer.priv' not described in 'nand_chip' ./include/linux/mtd/rawnand.h:848: WARNING: Unexpected indentation. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Boris Brezillon --- include/linux/mtd/rawnand.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index ba8d908f5cc7..a206172ec23b 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -747,8 +747,9 @@ enum nand_data_interface_type { /** * struct nand_data_interface - NAND interface timing - * @type: type of the timing - * @timings: The timing, type according to @type + * @type: type of the timing + * @timings: The timing, type according to @type + * @timings.sdr: Use it when @type is %NAND_SDR_IFACE. */ struct nand_data_interface { enum nand_data_interface_type type; @@ -805,8 +806,9 @@ struct nand_op_addr_instr { /** * struct nand_op_data_instr - Definition of a data instruction * @len: number of data bytes to move - * @in: buffer to fill when reading from the NAND chip - * @out: buffer to read from when writing to the NAND chip + * @buf: buffer to fill + * @buf.in: buffer to fill when reading from the NAND chip + * @buf.out: buffer to read from when writing to the NAND chip * @force_8bit: force 8-bit access * * Please note that "in" and "out" are inverted from the ONFI specification @@ -849,9 +851,13 @@ enum nand_op_instr_type { /** * struct nand_op_instr - Instruction object * @type: the instruction type - * @cmd/@addr/@data/@waitrdy: extra data associated to the instruction. - * You'll have to use the appropriate element - * depending on @type + * @ctx: extra data associated to the instruction. You'll have to use the + * appropriate element depending on @type + * @ctx.cmd: use it if @type is %NAND_OP_CMD_INSTR + * @ctx.addr: use it if @type is %NAND_OP_ADDR_INSTR + * @ctx.data: use it if @type is %NAND_OP_DATA_IN_INSTR + * or %NAND_OP_DATA_OUT_INSTR + * @ctx.waitrdy: use it if @type is %NAND_OP_WAITRDY_INSTR * @delay_ns: delay the controller should apply after the instruction has been * issued on the bus. Most modern controllers have internal timings * control logic, and in this case, the controller driver can ignore @@ -1004,7 +1010,9 @@ struct nand_op_parser_data_constraints { * struct nand_op_parser_pattern_elem - One element of a pattern * @type: the instructuction type * @optional: whether this element of the pattern is optional or mandatory - * @addr/@data: address or data constraint (number of cycles or data length) + * @ctx: address or data constraint + * @ctx.addr: address constraint (number of cycles) + * @ctx.data: data constraint (data length) */ struct nand_op_parser_pattern_elem { enum nand_op_instr_type type; @@ -1231,6 +1239,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * devices. * @priv: [OPTIONAL] pointer to private chip data * @manufacturer: [INTERN] Contains manufacturer information + * @manufacturer.desc: [INTERN] Contains manufacturer's description + * @manufacturer.priv: [INTERN] Contains manufacturer private information */ struct nand_chip { From 39138c1f4a31f3468fa2e44aba4495c0c30da205 Mon Sep 17 00:00:00 2001 From: "Wan, Jane (Nokia - US/Sunnyvale)" Date: Sun, 13 May 2018 04:30:02 +0000 Subject: [PATCH 30/32] mtd: rawnand: use bit-wise majority to recover the ONFI param page Per ONFI specification (Rev. 4.0), if all parameter pages have invalid CRC values, the bit-wise majority may be used to recover the contents of the parameter page from the parameter page copies present. Signed-off-by: Jane Wan Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 52 +++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 414a2a3a91d0..faf10c890a3d 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5086,6 +5086,37 @@ ext_out: return ret; } +/* + * Recover data with bit-wise majority + */ +static void nand_bit_wise_majority(const void **srcbufs, + unsigned int nsrcbufs, + void *dstbuf, + unsigned int bufsize) +{ + int i, j, k; + + for (i = 0; i < bufsize; i++) { + u8 val = 0; + + for (j = 0; j < 8; j++) { + unsigned int cnt = 0; + + for (k = 0; k < nsrcbufs; k++) { + const u8 *srcbuf = srcbufs[k]; + + if (srcbuf[i] & BIT(j)) + cnt++; + } + + if (cnt > nsrcbufs / 2) + val |= BIT(j); + } + + ((u8 *)dstbuf)[i] = val; + } +} + /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. */ @@ -5102,7 +5133,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) return 0; /* ONFI chip: allocate a buffer to hold its parameter page */ - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc((sizeof(*p) * 3), GFP_KERNEL); if (!p) return -ENOMEM; @@ -5113,21 +5144,32 @@ static int nand_flash_detect_onfi(struct nand_chip *chip) } for (i = 0; i < 3; i++) { - ret = nand_read_data_op(chip, p, sizeof(*p), true); + ret = nand_read_data_op(chip, &p[i], sizeof(*p), true); if (ret) { ret = 0; goto free_onfi_param_page; } - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) == le16_to_cpu(p->crc)) { + if (i) + memcpy(p, &p[i], sizeof(*p)); break; } } if (i == 3) { - pr_err("Could not find valid ONFI parameter page; aborting\n"); - goto free_onfi_param_page; + const void *srcbufs[3] = {p, p + 1, p + 2}; + + pr_warn("Could not find a valid ONFI parameter page, trying bit-wise majority to recover it\n"); + nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p, + sizeof(*p)); + + if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) != + le16_to_cpu(p->crc)) { + pr_err("ONFI parameter recovery failed, aborting\n"); + goto free_onfi_param_page; + } } /* Check version */ From 782d1967d0479ffd59412b2f3179c8bb35f50ff6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Fri, 11 May 2018 14:44:07 +0200 Subject: [PATCH 31/32] mtd: rawnand: Do not check FAIL bit when executing a SET_FEATURES op The ONFI spec clearly says that FAIL bit is only valid for PROGRAM, ERASE and READ-with-on-die-ECC operations, and should be ignored otherwise. It seems that checking it after sending a SET_FEATURES is a bad idea because a previous READ, PROGRAM or ERASE op may have failed, and depending on the implementation, the FAIL bit is not cleared until a new READ, PROGRAM or ERASE is started. This leads to ->set_features() returning -EIO while it actually worked, which can sometimes stop a batch of READ/PROGRAM ops. Note that we only fix the ->exec_op() path here, because some drivers are abusing the NAND_STATUS_FAIL flag in their ->waitfunc() implementation to propagate other kind of errors, like wait-ready-timeout or controller-related errors. Let's not try to fix those drivers since they worked fine so far. Fixes: 8878b126df76 ("mtd: nand: add ->exec_op() implementation") Cc: stable@vger.kernel.org Signed-off-by: Boris Brezillon Acked-by: Miquel Raynal --- drivers/mtd/nand/raw/nand_base.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index faf10c890a3d..327c60b06f87 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -2169,7 +2169,6 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, struct mtd_info *mtd = nand_to_mtd(chip); const u8 *params = data; int i, ret; - u8 status; if (chip->exec_op) { const struct nand_sdr_timings *sdr = @@ -2183,26 +2182,18 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, }; struct nand_operation op = NAND_OPERATION(instrs); - ret = nand_exec_op(chip, &op); - if (ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - } else { - chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); - for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) - chip->write_byte(mtd, params[i]); - - ret = chip->waitfunc(mtd, chip); - if (ret < 0) - return ret; - - status = ret; + return nand_exec_op(chip, &op); } - if (status & NAND_STATUS_FAIL) + chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1); + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) + chip->write_byte(mtd, params[i]); + + ret = chip->waitfunc(mtd, chip); + if (ret < 0) + return ret; + + if (ret & NAND_STATUS_FAIL) return -EIO; return 0; From f567c71f4f91d173795a2f46d6fc493387ce084d Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 15 May 2018 17:08:21 +0200 Subject: [PATCH 32/32] mtd: nand: Pass mode information to nand_page_io_req The NAND sub-layers are likely to need the MTD_OPS_XXX mode information in order to decide if they should enable/disable ECC or how they should place the OOB bytes in the provided OOB buffer. Add a field to nand_page_io_req to pass this information. Signed-off-by: Boris Brezillon Reviewed-by: Miquel Raynal Tested-by: Frieder Schrempf Reviewed-by: Frieder Schrempf --- include/linux/mtd/nand.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 792ea5c26329..abe975c87b90 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -86,6 +86,7 @@ struct nand_pos { * @ooboffs: the OOB offset within the page * @ooblen: the number of OOB bytes to read from/write to this page * @oobbuf: buffer to store OOB data in or get OOB data from + * @mode: one of the %MTD_OPS_XXX mode * * This object is used to pass per-page I/O requests to NAND sub-layers. This * way all useful information are already formatted in a useful way and @@ -106,6 +107,7 @@ struct nand_page_io_req { const void *out; void *in; } oobbuf; + int mode; }; /** @@ -599,6 +601,7 @@ static inline void nanddev_io_iter_init(struct nand_device *nand, { struct mtd_info *mtd = nanddev_to_mtd(nand); + iter->req.mode = req->mode; iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); iter->req.ooboffs = req->ooboffs; iter->oobbytes_per_page = mtd_oobavail(mtd, req);