From 1b8d1070857da3c11307b3130eb4b05bee7d521d Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 16 Oct 2020 18:36:13 -0300 Subject: mtd: rawnand: mxc: Move the ECC engine initialization to the right place No ECC initialization should happen during the host controller probe. In fact, we need the probe function to call nand_scan() in order to: - identify the device, its capabilities and constraints (nand_scan_ident()) - configure the ECC engine accordingly (->attach_chip()) - scan its content and prepare the core (nand_scan_tail()) Moving these lines to mxcnd_attach_chip() fixes a regression caused by a previous commit supposed to clarify these steps. When moving the ECC initialization from probe() to attach(), get rid of the pdata usage to determine the engine type and let the core decide instead. Tested on a imx27-pdk board. Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Reported-by: Fabio Estevam Co-developed-by: Miquel Raynal Signed-off-by: Fabio Estevam Tested-by: Sascha Hauer Tested-by: Martin Kaiser Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201016213613.1450-1-festevam@gmail.com --- drivers/mtd/nand/raw/mxc_nand.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index d4200eb2ad32..684c51e5e60d 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1681,6 +1681,11 @@ static int mxcnd_attach_chip(struct nand_chip *chip) struct mxc_nand_host *host = nand_get_controller_data(chip); struct device *dev = mtd->dev.parent; + chip->ecc.bytes = host->devtype_data->eccbytes; + host->eccsize = host->devtype_data->eccsize; + chip->ecc.size = 512; + mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); + switch (chip->ecc.engine_type) { case NAND_ECC_ENGINE_TYPE_ON_HOST: chip->ecc.read_page = mxc_nand_read_page; @@ -1836,19 +1841,7 @@ static int mxcnd_probe(struct platform_device *pdev) if (host->devtype_data->axi_offset) host->regs_axi = host->base + host->devtype_data->axi_offset; - this->ecc.bytes = host->devtype_data->eccbytes; - host->eccsize = host->devtype_data->eccsize; - this->legacy.select_chip = host->devtype_data->select_chip; - this->ecc.size = 512; - mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); - - if (host->pdata.hw_ecc) { - this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - } else { - this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - this->ecc.algo = NAND_ECC_ALGO_HAMMING; - } /* NAND bus width determines access functions used by upper layer */ if (host->pdata.width == 2) -- cgit v1.2.3 From 3aee8a3a88fa533b74fb75640ca23001358e5476 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 16 Oct 2020 10:26:26 -0300 Subject: mtd: rawnand: ifc: Move the ECC engine initialization to the right place No ECC initialization should happen during the host controller probe. In fact, we need the probe function to call nand_scan() in order to: - identify the device, its capabilities and constraints (nand_scan_ident()) - configure the ECC engine accordingly (->attach_chip()) - scan its content and prepare the core (nand_scan_tail()) Moving these lines to fsl_ifc_attach_chip() fixes a regression caused by a previous commit supposed to clarify these steps. Based on a fix done for the mxc_nand driver by Miquel Raynal. Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Reported-by: Han Xu Signed-off-by: Fabio Estevam Tested-by: Han Xu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201016132626.30112-1-festevam@gmail.com --- drivers/mtd/nand/raw/fsl_ifc_nand.c | 43 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 0e7a9b64301e..e345f9d9f8e8 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -707,6 +707,30 @@ static int fsl_ifc_attach_chip(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); + struct fsl_ifc_ctrl *ctrl = priv->ctrl; + struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; + u32 csor; + + csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); + + /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ + if (csor & CSOR_NAND_ECC_DEC_EN) { + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); + + /* Hardware generates ECC per 512 Bytes */ + chip->ecc.size = 512; + if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { + chip->ecc.bytes = 8; + chip->ecc.strength = 4; + } else { + chip->ecc.bytes = 16; + chip->ecc.strength = 8; + } + } else { + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + } dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, nanddev_ntargets(&chip->base)); @@ -910,25 +934,6 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) return -ENODEV; } - /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ - if (csor & CSOR_NAND_ECC_DEC_EN) { - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); - - /* Hardware generates ECC per 512 Bytes */ - chip->ecc.size = 512; - if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { - chip->ecc.bytes = 8; - chip->ecc.strength = 4; - } else { - chip->ecc.bytes = 16; - chip->ecc.strength = 8; - } - } else { - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - chip->ecc.algo = NAND_ECC_ALGO_HAMMING; - } - ret = fsl_ifc_sram_init(priv); if (ret) return ret; -- cgit v1.2.3 From 69a8eed58cc09aea3b01a64997031dd5d3c02c07 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 5 Oct 2020 10:48:03 +0200 Subject: mtd: spi-nor: Don't copy self-pointing struct around spi_nor_parse_sfdp() modifies the passed structure so that it points to itself (params.erase_map.regions to params.erase_map.uniform_region). This makes it impossible to copy the local struct anywhere else. Therefore only use memcpy() in backup-restore scenario. The bug may show up like below: BUG: unable to handle page fault for address: ffffc90000b377f8 Oops: 0000 [#1] PREEMPT SMP NOPTI CPU: 4 PID: 3500 Comm: flashcp Tainted: G O 5.4.53-... #1 ... RIP: 0010:spi_nor_erase+0x8e/0x5c0 Code: 64 24 18 89 db 4d 8b b5 d0 04 00 00 4c 89 64 24 18 4c 89 64 24 20 eb 12 a8 10 0f 85 59 02 00 00 49 83 c6 10 0f 84 4f 02 00 00 <49> 8b 06 48 89 c2 48 83 e2 c0 48 89 d1 49 03 4e 08 48 39 cb 73 d8 RSP: 0018:ffffc9000217fc48 EFLAGS: 00010206 RAX: 0000000000740000 RBX: 0000000000000000 RCX: 0000000000740000 RDX: ffff8884550c9980 RSI: ffff88844f9c0bc0 RDI: ffff88844ede7bb8 RBP: 0000000000740000 R08: ffffffff815bfbe0 R09: ffff88844f9c0bc0 R10: 0000000000000000 R11: 0000000000000000 R12: ffffc9000217fc60 R13: ffff88844ede7818 R14: ffffc90000b377f8 R15: 0000000000000000 FS: 00007f4699780500(0000) GS:ffff88846ff00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: ffffc90000b377f8 CR3: 00000004538ee000 CR4: 0000000000340fe0 Call Trace: part_erase+0x27/0x50 mtdchar_ioctl+0x831/0xba0 ? filemap_map_pages+0x186/0x3d0 ? do_filp_open+0xad/0x110 ? _copy_to_user+0x22/0x30 ? cp_new_stat+0x150/0x180 mtdchar_unlocked_ioctl+0x2a/0x40 do_vfs_ioctl+0xa0/0x630 ? __do_sys_newfstat+0x3c/0x60 ksys_ioctl+0x70/0x80 __x64_sys_ioctl+0x16/0x20 do_syscall_64+0x6a/0x200 ? prepare_exit_to_usermode+0x50/0xd0 entry_SYSCALL_64_after_hwframe+0x44/0xa9 RIP: 0033:0x7f46996b6817 Cc: stable@vger.kernel.org Fixes: c46872170a54 ("mtd: spi-nor: Move erase_map to 'struct spi_nor_flash_parameter'") Co-developed-by: Matija Glavinic Pecotic Signed-off-by: Matija Glavinic Pecotic Signed-off-by: Alexander Sverdlin Signed-off-by: Vignesh Raghavendra Tested-by: Baurzhan Ismagulov Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005084803.23460-1-alexander.sverdlin@nokia.com --- drivers/mtd/spi-nor/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 0369d98b2d12..b37d6c1936de 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2701,11 +2701,10 @@ static void spi_nor_sfdp_init_params(struct spi_nor *nor) memcpy(&sfdp_params, nor->params, sizeof(sfdp_params)); - if (spi_nor_parse_sfdp(nor, &sfdp_params)) { + if (spi_nor_parse_sfdp(nor, nor->params)) { + memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); nor->addr_width = 0; nor->flags &= ~SNOR_F_4B_OPCODES; - } else { - memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); } } -- cgit v1.2.3 From 324f78dfb442b82365548b657ec4e6974c677502 Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Tue, 6 Oct 2020 15:23:46 +0200 Subject: mtd: spi-nor: Fix address width on flash chips > 16MB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a flash chip has more than 16MB capacity but its BFPT reports BFPT_DWORD1_ADDRESS_BYTES_3_OR_4, the spi-nor framework defaults to 3. The check in spi_nor_set_addr_width() doesn't catch it because addr_width did get set. This fixes that check. Fixes: f9acd7fa80be ("mtd: spi-nor: sfdp: default to addr_width of 3 for configurable widths") Signed-off-by: Bert Vermeulen Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Reviewed-by: Joel Stanley Reviewed-by: Cédric Le Goater Tested-by: Joel Stanley Tested-by: Cédric Le Goater Link: https://lore.kernel.org/r/20201006132346.12652-1-bert@biot.com --- drivers/mtd/spi-nor/core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index b37d6c1936de..f0ae7a01703a 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3008,13 +3008,15 @@ static int spi_nor_set_addr_width(struct spi_nor *nor) /* already configured from SFDP */ } else if (nor->info->addr_width) { nor->addr_width = nor->info->addr_width; - } else if (nor->mtd.size > 0x1000000) { - /* enable 4-byte addressing if the device exceeds 16MiB */ - nor->addr_width = 4; } else { nor->addr_width = 3; } + if (nor->addr_width == 3 && nor->mtd.size > 0x1000000) { + /* enable 4-byte addressing if the device exceeds 16MiB */ + nor->addr_width = 4; + } + if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { dev_dbg(nor->dev, "address width is too large: %u\n", nor->addr_width); -- cgit v1.2.3 From 9efac6ce7f621c405d49a091e3e367be4250a27a Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Fri, 30 Oct 2020 14:33:39 +0100 Subject: mtd: rawnand: stm32_fmc2: fix broken ECC Since commit d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits"), ECC are broken in FMC2 driver in case of nand-ecc-step-size and nand-ecc-strength are not set in the device tree. To avoid this issue, the default settings are now set in stm32_fmc2_nfc_attach_chip function. Signed-off-by: Christophe Kerello Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1604064819-26861-1-git-send-email-christophe.kerello@st.com --- drivers/mtd/nand/raw/stm32_fmc2_nand.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 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 b31a5818234d..550bda4d1415 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1708,6 +1708,13 @@ static int stm32_fmc2_nfc_attach_chip(struct nand_chip *chip) return -EINVAL; } + /* Default ECC settings in case they are not set in the device tree */ + if (!chip->ecc.size) + chip->ecc.size = FMC2_ECC_STEP_SIZE; + + if (!chip->ecc.strength) + chip->ecc.strength = FMC2_ECC_BCH8; + ret = nand_ecc_choose_conf(chip, &stm32_fmc2_nfc_ecc_caps, mtd->oobsize - FMC2_BBM_LEN); if (ret) { @@ -1727,8 +1734,7 @@ static int stm32_fmc2_nfc_attach_chip(struct nand_chip *chip) mtd_set_ooblayout(mtd, &stm32_fmc2_nfc_ooblayout_ops); - if (chip->options & NAND_BUSWIDTH_16) - stm32_fmc2_nfc_set_buswidth_16(nfc, true); + stm32_fmc2_nfc_setup(chip); return 0; } @@ -1952,11 +1958,6 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA; - /* Default ECC settings */ - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - chip->ecc.size = FMC2_ECC_STEP_SIZE; - chip->ecc.strength = FMC2_ECC_BCH8; - /* Scan to find existence of the device */ ret = nand_scan(chip, nand->ncs); if (ret) -- cgit v1.2.3 From 39bdfb789bac58a323b1dac56d0f55b65ed894ee Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:24 +0530 Subject: mtd: spi-nor: core: use EOPNOTSUPP instead of ENOTSUPP ENOTSUPP is not a SUSV4 error code. Using EOPNOTSUPP is preferred in its stead. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-2-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index f0ae7a01703a..c164bb947f74 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2281,7 +2281,7 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) *@nor: pointer to a 'struct spi_nor' *@op: pointer to op template to be checked * - * Returns 0 if operation is supported, -ENOTSUPP otherwise. + * Returns 0 if operation is supported, -EOPNOTSUPP otherwise. */ static int spi_nor_spimem_check_op(struct spi_nor *nor, struct spi_mem_op *op) @@ -2295,12 +2295,12 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor, op->addr.nbytes = 4; if (!spi_mem_supports_op(nor->spimem, op)) { if (nor->mtd.size > SZ_16M) - return -ENOTSUPP; + return -EOPNOTSUPP; /* If flash size <= 16MB, 3 address bytes are sufficient */ op->addr.nbytes = 3; if (!spi_mem_supports_op(nor->spimem, op)) - return -ENOTSUPP; + return -EOPNOTSUPP; } return 0; @@ -2312,7 +2312,7 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor, *@nor: pointer to a 'struct spi_nor' *@read: pointer to op template to be checked * - * Returns 0 if operation is supported, -ENOTSUPP otherwise. + * Returns 0 if operation is supported, -EOPNOTSUPP otherwise. */ static int spi_nor_spimem_check_readop(struct spi_nor *nor, const struct spi_nor_read_command *read) @@ -2338,7 +2338,7 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor, *@nor: pointer to a 'struct spi_nor' *@pp: pointer to op template to be checked * - * Returns 0 if operation is supported, -ENOTSUPP otherwise. + * Returns 0 if operation is supported, -EOPNOTSUPP otherwise. */ static int spi_nor_spimem_check_pp(struct spi_nor *nor, const struct spi_nor_pp_command *pp) -- cgit v1.2.3 From 6e1bf55d7207aa360c8d1960dfac6af1940bd32e Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:25 +0530 Subject: mtd: spi-nor: add spi_nor_controller_ops_{read_reg, write_reg, erase}() They are thin wrappers around nor->controller_ops->{read_reg,write_reg,erase}(). In a future commit DTR support will be added. These ops can not be supported by the controller_ops hooks and these helpers will make it easier to reject those calls. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-3-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 87 ++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 34 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index c164bb947f74..2f24bde08c90 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -82,6 +82,23 @@ static int spi_nor_spimem_exec_op(struct spi_nor *nor, struct spi_mem_op *op) return spi_mem_exec_op(nor->spimem, op); } +static int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode, + u8 *buf, size_t len) +{ + return nor->controller_ops->read_reg(nor, opcode, buf, len); +} + +static int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, + const u8 *buf, size_t len) +{ + return nor->controller_ops->write_reg(nor, opcode, buf, len); +} + +static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs) +{ + return nor->controller_ops->erase(nor, offs); +} + /** * spi_nor_spimem_read_data() - read data from flash's memory region via * spi-mem @@ -229,8 +246,8 @@ int spi_nor_write_enable(struct spi_nor *nor) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREN, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREN, + NULL, 0); } if (ret) @@ -258,8 +275,8 @@ int spi_nor_write_disable(struct spi_nor *nor) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRDI, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRDI, + NULL, 0); } if (ret) @@ -289,8 +306,8 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR, - sr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR, sr, + 1); } if (ret) @@ -320,8 +337,8 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDFSR, - fsr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDFSR, fsr, + 1); } if (ret) @@ -352,7 +369,8 @@ static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDCR, cr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDCR, cr, + 1); } if (ret) @@ -385,10 +403,10 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, - enable ? SPINOR_OP_EN4B : - SPINOR_OP_EX4B, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, + enable ? SPINOR_OP_EN4B : + SPINOR_OP_EX4B, + NULL, 0); } if (ret) @@ -421,8 +439,8 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_BRWR, - nor->bouncebuf, 1); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_BRWR, + nor->bouncebuf, 1); } if (ret) @@ -453,8 +471,8 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WREAR, - nor->bouncebuf, 1); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREAR, + nor->bouncebuf, 1); } if (ret) @@ -484,8 +502,8 @@ int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_XRDSR, - sr, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_XRDSR, sr, + 1); } if (ret) @@ -529,8 +547,8 @@ static void spi_nor_clear_sr(struct spi_nor *nor) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLSR, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR, + NULL, 0); } if (ret) @@ -593,8 +611,8 @@ static void spi_nor_clear_fsr(struct spi_nor *nor) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CLFSR, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLFSR, + NULL, 0); } if (ret) @@ -737,8 +755,8 @@ static int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR, - sr, len); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR, sr, + len); } if (ret) { @@ -939,8 +957,8 @@ static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_WRSR2, - sr2, 1); + ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRSR2, + sr2, 1); } if (ret) { @@ -973,8 +991,8 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->read_reg(nor, SPINOR_OP_RDSR2, - sr2, 1); + ret = spi_nor_controller_ops_read_reg(nor, SPINOR_OP_RDSR2, sr2, + 1); } if (ret) @@ -1004,8 +1022,9 @@ static int spi_nor_erase_chip(struct spi_nor *nor) ret = spi_mem_exec_op(nor->spimem, &op); } else { - ret = nor->controller_ops->write_reg(nor, SPINOR_OP_CHIP_ERASE, - NULL, 0); + ret = spi_nor_controller_ops_write_reg(nor, + SPINOR_OP_CHIP_ERASE, + NULL, 0); } if (ret) @@ -1146,7 +1165,7 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) return spi_mem_exec_op(nor->spimem, &op); } else if (nor->controller_ops->erase) { - return nor->controller_ops->erase(nor, addr); + return spi_nor_controller_ops_erase(nor, addr); } /* @@ -1158,8 +1177,8 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) addr >>= 8; } - return nor->controller_ops->write_reg(nor, nor->erase_opcode, - nor->bouncebuf, nor->addr_width); + return spi_nor_controller_ops_write_reg(nor, nor->erase_opcode, + nor->bouncebuf, nor->addr_width); } /** -- cgit v1.2.3 From 0e30f47232ab57c685258aa91adc3a3e67bd023e Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:26 +0530 Subject: mtd: spi-nor: add support for DTR protocol Double Transfer Rate (DTR) is SPI protocol in which data is transferred on each clock edge as opposed to on each clock cycle. Make framework-level changes to allow supporting flashes in DTR mode. Right now, mixed DTR modes are not supported. So, for example a mode like 4S-4D-4D will not work. All phases need to be either DTR or STR. The xSPI spec says that "The program commands provide SPI backward compatible commands for programming data...". So 8D-8D-8D page program opcodes are populated with using 1S-1S-1S opcodes. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-4-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 319 ++++++++++++++++++++++++++++++++------------ drivers/mtd/spi-nor/core.h | 7 + drivers/mtd/spi-nor/sfdp.c | 9 +- include/linux/mtd/spi-nor.h | 51 +++++-- 4 files changed, 290 insertions(+), 96 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 2f24bde08c90..3cd025d79a98 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -40,6 +40,78 @@ #define SPI_NOR_MAX_ADDR_WIDTH 4 +/** + * spi_nor_get_cmd_ext() - Get the command opcode extension based on the + * extension type. + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to the 'struct spi_mem_op' whose properties + * need to be initialized. + * + * Right now, only "repeat" and "invert" are supported. + * + * Return: The opcode extension. + */ +static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor, + const struct spi_mem_op *op) +{ + switch (nor->cmd_ext_type) { + case SPI_NOR_EXT_INVERT: + return ~op->cmd.opcode; + + case SPI_NOR_EXT_REPEAT: + return op->cmd.opcode; + + default: + dev_err(nor->dev, "Unknown command extension type\n"); + return 0; + } +} + +/** + * spi_nor_spimem_setup_op() - Set up common properties of a spi-mem op. + * @nor: pointer to a 'struct spi_nor' + * @op: pointer to the 'struct spi_mem_op' whose properties + * need to be initialized. + * @proto: the protocol from which the properties need to be set. + */ +void spi_nor_spimem_setup_op(const struct spi_nor *nor, + struct spi_mem_op *op, + const enum spi_nor_protocol proto) +{ + u8 ext; + + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(proto); + + if (op->addr.nbytes) + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(proto); + + if (op->dummy.nbytes) + op->dummy.buswidth = spi_nor_get_protocol_addr_nbits(proto); + + if (op->data.nbytes) + op->data.buswidth = spi_nor_get_protocol_data_nbits(proto); + + if (spi_nor_protocol_is_dtr(proto)) { + /* + * SPIMEM supports mixed DTR modes, but right now we can only + * have all phases either DTR or STR. IOW, SPIMEM can have + * something like 4S-4D-4D, but SPI NOR can't. So, set all 4 + * phases to either DTR or STR. + */ + op->cmd.dtr = true; + op->addr.dtr = true; + op->dummy.dtr = true; + op->data.dtr = true; + + /* 2 bytes per clock cycle in DTR mode. */ + op->dummy.nbytes *= 2; + + ext = spi_nor_get_cmd_ext(nor, op); + op->cmd.opcode = (op->cmd.opcode << 8) | ext; + op->cmd.nbytes = 2; + } +} + /** * spi_nor_spimem_bounce() - check if a bounce buffer is needed for the data * transfer @@ -85,17 +157,26 @@ static int spi_nor_spimem_exec_op(struct spi_nor *nor, struct spi_mem_op *op) static int spi_nor_controller_ops_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, size_t len) { + if (spi_nor_protocol_is_dtr(nor->reg_proto)) + return -EOPNOTSUPP; + return nor->controller_ops->read_reg(nor, opcode, buf, len); } static int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, const u8 *buf, size_t len) { + if (spi_nor_protocol_is_dtr(nor->reg_proto)) + return -EOPNOTSUPP; + return nor->controller_ops->write_reg(nor, opcode, buf, len); } static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs) { + if (spi_nor_protocol_is_dtr(nor->write_proto)) + return -EOPNOTSUPP; + return nor->controller_ops->erase(nor, offs); } @@ -113,22 +194,20 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, from, 1), - SPI_MEM_OP_DUMMY(nor->read_dummy, 1), - SPI_MEM_OP_DATA_IN(len, buf, 1)); + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, from, 0), + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), + SPI_MEM_OP_DATA_IN(len, buf, 0)); bool usebouncebuf; ssize_t nbytes; int error; - /* get transfer protocols. */ - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); - op.dummy.buswidth = op.addr.buswidth; - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + spi_nor_spimem_setup_op(nor, &op, nor->read_proto); /* convert the dummy cycles to the number of bytes */ op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + if (spi_nor_protocol_is_dtr(nor->read_proto)) + op.dummy.nbytes *= 2; usebouncebuf = spi_nor_spimem_bounce(nor, &op); @@ -179,20 +258,18 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, size_t len, const u8 *buf) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, to, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, to, 0), SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(len, buf, 1)); + SPI_MEM_OP_DATA_OUT(len, buf, 0)); ssize_t nbytes; int error; - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); - if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op.addr.nbytes = 0; + spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + if (spi_nor_spimem_bounce(nor, &op)) memcpy(nor->bouncebuf, buf, op.data.nbytes); @@ -239,11 +316,13 @@ int spi_nor_write_enable(struct spi_nor *nor) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREN, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WREN, @@ -268,11 +347,13 @@ int spi_nor_write_disable(struct spi_nor *nor) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRDI, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_WRDI, @@ -299,10 +380,12 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr, 1)); + SPI_MEM_OP_DATA_IN(1, sr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -330,10 +413,12 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDFSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, fsr, 1)); + SPI_MEM_OP_DATA_IN(1, fsr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -362,10 +447,12 @@ static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, cr, 1)); + SPI_MEM_OP_DATA_IN(1, cr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -396,11 +483,13 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, - 1), + 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = spi_nor_controller_ops_write_reg(nor, @@ -432,10 +521,12 @@ static int spansion_set_4byte_addr_mode(struct spi_nor *nor, bool enable) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_BRWR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -464,10 +555,12 @@ int spi_nor_write_ear(struct spi_nor *nor, u8 ear) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WREAR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 1)); + SPI_MEM_OP_DATA_OUT(1, nor->bouncebuf, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -495,10 +588,12 @@ int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_XRDSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr, 1)); + SPI_MEM_OP_DATA_IN(1, sr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -540,11 +635,13 @@ static void spi_nor_clear_sr(struct spi_nor *nor) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLSR, @@ -604,11 +701,13 @@ static void spi_nor_clear_fsr(struct spi_nor *nor) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLFSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_CLFSR, @@ -748,10 +847,12 @@ static int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(len, sr, 1)); + SPI_MEM_OP_DATA_OUT(len, sr, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -950,10 +1051,12 @@ static int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(1, sr2, 1)); + SPI_MEM_OP_DATA_OUT(1, sr2, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -984,10 +1087,12 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_IN(1, sr2, 1)); + SPI_MEM_OP_DATA_IN(1, sr2, 0)); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -1015,11 +1120,13 @@ static int spi_nor_erase_chip(struct spi_nor *nor) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + ret = spi_mem_exec_op(nor->spimem, &op); } else { ret = spi_nor_controller_ops_write_reg(nor, @@ -1158,11 +1265,13 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) if (nor->spimem) { struct spi_mem_op op = - SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, addr, 1), + SPI_MEM_OP(SPI_MEM_OP_CMD(nor->erase_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, addr, 0), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_NO_DATA); + spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + return spi_mem_exec_op(nor->spimem, &op); } else if (nor->controller_ops->erase) { return spi_nor_controller_ops_erase(nor, addr); @@ -2272,6 +2381,7 @@ int spi_nor_hwcaps_read2cmd(u32 hwcaps) { SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 }, { SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 }, { SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR }, + { SNOR_HWCAPS_READ_8_8_8_DTR, SNOR_CMD_READ_8_8_8_DTR }, }; return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd, @@ -2288,6 +2398,7 @@ static int spi_nor_hwcaps_pp2cmd(u32 hwcaps) { SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 }, { SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 }, { SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 }, + { SNOR_HWCAPS_PP_8_8_8_DTR, SNOR_CMD_PP_8_8_8_DTR }, }; return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd, @@ -2336,17 +2447,17 @@ static int spi_nor_spimem_check_op(struct spi_nor *nor, static int spi_nor_spimem_check_readop(struct spi_nor *nor, const struct spi_nor_read_command *read) { - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 1), - SPI_MEM_OP_ADDR(3, 0, 1), - SPI_MEM_OP_DUMMY(0, 1), - SPI_MEM_OP_DATA_IN(0, NULL, 1)); + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(read->opcode, 0), + SPI_MEM_OP_ADDR(3, 0, 0), + SPI_MEM_OP_DUMMY(1, 0), + SPI_MEM_OP_DATA_IN(1, NULL, 0)); - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(read->proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(read->proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(read->proto); - op.dummy.buswidth = op.addr.buswidth; - op.dummy.nbytes = (read->num_mode_clocks + read->num_wait_states) * - op.dummy.buswidth / 8; + spi_nor_spimem_setup_op(nor, &op, read->proto); + + /* convert the dummy cycles to the number of bytes */ + op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8; + if (spi_nor_protocol_is_dtr(nor->read_proto)) + op.dummy.nbytes *= 2; return spi_nor_spimem_check_op(nor, &op); } @@ -2362,14 +2473,12 @@ static int spi_nor_spimem_check_readop(struct spi_nor *nor, static int spi_nor_spimem_check_pp(struct spi_nor *nor, const struct spi_nor_pp_command *pp) { - struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 1), - SPI_MEM_OP_ADDR(3, 0, 1), + struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(pp->opcode, 0), + SPI_MEM_OP_ADDR(3, 0, 0), SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(0, NULL, 1)); + SPI_MEM_OP_DATA_OUT(1, NULL, 0)); - op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(pp->proto); - op.addr.buswidth = spi_nor_get_protocol_addr_nbits(pp->proto); - op.data.buswidth = spi_nor_get_protocol_data_nbits(pp->proto); + spi_nor_spimem_setup_op(nor, &op, pp->proto); return spi_nor_spimem_check_op(nor, &op); } @@ -2387,12 +2496,16 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps) struct spi_nor_flash_parameter *params = nor->params; unsigned int cap; - /* DTR modes are not supported yet, mask them all. */ - *hwcaps &= ~SNOR_HWCAPS_DTR; - /* X-X-X modes are not supported yet, mask them all. */ *hwcaps &= ~SNOR_HWCAPS_X_X_X; + /* + * If the reset line is broken, we do not want to enter a stateful + * mode. + */ + if (nor->flags & SNOR_F_BROKEN_RESET) + *hwcaps &= ~(SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR); + for (cap = 0; cap < sizeof(*hwcaps) * BITS_PER_BYTE; cap++) { int rdidx, ppidx; @@ -2647,7 +2760,7 @@ static int spi_nor_default_setup(struct spi_nor *nor, * controller directly implements the spi_nor interface. * Yet another reason to switch to spi-mem. */ - ignored_mask = SNOR_HWCAPS_X_X_X; + ignored_mask = SNOR_HWCAPS_X_X_X | SNOR_HWCAPS_X_X_X_DTR; if (shared_mask & ignored_mask) { dev_dbg(nor->dev, "SPI n-n-n protocols are not supported.\n"); @@ -2792,11 +2905,28 @@ static void spi_nor_info_init_params(struct spi_nor *nor) SNOR_PROTO_1_1_8); } + if (info->flags & SPI_NOR_OCTAL_DTR_READ) { + params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 20, SPINOR_OP_READ_FAST, + SNOR_PROTO_8_8_8_DTR); + } + /* Page Program settings. */ params->hwcaps.mask |= SNOR_HWCAPS_PP; spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], SPINOR_OP_PP, SNOR_PROTO_1_1_1); + if (info->flags & SPI_NOR_OCTAL_DTR_PP) { + params->hwcaps.mask |= SNOR_HWCAPS_PP_8_8_8_DTR; + /* + * Since xSPI Page Program opcode is backward compatible with + * Legacy SPI, use Legacy SPI opcode there as well. + */ + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP, SNOR_PROTO_8_8_8_DTR); + } + /* * Sector Erase settings. Sort Erase Types in ascending order, with the * smallest erase size starting at BIT(0). @@ -2904,7 +3034,8 @@ static int spi_nor_init_params(struct spi_nor *nor) spi_nor_manufacturer_init_params(nor); - if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) && + if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ)) && !(nor->info->flags & SPI_NOR_SKIP_SFDP)) spi_nor_sfdp_init_params(nor); @@ -2966,7 +3097,9 @@ static int spi_nor_init(struct spi_nor *nor) return err; } - if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES)) { + if (nor->addr_width == 4 && + nor->read_proto != SNOR_PROTO_8_8_8_DTR && + !(nor->flags & SNOR_F_4B_OPCODES)) { /* * If the RESET# pin isn't hooked up properly, or the system * otherwise doesn't perform a reset command in the boot @@ -3025,6 +3158,20 @@ static int spi_nor_set_addr_width(struct spi_nor *nor) { if (nor->addr_width) { /* already configured from SFDP */ + } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { + /* + * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So + * in this protocol an odd address width cannot be used because + * then the address phase would only span a cycle and a half. + * Half a cycle would be left over. We would then have to start + * the dummy phase in the middle of a cycle and so too the data + * phase, and we will end the transaction with half a cycle left + * over. + * + * Force all 8D-8D-8D flashes to use an address width of 4 to + * avoid this situation. + */ + nor->addr_width = 4; } else if (nor->info->addr_width) { nor->addr_width = nor->info->addr_width; } else { @@ -3255,23 +3402,28 @@ EXPORT_SYMBOL_GPL(spi_nor_scan); static int spi_nor_create_read_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), - SPI_MEM_OP_DUMMY(nor->read_dummy, 1), - SPI_MEM_OP_DATA_IN(0, NULL, 1)), + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_DUMMY(nor->read_dummy, 0), + SPI_MEM_OP_DATA_IN(0, NULL, 0)), .offset = 0, .length = nor->mtd.size, }; struct spi_mem_op *op = &info.op_tmpl; - /* get transfer protocols. */ - op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); - op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); - op->dummy.buswidth = op->addr.buswidth; - op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + spi_nor_spimem_setup_op(nor, op, nor->read_proto); /* convert the dummy cycles to the number of bytes */ op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; + if (spi_nor_protocol_is_dtr(nor->read_proto)) + op->dummy.nbytes *= 2; + + /* + * Since spi_nor_spimem_setup_op() only sets buswidth when the number + * of data bytes is non-zero, the data buswidth won't be set here. So, + * do it explicitly. + */ + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, &info); @@ -3281,24 +3433,27 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor) static int spi_nor_create_write_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { - .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), SPI_MEM_OP_NO_DUMMY, - SPI_MEM_OP_DATA_OUT(0, NULL, 1)), + SPI_MEM_OP_DATA_OUT(0, NULL, 0)), .offset = 0, .length = nor->mtd.size, }; struct spi_mem_op *op = &info.op_tmpl; - /* get transfer protocols. */ - op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); - op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); - op->dummy.buswidth = op->addr.buswidth; - op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); - if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) op->addr.nbytes = 0; + spi_nor_spimem_setup_op(nor, op, nor->write_proto); + + /* + * Since spi_nor_spimem_setup_op() only sets buswidth when the number + * of data bytes is non-zero, the data buswidth won't be set here. So, + * do it explicitly. + */ + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); + nor->dirmap.wdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, &info); return PTR_ERR_OR_ZERO(nor->dirmap.wdesc); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 6f2f6b27173f..5d95b4183a33 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -62,6 +62,7 @@ enum spi_nor_read_command_index { SNOR_CMD_READ_1_8_8, SNOR_CMD_READ_8_8_8, SNOR_CMD_READ_1_8_8_DTR, + SNOR_CMD_READ_8_8_8_DTR, SNOR_CMD_READ_MAX }; @@ -78,6 +79,7 @@ enum spi_nor_pp_command_index { SNOR_CMD_PP_1_1_8, SNOR_CMD_PP_1_8_8, SNOR_CMD_PP_8_8_8, + SNOR_CMD_PP_8_8_8_DTR, SNOR_CMD_PP_MAX }; @@ -311,6 +313,8 @@ struct flash_info { * BP3 is bit 6 of status register. * Must be used with SPI_NOR_4BIT_BP. */ +#define SPI_NOR_OCTAL_DTR_READ BIT(19) /* Flash supports octal DTR Read. */ +#define SPI_NOR_OCTAL_DTR_PP BIT(20) /* Flash supports Octal DTR Page Program */ /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; @@ -399,6 +403,9 @@ extern const struct spi_nor_manufacturer spi_nor_winbond; extern const struct spi_nor_manufacturer spi_nor_xilinx; extern const struct spi_nor_manufacturer spi_nor_xmc; +void spi_nor_spimem_setup_op(const struct spi_nor *nor, + struct spi_mem_op *op, + const enum spi_nor_protocol proto); int spi_nor_write_enable(struct spi_nor *nor); int spi_nor_write_disable(struct spi_nor *nor); int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable); diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index e2a43d39eb5f..21fa9ab78eae 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -1047,9 +1047,16 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, } /* 4BAIT is the only SFDP table that indicates page program support. */ - if (pp_hwcaps & SNOR_HWCAPS_PP) + if (pp_hwcaps & SNOR_HWCAPS_PP) { spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP], SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); + /* + * Since xSPI Page Program opcode is backward compatible with + * Legacy SPI, use Legacy SPI opcode there as well. + */ + spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); + } if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_4) spi_nor_set_pp_settings(¶ms_pp[SNOR_CMD_PP_1_1_4], SPINOR_OP_PP_1_1_4_4B, diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 60bac2c0ec45..cd549042c53d 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -182,6 +182,7 @@ enum spi_nor_protocol { SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2), SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4), SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(1, 8, 8), + SNOR_PROTO_8_8_8_DTR = SNOR_PROTO_DTR(8, 8, 8), }; static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto) @@ -228,7 +229,7 @@ struct spi_nor_hwcaps { * then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly * (Slow) Read. */ -#define SNOR_HWCAPS_READ_MASK GENMASK(14, 0) +#define SNOR_HWCAPS_READ_MASK GENMASK(15, 0) #define SNOR_HWCAPS_READ BIT(0) #define SNOR_HWCAPS_READ_FAST BIT(1) #define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2) @@ -245,11 +246,12 @@ struct spi_nor_hwcaps { #define SNOR_HWCAPS_READ_4_4_4 BIT(9) #define SNOR_HWCAPS_READ_1_4_4_DTR BIT(10) -#define SNOR_HWCAPS_READ_OCTAL GENMASK(14, 11) +#define SNOR_HWCAPS_READ_OCTAL GENMASK(15, 11) #define SNOR_HWCAPS_READ_1_1_8 BIT(11) #define SNOR_HWCAPS_READ_1_8_8 BIT(12) #define SNOR_HWCAPS_READ_8_8_8 BIT(13) #define SNOR_HWCAPS_READ_1_8_8_DTR BIT(14) +#define SNOR_HWCAPS_READ_8_8_8_DTR BIT(15) /* * Page Program capabilities. @@ -260,18 +262,19 @@ struct spi_nor_hwcaps { * JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory * implements such commands. */ -#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16) -#define SNOR_HWCAPS_PP BIT(16) +#define SNOR_HWCAPS_PP_MASK GENMASK(23, 16) +#define SNOR_HWCAPS_PP BIT(16) -#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) -#define SNOR_HWCAPS_PP_1_1_4 BIT(17) -#define SNOR_HWCAPS_PP_1_4_4 BIT(18) -#define SNOR_HWCAPS_PP_4_4_4 BIT(19) +#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17) +#define SNOR_HWCAPS_PP_1_1_4 BIT(17) +#define SNOR_HWCAPS_PP_1_4_4 BIT(18) +#define SNOR_HWCAPS_PP_4_4_4 BIT(19) -#define SNOR_HWCAPS_PP_OCTAL GENMASK(22, 20) -#define SNOR_HWCAPS_PP_1_1_8 BIT(20) -#define SNOR_HWCAPS_PP_1_8_8 BIT(21) -#define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_PP_OCTAL GENMASK(23, 20) +#define SNOR_HWCAPS_PP_1_1_8 BIT(20) +#define SNOR_HWCAPS_PP_1_8_8 BIT(21) +#define SNOR_HWCAPS_PP_8_8_8 BIT(22) +#define SNOR_HWCAPS_PP_8_8_8_DTR BIT(23) #define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \ SNOR_HWCAPS_READ_4_4_4 | \ @@ -279,10 +282,14 @@ struct spi_nor_hwcaps { SNOR_HWCAPS_PP_4_4_4 | \ SNOR_HWCAPS_PP_8_8_8) +#define SNOR_HWCAPS_X_X_X_DTR (SNOR_HWCAPS_READ_8_8_8_DTR | \ + SNOR_HWCAPS_PP_8_8_8_DTR) + #define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \ SNOR_HWCAPS_READ_1_2_2_DTR | \ SNOR_HWCAPS_READ_1_4_4_DTR | \ - SNOR_HWCAPS_READ_1_8_8_DTR) + SNOR_HWCAPS_READ_1_8_8_DTR | \ + SNOR_HWCAPS_READ_8_8_8_DTR) #define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \ SNOR_HWCAPS_PP_MASK) @@ -318,6 +325,22 @@ struct spi_nor_controller_ops { int (*erase)(struct spi_nor *nor, loff_t offs); }; +/** + * enum spi_nor_cmd_ext - describes the command opcode extension in DTR mode + * @SPI_NOR_EXT_NONE: no extension. This is the default, and is used in Legacy + * SPI mode + * @SPI_NOR_EXT_REPEAT: the extension is same as the opcode + * @SPI_NOR_EXT_INVERT: the extension is the bitwise inverse of the opcode + * @SPI_NOR_EXT_HEX: the extension is any hex value. The command and opcode + * combine to form a 16-bit opcode. + */ +enum spi_nor_cmd_ext { + SPI_NOR_EXT_NONE = 0, + SPI_NOR_EXT_REPEAT, + SPI_NOR_EXT_INVERT, + SPI_NOR_EXT_HEX, +}; + /* * Forward declarations that are used internally by the core and manufacturer * drivers. @@ -345,6 +368,7 @@ struct spi_nor_flash_parameter; * @program_opcode: the program opcode * @sst_write_second: used by the SST write operation * @flags: flag options for the current SPI NOR (SNOR_F_*) + * @cmd_ext_type: the command opcode extension type for DTR mode. * @read_proto: the SPI protocol for read operations * @write_proto: the SPI protocol for write operations * @reg_proto: the SPI protocol for read_reg/write_reg/erase operations @@ -376,6 +400,7 @@ struct spi_nor { enum spi_nor_protocol reg_proto; bool sst_write_second; u32 flags; + enum spi_nor_cmd_ext cmd_ext_type; const struct spi_nor_controller_ops *controller_ops; -- cgit v1.2.3 From 0e1b2fc4e5f681ba716f5a50da710251c717413d Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:27 +0530 Subject: mtd: spi-nor: sfdp: get command opcode extension type from BFPT Some devices in DTR mode expect an extra command byte called the extension. The extension can either be same as the opcode, bitwise inverse of the opcode, or another additional byte forming a 16-byte opcode. Get the extension type from the BFPT. For now, only flashes with "repeat" and "inverse" extensions are supported. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-5-p.yadav@ti.com --- drivers/mtd/spi-nor/sfdp.c | 18 ++++++++++++++++++ drivers/mtd/spi-nor/sfdp.h | 6 ++++++ 2 files changed, 24 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 21fa9ab78eae..c77655968f80 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -606,6 +606,24 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); + /* 8D-8D-8D command extension. */ + switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { + case BFPT_DWORD18_CMD_EXT_REP: + nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + break; + + case BFPT_DWORD18_CMD_EXT_INV: + nor->cmd_ext_type = SPI_NOR_EXT_INVERT; + break; + + case BFPT_DWORD18_CMD_EXT_RES: + dev_dbg(nor->dev, "Reserved command extension used\n"); + break; + + case BFPT_DWORD18_CMD_EXT_16B: + dev_dbg(nor->dev, "16-bit opcodes not supported\n"); + return -EOPNOTSUPP; + } return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, params); } diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index 7f9846b3a1ad..6d7243067252 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -90,6 +90,12 @@ struct sfdp_bfpt { #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +#define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29) +#define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */ +#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */ +#define BFPT_DWORD18_CMD_EXT_RES (0x2UL << 29) /* Reserved */ +#define BFPT_DWORD18_CMD_EXT_16B (0x3UL << 29) /* 16-bit opcode */ + struct sfdp_parameter_header { u8 id_lsb; u8 minor; -- cgit v1.2.3 From fb27f198971a68fec59d5493b68e35e916fce62c Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:28 +0530 Subject: mtd: spi-nor: sfdp: parse xSPI Profile 1.0 table This table is indication that the flash is xSPI compliant and hence supports octal DTR mode. Extract information like the fast read opcode, dummy cycles, the number of dummy cycles needed for a Read Status Register command, and the number of address bytes needed for a Read Status Register command. We don't know what speed the controller is running at. Find the fast read dummy cycles for the fastest frequency the flash can run at to be sure we are never short of dummy cycles. If nothing is available, default to 20. Flashes that use a different value should update it in their fixup hooks. Since we want to set read settings, expose spi_nor_set_read_settings() in core.h. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-6-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 2 +- drivers/mtd/spi-nor/core.h | 10 +++++ drivers/mtd/spi-nor/sfdp.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 3cd025d79a98..fea619802825 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2332,7 +2332,7 @@ static int spi_nor_check(struct spi_nor *nor) return 0; } -static void +void spi_nor_set_read_settings(struct spi_nor_read_command *read, u8 num_mode_clocks, u8 num_wait_states, diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 5d95b4183a33..9a33c8d07335 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -192,6 +192,9 @@ struct spi_nor_locking_ops { * * @size: the flash memory density in bytes. * @page_size: the page size of the SPI NOR flash memory. + * @rdsr_dummy: dummy cycles needed for Read Status Register command. + * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register + * command. * @hwcaps: describes the read and page program hardware * capabilities. * @reads: read capabilities ordered by priority: the higher index @@ -214,6 +217,8 @@ struct spi_nor_locking_ops { struct spi_nor_flash_parameter { u64 size; u32 page_size; + u8 rdsr_dummy; + u8 rdsr_addr_nbytes; struct spi_nor_hwcaps hwcaps; struct spi_nor_read_command reads[SNOR_CMD_READ_MAX]; @@ -425,6 +430,11 @@ ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, int spi_nor_hwcaps_read2cmd(u32 hwcaps); u8 spi_nor_convert_3to4_read(u8 opcode); +void spi_nor_set_read_settings(struct spi_nor_read_command *read, + u8 num_mode_clocks, + u8 num_wait_states, + u8 opcode, + enum spi_nor_protocol proto); void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, enum spi_nor_protocol proto); diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index c77655968f80..b2d097b44a55 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -4,6 +4,7 @@ * Copyright (C) 2014, Freescale Semiconductor, Inc. */ +#include #include #include #include @@ -19,6 +20,7 @@ #define SFDP_BFPT_ID 0xff00 /* Basic Flash Parameter Table */ #define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ #define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */ +#define SFDP_PROFILE1_ID 0xff05 /* xSPI Profile 1.0 table. */ #define SFDP_SIGNATURE 0x50444653U @@ -1108,6 +1110,91 @@ out: return ret; } +#define PROFILE1_DWORD1_RDSR_ADDR_BYTES BIT(29) +#define PROFILE1_DWORD1_RDSR_DUMMY BIT(28) +#define PROFILE1_DWORD1_RD_FAST_CMD GENMASK(15, 8) +#define PROFILE1_DWORD4_DUMMY_200MHZ GENMASK(11, 7) +#define PROFILE1_DWORD5_DUMMY_166MHZ GENMASK(31, 27) +#define PROFILE1_DWORD5_DUMMY_133MHZ GENMASK(21, 17) +#define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7) + +/** + * spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table + * @nor: pointer to a 'struct spi_nor' + * @profile1_header: pointer to the 'struct sfdp_parameter_header' describing + * the Profile 1.0 Table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_profile1(struct spi_nor *nor, + const struct sfdp_parameter_header *profile1_header, + struct spi_nor_flash_parameter *params) +{ + u32 *dwords, addr; + size_t len; + int ret; + u8 dummy, opcode; + + len = profile1_header->length * sizeof(*dwords); + dwords = kmalloc(len, GFP_KERNEL); + if (!dwords) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(profile1_header); + ret = spi_nor_read_sfdp(nor, addr, len, dwords); + if (ret) + goto out; + + le32_to_cpu_array(dwords, profile1_header->length); + + /* Get 8D-8D-8D fast read opcode and dummy cycles. */ + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, dwords[0]); + + /* Set the Read Status Register dummy cycles and dummy address bytes. */ + if (dwords[0] & PROFILE1_DWORD1_RDSR_DUMMY) + params->rdsr_dummy = 8; + else + params->rdsr_dummy = 4; + + if (dwords[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES) + params->rdsr_addr_nbytes = 4; + else + params->rdsr_addr_nbytes = 0; + + /* + * We don't know what speed the controller is running at. Find the + * dummy cycles for the fastest frequency the flash can run at to be + * sure we are never short of dummy cycles. A value of 0 means the + * frequency is not supported. + * + * Default to PROFILE1_DUMMY_DEFAULT if we don't find anything, and let + * flashes set the correct value if needed in their fixup hooks. + */ + dummy = FIELD_GET(PROFILE1_DWORD4_DUMMY_200MHZ, dwords[3]); + if (!dummy) + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_166MHZ, dwords[4]); + if (!dummy) + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_133MHZ, dwords[4]); + if (!dummy) + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_100MHZ, dwords[4]); + if (!dummy) + dev_dbg(nor->dev, + "Can't find dummy cycles from Profile 1.0 table\n"); + + /* Round up to an even value to avoid tripping controllers up. */ + dummy = round_up(dummy, 2); + + /* Update the fast read settings. */ + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, dummy, opcode, + SNOR_PROTO_8_8_8_DTR); + +out: + kfree(dwords); + return ret; +} + /** * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' @@ -1209,6 +1296,10 @@ int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_parse_4bait(nor, param_header, params); break; + case SFDP_PROFILE1_ID: + err = spi_nor_parse_profile1(nor, param_header, params); + break; + default: break; } -- cgit v1.2.3 From 6c6a2b2b8ed6dd1ad1a318afd1035777a73936e0 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:29 +0530 Subject: mtd: spi-nor: core: use dummy cycle and address width info from SFDP The xSPI Profile 1.0 table specifies how many dummy cycles and address bytes are needed for the Read Status Register command in octal DTR mode. Use that information to send the correct Read SR command. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-7-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index fea619802825..d63dfd7d4b31 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -385,6 +385,11 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_IN(1, sr, 0)); + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy; + } + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); @@ -418,6 +423,11 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_IN(1, fsr, 0)); + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.addr.nbytes = nor->params->rdsr_addr_nbytes; + op.dummy.nbytes = nor->params->rdsr_dummy; + } + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); -- cgit v1.2.3 From 354b412967016e2f99fb2d5113e7b92b539f33b6 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:30 +0530 Subject: mtd: spi-nor: core: do 2 byte reads for SR and FSR in DTR mode Some controllers, like the cadence qspi controller, have trouble reading only 1 byte in DTR mode. So, do 2 byte reads for SR and FSR commands in DTR mode, and then discard the second byte. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-8-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index d63dfd7d4b31..518d16511dee 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -370,7 +370,7 @@ int spi_nor_write_disable(struct spi_nor *nor) * spi_nor_read_sr() - Read the Status Register. * @nor: pointer to 'struct spi_nor'. * @sr: pointer to a DMA-able buffer where the value of the - * Status Register will be written. + * Status Register will be written. Should be at least 2 bytes. * * Return: 0 on success, -errno otherwise. */ @@ -388,6 +388,11 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { op.addr.nbytes = nor->params->rdsr_addr_nbytes; op.dummy.nbytes = nor->params->rdsr_dummy; + /* + * We don't want to read only one byte in DTR mode. So, + * read 2 and then discard the second byte. + */ + op.data.nbytes = 2; } spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -408,7 +413,8 @@ static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) * spi_nor_read_fsr() - Read the Flag Status Register. * @nor: pointer to 'struct spi_nor' * @fsr: pointer to a DMA-able buffer where the value of the - * Flag Status Register will be written. + * Flag Status Register will be written. Should be at least 2 + * bytes. * * Return: 0 on success, -errno otherwise. */ @@ -426,6 +432,11 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr) if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { op.addr.nbytes = nor->params->rdsr_addr_nbytes; op.dummy.nbytes = nor->params->rdsr_dummy; + /* + * We don't want to read only one byte in DTR mode. So, + * read 2 and then discard the second byte. + */ + op.data.nbytes = 2; } spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); -- cgit v1.2.3 From c6908077b194e46e7180d241ff2dbb85dc30bbd1 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 5 Oct 2020 21:01:31 +0530 Subject: mtd: spi-nor: Introduce SNOR_F_IO_MODE_EN_VOLATILE We don't want to enter a stateful mode, where a X-X-X I/O mode is entered by setting a non-volatile bit, because in case of a reset or a crash, once in the non-volatile mode, we may not be able to recover in bootloaders and we may break the SPI NOR boot. Forbid by default the I/O modes that are set via a non-volatile bit. SPI_NOR_IO_MODE_EN_VOLATILE should be set just for the flashes that don't define the optional SFDP SCCR Map, so that we don't pollute the flash info flags. Signed-off-by: Tudor Ambarus Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Link: https://lore.kernel.org/r/20201005153138.6437-9-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 3 +++ drivers/mtd/spi-nor/core.h | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 518d16511dee..45d45b51705e 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3388,6 +3388,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (info->flags & SPI_NOR_4B_OPCODES) nor->flags |= SNOR_F_4B_OPCODES; + if (info->flags & SPI_NOR_IO_MODE_EN_VOLATILE) + nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + ret = spi_nor_set_addr_width(nor); if (ret) return ret; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 9a33c8d07335..eaece1123c0b 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -26,6 +26,7 @@ enum spi_nor_option_flags { SNOR_F_HAS_SR_TB_BIT6 = BIT(11), SNOR_F_HAS_4BIT_BP = BIT(12), SNOR_F_HAS_SR_BP3_BIT6 = BIT(13), + SNOR_F_IO_MODE_EN_VOLATILE = BIT(14), }; struct spi_nor_read_command { @@ -320,6 +321,11 @@ struct flash_info { */ #define SPI_NOR_OCTAL_DTR_READ BIT(19) /* Flash supports octal DTR Read. */ #define SPI_NOR_OCTAL_DTR_PP BIT(20) /* Flash supports Octal DTR Page Program */ +#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(21) /* + * Flash enables the best + * available I/O mode via a + * volatile bit. + */ /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; -- cgit v1.2.3 From 981a8d60e01f7e76d6bccb621598d087c9dd4a5d Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 5 Oct 2020 21:01:32 +0530 Subject: mtd: spi-nor: Parse SFDP SCCR Map Parse just the 22nd dword and look for the 'DTR Octal Mode Enable Volatile bit'. SPI_NOR_IO_MODE_EN_VOLATILE should be set just for the flashes that don't define the optional SFDP SCCR Map. For the others, let the SFDP do its job and fill the SNOR_F_IO_MODE_EN_VOLATILE flag. We avoid this way polluting the flash flags when declaring one. Signed-off-by: Tudor Ambarus Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Link: https://lore.kernel.org/r/20201005153138.6437-10-p.yadav@ti.com --- drivers/mtd/spi-nor/sfdp.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index b2d097b44a55..3efcba5e629a 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -21,6 +21,10 @@ #define SFDP_SECTOR_MAP_ID 0xff81 /* Sector Map Table */ #define SFDP_4BAIT_ID 0xff84 /* 4-byte Address Instruction Table */ #define SFDP_PROFILE1_ID 0xff05 /* xSPI Profile 1.0 table. */ +#define SFDP_SCCR_MAP_ID 0xff87 /* + * Status, Control and Configuration + * Register Map. + */ #define SFDP_SIGNATURE 0x50444653U @@ -1195,6 +1199,46 @@ out: return ret; } +#define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31) + +/** + * spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register + * Map. + * @nor: pointer to a 'struct spi_nor' + * @sccr_header: pointer to the 'struct sfdp_parameter_header' describing + * the SCCR Map table length and version. + * @params: pointer to the 'struct spi_nor_flash_parameter' to be. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_parse_sccr(struct spi_nor *nor, + const struct sfdp_parameter_header *sccr_header, + struct spi_nor_flash_parameter *params) +{ + u32 *dwords, addr; + size_t len; + int ret; + + len = sccr_header->length * sizeof(*dwords); + dwords = kmalloc(len, GFP_KERNEL); + if (!dwords) + return -ENOMEM; + + addr = SFDP_PARAM_HEADER_PTP(sccr_header); + ret = spi_nor_read_sfdp(nor, addr, len, dwords); + if (ret) + goto out; + + le32_to_cpu_array(dwords, sccr_header->length); + + if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[22])) + nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + +out: + kfree(dwords); + return ret; +} + /** * spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters. * @nor: pointer to a 'struct spi_nor' @@ -1300,6 +1344,10 @@ int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_parse_profile1(nor, param_header, params); break; + case SFDP_SCCR_MAP_ID: + err = spi_nor_parse_sccr(nor, param_header, params); + break; + default: break; } -- cgit v1.2.3 From a33c89db4c3ba8e5c3df7726491329cd35d4bb59 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:33 +0530 Subject: mtd: spi-nor: core: enable octal DTR mode when possible Allow flashes to specify a hook to enable octal DTR mode. Use this hook whenever possible to get optimal transfer speeds. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-11-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/core.h | 2 ++ 2 files changed, 40 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 45d45b51705e..7b9ec02734f2 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3067,6 +3067,38 @@ static int spi_nor_init_params(struct spi_nor *nor) return 0; } +/** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed + * @nor: pointer to a 'struct spi_nor' + * @enable: whether to enable or disable Octal DTR + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + int ret; + + if (!nor->params->octal_dtr_enable) + return 0; + + if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR && + nor->write_proto == SNOR_PROTO_8_8_8_DTR)) + return 0; + + if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE)) + return 0; + + ret = nor->params->octal_dtr_enable(nor, enable); + if (ret) + return ret; + + if (enable) + nor->reg_proto = SNOR_PROTO_8_8_8_DTR; + else + nor->reg_proto = SNOR_PROTO_1_1_1; + + return 0; +} + /** * spi_nor_quad_enable() - enable Quad I/O if needed. * @nor: pointer to a 'struct spi_nor' @@ -3106,6 +3138,12 @@ static int spi_nor_init(struct spi_nor *nor) { int err; + err = spi_nor_octal_dtr_enable(nor, true); + if (err) { + dev_dbg(nor->dev, "octal mode not supported\n"); + return err; + } + err = spi_nor_quad_enable(nor); if (err) { dev_dbg(nor->dev, "quad mode not supported\n"); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index eaece1123c0b..105a4ddeb309 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -204,6 +204,7 @@ struct spi_nor_locking_ops { * higher index in the array, the higher priority. * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. + * @octal_dtr_enable: enables SPI NOR octal DTR mode. * @quad_enable: enables SPI NOR quad mode. * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. * @convert_addr: converts an absolute address into something the flash @@ -227,6 +228,7 @@ struct spi_nor_flash_parameter { struct spi_nor_erase_map erase_map; + int (*octal_dtr_enable)(struct spi_nor *nor, bool enable); int (*quad_enable)(struct spi_nor *nor); int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); u32 (*convert_addr)(struct spi_nor *nor, u32 addr); -- cgit v1.2.3 From 1131324aa53ad3465a36c4e58a56615e1bf52932 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:34 +0530 Subject: mtd: spi-nor: sfdp: detect Soft Reset sequence support from BFPT A Soft Reset sequence will return the flash to Power-on-Reset (POR) state. It consists of two commands: Soft Reset Enable and Soft Reset. Find out if the sequence is supported from BFPT DWORD 16. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-12-p.yadav@ti.com --- drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/sfdp.c | 4 ++++ drivers/mtd/spi-nor/sfdp.h | 2 ++ 3 files changed, 7 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 105a4ddeb309..0a775a7b5606 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -27,6 +27,7 @@ enum spi_nor_option_flags { SNOR_F_HAS_4BIT_BP = BIT(12), SNOR_F_HAS_SR_BP3_BIT6 = BIT(13), SNOR_F_IO_MODE_EN_VOLATILE = BIT(14), + SNOR_F_SOFT_RESET = BIT(15), }; struct spi_nor_read_command { diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 3efcba5e629a..22cb519efe3f 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -608,6 +608,10 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, break; } + /* Soft Reset support. */ + if (bfpt.dwords[BFPT_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST) + nor->flags |= SNOR_F_SOFT_RESET; + /* Stop here if not JESD216 rev C or later. */ if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt, diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index 6d7243067252..89152ae1cf3e 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -90,6 +90,8 @@ struct sfdp_bfpt { #define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20) #define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */ +#define BFPT_DWORD16_SWRST_EN_RST BIT(12) + #define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29) #define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */ #define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */ -- cgit v1.2.3 From d73ee7534cc537b98b61bfd83dbce5bb12aecb80 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:35 +0530 Subject: mtd: spi-nor: core: perform a Soft Reset on shutdown Perform a Soft Reset on shutdown on flashes that support it so that the flash can be reset to its initial state and any configurations made by spi-nor (given that they're only done in volatile registers) will be reset. This will hand back the flash in pristine state for any further operations on it. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-13-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spi-nor.h | 2 ++ 2 files changed, 47 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 7b9ec02734f2..633a1af8a0fe 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -40,6 +40,9 @@ #define SPI_NOR_MAX_ADDR_WIDTH 4 +#define SPI_NOR_SRST_SLEEP_MIN 200 +#define SPI_NOR_SRST_SLEEP_MAX 400 + /** * spi_nor_get_cmd_ext() - Get the command opcode extension based on the * extension type. @@ -3174,6 +3177,45 @@ static int spi_nor_init(struct spi_nor *nor) return 0; } +static void spi_nor_soft_reset(struct spi_nor *nor) +{ + struct spi_mem_op op; + int ret; + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRSTEN, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DATA); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) { + dev_warn(nor->dev, "Software reset failed: %d\n", ret); + return; + } + + op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_SRST, 0), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_NO_DATA); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) { + dev_warn(nor->dev, "Software reset failed: %d\n", ret); + return; + } + + /* + * Software Reset is not instant, and the delay varies from flash to + * flash. Looking at a few flashes, most range somewhere below 100 + * microseconds. So, sleep for a range of 200-400 us. + */ + usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX); +} + /* mtd resume handler */ static void spi_nor_resume(struct mtd_info *mtd) { @@ -3193,6 +3235,9 @@ void spi_nor_restore(struct spi_nor *nor) if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) nor->params->set_4byte_addr_mode(nor, false); + + if (nor->flags & SNOR_F_SOFT_RESET) + spi_nor_soft_reset(nor); } EXPORT_SYMBOL_GPL(spi_nor_restore); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index cd549042c53d..299685d15dc2 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -51,6 +51,8 @@ #define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ #define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ #define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */ +#define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */ +#define SPINOR_OP_SRST 0x99 /* Software Reset */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */ -- cgit v1.2.3 From 1b65c43f7078eb6e4c2c0231c543a88c272bcb0c Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:36 +0530 Subject: mtd: spi-nor: core: disable Octal DTR mode on suspend. On resume, the init procedure will be run that will re-enable it. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-14-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 633a1af8a0fe..5bee7c8da4dc 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3216,6 +3216,20 @@ static void spi_nor_soft_reset(struct spi_nor *nor) usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX); } +/* mtd suspend handler */ +static int spi_nor_suspend(struct mtd_info *mtd) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + int ret; + + /* Disable octal DTR mode if we enabled it. */ + ret = spi_nor_octal_dtr_enable(nor, false); + if (ret) + dev_err(nor->dev, "suspend() failed\n"); + + return ret; +} + /* mtd resume handler */ static void spi_nor_resume(struct mtd_info *mtd) { @@ -3421,6 +3435,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->size = nor->params->size; mtd->_erase = spi_nor_erase; mtd->_read = spi_nor_read; + mtd->_suspend = spi_nor_suspend; mtd->_resume = spi_nor_resume; if (nor->params->locking_ops) { -- cgit v1.2.3 From c3266af101f28e87505c976fad8db96c68f8afe4 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:37 +0530 Subject: mtd: spi-nor: spansion: add support for Cypress Semper flash The Cypress Semper flash is an xSPI compliant octal DTR flash. Add support for using it in octal DTR mode. The flash by default boots in a hybrid sector mode. But the sector map table on the part I had was programmed incorrectly and the SMPT values on the flash don't match the public datasheet. Specifically, in some places erase type 3 was used instead of 4. In addition, the region sizes were incorrect in some places. So, for testing I set CFR3N[3] to enable uniform sector sizes. Since the uniform sector mode bit is a non-volatile bit, this series does not change it to avoid making any permanent changes to the flash configuration. The correct data to implement a fixup is not available right now and will be done in a follow-up patch if needed. Signed-off-by: Pratyush Yadav [vigneshr@ti.com: Drop unnecessary sleep in Octal DTR switch sequence] Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-15-p.yadav@ti.com --- drivers/mtd/spi-nor/spansion.c | 171 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 8429b4af999a..e487fd341a56 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -8,6 +8,172 @@ #include "core.h" +#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ +#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ +#define SPINOR_REG_CYPRESS_CFR2V 0x00800003 +#define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb +#define SPINOR_REG_CYPRESS_CFR3V 0x00800004 +#define SPINOR_REG_CYPRESS_CFR3V_PGSZ BIT(4) /* Page size. */ +#define SPINOR_REG_CYPRESS_CFR5V 0x00800006 +#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN 0x3 +#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0 +#define SPINOR_OP_CYPRESS_RD_FAST 0xee + +/** + * spi_nor_cypress_octal_dtr_enable() - Enable octal DTR on Cypress flashes. + * @nor: pointer to a 'struct spi_nor' + * @enable: whether to enable or disable Octal DTR + * + * This also sets the memory access latency cycles to 24 to allow the flash to + * run at up to 200MHz. + * + * Return: 0 on success, -errno otherwise. + */ +static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + if (enable) { + /* Use 24 dummy cycles for memory array reads. */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR2V, + 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + + nor->read_dummy = 24; + } + + /* Set/unset the octal and DTR enable bits. */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (enable) + *buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; + else + *buf = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; + + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(enable ? 3 : 4, + SPINOR_REG_CYPRESS_CFR5V, + 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + if (!enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + /* Read flash ID to make sure the switch was successful. */ + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), + SPI_MEM_OP_ADDR(enable ? 4 : 0, 0, 1), + SPI_MEM_OP_DUMMY(enable ? 3 : 0, 1), + SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), + buf, 1)); + + if (enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; + + return 0; +} + +static void s28hs512t_default_init(struct spi_nor *nor) +{ + nor->params->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable; +} + +static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor) +{ + /* + * On older versions of the flash the xSPI Profile 1.0 table has the + * 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE. + */ + if (nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0) + nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode = + SPINOR_OP_CYPRESS_RD_FAST; + + /* This flash is also missing the 4-byte Page Program opcode bit. */ + spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP], + SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); + /* + * Since xSPI Page Program opcode is backward compatible with + * Legacy SPI, use Legacy SPI opcode there as well. + */ + spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR], + SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); + + /* + * The xSPI Profile 1.0 table advertises the number of additional + * address bytes needed for Read Status Register command as 0 but the + * actual value for that is 4. + */ + nor->params->rdsr_addr_nbytes = 4; +} + +static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt, + struct spi_nor_flash_parameter *params) +{ + /* + * The BFPT table advertises a 512B page size but the page size is + * actually configurable (with the default being 256B). Read from + * CFR3V[4] and set the correct size. + */ + struct spi_mem_op op = + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RD_ANY_REG, 1), + SPI_MEM_OP_ADDR(3, SPINOR_REG_CYPRESS_CFR3V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_IN(1, nor->bouncebuf, 1)); + int ret; + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) + params->page_size = 512; + else + params->page_size = 256; + + return 0; +} + +static struct spi_nor_fixups s28hs512t_fixups = { + .default_init = s28hs512t_default_init, + .post_sfdp = s28hs512t_post_sfdp_fixup, + .post_bfpt = s28hs512t_post_bfpt_fixup, +}; + static int s25fs_s_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -104,6 +270,11 @@ static const struct flash_info spansion_parts[] = { SPI_NOR_4B_OPCODES) }, { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1, SPI_NOR_NO_ERASE) }, + { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256, + SECT_4K | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP) + .fixups = &s28hs512t_fixups, + }, }; static void spansion_post_sfdp_fixups(struct spi_nor *nor) -- cgit v1.2.3 From ad624dfd7bb69a20ff06804ece25bc27200f2ea2 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 5 Oct 2020 21:01:38 +0530 Subject: mtd: spi-nor: micron-st: allow using MT35XU512ABA in Octal DTR mode Since this flash doesn't have a Profile 1.0 table, the Octal DTR capabilities are enabled in the post SFDP fixup, along with the 8D-8D-8D fast read settings. Enable Octal DTR mode with 20 dummy cycles to allow running at the maximum supported frequency of 200Mhz. The flash supports the soft reset sequence. So, add the flag in the flash's info. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201005153138.6437-16-p.yadav@ti.com --- drivers/mtd/spi-nor/micron-st.c | 115 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index ef3695080710..c224e59820a1 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -8,10 +8,123 @@ #include "core.h" +#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */ +#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */ +#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */ +#define SPINOR_REG_MT_CFR0V 0x00 /* For setting octal DTR mode */ +#define SPINOR_REG_MT_CFR1V 0x01 /* For setting dummy cycles */ +#define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR. */ +#define SPINOR_MT_EXSPI 0xff /* Enable Extended SPI (default) */ + +static int spi_nor_micron_octal_dtr_enable(struct spi_nor *nor, bool enable) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + int ret; + + if (enable) { + /* Use 20 dummy cycles for memory array reads. */ + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + *buf = 20; + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(3, SPINOR_REG_MT_CFR1V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + ret = spi_nor_wait_till_ready(nor); + if (ret) + return ret; + } + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + if (enable) + *buf = SPINOR_MT_OCT_DTR; + else + *buf = SPINOR_MT_EXSPI; + + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_MT_WR_ANY_REG, 1), + SPI_MEM_OP_ADDR(enable ? 3 : 4, + SPINOR_REG_MT_CFR0V, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(1, buf, 1)); + + if (!enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + /* Read flash ID to make sure the switch was successful. */ + op = (struct spi_mem_op) + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDID, 1), + SPI_MEM_OP_NO_ADDR, + SPI_MEM_OP_DUMMY(enable ? 8 : 0, 1), + SPI_MEM_OP_DATA_IN(round_up(nor->info->id_len, 2), + buf, 1)); + + if (enable) + spi_nor_spimem_setup_op(nor, &op, SNOR_PROTO_8_8_8_DTR); + + ret = spi_mem_exec_op(nor->spimem, &op); + if (ret) + return ret; + + if (memcmp(buf, nor->info->id, nor->info->id_len)) + return -EINVAL; + + return 0; +} + +static void mt35xu512aba_default_init(struct spi_nor *nor) +{ + nor->params->octal_dtr_enable = spi_nor_micron_octal_dtr_enable; +} + +static void mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) +{ + /* Set the Fast Read settings. */ + nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; + spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 20, SPINOR_OP_MT_DTR_RD, + SNOR_PROTO_8_8_8_DTR); + + nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; + nor->params->rdsr_dummy = 8; + nor->params->rdsr_addr_nbytes = 0; + + /* + * The BFPT quad enable field is set to a reserved value so the quad + * enable function is ignored by spi_nor_parse_bfpt(). Make sure we + * disable it. + */ + nor->params->quad_enable = NULL; +} + +static struct spi_nor_fixups mt35xu512aba_fixups = { + .default_init = mt35xu512aba_default_init, + .post_sfdp = mt35xu512aba_post_sfdp_fixup, +}; + static const struct flash_info micron_parts[] = { { "mt35xu512aba", INFO(0x2c5b1a, 0, 128 * 1024, 512, SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | - SPI_NOR_4B_OPCODES) }, + SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ | + SPI_NOR_OCTAL_DTR_PP | + SPI_NOR_IO_MODE_EN_VOLATILE) + .fixups = &mt35xu512aba_fixups}, { "mt35xu02g", INFO(0x2c5b1c, 0, 128 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, -- cgit v1.2.3 From f6341f6448e04c9a0ab22fabe38d0c6b43aab848 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:06 +0100 Subject: mtd: rawnand: gpio: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(), a NAND controller hook. Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Reported-by: Christophe Leroy Signed-off-by: Miquel Raynal Tested-by: Christophe Leroy Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-2-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/gpio.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpio.c b/drivers/mtd/nand/raw/gpio.c index 4ec0a1e10867..eb03b8cea1cb 100644 --- a/drivers/mtd/nand/raw/gpio.c +++ b/drivers/mtd/nand/raw/gpio.c @@ -161,8 +161,17 @@ static int gpio_nand_exec_op(struct nand_chip *chip, return ret; } +static int gpio_nand_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + static const struct nand_controller_ops gpio_nand_ops = { .exec_op = gpio_nand_exec_op, + .attach_chip = gpio_nand_attach_chip, }; #ifdef CONFIG_OF @@ -342,8 +351,6 @@ static int gpio_nand_probe(struct platform_device *pdev) gpiomtd->base.ops = &gpio_nand_ops; nand_set_flash_node(chip, pdev->dev.of_node); - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - chip->ecc.algo = NAND_ECC_ALGO_HAMMING; chip->options = gpiomtd->plat.options; chip->controller = &gpiomtd->base; -- cgit v1.2.3 From d525914b5bd8d71f7e92a30a170c108c485814ad Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:07 +0100 Subject: mtd: rawnand: xway: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(), a NAND controller hook. Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-3-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/xway_nand.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c index f2dbd63a5c1f..efc5bf5434e0 100644 --- a/drivers/mtd/nand/raw/xway_nand.c +++ b/drivers/mtd/nand/raw/xway_nand.c @@ -62,6 +62,7 @@ #define NAND_CON_NANDM 1 struct xway_nand_data { + struct nand_controller controller; struct nand_chip chip; unsigned long csflags; void __iomem *nandaddr; @@ -145,6 +146,18 @@ static void xway_write_buf(struct nand_chip *chip, const u_char *buf, int len) xway_writeb(nand_to_mtd(chip), NAND_WRITE_DATA, buf[i]); } +static int xway_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + +static const struct nand_controller_ops xway_nand_ops = { + .attach_chip = xway_attach_chip, +}; + /* * Probe for the NAND device. */ @@ -180,8 +193,9 @@ static int xway_nand_probe(struct platform_device *pdev) data->chip.legacy.read_byte = xway_read_byte; data->chip.legacy.chip_delay = 30; - data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - data->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; + nand_controller_init(&data->controller); + data->controller.ops = &xway_nand_ops; + data->chip.controller = &data->controller; platform_set_drvdata(pdev, data); nand_set_controller_data(&data->chip, data); -- cgit v1.2.3 From 59d93473323ab104c733778831c459f4cdbe95b2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:08 +0100 Subject: mtd: rawnand: ams-delta: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-4-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/ams-delta.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index d3c5cc513c8f..0c352b39ad4b 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -215,8 +215,17 @@ static int gpio_nand_setup_interface(struct nand_chip *this, int csline, return 0; } +static int gpio_nand_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + static const struct nand_controller_ops gpio_nand_ops = { .exec_op = gpio_nand_exec_op, + .attach_chip = gpio_nand_attach_chip, .setup_interface = gpio_nand_setup_interface, }; @@ -260,9 +269,6 @@ static int gpio_nand_probe(struct platform_device *pdev) return err; } - this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - this->ecc.algo = NAND_ECC_ALGO_HAMMING; - platform_set_drvdata(pdev, priv); /* Set chip enabled but write protected */ -- cgit v1.2.3 From dbffc8ccdf3a1d0c93bc923cb2dce3272d5fd4e8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:09 +0100 Subject: mtd: rawnand: au1550: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-5-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/au1550nd.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 79b057400fe9..7892022bd6dd 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -236,8 +236,17 @@ static int au1550nd_exec_op(struct nand_chip *this, return ret; } +static int au1550nd_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + static const struct nand_controller_ops au1550nd_ops = { .exec_op = au1550nd_exec_op, + .attach_chip = au1550nd_attach_chip, }; static int au1550nd_probe(struct platform_device *pdev) @@ -294,8 +303,6 @@ static int au1550nd_probe(struct platform_device *pdev) nand_controller_init(&ctx->controller); ctx->controller.ops = &au1550nd_ops; this->controller = &ctx->controller; - this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - this->ecc.algo = NAND_ECC_ALGO_HAMMING; if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; -- cgit v1.2.3 From 58e111002887ad5f0b665685aac3d4c3bc3768db Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:10 +0100 Subject: mtd: rawnand: cs553x: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/cs553x_nand.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index b7f3f6347761..282203debd0c 100644 --- a/drivers/mtd/nand/raw/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -243,8 +243,24 @@ static int cs_calculate_ecc(struct nand_chip *this, const u_char *dat, static struct cs553x_nand_controller *controllers[4]; +static int cs553x_attach_chip(struct nand_chip *chip) +{ + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + chip->ecc.size = 256; + chip->ecc.bytes = 3; + chip->ecc.hwctl = cs_enable_hwecc; + chip->ecc.calculate = cs_calculate_ecc; + chip->ecc.correct = nand_correct_data; + chip->ecc.strength = 1; + + return 0; +} + static const struct nand_controller_ops cs553x_nand_controller_ops = { .exec_op = cs553x_exec_op, + .attach_chip = cs553x_attach_chip, }; static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) @@ -286,14 +302,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) goto out_mtd; } - this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - this->ecc.size = 256; - this->ecc.bytes = 3; - this->ecc.hwctl = cs_enable_hwecc; - this->ecc.calculate = cs_calculate_ecc; - this->ecc.correct = nand_correct_data; - this->ecc.strength = 1; - /* Enable the following for a flash based bad block table */ this->bbt_options = NAND_BBT_USE_FLASH; -- cgit v1.2.3 From 3500bd7035ee6df2a465f37439d3cb9e00d2f66a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:11 +0100 Subject: mtd: rawnand: davinci: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Cc: Bartosz Golaszewski Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-7-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/davinci_nand.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 427f320fb79b..f8c36d19ab47 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -585,6 +585,10 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) if (IS_ERR(pdata)) return PTR_ERR(pdata); + /* Use board-specific ECC config */ + info->chip.ecc.engine_type = pdata->engine_type; + info->chip.ecc.placement = pdata->ecc_placement; + switch (info->chip.ecc.engine_type) { case NAND_ECC_ENGINE_TYPE_NONE: pdata->ecc_bits = 0; @@ -850,10 +854,6 @@ static int nand_davinci_probe(struct platform_device *pdev) info->mask_ale = pdata->mask_ale ? : MASK_ALE; info->mask_cle = pdata->mask_cle ? : MASK_CLE; - /* Use board-specific ECC config */ - info->chip.ecc.engine_type = pdata->engine_type; - info->chip.ecc.placement = pdata->ecc_placement; - spin_lock_irq(&davinci_nand_lock); /* put CSxNAND into NAND mode */ -- cgit v1.2.3 From 7f4ea0340ed4fa5cdfff6b1dd9f51f293d3f5ee7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:12 +0100 Subject: mtd: rawnand: diskonchip: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-8-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/diskonchip.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 94432a453e5e..26b265e4384a 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1269,12 +1269,31 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) return 1; } +static int doc200x_attach_chip(struct nand_chip *chip) +{ + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; + chip->ecc.size = 512; + chip->ecc.bytes = 6; + chip->ecc.strength = 2; + chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; + chip->ecc.hwctl = doc200x_enable_hwecc; + chip->ecc.calculate = doc200x_calculate_ecc; + chip->ecc.correct = doc200x_correct_data; + + return 0; +} + static const struct nand_controller_ops doc200x_ops = { .exec_op = doc200x_exec_op, + .attach_chip = doc200x_attach_chip, }; static const struct nand_controller_ops doc2001plus_ops = { .exec_op = doc2001plus_exec_op, + .attach_chip = doc200x_attach_chip, }; static int __init doc_probe(unsigned long physadr) @@ -1452,16 +1471,6 @@ static int __init doc_probe(unsigned long physadr) nand->controller = &doc->base; nand_set_controller_data(nand, doc); - nand->ecc.hwctl = doc200x_enable_hwecc; - nand->ecc.calculate = doc200x_calculate_ecc; - nand->ecc.correct = doc200x_correct_data; - - nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - nand->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; - nand->ecc.size = 512; - nand->ecc.bytes = 6; - nand->ecc.strength = 2; - nand->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; nand->bbt_options = NAND_BBT_USE_FLASH; /* Skip the automatic BBT scan so we can run it manually */ nand->options |= NAND_SKIP_BBTSCAN | NAND_NO_BBM_QUIRK; -- cgit v1.2.3 From 98591a68736f3d2431384b5284713fb98da488a6 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:13 +0100 Subject: mtd: rawnand: fsmc: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/fsmc_nand.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 4191831df182..c88421a1c078 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -880,6 +880,20 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) struct mtd_info *mtd = nand_to_mtd(nand); struct fsmc_nand_data *host = nand_to_fsmc(nand); + if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + + if (!nand->ecc.size) + nand->ecc.size = 512; + + if (AMBA_REV_BITS(host->pid) >= 8) { + nand->ecc.read_page = fsmc_read_page_hwecc; + nand->ecc.calculate = fsmc_read_hwecc_ecc4; + nand->ecc.correct = fsmc_bch8_correct_data; + nand->ecc.bytes = 13; + nand->ecc.strength = 8; + } + if (AMBA_REV_BITS(host->pid) >= 8) { switch (mtd->oobsize) { case 16: @@ -905,6 +919,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) dev_info(host->dev, "Using 1-bit HW ECC scheme\n"); nand->ecc.calculate = fsmc_read_hwecc_ecc1; nand->ecc.correct = nand_correct_data; + nand->ecc.hwctl = fsmc_enable_hwecc; nand->ecc.bytes = 3; nand->ecc.strength = 1; nand->ecc.options |= NAND_ECC_SOFT_HAMMING_SM_ORDER; @@ -1055,13 +1070,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; - /* - * Setup default ECC mode. nand_dt_init() called from nand_scan_ident() - * can overwrite this value if the DT provides a different value. - */ - nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - nand->ecc.hwctl = fsmc_enable_hwecc; - nand->ecc.size = 512; nand->badblockbits = 7; if (host->mode == USE_DMA_ACCESS) { @@ -1084,14 +1092,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) nand->options |= NAND_KEEP_TIMINGS; } - if (AMBA_REV_BITS(host->pid) >= 8) { - nand->ecc.read_page = fsmc_read_page_hwecc; - nand->ecc.calculate = fsmc_read_hwecc_ecc4; - nand->ecc.correct = fsmc_bch8_correct_data; - nand->ecc.bytes = 13; - nand->ecc.strength = 8; - } - nand_controller_init(&host->base); host->base.ops = &fsmc_nand_controller_ops; nand->controller = &host->base; -- cgit v1.2.3 From 2dbd8382a2e1a9b167712dc3764616bfdb189818 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:14 +0100 Subject: mtd: rawnand: lpc32xx_mlc: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Cc: Vladimir Zapolskiy Cc: Sylvain Lemieux Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-10-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/lpc32xx_mlc.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 4940bb2e3c07..9e728c731795 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -648,6 +648,9 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) struct lpc32xx_nand_host *host = nand_get_controller_data(chip); struct device *dev = &host->pdev->dev; + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + host->dma_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL); if (!host->dma_buf) return -ENOMEM; @@ -656,8 +659,17 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) if (!host->dummy_buf) return -ENOMEM; - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; + chip->ecc.hwctl = lpc32xx_ecc_enable; + chip->ecc.read_page_raw = lpc32xx_read_page; + chip->ecc.read_page = lpc32xx_read_page; + chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel; + chip->ecc.write_page = lpc32xx_write_page_lowlevel; + chip->ecc.write_oob = lpc32xx_write_oob; + chip->ecc.read_oob = lpc32xx_read_oob; + chip->ecc.strength = 4; + chip->ecc.bytes = 10; + mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); host->mlcsubpages = mtd->writesize / 512; @@ -741,15 +753,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); /* Initialize function pointers */ - nand_chip->ecc.hwctl = lpc32xx_ecc_enable; - nand_chip->ecc.read_page_raw = lpc32xx_read_page; - nand_chip->ecc.read_page = lpc32xx_read_page; - nand_chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel; - nand_chip->ecc.write_page = lpc32xx_write_page_lowlevel; - nand_chip->ecc.write_oob = lpc32xx_write_oob; - nand_chip->ecc.read_oob = lpc32xx_read_oob; - nand_chip->ecc.strength = 4; - nand_chip->ecc.bytes = 10; nand_chip->legacy.waitfunc = lpc32xx_waitfunc; nand_chip->options = NAND_NO_SUBPAGE_WRITE; -- cgit v1.2.3 From e044b8b72151637738b0d2880d62ee5e21f6be5d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:15 +0100 Subject: mtd: rawnand: lpc32xx_slc: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Cc: Vladimir Zapolskiy Cc: Sylvain Lemieux --- drivers/mtd/nand/raw/lpc32xx_slc.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 6db9d2ed6881..dc7785e30d2f 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -775,6 +775,9 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) struct mtd_info *mtd = nand_to_mtd(chip); struct lpc32xx_nand_host *host = nand_get_controller_data(chip); + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + /* OOB and ECC CPU and DMA work areas */ host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); @@ -786,11 +789,22 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) if (mtd->writesize <= 512) mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); + chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; /* These sizes remain the same regardless of page size */ chip->ecc.size = 256; + chip->ecc.strength = 1; chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; chip->ecc.prepad = 0; chip->ecc.postpad = 0; + chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; + chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; + chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; + chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; + chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; + chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; + chip->ecc.calculate = lpc32xx_nand_ecc_calculate; + chip->ecc.correct = nand_correct_data; + chip->ecc.hwctl = lpc32xx_nand_ecc_enable; /* * Use a custom BBT marker setup for small page FLASH that @@ -881,21 +895,9 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); /* NAND callbacks for LPC32xx SLC hardware */ - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; chip->legacy.read_byte = lpc32xx_nand_read_byte; chip->legacy.read_buf = lpc32xx_nand_read_buf; chip->legacy.write_buf = lpc32xx_nand_write_buf; - chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; - chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; - chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; - chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; - chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; - chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; - chip->ecc.calculate = lpc32xx_nand_ecc_calculate; - chip->ecc.correct = nand_correct_data; - chip->ecc.strength = 1; - chip->ecc.hwctl = lpc32xx_nand_ecc_enable; /* * Allocate a large enough buffer for a single huge page plus -- cgit v1.2.3 From 6dd09f775b729478e180eed295ddfa50569e61be Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:16 +0100 Subject: mtd: rawnand: mpc5121: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-12-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/mpc5121_nfc.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index dfd0d3ed5ed0..fb4c0b11689f 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -104,6 +104,7 @@ #define NFC_TIMEOUT (HZ / 10) /* 1/10 s */ struct mpc5121_nfc_prv { + struct nand_controller controller; struct nand_chip chip; int irq; void __iomem *regs; @@ -602,6 +603,18 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) iounmap(prv->csreg); } +static int mpc5121_nfc_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + +static const struct nand_controller_ops mpc5121_nfc_ops = { + .attach_chip = mpc5121_nfc_attach_chip, +}; + static int mpc5121_nfc_probe(struct platform_device *op) { struct device_node *dn = op->dev.of_node; @@ -634,6 +647,10 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip = &prv->chip; mtd = nand_to_mtd(chip); + nand_controller_init(&prv->controller); + prv->controller.ops = &mpc5121_nfc_ops; + chip->controller = &prv->controller; + mtd->dev.parent = dev; nand_set_controller_data(chip, prv); nand_set_flash_node(chip, dn); @@ -688,8 +705,6 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip->legacy.set_features = nand_get_set_features_notsupp; chip->legacy.get_features = nand_get_set_features_notsupp; chip->bbt_options = NAND_BBT_USE_FLASH; - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - chip->ecc.algo = NAND_ECC_ALGO_HAMMING; /* Support external chip-select logic on ADS5121 board */ if (of_machine_is_compatible("fsl,mpc5121ads")) { -- cgit v1.2.3 From 553508cec2e8138ec50f284bc8ec10e7ef0d44b1 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:17 +0100 Subject: mtd: rawnand: orion: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-13-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/orion_nand.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c index df9c0f8e4b4e..e3bb65fd3ab2 100644 --- a/drivers/mtd/nand/raw/orion_nand.c +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -22,6 +22,7 @@ #include struct orion_nand_info { + struct nand_controller controller; struct nand_chip chip; struct clk *clk; }; @@ -82,6 +83,18 @@ static void orion_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len) buf[i++] = readb(io_base); } +static int orion_nand_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + +static const struct nand_controller_ops orion_nand_ops = { + .attach_chip = orion_nand_attach_chip, +}; + static int __init orion_nand_probe(struct platform_device *pdev) { struct orion_nand_info *info; @@ -101,6 +114,10 @@ static int __init orion_nand_probe(struct platform_device *pdev) nc = &info->chip; mtd = nand_to_mtd(nc); + nand_controller_init(&info->controller); + info->controller.ops = &orion_nand_ops; + nc->controller = &info->controller; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); io_base = devm_ioremap_resource(&pdev->dev, res); @@ -139,8 +156,6 @@ static int __init orion_nand_probe(struct platform_device *pdev) nc->legacy.IO_ADDR_R = nc->legacy.IO_ADDR_W = io_base; nc->legacy.cmd_ctrl = orion_nand_cmd_ctrl; nc->legacy.read_buf = orion_nand_read_buf; - nc->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - nc->ecc.algo = NAND_ECC_ALGO_HAMMING; if (board->chip_delay) nc->legacy.chip_delay = board->chip_delay; -- cgit v1.2.3 From 3c3bbf014ab3bc9793a51d550a048873e832f2fa Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:18 +0100 Subject: mtd: rawnand: txx9ndfmc: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-14-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/txx9ndfmc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index ef81dce6b5c4..fe8ed2441588 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -253,6 +253,11 @@ static int txx9ndfmc_attach_chip(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + chip->ecc.strength = 1; + if (mtd->writesize >= 512) { chip->ecc.size = 512; chip->ecc.bytes = 6; @@ -261,6 +266,10 @@ static int txx9ndfmc_attach_chip(struct nand_chip *chip) chip->ecc.bytes = 3; } + chip->ecc.calculate = txx9ndfmc_calculate_ecc; + chip->ecc.correct = txx9ndfmc_correct_data; + chip->ecc.hwctl = txx9ndfmc_enable_hwecc; + return 0; } @@ -326,11 +335,6 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) chip->legacy.write_buf = txx9ndfmc_write_buf; chip->legacy.cmd_ctrl = txx9ndfmc_cmd_ctrl; chip->legacy.dev_ready = txx9ndfmc_dev_ready; - chip->ecc.calculate = txx9ndfmc_calculate_ecc; - chip->ecc.correct = txx9ndfmc_correct_data; - chip->ecc.hwctl = txx9ndfmc_enable_hwecc; - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - chip->ecc.strength = 1; chip->legacy.chip_delay = 100; chip->controller = &drvdata->controller; -- cgit v1.2.3 From 1f65976b55865adf84340d6e07c4c773cb8a728b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:19 +0100 Subject: mtd: rawnand: tmio: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-15-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/tmio_nand.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index 235a2f7b1bad..aa6c7e7bbf1b 100644 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -103,6 +103,7 @@ /*--------------------------------------------------------------------------*/ struct tmio_nand { + struct nand_controller controller; struct nand_chip chip; struct completion comp; @@ -355,6 +356,25 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) cell->disable(dev); } +static int tmio_attach_chip(struct nand_chip *chip) +{ + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + chip->ecc.size = 512; + chip->ecc.bytes = 6; + chip->ecc.strength = 2; + chip->ecc.hwctl = tmio_nand_enable_hwecc; + chip->ecc.calculate = tmio_nand_calculate_ecc; + chip->ecc.correct = tmio_nand_correct_data; + + return 0; +} + +static const struct nand_controller_ops tmio_ops = { + .attach_chip = tmio_attach_chip, +}; + static int tmio_probe(struct platform_device *dev) { struct tmio_nand_data *data = dev_get_platdata(&dev->dev); @@ -385,6 +405,10 @@ static int tmio_probe(struct platform_device *dev) mtd->name = "tmio-nand"; mtd->dev.parent = &dev->dev; + nand_controller_init(&tmio->controller); + tmio->controller.ops = &tmio_ops; + nand_chip->controller = &tmio->controller; + tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); if (!tmio->ccr) return -EIO; @@ -409,15 +433,6 @@ static int tmio_probe(struct platform_device *dev) nand_chip->legacy.write_buf = tmio_nand_write_buf; nand_chip->legacy.read_buf = tmio_nand_read_buf; - /* set eccmode using hardware ECC */ - nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - nand_chip->ecc.size = 512; - nand_chip->ecc.bytes = 6; - nand_chip->ecc.strength = 2; - nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; - nand_chip->ecc.calculate = tmio_nand_calculate_ecc; - nand_chip->ecc.correct = tmio_nand_correct_data; - if (data) nand_chip->badblock_pattern = data->badblock_pattern; -- cgit v1.2.3 From 8fc6f1f042b2d383f57110ab808b788592550b25 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:20 +0100 Subject: mtd: rawnand: pasemi: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-16-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/pasemi_nand.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c index 2b8f155cc0c5..4dfff34800f4 100644 --- a/drivers/mtd/nand/raw/pasemi_nand.c +++ b/drivers/mtd/nand/raw/pasemi_nand.c @@ -29,6 +29,7 @@ static unsigned int lpcctl; static struct mtd_info *pasemi_nand_mtd; +static struct nand_controller controller; static const char driver_name[] = "pasemi-nand"; static void pasemi_read_buf(struct nand_chip *chip, u_char *buf, int len) @@ -73,6 +74,18 @@ static int pasemi_device_ready(struct nand_chip *chip) return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); } +static int pasemi_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + +static const struct nand_controller_ops pasemi_ops = { + .attach_chip = pasemi_attach_chip, +}; + static int pasemi_nand_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; @@ -100,6 +113,10 @@ static int pasemi_nand_probe(struct platform_device *ofdev) goto out; } + controller.ops = &pasemi_ops; + nand_controller_init(&controller); + chip->controller = &controller; + pasemi_nand_mtd = nand_to_mtd(chip); /* Link the private data with the MTD structure */ @@ -132,8 +149,6 @@ static int pasemi_nand_probe(struct platform_device *ofdev) chip->legacy.read_buf = pasemi_read_buf; chip->legacy.write_buf = pasemi_write_buf; chip->legacy.chip_delay = 0; - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - chip->ecc.algo = NAND_ECC_ALGO_HAMMING; /* Enable the following for a flash based bad block table */ chip->bbt_options = NAND_BBT_USE_FLASH; -- cgit v1.2.3 From 612e048e6aabbc5d042140c0ec494753f36bdfe6 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:21 +0100 Subject: mtd: rawnand: plat_nand: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-17-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/plat_nand.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c index b98c0d5c413f..93d9f1694dc1 100644 --- a/drivers/mtd/nand/raw/plat_nand.c +++ b/drivers/mtd/nand/raw/plat_nand.c @@ -14,10 +14,23 @@ #include struct plat_nand_data { + struct nand_controller controller; struct nand_chip chip; void __iomem *io_base; }; +static int plat_nand_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + +static const struct nand_controller_ops plat_nand_ops = { + .attach_chip = plat_nand_attach_chip, +}; + /* * Probe for the NAND device. */ @@ -46,6 +59,10 @@ static int plat_nand_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->controller.ops = &plat_nand_ops; + nand_controller_init(&data->controller); + data->chip.controller = &data->controller; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(data->io_base)) @@ -66,9 +83,6 @@ static int plat_nand_probe(struct platform_device *pdev) data->chip.options |= pdata->chip.options; data->chip.bbt_options |= pdata->chip.bbt_options; - data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - data->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; - platform_set_drvdata(pdev, data); /* Handle any platform specific setup */ -- cgit v1.2.3 From 7ef969a042281bdcdba31f1b69daeea4f0789ed1 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:22 +0100 Subject: mtd: rawnand: r852: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Cc: Maxim Levitsky Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-18-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/r852.c | 40 +++++++++++++++++++++++++++------------- drivers/mtd/nand/raw/r852.h | 1 + 2 files changed, 28 insertions(+), 13 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c index 6b7addd2c420..c742354c1b0b 100644 --- a/drivers/mtd/nand/raw/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -817,6 +817,29 @@ out: return ret; } +static int r852_attach_chip(struct nand_chip *chip) +{ + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; + chip->ecc.size = R852_DMA_LEN; + chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.strength = 2; + chip->ecc.hwctl = r852_ecc_hwctl; + chip->ecc.calculate = r852_ecc_calculate; + chip->ecc.correct = r852_ecc_correct; + + /* TODO: hack */ + chip->ecc.read_oob = r852_read_oob; + + return 0; +} + +static const struct nand_controller_ops r852_ops = { + .attach_chip = r852_attach_chip, +}; + static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { int error; @@ -858,19 +881,6 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) chip->legacy.read_buf = r852_read_buf; chip->legacy.write_buf = r852_write_buf; - /* ecc */ - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; - chip->ecc.size = R852_DMA_LEN; - chip->ecc.bytes = SM_OOB_SIZE; - chip->ecc.strength = 2; - chip->ecc.hwctl = r852_ecc_hwctl; - chip->ecc.calculate = r852_ecc_calculate; - chip->ecc.correct = r852_ecc_correct; - - /* TODO: hack */ - chip->ecc.read_oob = r852_read_oob; - /* init our device structure */ dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL); @@ -882,6 +892,10 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) dev->pci_dev = pci_dev; pci_set_drvdata(pci_dev, dev); + nand_controller_init(&dev->controller); + dev->controller.ops = &r852_ops; + chip->controller = &dev->controller; + dev->bounce_buffer = dma_alloc_coherent(&pci_dev->dev, R852_DMA_LEN, &dev->phys_bounce_buffer, GFP_KERNEL); diff --git a/drivers/mtd/nand/raw/r852.h b/drivers/mtd/nand/raw/r852.h index e9ce299c499d..96fe301d15da 100644 --- a/drivers/mtd/nand/raw/r852.h +++ b/drivers/mtd/nand/raw/r852.h @@ -104,6 +104,7 @@ #define DMA_MEMORY 1 struct r852_device { + struct nand_controller controller; void __iomem *mmio; /* mmio */ struct nand_chip *chip; /* nand chip backpointer */ struct pci_dev *pci_dev; /* pci backpointer */ -- cgit v1.2.3 From 1ac6870991939c9351d4c5c49c38b52c97ee7e19 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:23 +0100 Subject: mtd: rawnand: sharpsl: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/sharpsl.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c index 1327bfb3d5d3..af98bcc9d689 100644 --- a/drivers/mtd/nand/raw/sharpsl.c +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -20,6 +20,7 @@ #include struct sharpsl_nand { + struct nand_controller controller; struct nand_chip chip; void __iomem *io; @@ -96,6 +97,25 @@ static int sharpsl_nand_calculate_ecc(struct nand_chip *chip, return readb(sharpsl->io + ECCCNTR) != 0; } +static int sharpsl_attach_chip(struct nand_chip *chip) +{ + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + chip->ecc.size = 256; + chip->ecc.bytes = 3; + chip->ecc.strength = 1; + chip->ecc.hwctl = sharpsl_nand_enable_hwecc; + chip->ecc.calculate = sharpsl_nand_calculate_ecc; + chip->ecc.correct = nand_correct_data; + + return 0; +} + +static const struct nand_controller_ops sharpsl_ops = { + .attach_chip = sharpsl_attach_chip, +}; + /* * Main initialization routine */ @@ -136,6 +156,10 @@ static int sharpsl_nand_probe(struct platform_device *pdev) /* Get pointer to private data */ this = (struct nand_chip *)(&sharpsl->chip); + nand_controller_init(&sharpsl->controller); + sharpsl->controller.ops = &sharpsl_ops; + this->controller = &sharpsl->controller; + /* Link the private data with the MTD structure */ mtd = nand_to_mtd(this); mtd->dev.parent = &pdev->dev; @@ -156,15 +180,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) this->legacy.dev_ready = sharpsl_nand_dev_ready; /* 15 us command delay time */ this->legacy.chip_delay = 15; - /* set eccmode using hardware ECC */ - this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - this->ecc.size = 256; - this->ecc.bytes = 3; - this->ecc.strength = 1; this->badblock_pattern = data->badblock_pattern; - this->ecc.hwctl = sharpsl_nand_enable_hwecc; - this->ecc.calculate = sharpsl_nand_calculate_ecc; - this->ecc.correct = nand_correct_data; /* Scan to find existence of the device */ err = nand_scan(this, 1); -- cgit v1.2.3 From b36bf0a0fe5d18561dd98eb774ef61dd396edc42 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:34:24 +0100 Subject: mtd: rawnand: socrates: Move the ECC initialization to ->attach_chip() The probe function is only supposed to initialize the controller hardware but not the ECC engine. Indeed, we don't know anything about the NAND chip(s) at this stage. Let's move the logic initializing the ECC engine, even pretty simple, to the ->attach_chip() hook which gets called during nand_scan() routine, after the NAND chip discovery. As the previously mentioned logic is supposed to parse the DT for us, it is likely that the chip->ecc.* entries be overwritten. So let's avoid this by moving these lines to ->attach_chip(). Fixes: d7157ff49a5b ("mtd: rawnand: Use the ECC framework user input parsing bits") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113123424.32233-20-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/socrates_nand.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c index 0f63ff6f7fe7..107208311987 100644 --- a/drivers/mtd/nand/raw/socrates_nand.c +++ b/drivers/mtd/nand/raw/socrates_nand.c @@ -22,6 +22,7 @@ #define FPGA_NAND_DATA_SHIFT 16 struct socrates_nand_host { + struct nand_controller controller; struct nand_chip nand_chip; void __iomem *io_base; struct device *dev; @@ -116,6 +117,18 @@ static int socrates_nand_device_ready(struct nand_chip *nand_chip) return 1; } +static int socrates_attach_chip(struct nand_chip *chip) +{ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + + return 0; +} + +static const struct nand_controller_ops socrates_ops = { + .attach_chip = socrates_attach_chip, +}; + /* * Probe for the NAND device. */ @@ -141,6 +154,10 @@ static int socrates_nand_probe(struct platform_device *ofdev) mtd = nand_to_mtd(nand_chip); host->dev = &ofdev->dev; + nand_controller_init(&host->controller); + host->controller.ops = &socrates_ops; + nand_chip->controller = &host->controller; + /* link the private data structures */ nand_set_controller_data(nand_chip, host); nand_set_flash_node(nand_chip, ofdev->dev.of_node); @@ -153,10 +170,6 @@ static int socrates_nand_probe(struct platform_device *ofdev) nand_chip->legacy.read_buf = socrates_nand_read_buf; nand_chip->legacy.dev_ready = socrates_nand_device_ready; - /* enable ECC */ - nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; - nand_chip->ecc.algo = NAND_ECC_ALGO_HAMMING; - /* TODO: I have no idea what real delay is. */ nand_chip->legacy.chip_delay = 20; /* 20us command delay time */ -- cgit v1.2.3 From 51e7bf4534da678da27c0f51e7ff21804fae88ca Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:05 +0200 Subject: mtd: nand: ecc: Add an I/O request tweaking mechanism Currently, BCH and Hamming engine are sharing the same tweaking/restoring I/O mechanism: they need the I/O request to fully cover the main/OOB area. Let's make this code generic as sharing the code between two drivers is already a win. Maybe other ECC engine drivers will need it too. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-2-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 32 ++++++++++++++ 2 files changed, 138 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c index 4a56e6c0da67..541137736fd3 100644 --- a/drivers/mtd/nand/ecc.c +++ b/drivers/mtd/nand/ecc.c @@ -95,6 +95,7 @@ #include #include +#include /** * nand_ecc_init_ctx - Init the ECC engine context @@ -479,6 +480,111 @@ bool nand_ecc_is_strong_enough(struct nand_device *nand) } EXPORT_SYMBOL(nand_ecc_is_strong_enough); +/* ECC engine driver internal helpers */ +int nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_device *nand) +{ + unsigned int total_buffer_size; + + ctx->nand = nand; + + /* Let the user decide the exact length of each buffer */ + if (!ctx->page_buffer_size) + ctx->page_buffer_size = nanddev_page_size(nand); + if (!ctx->oob_buffer_size) + ctx->oob_buffer_size = nanddev_per_page_oobsize(nand); + + total_buffer_size = ctx->page_buffer_size + ctx->oob_buffer_size; + + ctx->spare_databuf = kzalloc(total_buffer_size, GFP_KERNEL); + if (!ctx->spare_databuf) + return -ENOMEM; + + ctx->spare_oobbuf = ctx->spare_databuf + ctx->page_buffer_size; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_ecc_init_req_tweaking); + +void nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx) +{ + kfree(ctx->spare_databuf); +} +EXPORT_SYMBOL_GPL(nand_ecc_cleanup_req_tweaking); + +/* + * Ensure data and OOB area is fully read/written otherwise the correction might + * not work as expected. + */ +void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_page_io_req *req) +{ + struct nand_device *nand = ctx->nand; + struct nand_page_io_req *orig, *tweak; + + /* Save the original request */ + ctx->orig_req = *req; + ctx->bounce_data = false; + ctx->bounce_oob = false; + orig = &ctx->orig_req; + tweak = req; + + /* Ensure the request covers the entire page */ + if (orig->datalen < nanddev_page_size(nand)) { + ctx->bounce_data = true; + tweak->dataoffs = 0; + tweak->datalen = nanddev_page_size(nand); + tweak->databuf.in = ctx->spare_databuf; + memset(tweak->databuf.in, 0xFF, ctx->page_buffer_size); + } + + if (orig->ooblen < nanddev_per_page_oobsize(nand)) { + ctx->bounce_oob = true; + tweak->ooboffs = 0; + tweak->ooblen = nanddev_per_page_oobsize(nand); + tweak->oobbuf.in = ctx->spare_oobbuf; + memset(tweak->oobbuf.in, 0xFF, ctx->oob_buffer_size); + } + + /* Copy the data that must be writen in the bounce buffers, if needed */ + if (orig->type == NAND_PAGE_WRITE) { + if (ctx->bounce_data) + memcpy((void *)tweak->databuf.out + orig->dataoffs, + orig->databuf.out, orig->datalen); + + if (ctx->bounce_oob) + memcpy((void *)tweak->oobbuf.out + orig->ooboffs, + orig->oobbuf.out, orig->ooblen); + } +} +EXPORT_SYMBOL_GPL(nand_ecc_tweak_req); + +void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_page_io_req *req) +{ + struct nand_page_io_req *orig, *tweak; + + orig = &ctx->orig_req; + tweak = req; + + /* Restore the data read from the bounce buffers, if needed */ + if (orig->type == NAND_PAGE_READ) { + if (ctx->bounce_data) + memcpy(orig->databuf.in, + tweak->databuf.in + orig->dataoffs, + orig->datalen); + + if (ctx->bounce_oob) + memcpy(orig->oobbuf.in, + tweak->oobbuf.in + orig->ooboffs, + orig->ooblen); + } + + /* Ensure the original request is restored */ + *req = *orig; +} +EXPORT_SYMBOL_GPL(nand_ecc_restore_req); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Miquel Raynal "); MODULE_DESCRIPTION("Generic ECC engine"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 697ea2474a7c..36e4fe08d0ea 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -278,6 +278,38 @@ int nand_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req); bool nand_ecc_is_strong_enough(struct nand_device *nand); +/** + * struct nand_ecc_req_tweak_ctx - Help for automatically tweaking requests + * @orig_req: Pointer to the original IO request + * @nand: Related NAND device, to have access to its memory organization + * @page_buffer_size: Real size of the page buffer to use (can be set by the + * user before the tweaking mechanism initialization) + * @oob_buffer_size: Real size of the OOB buffer to use (can be set by the + * user before the tweaking mechanism initialization) + * @spare_databuf: Data bounce buffer + * @spare_oobbuf: OOB bounce buffer + * @bounce_data: Flag indicating a data bounce buffer is used + * @bounce_oob: Flag indicating an OOB bounce buffer is used + */ +struct nand_ecc_req_tweak_ctx { + struct nand_page_io_req orig_req; + struct nand_device *nand; + unsigned int page_buffer_size; + unsigned int oob_buffer_size; + void *spare_databuf; + void *spare_oobbuf; + bool bounce_data; + bool bounce_oob; +}; + +int nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_device *nand); +void nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx); +void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_page_io_req *req); +void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_page_io_req *req); + /** * struct nand_ecc - Information relative to the ECC * @defaults: Default values, depend on the underlying subsystem -- cgit v1.2.3 From cdbe8df5e28e452c232c0c16b205edfd390d28e5 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:06 +0200 Subject: mtd: nand: ecc-bch: Move BCH code to the generic NAND layer BCH ECC code might be later re-used by the SPI NAND layer. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-3-miquel.raynal@bootlin.com --- drivers/mtd/nand/Kconfig | 11 ++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/ecc-sw-bch.c | 219 ++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/Kconfig | 10 -- drivers/mtd/nand/raw/Makefile | 1 - drivers/mtd/nand/raw/nand_base.c | 2 +- drivers/mtd/nand/raw/nand_bch.c | 219 ------------------------------------ drivers/mtd/nand/raw/nandsim.c | 2 +- drivers/mtd/nand/raw/omap2.c | 2 +- include/linux/mtd/nand-ecc-sw-bch.h | 66 +++++++++++ include/linux/mtd/nand_bch.h | 66 ----------- 11 files changed, 300 insertions(+), 299 deletions(-) create mode 100644 drivers/mtd/nand/ecc-sw-bch.c delete mode 100644 drivers/mtd/nand/raw/nand_bch.c create mode 100644 include/linux/mtd/nand-ecc-sw-bch.h delete mode 100644 include/linux/mtd/nand_bch.h (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 4a9aed4f0104..55c17fb0dee1 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -15,6 +15,17 @@ config MTD_NAND_ECC bool depends on MTD_NAND_CORE +config MTD_NAND_ECC_SW_BCH + bool "Software BCH ECC engine" + select BCH + select MTD_NAND_ECC + default n + help + This enables support for software BCH error correction. Binary BCH + codes are more powerful and cpu intensive than traditional Hamming + ECC codes. They are used with NAND devices requiring more than 1 bit + of error correction. + endmenu endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 981372953b56..c7179ff23753 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -8,3 +8,4 @@ obj-y += raw/ obj-y += spi/ nandcore-$(CONFIG_MTD_NAND_ECC) += ecc.o +nandcore-$(CONFIG_MTD_NAND_ECC_SW_BCH) += ecc-sw-bch.o diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c new file mode 100644 index 000000000000..6da14d0263d9 --- /dev/null +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct nand_bch_control - private NAND BCH control structure + * @bch: BCH control structure + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_bch_control { + struct bch_control *bch; + unsigned int *errloc; + unsigned char *eccmask; +}; + +/** + * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block + * @chip: NAND chip object + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, + unsigned char *code) +{ + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int i; + + memset(code, 0, chip->ecc.bytes); + bch_encode(nbc->bch, buf, chip->ecc.size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < chip->ecc.bytes; i++) + code[i] ^= nbc->eccmask[i]; + + return 0; +} +EXPORT_SYMBOL(nand_bch_calculate_ecc); + +/** + * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) + * @chip: NAND chip object + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct bit errors for a data byte block + */ +int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + struct nand_bch_control *nbc = chip->ecc.priv; + unsigned int *errloc = nbc->errloc; + int i, count; + + count = bch_decode(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, + NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (chip->ecc.size*8)) + /* error is located in data, correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + /* else error in ecc, no action needed */ + + pr_debug("%s: corrected bitflip %u\n", __func__, + errloc[i]); + } + } else if (count < 0) { + pr_err("ecc unrecoverable error\n"); + count = -EBADMSG; + } + return count; +} +EXPORT_SYMBOL(nand_bch_correct_data); + +/** + * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction + * @mtd: MTD block structure + * + * Returns: + * a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes + * are used to compute BCH parameters m (Galois field order) and t (error + * correction capability). @eccbytes should be equal to the number of bytes + * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) + * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + */ +struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + unsigned int m, t, eccsteps, i; + struct nand_bch_control *nbc = NULL; + unsigned char *erased_page; + unsigned int eccsize = nand->ecc.size; + unsigned int eccbytes = nand->ecc.bytes; + unsigned int eccstrength = nand->ecc.strength; + + if (!eccbytes && eccstrength) { + eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); + nand->ecc.bytes = eccbytes; + } + + if (!eccsize || !eccbytes) { + pr_warn("ecc parameters not supplied\n"); + goto fail; + } + + m = fls(1+8*eccsize); + t = (eccbytes*8)/m; + + nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); + if (!nbc) + goto fail; + + nbc->bch = bch_init(m, t, 0, false); + if (!nbc->bch) + goto fail; + + /* verify that eccbytes has the expected value */ + if (nbc->bch->ecc_bytes != eccbytes) { + pr_warn("invalid eccbytes %u, should be %u\n", + eccbytes, nbc->bch->ecc_bytes); + goto fail; + } + + eccsteps = mtd->writesize/eccsize; + + /* Check that we have an oob layout description. */ + if (!mtd->ooblayout) { + pr_warn("missing oob scheme"); + goto fail; + } + + /* sanity checks */ + if (8*(eccsize+eccbytes) >= (1 << m)) { + pr_warn("eccsize %u is too large\n", eccsize); + goto fail; + } + + /* + * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), + * which is called by mtd_ooblayout_count_eccbytes(). + * Make sure they are properly initialized before calling + * mtd_ooblayout_count_eccbytes(). + * FIXME: we should probably rework the sequencing in nand_scan_tail() + * to avoid setting those fields twice. + */ + nand->ecc.steps = eccsteps; + nand->ecc.total = eccsteps * eccbytes; + nand->base.ecc.ctx.total = nand->ecc.total; + if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { + pr_warn("invalid ecc layout\n"); + goto fail; + } + + nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL); + nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); + if (!nbc->eccmask || !nbc->errloc) + goto fail; + /* + * compute and store the inverted ecc of an erased ecc block + */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) + goto fail; + + memset(erased_page, 0xff, eccsize); + bch_encode(nbc->bch, erased_page, eccsize, nbc->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + nbc->eccmask[i] ^= 0xff; + + if (!eccstrength) + nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); + + return nbc; +fail: + nand_bch_free(nbc); + return NULL; +} +EXPORT_SYMBOL(nand_bch_init); + +/** + * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources + * @nbc: NAND BCH control structure + */ +void nand_bch_free(struct nand_bch_control *nbc) +{ + if (nbc) { + bch_free(nbc->bch); + kfree(nbc->errloc); + kfree(nbc->eccmask); + kfree(nbc); + } +} +EXPORT_SYMBOL(nand_bch_free); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ivan Djelic "); +MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 6c46f25b57e2..b73860aa77c6 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -22,16 +22,6 @@ menuconfig MTD_RAW_NAND if MTD_RAW_NAND -config MTD_NAND_ECC_SW_BCH - bool "Support software BCH ECC" - select BCH - default n - help - This enables support for software BCH error correction. Binary BCH - codes are more powerful and cpu intensive than traditional Hamming - ECC codes. They are used with NAND devices requiring more than 1 bit - of error correction. - comment "Raw/parallel NAND flash controllers" config MTD_NAND_DENALI diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 2930f5b9015d..76904305d091 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_MTD_RAW_NAND) += nand.o obj-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += nand_ecc.o -nand-$(CONFIG_MTD_NAND_ECC_SW_BCH) += nand_bch.o obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 1f0d542d5923..7edcf08a2e81 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c deleted file mode 100644 index 9d19ac14c196..000000000000 --- a/drivers/mtd/nand/raw/nand_bch.c +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * This file provides ECC correction for more than 1 bit per block of data, - * using binary BCH codes. It relies on the generic BCH library lib/bch.c. - * - * Copyright © 2011 Ivan Djelic - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * struct nand_bch_control - private NAND BCH control structure - * @bch: BCH control structure - * @errloc: error location array - * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid - */ -struct nand_bch_control { - struct bch_control *bch; - unsigned int *errloc; - unsigned char *eccmask; -}; - -/** - * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block - * @chip: NAND chip object - * @buf: input buffer with raw data - * @code: output buffer with ECC - */ -int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, - unsigned char *code) -{ - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int i; - - memset(code, 0, chip->ecc.bytes); - bch_encode(nbc->bch, buf, chip->ecc.size, code); - - /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) - code[i] ^= nbc->eccmask[i]; - - return 0; -} -EXPORT_SYMBOL(nand_bch_calculate_ecc); - -/** - * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) - * @chip: NAND chip object - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * - * Detect and correct bit errors for a data byte block - */ -int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int *errloc = nbc->errloc; - int i, count; - - count = bch_decode(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, - NULL, errloc); - if (count > 0) { - for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) - /* error is located in data, correct it */ - buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); - /* else error in ecc, no action needed */ - - pr_debug("%s: corrected bitflip %u\n", __func__, - errloc[i]); - } - } else if (count < 0) { - pr_err("ecc unrecoverable error\n"); - count = -EBADMSG; - } - return count; -} -EXPORT_SYMBOL(nand_bch_correct_data); - -/** - * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction - * @mtd: MTD block structure - * - * Returns: - * a pointer to a new NAND BCH control structure, or NULL upon failure - * - * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes - * are used to compute BCH parameters m (Galois field order) and t (error - * correction capability). @eccbytes should be equal to the number of bytes - * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. - * - * Example: to configure 4 bit correction per 512 bytes, you should pass - * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) - * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) - */ -struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - unsigned int m, t, eccsteps, i; - struct nand_bch_control *nbc = NULL; - unsigned char *erased_page; - unsigned int eccsize = nand->ecc.size; - unsigned int eccbytes = nand->ecc.bytes; - unsigned int eccstrength = nand->ecc.strength; - - if (!eccbytes && eccstrength) { - eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); - nand->ecc.bytes = eccbytes; - } - - if (!eccsize || !eccbytes) { - pr_warn("ecc parameters not supplied\n"); - goto fail; - } - - m = fls(1+8*eccsize); - t = (eccbytes*8)/m; - - nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); - if (!nbc) - goto fail; - - nbc->bch = bch_init(m, t, 0, false); - if (!nbc->bch) - goto fail; - - /* verify that eccbytes has the expected value */ - if (nbc->bch->ecc_bytes != eccbytes) { - pr_warn("invalid eccbytes %u, should be %u\n", - eccbytes, nbc->bch->ecc_bytes); - goto fail; - } - - eccsteps = mtd->writesize/eccsize; - - /* Check that we have an oob layout description. */ - if (!mtd->ooblayout) { - pr_warn("missing oob scheme"); - goto fail; - } - - /* sanity checks */ - if (8*(eccsize+eccbytes) >= (1 << m)) { - pr_warn("eccsize %u is too large\n", eccsize); - goto fail; - } - - /* - * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), - * which is called by mtd_ooblayout_count_eccbytes(). - * Make sure they are properly initialized before calling - * mtd_ooblayout_count_eccbytes(). - * FIXME: we should probably rework the sequencing in nand_scan_tail() - * to avoid setting those fields twice. - */ - nand->ecc.steps = eccsteps; - nand->ecc.total = eccsteps * eccbytes; - nand->base.ecc.ctx.total = nand->ecc.total; - if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { - pr_warn("invalid ecc layout\n"); - goto fail; - } - - nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL); - nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); - if (!nbc->eccmask || !nbc->errloc) - goto fail; - /* - * compute and store the inverted ecc of an erased ecc block - */ - erased_page = kmalloc(eccsize, GFP_KERNEL); - if (!erased_page) - goto fail; - - memset(erased_page, 0xff, eccsize); - bch_encode(nbc->bch, erased_page, eccsize, nbc->eccmask); - kfree(erased_page); - - for (i = 0; i < eccbytes; i++) - nbc->eccmask[i] ^= 0xff; - - if (!eccstrength) - nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); - - return nbc; -fail: - nand_bch_free(nbc); - return NULL; -} -EXPORT_SYMBOL(nand_bch_init); - -/** - * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources - * @nbc: NAND BCH control structure - */ -void nand_bch_free(struct nand_bch_control *nbc) -{ - if (nbc) { - bch_free(nbc->bch); - kfree(nbc->errloc); - kfree(nbc->eccmask); - kfree(nbc); - } -} -EXPORT_SYMBOL(nand_bch_free); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ivan Djelic "); -MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index a8048cb8d220..9c940ead66fd 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 512f60780a50..0ef209e1cd87 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h new file mode 100644 index 000000000000..1e1ee3af82b1 --- /dev/null +++ b/include/linux/mtd/nand-ecc-sw-bch.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright © 2011 Ivan Djelic + * + * This file is the header for the NAND BCH ECC implementation. + */ + +#ifndef __MTD_NAND_ECC_SW_BCH_H__ +#define __MTD_NAND_ECC_SW_BCH_H__ + +struct mtd_info; +struct nand_chip; +struct nand_bch_control; + +#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) + +static inline int mtd_nand_has_bch(void) { return 1; } + +/* + * Calculate BCH ecc code + */ +int nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat, + u_char *ecc_code); + +/* + * Detect and correct bit errors + */ +int nand_bch_correct_data(struct nand_chip *chip, u_char *dat, + u_char *read_ecc, u_char *calc_ecc); +/* + * Initialize BCH encoder/decoder + */ +struct nand_bch_control *nand_bch_init(struct mtd_info *mtd); +/* + * Release BCH encoder/decoder resources + */ +void nand_bch_free(struct nand_bch_control *nbc); + +#else /* !CONFIG_MTD_NAND_ECC_SW_BCH */ + +static inline int mtd_nand_has_bch(void) { return 0; } + +static inline int +nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat, + u_char *ecc_code) +{ + return -1; +} + +static inline int +nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + return -ENOTSUPP; +} + +static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) +{ + return NULL; +} + +static inline void nand_bch_free(struct nand_bch_control *nbc) {} + +#endif /* CONFIG_MTD_NAND_ECC_SW_BCH */ + +#endif /* __MTD_NAND_ECC_SW_BCH_H__ */ diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h deleted file mode 100644 index d5956cc48ba9..000000000000 --- a/include/linux/mtd/nand_bch.h +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright © 2011 Ivan Djelic - * - * This file is the header for the NAND BCH ECC implementation. - */ - -#ifndef __MTD_NAND_BCH_H__ -#define __MTD_NAND_BCH_H__ - -struct mtd_info; -struct nand_chip; -struct nand_bch_control; - -#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) - -static inline int mtd_nand_has_bch(void) { return 1; } - -/* - * Calculate BCH ecc code - */ -int nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat, - u_char *ecc_code); - -/* - * Detect and correct bit errors - */ -int nand_bch_correct_data(struct nand_chip *chip, u_char *dat, - u_char *read_ecc, u_char *calc_ecc); -/* - * Initialize BCH encoder/decoder - */ -struct nand_bch_control *nand_bch_init(struct mtd_info *mtd); -/* - * Release BCH encoder/decoder resources - */ -void nand_bch_free(struct nand_bch_control *nbc); - -#else /* !CONFIG_MTD_NAND_ECC_SW_BCH */ - -static inline int mtd_nand_has_bch(void) { return 0; } - -static inline int -nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat, - u_char *ecc_code) -{ - return -1; -} - -static inline int -nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - return -ENOTSUPP; -} - -static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) -{ - return NULL; -} - -static inline void nand_bch_free(struct nand_bch_control *nbc) {} - -#endif /* CONFIG_MTD_NAND_ECC_SW_BCH */ - -#endif /* __MTD_NAND_BCH_H__ */ -- cgit v1.2.3 From 8c5c209218564a180e8b90fb12d29f72485c6c72 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:07 +0200 Subject: mtd: nand: ecc-bch: Cleanup and style fixes Fix function headers, capitals and reword a little bit the comments to make this driver more readable. There is not functional change. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-4-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-bch.c | 55 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c index 6da14d0263d9..f98a03e8efae 100644 --- a/drivers/mtd/nand/ecc-sw-bch.c +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -29,10 +29,10 @@ struct nand_bch_control { }; /** - * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block - * @chip: NAND chip object - * @buf: input buffer with raw data - * @code: output buffer with ECC + * nand_bch_calcuate_ecc - Calculate the ECC corresponding to a data block + * @chip: NAND chip object + * @buf: Input buffer with raw data + * @code: Output buffer with ECC */ int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, unsigned char *code) @@ -52,13 +52,13 @@ int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, EXPORT_SYMBOL(nand_bch_calculate_ecc); /** - * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) - * @chip: NAND chip object - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data + * nand_bch_correct_data - Detect, correct and report bit error(s) + * @chip: NAND chip object + * @buf: Raw data read from the chip + * @read_ecc: ECC bytes from the chip + * @calc_ecc: ECC calculated from the raw data * - * Detect and correct bit errors for a data byte block + * Detect and correct bit errors for a data block. */ int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) @@ -71,37 +71,39 @@ int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, NULL, errloc); if (count > 0) { for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) - /* error is located in data, correct it */ + if (errloc[i] < (chip->ecc.size * 8)) + /* The error is in the data area: correct it */ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); - /* else error in ecc, no action needed */ + /* Otherwise the error is in the ECC area: nothing to do */ pr_debug("%s: corrected bitflip %u\n", __func__, - errloc[i]); + errloc[i]); } } else if (count < 0) { - pr_err("ecc unrecoverable error\n"); + pr_err("ECC unrecoverable error\n"); count = -EBADMSG; } + return count; } EXPORT_SYMBOL(nand_bch_correct_data); /** - * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction - * @mtd: MTD block structure + * nand_bch_init - Initialize software BCH ECC engine + * @mtd: MTD device * - * Returns: - * a pointer to a new NAND BCH control structure, or NULL upon failure + * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure * * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes - * are used to compute BCH parameters m (Galois field order) and t (error - * correction capability). @eccbytes should be equal to the number of bytes - * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. + * are used to compute the following BCH parameters: + * m, the Galois field order + * t, the error correction capability + * @eccbytes should be equal to the number of bytes required to store m * t + * bits, where m is such that 2^m - 1 > step_size * 8. * * Example: to configure 4 bit correction per 512 bytes, you should pass - * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) - * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) + * @eccsize = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) + * @eccbytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) */ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) { @@ -175,6 +177,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); if (!nbc->eccmask || !nbc->errloc) goto fail; + /* * compute and store the inverted ecc of an erased ecc block */ @@ -200,8 +203,8 @@ fail: EXPORT_SYMBOL(nand_bch_init); /** - * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources - * @nbc: NAND BCH control structure + * nand_bch_free - Release NAND BCH ECC resources + * @nbc: NAND BCH control structure */ void nand_bch_free(struct nand_bch_control *nbc) { -- cgit v1.2.3 From 3c0fe36abebee55821badaa9d6cecd03799f7843 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:08 +0200 Subject: mtd: nand: ecc-bch: Stop exporting the private structure The NAND BCH control structure has nothing to do outside of this driver, all users of the nand_bch_init/free() functions just save it to chip->ecc.priv so do it in this driver directly and return a regular error code instead. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-5-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-bch.c | 38 ++++++++++++++++++++----------------- drivers/mtd/nand/raw/fsmc_nand.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 12 +++++++----- drivers/mtd/nand/raw/omap2.c | 16 ++++++++-------- include/linux/mtd/nand-ecc-sw-bch.h | 11 +++++------ 5 files changed, 42 insertions(+), 37 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c index f98a03e8efae..b6bfee9805ae 100644 --- a/drivers/mtd/nand/ecc-sw-bch.c +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -90,7 +90,7 @@ EXPORT_SYMBOL(nand_bch_correct_data); /** * nand_bch_init - Initialize software BCH ECC engine - * @mtd: MTD device + * @chip: NAND chip object * * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure * @@ -105,24 +105,24 @@ EXPORT_SYMBOL(nand_bch_correct_data); * @eccsize = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) * @eccbytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) */ -struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) +int nand_bch_init(struct nand_chip *chip) { - struct nand_chip *nand = mtd_to_nand(mtd); + struct mtd_info *mtd = nand_to_mtd(chip); unsigned int m, t, eccsteps, i; struct nand_bch_control *nbc = NULL; unsigned char *erased_page; - unsigned int eccsize = nand->ecc.size; - unsigned int eccbytes = nand->ecc.bytes; - unsigned int eccstrength = nand->ecc.strength; + unsigned int eccsize = chip->ecc.size; + unsigned int eccbytes = chip->ecc.bytes; + unsigned int eccstrength = chip->ecc.strength; if (!eccbytes && eccstrength) { eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); - nand->ecc.bytes = eccbytes; + chip->ecc.bytes = eccbytes; } if (!eccsize || !eccbytes) { pr_warn("ecc parameters not supplied\n"); - goto fail; + return -EINVAL; } m = fls(1+8*eccsize); @@ -130,7 +130,9 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); if (!nbc) - goto fail; + return -ENOMEM; + + chip->ecc.priv = nbc; nbc->bch = bch_init(m, t, 0, false); if (!nbc->bch) @@ -165,9 +167,9 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) * FIXME: we should probably rework the sequencing in nand_scan_tail() * to avoid setting those fields twice. */ - nand->ecc.steps = eccsteps; - nand->ecc.total = eccsteps * eccbytes; - nand->base.ecc.ctx.total = nand->ecc.total; + chip->ecc.steps = eccsteps; + chip->ecc.total = eccsteps * eccbytes; + nand->base.ecc.ctx.total = chip->ecc.total; if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { pr_warn("invalid ecc layout\n"); goto fail; @@ -193,12 +195,12 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) nbc->eccmask[i] ^= 0xff; if (!eccstrength) - nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); + chip->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); - return nbc; + return 0; fail: - nand_bch_free(nbc); - return NULL; + nand_bch_free(chip); + return -EINVAL; } EXPORT_SYMBOL(nand_bch_init); @@ -206,8 +208,10 @@ EXPORT_SYMBOL(nand_bch_init); * nand_bch_free - Release NAND BCH ECC resources * @nbc: NAND BCH control structure */ -void nand_bch_free(struct nand_bch_control *nbc) +void nand_bch_free(struct nand_chip *chip) { + struct nand_bch_control *nbc = chip->ecc.priv; + if (nbc) { bch_free(nbc->bch); kfree(nbc->errloc); diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index c88421a1c078..984b05e6bd38 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -942,7 +942,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) /* * Don't set layout for BCH4 SW ECC. This will be - * generated later in nand_bch_init() later. + * generated later during BCH initialization. */ if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { switch (mtd->oobsize) { diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 7edcf08a2e81..287c521a3d43 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5203,6 +5203,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) struct mtd_info *mtd = nand_to_mtd(chip); struct nand_device *nanddev = mtd_to_nanddev(mtd); struct nand_ecc_ctrl *ecc = &chip->ecc; + int ret; if (WARN_ON(ecc->engine_type != NAND_ECC_ENGINE_TYPE_SOFT)) return -EINVAL; @@ -5289,13 +5290,14 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) ecc->strength = bytes * 8 / fls(8 * ecc->size); } - /* See nand_bch_init() for details. */ + /* See the software BCH ECC initialization for details */ ecc->bytes = 0; - ecc->priv = nand_bch_init(mtd); - if (!ecc->priv) { + ret = nand_bch_init(chip); + if (ret) { WARN(1, "BCH ECC initialization failed!\n"); - return -EINVAL; + return ret; } + return 0; default: WARN(1, "Unsupported ECC algorithm!\n"); @@ -5955,7 +5957,7 @@ void nand_cleanup(struct nand_chip *chip) { if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && chip->ecc.algo == NAND_ECC_ALGO_BCH) - nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + nand_bch_free(chip); nanddev_cleanup(&chip->base); diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 0ef209e1cd87..6aab57336690 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -2047,10 +2047,10 @@ static int omap_nand_attach_chip(struct nand_chip *chip) /* Reserve one byte for the OMAP marker */ oobbytes_per_step = chip->ecc.bytes + 1; /* Software BCH library is used for locating errors */ - chip->ecc.priv = nand_bch_init(mtd); - if (!chip->ecc.priv) { + err = nand_bch_init(chip); + if (err) { dev_err(dev, "Unable to use BCH library\n"); - return -EINVAL; + return err; } break; @@ -2089,10 +2089,10 @@ static int omap_nand_attach_chip(struct nand_chip *chip) /* Reserve one byte for the OMAP marker */ oobbytes_per_step = chip->ecc.bytes + 1; /* Software BCH library is used for locating errors */ - chip->ecc.priv = nand_bch_init(mtd); - if (!chip->ecc.priv) { + err = nand_bch_init(chip); + if (err) { dev_err(dev, "unable to use BCH library\n"); - return -EINVAL; + return err; } break; @@ -2272,7 +2272,7 @@ return_error: if (!IS_ERR_OR_NULL(info->dma)) dma_release_channel(info->dma); if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip->ecc.priv); + nand_bch_free(nand_chip); nand_chip->ecc.priv = NULL; } return err; @@ -2286,7 +2286,7 @@ static int omap_nand_remove(struct platform_device *pdev) int ret; if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip->ecc.priv); + nand_bch_free(nand_chip); nand_chip->ecc.priv = NULL; } if (info->dma) diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h index 1e1ee3af82b1..b62b8bd4669f 100644 --- a/include/linux/mtd/nand-ecc-sw-bch.h +++ b/include/linux/mtd/nand-ecc-sw-bch.h @@ -10,7 +10,6 @@ struct mtd_info; struct nand_chip; -struct nand_bch_control; #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) @@ -30,11 +29,11 @@ int nand_bch_correct_data(struct nand_chip *chip, u_char *dat, /* * Initialize BCH encoder/decoder */ -struct nand_bch_control *nand_bch_init(struct mtd_info *mtd); +int nand_bch_init(struct nand_chip *chip); /* * Release BCH encoder/decoder resources */ -void nand_bch_free(struct nand_bch_control *nbc); +void nand_bch_free(struct nand_chip *chip); #else /* !CONFIG_MTD_NAND_ECC_SW_BCH */ @@ -54,12 +53,12 @@ nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, return -ENOTSUPP; } -static inline struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) +static inline int nand_bch_init(struct nand_chip *chip) { - return NULL; + return -ENOTSUPP; } -static inline void nand_bch_free(struct nand_bch_control *nbc) {} +static inline void nand_bch_free(struct nand_chip *chip) {} #endif /* CONFIG_MTD_NAND_ECC_SW_BCH */ -- cgit v1.2.3 From 127aae6077562e3926ebad7c782123c2afe95846 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:10 +0200 Subject: mtd: nand: ecc-bch: Drop mtd_nand_has_bch() Like for any other compilation option, use the IS_ENABLED() macro instead of hardcoding it. By droping this helper we can get rid of the BCH header in nandsim.c. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-7-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/nand_base.c | 2 +- drivers/mtd/nand/raw/nandsim.c | 3 +-- include/linux/mtd/nand-ecc-sw-bch.h | 4 ---- 3 files changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 287c521a3d43..b49483d04755 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5231,7 +5231,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) return 0; case NAND_ECC_ALGO_BCH: - if (!mtd_nand_has_bch()) { + if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) { WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n"); return -EINVAL; } diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index 9c940ead66fd..f2b9250c0ea8 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -2214,7 +2213,7 @@ static int ns_attach_chip(struct nand_chip *chip) if (!bch) return 0; - if (!mtd_nand_has_bch()) { + if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) { NS_ERR("BCH ECC support is disabled\n"); return -EINVAL; } diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h index d92d2abcfcef..7b62996d9e01 100644 --- a/include/linux/mtd/nand-ecc-sw-bch.h +++ b/include/linux/mtd/nand-ecc-sw-bch.h @@ -13,8 +13,6 @@ struct nand_chip; #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) -static inline int mtd_nand_has_bch(void) { return 1; } - /* * Calculate BCH ecc code */ @@ -37,8 +35,6 @@ void nand_bch_free(struct nand_chip *chip); #else /* !CONFIG_MTD_NAND_ECC_SW_BCH */ -static inline int mtd_nand_has_bch(void) { return 0; } - static inline int nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat, u_char *ecc_code) -- cgit v1.2.3 From ea146d7fbf5081b5eb2777df5e30ed70ca68985b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:11 +0200 Subject: mtd: nand: ecc-bch: Update the prototypes to be more generic These functions must be usable by the main NAND core, so their names must be technology-agnostic as well as the parameters. Hence, we pass a generic nand_device instead of a raw nand_chip structure. As it seems that changing the raw NAND functions to always pass a generic NAND device is a lost of time, we prefer to create dedicated raw NAND wrappers that will be useful in the near future to do the translation. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-8-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-bch.c | 47 +++++++++++++++++++++---------------- drivers/mtd/nand/raw/nand_base.c | 44 ++++++++++++++++++++++++++++++---- drivers/mtd/nand/raw/omap2.c | 23 +++++++----------- include/linux/mtd/nand-ecc-sw-bch.h | 45 +++++++++++++---------------------- include/linux/mtd/rawnand.h | 5 ++++ 5 files changed, 97 insertions(+), 67 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c index b6bfee9805ae..eae81bace01c 100644 --- a/drivers/mtd/nand/ecc-sw-bch.c +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -29,14 +30,15 @@ struct nand_bch_control { }; /** - * nand_bch_calcuate_ecc - Calculate the ECC corresponding to a data block - * @chip: NAND chip object + * nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block + * @nand: NAND device * @buf: Input buffer with raw data * @code: Output buffer with ECC */ -int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, - unsigned char *code) +int nand_ecc_sw_bch_calculate(struct nand_device *nand, + const unsigned char *buf, unsigned char *code) { + struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); struct nand_bch_control *nbc = chip->ecc.priv; unsigned int i; @@ -49,20 +51,21 @@ int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, return 0; } -EXPORT_SYMBOL(nand_bch_calculate_ecc); +EXPORT_SYMBOL(nand_ecc_sw_bch_calculate); /** - * nand_bch_correct_data - Detect, correct and report bit error(s) - * @chip: NAND chip object + * nand_ecc_sw_bch_correct - Detect, correct and report bit error(s) + * @nand: NAND device * @buf: Raw data read from the chip * @read_ecc: ECC bytes from the chip * @calc_ecc: ECC calculated from the raw data * * Detect and correct bit errors for a data block. */ -int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) +int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) { + struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); struct nand_bch_control *nbc = chip->ecc.priv; unsigned int *errloc = nbc->errloc; int i, count; @@ -86,11 +89,11 @@ int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, return count; } -EXPORT_SYMBOL(nand_bch_correct_data); +EXPORT_SYMBOL(nand_ecc_sw_bch_correct); /** - * nand_bch_init - Initialize software BCH ECC engine - * @chip: NAND chip object + * nand_ecc_sw_bch_init - Initialize software BCH ECC engine + * @nand: NAND device * * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure * @@ -105,9 +108,10 @@ EXPORT_SYMBOL(nand_bch_correct_data); * @eccsize = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) * @eccbytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) */ -int nand_bch_init(struct nand_chip *chip) +int nand_ecc_sw_bch_init(struct nand_device *nand) { - struct mtd_info *mtd = nand_to_mtd(chip); + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_chip *chip = mtd_to_nand(mtd); unsigned int m, t, eccsteps, i; struct nand_bch_control *nbc = NULL; unsigned char *erased_page; @@ -198,18 +202,21 @@ int nand_bch_init(struct nand_chip *chip) chip->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); return 0; + fail: - nand_bch_free(chip); + nand_ecc_sw_bch_cleanup(nand); + return -EINVAL; } -EXPORT_SYMBOL(nand_bch_init); +EXPORT_SYMBOL(nand_ecc_sw_bch_init); /** - * nand_bch_free - Release NAND BCH ECC resources - * @nbc: NAND BCH control structure + * nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources + * @nand: NAND device */ -void nand_bch_free(struct nand_chip *chip) +void nand_ecc_sw_bch_cleanup(struct nand_device *nand) { + struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); struct nand_bch_control *nbc = chip->ecc.priv; if (nbc) { @@ -219,7 +226,7 @@ void nand_bch_free(struct nand_chip *chip) kfree(nbc); } } -EXPORT_SYMBOL(nand_bch_free); +EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ivan Djelic "); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index b49483d04755..2ef674c10ac8 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5139,6 +5139,42 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) kfree(chip->parameters.onfi); } +int rawnand_sw_bch_init(struct nand_chip *chip) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_bch_init(base); +} +EXPORT_SYMBOL(rawnand_sw_bch_init); + +static int rawnand_sw_bch_calculate(struct nand_chip *chip, + const unsigned char *buf, + unsigned char *code) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_bch_calculate(base, buf, code); +} + +int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_bch_correct(base, buf, read_ecc, calc_ecc); +} +EXPORT_SYMBOL(rawnand_sw_bch_correct); + +void rawnand_sw_bch_cleanup(struct nand_chip *chip) +{ + struct nand_device *base = &chip->base; + + nand_ecc_sw_bch_cleanup(base); + + chip->ecc.priv = NULL; +} +EXPORT_SYMBOL(rawnand_sw_bch_cleanup); + static int nand_set_ecc_on_host_ops(struct nand_chip *chip) { struct nand_ecc_ctrl *ecc = &chip->ecc; @@ -5235,8 +5271,8 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n"); return -EINVAL; } - ecc->calculate = nand_bch_calculate_ecc; - ecc->correct = nand_bch_correct_data; + ecc->calculate = rawnand_sw_bch_calculate; + ecc->correct = rawnand_sw_bch_correct; ecc->read_page = nand_read_page_swecc; ecc->read_subpage = nand_read_subpage; ecc->write_page = nand_write_page_swecc; @@ -5292,7 +5328,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) /* See the software BCH ECC initialization for details */ ecc->bytes = 0; - ret = nand_bch_init(chip); + ret = rawnand_sw_bch_init(chip); if (ret) { WARN(1, "BCH ECC initialization failed!\n"); return ret; @@ -5957,7 +5993,7 @@ void nand_cleanup(struct nand_chip *chip) { if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && chip->ecc.algo == NAND_ECC_ALGO_BCH) - nand_bch_free(chip); + rawnand_sw_bch_cleanup(chip); nanddev_cleanup(&chip->base); diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 6aab57336690..4cc47ab7f01a 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -2041,13 +2040,13 @@ static int omap_nand_attach_chip(struct nand_chip *chip) chip->ecc.bytes = 7; chip->ecc.strength = 4; chip->ecc.hwctl = omap_enable_hwecc_bch; - chip->ecc.correct = nand_bch_correct_data; + chip->ecc.correct = rawnand_sw_bch_correct; chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ oobbytes_per_step = chip->ecc.bytes + 1; /* Software BCH library is used for locating errors */ - err = nand_bch_init(chip); + err = rawnand_sw_bch_init(chip); if (err) { dev_err(dev, "Unable to use BCH library\n"); return err; @@ -2083,13 +2082,13 @@ static int omap_nand_attach_chip(struct nand_chip *chip) chip->ecc.bytes = 13; chip->ecc.strength = 8; chip->ecc.hwctl = omap_enable_hwecc_bch; - chip->ecc.correct = nand_bch_correct_data; + chip->ecc.correct = rawnand_sw_bch_correct; chip->ecc.calculate = omap_calculate_ecc_bch_sw; mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); /* Reserve one byte for the OMAP marker */ oobbytes_per_step = chip->ecc.bytes + 1; /* Software BCH library is used for locating errors */ - err = nand_bch_init(chip); + err = rawnand_sw_bch_init(chip); if (err) { dev_err(dev, "unable to use BCH library\n"); return err; @@ -2195,7 +2194,6 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip = &info->nand; mtd = nand_to_mtd(nand_chip); mtd->dev.parent = &pdev->dev; - nand_chip->ecc.priv = NULL; nand_set_flash_node(nand_chip, dev->of_node); if (!mtd->name) { @@ -2271,10 +2269,9 @@ cleanup_nand: return_error: if (!IS_ERR_OR_NULL(info->dma)) dma_release_channel(info->dma); - if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip); - nand_chip->ecc.priv = NULL; - } + + rawnand_sw_bch_cleanup(nand_chip); + return err; } @@ -2285,10 +2282,8 @@ static int omap_nand_remove(struct platform_device *pdev) struct omap_nand_info *info = mtd_to_omap(mtd); int ret; - if (nand_chip->ecc.priv) { - nand_bch_free(nand_chip); - nand_chip->ecc.priv = NULL; - } + rawnand_sw_bch_cleanup(nand_chip); + if (info->dma) dma_release_channel(info->dma); ret = mtd_device_unregister(mtd); diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h index 7b62996d9e01..f0caee3b03d0 100644 --- a/include/linux/mtd/nand-ecc-sw-bch.h +++ b/include/linux/mtd/nand-ecc-sw-bch.h @@ -8,53 +8,40 @@ #ifndef __MTD_NAND_ECC_SW_BCH_H__ #define __MTD_NAND_ECC_SW_BCH_H__ -struct mtd_info; -struct nand_chip; +#include #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) -/* - * Calculate BCH ecc code - */ -int nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat, - u_char *ecc_code); - -/* - * Detect and correct bit errors - */ -int nand_bch_correct_data(struct nand_chip *chip, u_char *dat, - u_char *read_ecc, u_char *calc_ecc); -/* - * Initialize BCH encoder/decoder - */ -int nand_bch_init(struct nand_chip *chip); -/* - * Release BCH encoder/decoder resources - */ -void nand_bch_free(struct nand_chip *chip); +int nand_ecc_sw_bch_calculate(struct nand_device *nand, + const unsigned char *buf, unsigned char *code); +int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc); +int nand_ecc_sw_bch_init(struct nand_device *nand); +void nand_ecc_sw_bch_cleanup(struct nand_device *nand); #else /* !CONFIG_MTD_NAND_ECC_SW_BCH */ -static inline int -nand_bch_calculate_ecc(struct nand_chip *chip, const u_char *dat, - u_char *ecc_code) +static inline int nand_ecc_sw_bch_calculate(struct nand_device *nand, + const unsigned char *buf, + unsigned char *code) { return -ENOTSUPP; } -static inline int -nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) +static inline int nand_ecc_sw_bch_correct(struct nand_device *nand, + unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc) { return -ENOTSUPP; } -static inline int nand_bch_init(struct nand_chip *chip) +static inline int nand_ecc_sw_bch_init(struct nand_device *nand) { return -ENOTSUPP; } -static inline void nand_bch_free(struct nand_chip *chip) {} +static inline void nand_ecc_sw_bch_cleanup(struct nand_device *nand) {} #endif /* CONFIG_MTD_NAND_ECC_SW_BCH */ diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index aac07940de09..23623beaad1d 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1303,6 +1303,11 @@ static inline int nand_opcode_8bits(unsigned int command) return 0; } +int rawnand_sw_bch_init(struct nand_chip *chip); +int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc); +void rawnand_sw_bch_cleanup(struct nand_chip *chip); + int nand_check_erased_ecc_chunk(void *data, int datalen, void *ecc, int ecclen, void *extraoob, int extraooblen, -- cgit v1.2.3 From 80fe603160a4732a08f0f08f3e3312a3f3a79eee Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:12 +0200 Subject: mtd: nand: ecc-bch: Stop using raw NAND structures This code is meant to be reused by the SPI-NAND core. Now that the driver has been cleaned and reorganized, use a generic ECC engine object to store the driver's data instead of accessing members of the nand_chip structure. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-9-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-bch.c | 114 +++++++++++++----------------------- drivers/mtd/nand/raw/nand_base.c | 38 ++++++++++-- include/linux/mtd/nand-ecc-sw-bch.h | 25 ++++++++ 3 files changed, 98 insertions(+), 79 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c index eae81bace01c..16a54bd2ca31 100644 --- a/drivers/mtd/nand/ecc-sw-bch.c +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -11,23 +11,8 @@ #include #include #include -#include -#include #include #include -#include - -/** - * struct nand_bch_control - private NAND BCH control structure - * @bch: BCH control structure - * @errloc: error location array - * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid - */ -struct nand_bch_control { - struct bch_control *bch; - unsigned int *errloc; - unsigned char *eccmask; -}; /** * nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block @@ -38,16 +23,15 @@ struct nand_bch_control { int nand_ecc_sw_bch_calculate(struct nand_device *nand, const unsigned char *buf, unsigned char *code) { - struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); - struct nand_bch_control *nbc = chip->ecc.priv; + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; unsigned int i; - memset(code, 0, chip->ecc.bytes); - bch_encode(nbc->bch, buf, chip->ecc.size, code); + memset(code, 0, engine_conf->code_size); + bch_encode(engine_conf->bch, buf, nand->ecc.ctx.conf.step_size, code); /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) - code[i] ^= nbc->eccmask[i]; + for (i = 0; i < engine_conf->code_size; i++) + code[i] ^= engine_conf->eccmask[i]; return 0; } @@ -65,16 +49,16 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_calculate); int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { - struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int *errloc = nbc->errloc; + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int step_size = nand->ecc.ctx.conf.step_size; + unsigned int *errloc = engine_conf->errloc; int i, count; - count = bch_decode(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, - NULL, errloc); + count = bch_decode(engine_conf->bch, NULL, step_size, read_ecc, + calc_ecc, NULL, errloc); if (count > 0) { for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size * 8)) + if (errloc[i] < (step_size * 8)) /* The error is in the data area: correct it */ buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); @@ -97,31 +81,30 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_correct); * * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure * - * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes - * are used to compute the following BCH parameters: + * Initialize NAND BCH error correction. @nand.ecc parameters 'step_size' and + * 'bytes' are used to compute the following BCH parameters: * m, the Galois field order * t, the error correction capability - * @eccbytes should be equal to the number of bytes required to store m * t + * 'bytes' should be equal to the number of bytes required to store m * t * bits, where m is such that 2^m - 1 > step_size * 8. * * Example: to configure 4 bit correction per 512 bytes, you should pass - * @eccsize = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) - * @eccbytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) + * step_size = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) + * bytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) */ int nand_ecc_sw_bch_init(struct nand_device *nand) { struct mtd_info *mtd = nanddev_to_mtd(nand); - struct nand_chip *chip = mtd_to_nand(mtd); unsigned int m, t, eccsteps, i; - struct nand_bch_control *nbc = NULL; + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; unsigned char *erased_page; - unsigned int eccsize = chip->ecc.size; - unsigned int eccbytes = chip->ecc.bytes; - unsigned int eccstrength = chip->ecc.strength; + unsigned int eccsize = nand->ecc.ctx.conf.step_size; + unsigned int eccbytes = engine_conf->code_size; + unsigned int eccstrength = nand->ecc.ctx.conf.strength; if (!eccbytes && eccstrength) { eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); - chip->ecc.bytes = eccbytes; + engine_conf->code_size = eccbytes; } if (!eccsize || !eccbytes) { @@ -132,20 +115,14 @@ int nand_ecc_sw_bch_init(struct nand_device *nand) m = fls(1+8*eccsize); t = (eccbytes*8)/m; - nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); - if (!nbc) - return -ENOMEM; - - chip->ecc.priv = nbc; - - nbc->bch = bch_init(m, t, 0, false); - if (!nbc->bch) - goto fail; + engine_conf->bch = bch_init(m, t, 0, false); + if (!engine_conf->bch) + return -EINVAL; /* verify that eccbytes has the expected value */ - if (nbc->bch->ecc_bytes != eccbytes) { + if (engine_conf->bch->ecc_bytes != eccbytes) { pr_warn("invalid eccbytes %u, should be %u\n", - eccbytes, nbc->bch->ecc_bytes); + eccbytes, engine_conf->bch->ecc_bytes); goto fail; } @@ -163,25 +140,15 @@ int nand_ecc_sw_bch_init(struct nand_device *nand) goto fail; } - /* - * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), - * which is called by mtd_ooblayout_count_eccbytes(). - * Make sure they are properly initialized before calling - * mtd_ooblayout_count_eccbytes(). - * FIXME: we should probably rework the sequencing in nand_scan_tail() - * to avoid setting those fields twice. - */ - chip->ecc.steps = eccsteps; - chip->ecc.total = eccsteps * eccbytes; - nand->base.ecc.ctx.total = chip->ecc.total; if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { pr_warn("invalid ecc layout\n"); goto fail; } - nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL); - nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); - if (!nbc->eccmask || !nbc->errloc) + engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL); + engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc), + GFP_KERNEL); + if (!engine_conf->eccmask || !engine_conf->errloc) goto fail; /* @@ -192,14 +159,15 @@ int nand_ecc_sw_bch_init(struct nand_device *nand) goto fail; memset(erased_page, 0xff, eccsize); - bch_encode(nbc->bch, erased_page, eccsize, nbc->eccmask); + bch_encode(engine_conf->bch, erased_page, eccsize, + engine_conf->eccmask); kfree(erased_page); for (i = 0; i < eccbytes; i++) - nbc->eccmask[i] ^= 0xff; + engine_conf->eccmask[i] ^= 0xff; if (!eccstrength) - chip->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); + nand->ecc.ctx.conf.strength = (eccbytes * 8) / fls(8 * eccsize); return 0; @@ -216,14 +184,12 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_init); */ void nand_ecc_sw_bch_cleanup(struct nand_device *nand) { - struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); - struct nand_bch_control *nbc = chip->ecc.priv; - - if (nbc) { - bch_free(nbc->bch); - kfree(nbc->errloc); - kfree(nbc->eccmask); - kfree(nbc); + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + + if (engine_conf) { + bch_free(engine_conf->bch); + kfree(engine_conf->errloc); + kfree(engine_conf->eccmask); } } EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 2ef674c10ac8..03106bf629dd 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5142,8 +5142,33 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) int rawnand_sw_bch_init(struct nand_chip *chip) { struct nand_device *base = &chip->base; + struct nand_ecc_sw_bch_conf *engine_conf; + int ret; + + base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + base->ecc.user_conf.algo = NAND_ECC_ALGO_BCH; + base->ecc.user_conf.step_size = chip->ecc.size; + base->ecc.user_conf.strength = chip->ecc.strength; + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + engine_conf->code_size = chip->ecc.bytes; + + base->ecc.ctx.priv = engine_conf; - return nand_ecc_sw_bch_init(base); + ret = nand_ecc_sw_bch_init(base); + if (ret) + kfree(base->ecc.ctx.priv); + + chip->ecc.size = base->ecc.ctx.conf.step_size; + chip->ecc.strength = base->ecc.ctx.conf.strength; + chip->ecc.total = base->ecc.ctx.total; + chip->ecc.steps = engine_conf->nsteps; + chip->ecc.bytes = engine_conf->code_size; + + return ret; } EXPORT_SYMBOL(rawnand_sw_bch_init); @@ -5171,7 +5196,7 @@ void rawnand_sw_bch_cleanup(struct nand_chip *chip) nand_ecc_sw_bch_cleanup(base); - chip->ecc.priv = NULL; + kfree(base->ecc.ctx.priv); } EXPORT_SYMBOL(rawnand_sw_bch_cleanup); @@ -5794,15 +5819,18 @@ static int nand_scan_tail(struct nand_chip *chip) * Set the number of read / write steps for one page depending on ECC * mode. */ - ecc->steps = mtd->writesize / ecc->size; + if (!ecc->steps) + ecc->steps = mtd->writesize / ecc->size; if (ecc->steps * ecc->size != mtd->writesize) { WARN(1, "Invalid ECC parameters\n"); ret = -EINVAL; goto err_nand_manuf_cleanup; } - ecc->total = ecc->steps * ecc->bytes; - chip->base.ecc.ctx.total = ecc->total; + if (!ecc->total) { + ecc->total = ecc->steps * ecc->bytes; + chip->base.ecc.ctx.total = ecc->total; + } if (ecc->total > mtd->oobsize) { WARN(1, "Total number of ECC bytes exceeded oobsize\n"); diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h index f0caee3b03d0..ce005528e55f 100644 --- a/include/linux/mtd/nand-ecc-sw-bch.h +++ b/include/linux/mtd/nand-ecc-sw-bch.h @@ -9,6 +9,31 @@ #define __MTD_NAND_ECC_SW_BCH_H__ #include +#include + +/** + * struct nand_ecc_sw_bch_conf - private software BCH ECC engine structure + * @reqooblen: Save the actual user OOB length requested before overwriting it + * @spare_oobbuf: Spare OOB buffer if none is provided + * @code_size: Number of bytes needed to store a code (one code per step) + * @nsteps: Number of steps + * @calc_buf: Buffer to use when calculating ECC bytes + * @code_buf: Buffer to use when reading (raw) ECC bytes from the chip + * @bch: BCH control structure + * @errloc: error location array + * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid + */ +struct nand_ecc_sw_bch_conf { + unsigned int reqooblen; + void *spare_oobbuf; + unsigned int code_size; + unsigned int nsteps; + u8 *calc_buf; + u8 *code_buf; + struct bch_control *bch; + unsigned int *errloc; + unsigned char *eccmask; +}; #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) -- cgit v1.2.3 From c69942bda5152d764ee7d897d1627d64c7177ea1 Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Mon, 30 Nov 2020 16:24:15 +0100 Subject: mtd: spi-nor: Fix multiple typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are a few typos in comments in the SPI NOR framework; fix them. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201130152416.1283972-1-j.neuschaefer@gmx.net --- drivers/mtd/spi-nor/core.c | 4 ++-- drivers/mtd/spi-nor/sfdp.c | 2 +- include/linux/mtd/spi-nor.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 5bee7c8da4dc..7b4850f94223 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1599,7 +1599,7 @@ destroy_erase_cmd_list: /* * Erase an address range on the nor chip. The address range may extend - * one or more erase sectors. Return an error is there is a problem erasing. + * one or more erase sectors. Return an error if there is a problem erasing. */ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) { @@ -2693,7 +2693,7 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map, } /* - * Otherwise, the current erase size is still a valid canditate. + * Otherwise, the current erase size is still a valid candidate. * Select the biggest valid candidate. */ if (!erase && tested_erase->size) diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 22cb519efe3f..6ee7719e5903 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -65,7 +65,7 @@ struct sfdp_bfpt_read { struct sfdp_bfpt_erase { /* - * The half-word at offset in DWORD encodes the + * The half-word at offset in DWORD encodes the * op code and erase sector size to be used by Sector Erase commands. */ u32 dword; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 299685d15dc2..d13958de6d8a 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -433,7 +433,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor) * @name: the chip type name * @hwcaps: the hardware capabilities supported by the controller driver * - * The drivers can use this fuction to scan the SPI NOR. + * The drivers can use this function to scan the SPI NOR. * In the scanning, it will try to get all the necessary information to * fill the mtd_info{} and the spi_nor{}. * -- cgit v1.2.3 From afd473e8582702e89aed89a4be957ffd37423009 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Tue, 1 Dec 2020 15:57:10 +0530 Subject: mtd: spi-nor: core: Allow flashes to specify MTD writesize Some flashes like the Cypress S28 family use ECC. Under this ECC scheme, multi-pass writes to an ECC block is not allowed. In other words, once data is programmed to an ECC block, it can't be programmed again without erasing it first. Upper layers like file systems need to be given this information so they do not cause error conditions on the flash by attempting multi-pass programming. This can be done by setting 'writesize' in 'struct mtd_info'. Set the default to 1 but allow flashes to modify it in fixup hooks. If more flashes show up with this constraint in the future it might be worth it to add it to 'struct flash_info', but for now increasing its size is not worth it. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201201102711.8727-3-p.yadav@ti.com --- drivers/mtd/spi-nor/core.c | 3 ++- drivers/mtd/spi-nor/core.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 7b4850f94223..ae991ff458a0 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2885,6 +2885,7 @@ static void spi_nor_info_init_params(struct spi_nor *nor) nor->flags |= SNOR_F_HAS_16BIT_SR; /* Set SPI NOR sizes. */ + params->writesize = 1; params->size = (u64)info->sector_size * info->n_sectors; params->page_size = info->page_size; @@ -3430,7 +3431,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, mtd->name = dev_name(dev); mtd->priv = nor; mtd->type = MTD_NORFLASH; - mtd->writesize = 1; + mtd->writesize = nor->params->writesize; mtd->flags = MTD_CAP_NORFLASH; mtd->size = nor->params->size; mtd->_erase = spi_nor_erase; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 0a775a7b5606..5ab3f269a23c 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -193,6 +193,8 @@ struct spi_nor_locking_ops { * Serial Flash Discoverable Parameters (SFDP) tables. * * @size: the flash memory density in bytes. + * @writesize Minimal writable flash unit size. Defaults to 1. Set to + * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. * @rdsr_dummy: dummy cycles needed for Read Status Register command. * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register @@ -219,6 +221,7 @@ struct spi_nor_locking_ops { */ struct spi_nor_flash_parameter { u64 size; + u32 writesize; u32 page_size; u8 rdsr_dummy; u8 rdsr_addr_nbytes; -- cgit v1.2.3 From 294cca6ce5cf5b15ce4ebda4c266b4a849735c65 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Tue, 1 Dec 2020 15:57:11 +0530 Subject: mtd: spi-nor: spansion: Set ECC block size The S28 flash family uses 2-bit ECC by default with each ECC block being 16 bytes. Under this scheme multi-pass programming to an ECC block is not allowed. Set the writesize to make sure multi-pass programming is not attempted on the flash. Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201201102711.8727-4-p.yadav@ti.com --- drivers/mtd/spi-nor/spansion.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index e487fd341a56..b0c5521c1e27 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -109,6 +109,7 @@ static int spi_nor_cypress_octal_dtr_enable(struct spi_nor *nor, bool enable) static void s28hs512t_default_init(struct spi_nor *nor) { nor->params->octal_dtr_enable = spi_nor_cypress_octal_dtr_enable; + nor->params->writesize = 16; } static void s28hs512t_post_sfdp_fixup(struct spi_nor *nor) -- cgit v1.2.3 From 989d4b72bae3b05c1564d38e71e18f65b12734fb Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Dec 2020 17:29:53 +0100 Subject: mtd: spi-nor: sst: fix BPn bits for the SST25VF064C This flash part actually has 4 block protection bits. Please note, that this patch is just based on information of the datasheet of the datasheet and wasn't tested. Fixes: 3e0930f109e7 ("mtd: spi-nor: Rework the disabling of block write protection") Reported-by: Tudor Ambarus Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201203162959.29589-2-michael@walle.cc --- drivers/mtd/spi-nor/sst.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index e0af6d25d573..0ab07624fb73 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -18,7 +18,8 @@ static const struct flash_info sst_parts[] = { SECT_4K | SST_WRITE) }, { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) }, - { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) }, + { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_4BIT_BP) }, { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) }, { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, -- cgit v1.2.3 From bdb1a75e4b9df6861ec6a6e3e3997820d3cebabe Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Dec 2020 17:29:54 +0100 Subject: mtd: spi-nor: ignore errors in spi_nor_unlock_all() Just try to unlock the whole SPI-NOR flash array. Don't abort the probing in case of an error. Justifications: (1) For some boards, this just works because spi_nor_write_16bit_sr_and_check() is broken and just checks the second half of the 16bit. Once that will be fixed, SPI probe will fail for boards which has hardware-write protected SPI-NOR flashes. (2) Until now, hardware write-protection was the only viable solution to use the block protection bits. This is because this very function spi_nor_unlock_all() will be called unconditionally on every linux boot. Therefore, this bits only makes sense in combination with the hardware write-protection. If we would fail the SPI probe on an error in spi_nor_unlock_all() we'd break virtually all users of the block protection bits. (3) We should try hard to keep the MTD working even if the flash might not be writable/erasable. Fixes: 3e0930f109e7 ("mtd: spi-nor: Rework the disabling of block write protection") Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201203162959.29589-3-michael@walle.cc --- drivers/mtd/spi-nor/core.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ae991ff458a0..4b532c79847b 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3122,20 +3122,27 @@ static int spi_nor_quad_enable(struct spi_nor *nor) } /** - * spi_nor_unlock_all() - Unlocks the entire flash memory array. + * spi_nor_try_unlock_all() - Tries to unlock the entire flash memory array. * @nor: pointer to a 'struct spi_nor'. * * Some SPI NOR flashes are write protected by default after a power-on reset * cycle, in order to avoid inadvertent writes during power-up. Backward * compatibility imposes to unlock the entire flash memory array at power-up * by default. + * + * Unprotecting the entire flash array will fail for boards which are hardware + * write-protected. Thus any errors are ignored. */ -static int spi_nor_unlock_all(struct spi_nor *nor) +static void spi_nor_try_unlock_all(struct spi_nor *nor) { - if (nor->flags & SNOR_F_HAS_LOCK) - return spi_nor_unlock(&nor->mtd, 0, nor->params->size); + int ret; - return 0; + if (!(nor->flags & SNOR_F_HAS_LOCK)) + return; + + ret = spi_nor_unlock(&nor->mtd, 0, nor->params->size); + if (ret) + dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n"); } static int spi_nor_init(struct spi_nor *nor) @@ -3154,11 +3161,7 @@ static int spi_nor_init(struct spi_nor *nor) return err; } - err = spi_nor_unlock_all(nor); - if (err) { - dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n"); - return err; - } + spi_nor_try_unlock_all(nor); if (nor->addr_width == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && -- cgit v1.2.3 From e6204d4620276398ed7317d64c369813a1f96615 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Dec 2020 17:29:55 +0100 Subject: mtd: spi-nor: atmel: remove global protection flag This is considered bad for the following reasons: (1) We only support the block protection with BPn bits for write protection. Not all Atmel parts support this. (2) Newly added flash chip will automatically inherit the "has locking" support and thus needs to explicitly tested. Better be opt-in instead of opt-out. (3) There are already supported flashes which doesn't support the locking scheme. So I assume this wasn't properly tested before adding that chip; which enforces my previous argument that locking support should be an opt-in. Remove the global flag and add individual flags to all flashes which supports BP locking. In particular the following flashes don't support the BP scheme: - AT26F004 - AT25SL321 - AT45DB081D Please note, that some flashes which are marked as SPI_NOR_HAS_LOCK just support Global Protection, i.e. not our supported block protection locking scheme. This is to keep backwards compatibility with the current "unlock all at boot" mechanism. In particular the following flashes doesn't have BP bits: - AT25DF041A - AT25DF321 - AT25DF321A - AT25DF641 - AT26DF081A - AT26DF161A - AT26DF321 Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201203162959.29589-4-michael@walle.cc --- drivers/mtd/spi-nor/atmel.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index 3f5f21a473a6..49d392c6c8bc 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -10,37 +10,27 @@ static const struct flash_info atmel_parts[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) }, - { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) }, + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) }, - { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) }, - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) }, + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) }, { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) }, - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, }; -static void atmel_default_init(struct spi_nor *nor) -{ - nor->flags |= SNOR_F_HAS_LOCK; -} - -static const struct spi_nor_fixups atmel_fixups = { - .default_init = atmel_default_init, -}; - const struct spi_nor_manufacturer spi_nor_atmel = { .name = "atmel", .parts = atmel_parts, .nparts = ARRAY_SIZE(atmel_parts), - .fixups = &atmel_fixups, }; -- cgit v1.2.3 From a833383732116c2afe665520bbe6951999631ef1 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Dec 2020 17:29:56 +0100 Subject: mtd: spi-nor: sst: remove global protection flag This is considered bad for the following reasons: (1) We only support the block protection with BPn bits for write protection. Not all SST parts support this. (2) Newly added flash chip will automatically inherit the "has locking" support and thus needs to explicitly tested. Better be opt-in instead of opt-out. (3) There are already supported flashes which doesn't support the locking scheme. So I assume this wasn't properly tested before adding that chip; which enforces my previous argument that locking support should be an opt-in. Remove the global flag and add individual flags to all flashes which supports BP locking. In particular the following flashes don't support the BP scheme: - SST26VF016B - SST26WF016B - SST26VF064B Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201203162959.29589-5-michael@walle.cc --- drivers/mtd/spi-nor/sst.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index 0ab07624fb73..0d9d319f61e6 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -11,27 +11,27 @@ static const struct flash_info sst_parts[] = { /* SST -- large erase sizes are "overlays", "sectors" are 4K */ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_4BIT_BP) }, + SECT_4K | SPI_NOR_4BIT_BP | SPI_NOR_HAS_LOCK) }, { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, - SECT_4K | SST_WRITE) }, - { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K) }, - { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, - SECT_4K | SST_WRITE) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, @@ -128,11 +128,6 @@ out: return ret; } -static void sst_default_init(struct spi_nor *nor) -{ - nor->flags |= SNOR_F_HAS_LOCK; -} - static void sst_post_sfdp_fixups(struct spi_nor *nor) { if (nor->info->flags & SST_WRITE) @@ -140,7 +135,6 @@ static void sst_post_sfdp_fixups(struct spi_nor *nor) } static const struct spi_nor_fixups sst_fixups = { - .default_init = sst_default_init, .post_sfdp = sst_post_sfdp_fixups, }; -- cgit v1.2.3 From afcf93e9d63fc1e15935a2df9457f803394e4f20 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Dec 2020 17:29:57 +0100 Subject: mtd: spi-nor: intel: remove global protection flag For the Atmel and SST parts this flag was already moved to individual flash parts because it is considered bad esp. because newer flash chips will automatically inherit the "has locking" support. While this won't likely be the case for the Intel parts, we do it for consistency reasons. Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201203162959.29589-6-michael@walle.cc --- drivers/mtd/spi-nor/intel.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/intel.c b/drivers/mtd/spi-nor/intel.c index d8196f101368..6c31bef3fc60 100644 --- a/drivers/mtd/spi-nor/intel.c +++ b/drivers/mtd/spi-nor/intel.c @@ -10,23 +10,13 @@ static const struct flash_info intel_parts[] = { /* Intel/Numonyx -- xxxs33b */ - { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) }, - { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) }, - { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, -}; - -static void intel_default_init(struct spi_nor *nor) -{ - nor->flags |= SNOR_F_HAS_LOCK; -} - -static const struct spi_nor_fixups intel_fixups = { - .default_init = intel_default_init, + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, SPI_NOR_HAS_LOCK) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, SPI_NOR_HAS_LOCK) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, SPI_NOR_HAS_LOCK) }, }; const struct spi_nor_manufacturer spi_nor_intel = { .name = "intel", .parts = intel_parts, .nparts = ARRAY_SIZE(intel_parts), - .fixups = &intel_fixups, }; -- cgit v1.2.3 From 8c174d1511d235ed6c049dcb2b704777ad0df7a5 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Dec 2020 17:29:58 +0100 Subject: mtd: spi-nor: atmel: fix unlock_all() for AT25FS010/040 These flashes have some weird BP bits mapping which aren't supported in the current locking code. Just add a simple unlock op to unprotect the entire flash array which is needed for legacy behavior. Fixes: 3e0930f109e7 ("mtd: spi-nor: Rework the disabling of block write protection") Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201203162959.29589-7-michael@walle.cc --- drivers/mtd/spi-nor/atmel.c | 53 +++++++++++++++++++++++++++++++++++++++++++-- drivers/mtd/spi-nor/core.c | 2 +- drivers/mtd/spi-nor/core.h | 1 + 3 files changed, 53 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index 49d392c6c8bc..deacf87a68a0 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -8,10 +8,59 @@ #include "core.h" +/* + * The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the + * block protection bits. We don't support them. But legacy behavior in linux + * is to unlock the whole flash array on startup. Therefore, we have to support + * exactly this operation. + */ +static int atmel_at25fs_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return -EOPNOTSUPP; +} + +static int atmel_at25fs_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + int ret; + + /* We only support unlocking the whole flash array */ + if (ofs || len != nor->params->size) + return -EINVAL; + + /* Write 0x00 to the status register to disable write protection */ + ret = spi_nor_write_sr_and_check(nor, 0); + if (ret) + dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n"); + + return ret; +} + +static int atmel_at25fs_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return -EOPNOTSUPP; +} + +static const struct spi_nor_locking_ops atmel_at25fs_locking_ops = { + .lock = atmel_at25fs_lock, + .unlock = atmel_at25fs_unlock, + .is_locked = atmel_at25fs_is_locked, +}; + +static void atmel_at25fs_default_init(struct spi_nor *nor) +{ + nor->params->locking_ops = &atmel_at25fs_locking_ops; +} + +static const struct spi_nor_fixups atmel_at25fs_fixups = { + .default_init = atmel_at25fs_default_init, +}; + static const struct flash_info atmel_parts[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ - { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) + .fixups = &atmel_at25fs_fixups }, + { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) + .fixups = &atmel_at25fs_fixups }, { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 4b532c79847b..53603efa4a4f 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1049,7 +1049,7 @@ static int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1) +int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1) { if (nor->flags & SNOR_F_HAS_16BIT_SR) return spi_nor_write_16bit_sr_and_check(nor, sr1); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 5ab3f269a23c..caf8b7def828 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -433,6 +433,7 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor); int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); +int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1); int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr); ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, -- cgit v1.2.3 From 31ad3eff093cf21872f385021242c00c7a2abf6b Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Dec 2020 17:29:59 +0100 Subject: mtd: spi-nor: keep lock bits if they are non-volatile Traditionally, Linux unlocks the whole flash because there are legacy devices which has the write protection bits set by default at startup. If you actually want to use the flash protection bits, eg. because there is a read-only part for a bootloader, this automatic unlocking is harmful. If there is no hardware write protection in place (usually called WP#), a startup of the kernel just discards this protection. I've gone through the datasheets of all the flashes (except the Intel ones where I could not find any datasheet nor reference) which supports the unlocking feature and looked how the sector protection was implemented. The currently supported flashes can be divided into the following two categories: (1) block protection bits are non-volatile. Thus they keep their values at reset and power-cycle (2) flashes where these bits are volatile. After reset or power-cycle, the whole memory array is protected. (a) some devices needs a special "Global Unprotect" command, eg. the Atmel AT25DF041A. (b) some devices require to clear the BPn bits in the status register. Due to the reasons above, we do not want to clear the bits for flashes which belong to category (1). Fortunately for us, only Atmel flashes fall into category (2a). Implement the "Global Protect" and "Global Unprotect" commands for these. For (2b) we can use normal block protection locking scheme. This patch adds a new flag to indicate the case (2). Only if we have such a flash we unlock the whole flash array. To be backwards compatible it also introduces a kernel configuration option which restores the complete legacy behavior ("Disable write protection on any flashes"). Hopefully, this will clean up "unlock the entire flash for legacy devices" once and for all. For reference here are the actually commits which introduced the legacy behavior (and extended the behavior to other chip manufacturers): commit f80e521c916cb ("mtd: m25p80: add support for the Intel/Numonyx {16,32,64}0S33B SPI flash chips") commit ea60658a08f8f ("mtd: m25p80: disable SST software protection bits by default") commit 7228982442365 ("[MTD] m25p80: fix bug - ATmel spi flash fails to be copied to") Actually, this might also fix handling of the Atmel AT25DF flashes, because the original commit 7228982442365 ("[MTD] m25p80: fix bug - ATmel spi flash fails to be copied to") was writing a 0 to the status register, which is a "Global Unprotect". This might not be the case in the current code which only handles the block protection bits BP2, BP1 and BP0. Thus, it depends on the current contents of the status register if this unlock actually corresponds to a "Global Unprotect" command. In the worst case, the current code might leave the AT25DF flashes in a write protected state. The commit 191f5c2ed4b6f ("mtd: spi-nor: use 16-bit WRR command when QE is set on spansion flashes") changed that behavior by just clearing BP2 to BP0 instead of writing a 0 to the status register. Further, the commit 3e0930f109e76 ("mtd: spi-nor: Rework the disabling of block write protection") expanded the unlock_all() feature to ANY flash which supports locking. Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20201203162959.29589-8-michael@walle.cc --- drivers/mtd/spi-nor/Kconfig | 44 +++++++++++++++ drivers/mtd/spi-nor/atmel.c | 128 +++++++++++++++++++++++++++++++++++++++++--- drivers/mtd/spi-nor/core.c | 23 ++++++-- drivers/mtd/spi-nor/core.h | 8 +++ drivers/mtd/spi-nor/esmt.c | 2 +- drivers/mtd/spi-nor/intel.c | 9 ++-- drivers/mtd/spi-nor/sst.c | 21 ++++---- 7 files changed, 211 insertions(+), 24 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index ffc4b380f2b1..24cd25de2b8b 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -24,6 +24,50 @@ config MTD_SPI_NOR_USE_4K_SECTORS Please note that some tools/drivers/filesystems may not work with 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). +choice + prompt "Software write protection at boot" + default MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE + +config MTD_SPI_NOR_SWP_DISABLE + bool "Disable SWP on any flashes (legacy behavior)" + help + This option disables the software write protection on any SPI + flashes at boot-up. + + Depending on the flash chip this either clears the block protection + bits or does a "Global Unprotect" command. + + Don't use this if you intent to use the software write protection + of your SPI flash. This is only to keep backwards compatibility. + +config MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE + bool "Disable SWP on flashes w/ volatile protection bits" + help + Some SPI flashes have volatile block protection bits, ie. after a + power-up or a reset the flash is software write protected by + default. + + This option disables the software write protection for these kind + of flashes while keeping it enabled for any other SPI flashes + which have non-volatile write protection bits. + + If the software write protection will be disabled depending on + the flash either the block protection bits are cleared or a + "Global Unprotect" command is issued. + + If you are unsure, select this option. + +config MTD_SPI_NOR_SWP_KEEP + bool "Keep software write protection as is" + help + If you select this option the software write protection of any + SPI flashes will not be changed. If your flash is software write + protected or will be automatically software write protected after + power-up you have to manually unlock it before you are able to + write to it. + +endchoice + source "drivers/mtd/spi-nor/controllers/Kconfig" endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index deacf87a68a0..1fea5cab492c 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -8,6 +8,8 @@ #include "core.h" +#define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2) + /* * The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the * block protection bits. We don't support them. But legacy behavior in linux @@ -55,6 +57,104 @@ static const struct spi_nor_fixups atmel_at25fs_fixups = { .default_init = atmel_at25fs_default_init, }; +/** + * atmel_set_global_protection - Do a Global Protect or Unprotect command + * @nor: pointer to 'struct spi_nor' + * @ofs: offset in bytes + * @len: len in bytes + * @is_protect: if true do a Global Protect otherwise it is a Global Unprotect + * + * Return: 0 on success, -error otherwise. + */ +static int atmel_set_global_protection(struct spi_nor *nor, loff_t ofs, + uint64_t len, bool is_protect) +{ + int ret; + u8 sr; + + /* We only support locking the whole flash array */ + if (ofs || len != nor->params->size) + return -EINVAL; + + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + sr = nor->bouncebuf[0]; + + /* SRWD bit needs to be cleared, otherwise the protection doesn't change */ + if (sr & SR_SRWD) { + sr &= ~SR_SRWD; + ret = spi_nor_write_sr_and_check(nor, sr); + if (ret) { + dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n"); + return ret; + } + } + + if (is_protect) { + sr |= ATMEL_SR_GLOBAL_PROTECT_MASK; + /* + * Set the SRWD bit again as soon as we are protecting + * anything. This will ensure that the WP# pin is working + * correctly. By doing this we also behave the same as + * spi_nor_sr_lock(), which sets SRWD if any block protection + * is active. + */ + sr |= SR_SRWD; + } else { + sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK; + } + + nor->bouncebuf[0] = sr; + + /* + * We cannot use the spi_nor_write_sr_and_check() because this command + * isn't really setting any bits, instead it is an pseudo command for + * "Global Unprotect" or "Global Protect" + */ + return spi_nor_write_sr(nor, nor->bouncebuf, 1); +} + +static int atmel_global_protect(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return atmel_set_global_protection(nor, ofs, len, true); +} + +static int atmel_global_unprotect(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + return atmel_set_global_protection(nor, ofs, len, false); +} + +static int atmel_is_global_protected(struct spi_nor *nor, loff_t ofs, uint64_t len) +{ + int ret; + + if (ofs >= nor->params->size || (ofs + len) > nor->params->size) + return -EINVAL; + + ret = spi_nor_read_sr(nor, nor->bouncebuf); + if (ret) + return ret; + + return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK); +} + +static const struct spi_nor_locking_ops atmel_global_protection_ops = { + .lock = atmel_global_protect, + .unlock = atmel_global_unprotect, + .is_locked = atmel_is_global_protected, +}; + +static void atmel_global_protection_default_init(struct spi_nor *nor) +{ + nor->params->locking_ops = &atmel_global_protection_ops; +} + +static const struct spi_nor_fixups atmel_global_protection_fixups = { + .default_init = atmel_global_protection_default_init, +}; + static const struct flash_info atmel_parts[] = { /* Atmel -- some are (confusingly) marketed as "DataFlash" */ { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) @@ -62,18 +162,32 @@ static const struct flash_info atmel_parts[] = { { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) .fixups = &atmel_at25fs_fixups }, - { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &atmel_global_protection_fixups }, + { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &atmel_global_protection_fixups }, + { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &atmel_global_protection_fixups }, + { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &atmel_global_protection_fixups }, { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) }, - { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_HAS_LOCK) }, - { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, + { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &atmel_global_protection_fixups }, + { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &atmel_global_protection_fixups }, + { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + .fixups = &atmel_global_protection_fixups }, { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, }; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 53603efa4a4f..20df44b753da 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -377,7 +377,7 @@ int spi_nor_write_disable(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) +int spi_nor_read_sr(struct spi_nor *nor, u8 *sr) { int ret; @@ -861,7 +861,7 @@ int spi_nor_wait_till_ready(struct spi_nor *nor) * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) +int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len) { int ret; @@ -3140,6 +3140,8 @@ static void spi_nor_try_unlock_all(struct spi_nor *nor) if (!(nor->flags & SNOR_F_HAS_LOCK)) return; + dev_dbg(nor->dev, "Unprotecting entire flash array\n"); + ret = spi_nor_unlock(&nor->mtd, 0, nor->params->size); if (ret) dev_dbg(nor->dev, "Failed to unlock the entire flash memory array\n"); @@ -3161,7 +3163,20 @@ static int spi_nor_init(struct spi_nor *nor) return err; } - spi_nor_try_unlock_all(nor); + /* + * Some SPI NOR flashes are write protected by default after a power-on + * reset cycle, in order to avoid inadvertent writes during power-up. + * Backward compatibility imposes to unlock the entire flash memory + * array at power-up by default. Depending on the kernel configuration + * (1) do nothing, (2) always unlock the entire flash array or (3) + * unlock the entire flash array only when the software write + * protection bits are volatile. The latter is indicated by + * SNOR_F_SWP_IS_VOLATILE. + */ + if (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE) || + (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE) && + nor->flags & SNOR_F_SWP_IS_VOLATILE)) + spi_nor_try_unlock_all(nor); if (nor->addr_width == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && @@ -3460,6 +3475,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; if (info->flags & USE_CLSR) nor->flags |= SNOR_F_USE_CLSR; + if (info->flags & SPI_NOR_SWP_IS_VOLATILE) + nor->flags |= SNOR_F_SWP_IS_VOLATILE; if (info->flags & SPI_NOR_4BIT_BP) { nor->flags |= SNOR_F_HAS_4BIT_BP; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index caf8b7def828..d631ee299de3 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -28,6 +28,7 @@ enum spi_nor_option_flags { SNOR_F_HAS_SR_BP3_BIT6 = BIT(13), SNOR_F_IO_MODE_EN_VOLATILE = BIT(14), SNOR_F_SOFT_RESET = BIT(15), + SNOR_F_SWP_IS_VOLATILE = BIT(16), }; struct spi_nor_read_command { @@ -332,6 +333,11 @@ struct flash_info { * available I/O mode via a * volatile bit. */ +#define SPI_NOR_SWP_IS_VOLATILE BIT(22) /* + * Flash has volatile software write + * protection bits. Usually these will + * power-up in a write-protected state. + */ /* Part specific fixup hooks. */ const struct spi_nor_fixups *fixups; @@ -433,6 +439,8 @@ void spi_nor_unlock_and_unprep(struct spi_nor *nor); int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor); int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor); +int spi_nor_read_sr(struct spi_nor *nor, u8 *sr); +int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len); int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1); int spi_nor_xread_sr(struct spi_nor *nor, u8 *sr); diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c index c93170008118..cfc9218c1053 100644 --- a/drivers/mtd/spi-nor/esmt.c +++ b/drivers/mtd/spi-nor/esmt.c @@ -11,7 +11,7 @@ static const struct flash_info esmt_parts[] = { /* ESMT */ { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, - SECT_4K | SPI_NOR_HAS_LOCK) }, + SECT_4K | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) }, { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128, diff --git a/drivers/mtd/spi-nor/intel.c b/drivers/mtd/spi-nor/intel.c index 6c31bef3fc60..8ece9cceb3cf 100644 --- a/drivers/mtd/spi-nor/intel.c +++ b/drivers/mtd/spi-nor/intel.c @@ -10,9 +10,12 @@ static const struct flash_info intel_parts[] = { /* Intel/Numonyx -- xxxs33b */ - { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, SPI_NOR_HAS_LOCK) }, - { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, SPI_NOR_HAS_LOCK) }, - { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, SPI_NOR_HAS_LOCK) }, + { "160s33b", INFO(0x898911, 0, 64 * 1024, 32, + SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, + { "320s33b", INFO(0x898912, 0, 64 * 1024, 64, + SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, + SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, }; const struct spi_nor_manufacturer spi_nor_intel = { diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index 0d9d319f61e6..00e48da0744a 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -11,27 +11,28 @@ static const struct flash_info sst_parts[] = { /* SST -- large erase sizes are "overlays", "sectors" are 4K */ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, - SECT_4K | SPI_NOR_4BIT_BP | SPI_NOR_HAS_LOCK) }, + SECT_4K | SPI_NOR_4BIT_BP | SPI_NOR_HAS_LOCK | + SPI_NOR_SWP_IS_VOLATILE) }, { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25wf020a", INFO(0x621612, 0, 64 * 1024, 4, SECT_4K | SPI_NOR_HAS_LOCK) }, { "sst25wf040b", INFO(0x621613, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_HAS_LOCK) }, { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, - SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK) }, + SECT_4K | SST_WRITE | SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) }, { "sst26wf016b", INFO(0xbf2651, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, -- cgit v1.2.3 From 9994bb3f36e3d181d9f0a078609038080cfd7a3d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:13 +0200 Subject: mtd: nand: ecc-bch: Create the software BCH engine Let's continue introducing the generic ECC engine abstraction in the NAND subsystem by instantiating a first ECC engine: the software BCH one. While at it, make a very tidy ecc_sw_bch_init() function and move all the sanity checks and user input management in nand_ecc_sw_bch_init_ctx(). This second helper will be called from the raw RAND core. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-10-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-bch.c | 333 +++++++++++++++++++++++++++++------- drivers/mtd/nand/raw/nand_base.c | 62 +------ include/linux/mtd/nand-ecc-sw-bch.h | 16 +- include/linux/mtd/nand.h | 9 + 4 files changed, 296 insertions(+), 124 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c index 16a54bd2ca31..0a0ac11d5725 100644 --- a/drivers/mtd/nand/ecc-sw-bch.c +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -75,6 +75,19 @@ int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, } EXPORT_SYMBOL(nand_ecc_sw_bch_correct); +/** + * nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources + * @nand: NAND device + */ +static void nand_ecc_sw_bch_cleanup(struct nand_device *nand) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + + bch_free(engine_conf->bch); + kfree(engine_conf->errloc); + kfree(engine_conf->eccmask); +} + /** * nand_ecc_sw_bch_init - Initialize software BCH ECC engine * @nand: NAND device @@ -92,107 +105,301 @@ EXPORT_SYMBOL(nand_ecc_sw_bch_correct); * step_size = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) * bytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) */ -int nand_ecc_sw_bch_init(struct nand_device *nand) +static int nand_ecc_sw_bch_init(struct nand_device *nand) { - struct mtd_info *mtd = nanddev_to_mtd(nand); - unsigned int m, t, eccsteps, i; struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; - unsigned char *erased_page; unsigned int eccsize = nand->ecc.ctx.conf.step_size; unsigned int eccbytes = engine_conf->code_size; - unsigned int eccstrength = nand->ecc.ctx.conf.strength; - - if (!eccbytes && eccstrength) { - eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); - engine_conf->code_size = eccbytes; - } - - if (!eccsize || !eccbytes) { - pr_warn("ecc parameters not supplied\n"); - return -EINVAL; - } + unsigned int m, t, i; + unsigned char *erased_page; + int ret; - m = fls(1+8*eccsize); - t = (eccbytes*8)/m; + m = fls(1 + (8 * eccsize)); + t = (eccbytes * 8) / m; engine_conf->bch = bch_init(m, t, 0, false); if (!engine_conf->bch) return -EINVAL; - /* verify that eccbytes has the expected value */ - if (engine_conf->bch->ecc_bytes != eccbytes) { - pr_warn("invalid eccbytes %u, should be %u\n", - eccbytes, engine_conf->bch->ecc_bytes); - goto fail; + engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL); + engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc), + GFP_KERNEL); + if (!engine_conf->eccmask || !engine_conf->errloc) { + ret = -ENOMEM; + goto cleanup; + } + + /* Compute and store the inverted ECC of an erased step */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) { + ret = -ENOMEM; + goto cleanup; } - eccsteps = mtd->writesize/eccsize; + memset(erased_page, 0xff, eccsize); + bch_encode(engine_conf->bch, erased_page, eccsize, + engine_conf->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + engine_conf->eccmask[i] ^= 0xff; - /* Check that we have an oob layout description. */ - if (!mtd->ooblayout) { - pr_warn("missing oob scheme"); - goto fail; + /* Verify that the number of code bytes has the expected value */ + if (engine_conf->bch->ecc_bytes != eccbytes) { + pr_err("Invalid number of ECC bytes: %u, expected: %u\n", + eccbytes, engine_conf->bch->ecc_bytes); + ret = -EINVAL; + goto cleanup; } - /* sanity checks */ - if (8*(eccsize+eccbytes) >= (1 << m)) { - pr_warn("eccsize %u is too large\n", eccsize); - goto fail; + /* Sanity checks */ + if (8 * (eccsize + eccbytes) >= (1 << m)) { + pr_err("ECC step size is too large (%u)\n", eccsize); + ret = -EINVAL; + goto cleanup; } - if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { - pr_warn("invalid ecc layout\n"); - goto fail; + return 0; + +cleanup: + nand_ecc_sw_bch_cleanup(nand); + + return ret; +} + +int nand_ecc_sw_bch_init_ctx(struct nand_device *nand) +{ + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_ecc_sw_bch_conf *engine_conf; + unsigned int code_size = 0, nsteps; + int ret; + + /* Only large page NAND chips may use BCH */ + if (mtd->oobsize < 64) { + pr_err("BCH cannot be used with small page NAND chips\n"); + return -EINVAL; } - engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL); - engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc), - GFP_KERNEL); - if (!engine_conf->eccmask || !engine_conf->errloc) - goto fail; + if (!mtd->ooblayout) + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); + + conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + conf->algo = NAND_ECC_ALGO_BCH; + conf->step_size = nand->ecc.user_conf.step_size; + conf->strength = nand->ecc.user_conf.strength; /* - * compute and store the inverted ecc of an erased ecc block + * Board driver should supply ECC size and ECC strength + * values to select how many bits are correctable. + * Otherwise, default to 512 bytes for large page devices and 256 for + * small page devices. */ - erased_page = kmalloc(eccsize, GFP_KERNEL); - if (!erased_page) - goto fail; + if (!conf->step_size) { + if (mtd->oobsize >= 64) + conf->step_size = 512; + else + conf->step_size = 256; - memset(erased_page, 0xff, eccsize); - bch_encode(engine_conf->bch, erased_page, eccsize, - engine_conf->eccmask); - kfree(erased_page); + conf->strength = 4; + } - for (i = 0; i < eccbytes; i++) - engine_conf->eccmask[i] ^= 0xff; + nsteps = mtd->writesize / conf->step_size; + + /* Maximize */ + if (nand->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { + conf->step_size = 1024; + nsteps = mtd->writesize / conf->step_size; + /* Reserve 2 bytes for the BBM */ + code_size = (mtd->oobsize - 2) / nsteps; + conf->strength = code_size * 8 / fls(8 * conf->step_size); + } + + if (!code_size) + code_size = DIV_ROUND_UP(conf->strength * + fls(8 * conf->step_size), 8); + + if (!conf->strength) + conf->strength = (code_size * 8) / fls(8 * conf->step_size); + + if (!code_size && !conf->strength) { + pr_err("Missing ECC parameters\n"); + return -EINVAL; + } + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand); + if (ret) + goto free_engine_conf; + + engine_conf->code_size = code_size; + engine_conf->nsteps = nsteps; + engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!engine_conf->calc_buf || !engine_conf->code_buf) { + ret = -ENOMEM; + goto free_bufs; + } - if (!eccstrength) - nand->ecc.ctx.conf.strength = (eccbytes * 8) / fls(8 * eccsize); + nand->ecc.ctx.priv = engine_conf; + nand->ecc.ctx.total = nsteps * code_size; + + ret = nand_ecc_sw_bch_init(nand); + if (ret) + goto free_bufs; + + /* Verify the layout validity */ + if (mtd_ooblayout_count_eccbytes(mtd) != + engine_conf->nsteps * engine_conf->code_size) { + pr_err("Invalid ECC layout\n"); + ret = -EINVAL; + goto cleanup_bch_ctx; + } return 0; -fail: +cleanup_bch_ctx: nand_ecc_sw_bch_cleanup(nand); +free_bufs: + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); +free_engine_conf: + kfree(engine_conf); - return -EINVAL; + return ret; } -EXPORT_SYMBOL(nand_ecc_sw_bch_init); +EXPORT_SYMBOL(nand_ecc_sw_bch_init_ctx); -/** - * nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources - * @nand: NAND device - */ -void nand_ecc_sw_bch_cleanup(struct nand_device *nand) +void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand) { struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; if (engine_conf) { - bch_free(engine_conf->bch); - kfree(engine_conf->errloc); - kfree(engine_conf->eccmask); + nand_ecc_sw_bch_cleanup(nand); + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); + kfree(engine_conf); + } +} +EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup_ctx); + +static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int eccbytes = engine_conf->code_size; + int eccsteps = engine_conf->nsteps; + int total = nand->ecc.ctx.total; + u8 *ecccalc = engine_conf->calc_buf; + const u8 *data; + int i; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + nand_ecc_tweak_req(&engine_conf->req_ctx, req); + + /* No more preparation for page read */ + if (req->type == NAND_PAGE_READ) + return 0; + + /* Preparation for page write: derive the ECC bytes and place them */ + for (i = 0, data = req->databuf.out; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]); + + return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out, + 0, total); +} + +static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int total = nand->ecc.ctx.total; + int eccbytes = engine_conf->code_size; + int eccsteps = engine_conf->nsteps; + u8 *ecccalc = engine_conf->calc_buf; + u8 *ecccode = engine_conf->code_buf; + unsigned int max_bitflips = 0; + u8 *data = req->databuf.in; + int i, ret; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + /* No more preparation for page write */ + if (req->type == NAND_PAGE_WRITE) { + nand_ecc_restore_req(&engine_conf->req_ctx, req); + return 0; } + + /* Finish a page read: retrieve the (raw) ECC bytes*/ + ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0, + total); + if (ret) + return ret; + + /* Calculate the ECC bytes */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]); + + /* Finish a page read: compare and correct */ + for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) { + int stat = nand_ecc_sw_bch_correct(nand, data, + &ecccode[i], + &ecccalc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + nand_ecc_restore_req(&engine_conf->req_ctx, req); + + return max_bitflips; +} + +static struct nand_ecc_engine_ops nand_ecc_sw_bch_engine_ops = { + .init_ctx = nand_ecc_sw_bch_init_ctx, + .cleanup_ctx = nand_ecc_sw_bch_cleanup_ctx, + .prepare_io_req = nand_ecc_sw_bch_prepare_io_req, + .finish_io_req = nand_ecc_sw_bch_finish_io_req, +}; + +static struct nand_ecc_engine nand_ecc_sw_bch_engine = { + .ops = &nand_ecc_sw_bch_engine_ops, +}; + +struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void) +{ + return &nand_ecc_sw_bch_engine; } -EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup); +EXPORT_SYMBOL(nand_ecc_sw_bch_get_engine); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ivan Djelic "); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 03106bf629dd..ebaf3bbfc1b2 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5150,17 +5150,11 @@ int rawnand_sw_bch_init(struct nand_chip *chip) base->ecc.user_conf.step_size = chip->ecc.size; base->ecc.user_conf.strength = chip->ecc.strength; - engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); - if (!engine_conf) - return -ENOMEM; - - engine_conf->code_size = chip->ecc.bytes; - - base->ecc.ctx.priv = engine_conf; - - ret = nand_ecc_sw_bch_init(base); + ret = nand_ecc_sw_bch_init_ctx(base); if (ret) - kfree(base->ecc.ctx.priv); + return ret; + + engine_conf = base->ecc.ctx.priv; chip->ecc.size = base->ecc.ctx.conf.step_size; chip->ecc.strength = base->ecc.ctx.conf.strength; @@ -5168,7 +5162,7 @@ int rawnand_sw_bch_init(struct nand_chip *chip) chip->ecc.steps = engine_conf->nsteps; chip->ecc.bytes = engine_conf->code_size; - return ret; + return 0; } EXPORT_SYMBOL(rawnand_sw_bch_init); @@ -5194,9 +5188,7 @@ void rawnand_sw_bch_cleanup(struct nand_chip *chip) { struct nand_device *base = &chip->base; - nand_ecc_sw_bch_cleanup(base); - - kfree(base->ecc.ctx.priv); + nand_ecc_sw_bch_cleanup_ctx(base); } EXPORT_SYMBOL(rawnand_sw_bch_cleanup); @@ -5308,51 +5300,15 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) ecc->read_oob = nand_read_oob_std; ecc->write_oob = nand_write_oob_std; - /* - * Board driver should supply ecc.size and ecc.strength - * values to select how many bits are correctable. - * Otherwise, default to 4 bits for large page devices. - */ - if (!ecc->size && (mtd->oobsize >= 64)) { - ecc->size = 512; - ecc->strength = 4; - } - - /* - * if no ecc placement scheme was provided pickup the default - * large page one. - */ - if (!mtd->ooblayout) { - /* handle large page devices only */ - if (mtd->oobsize < 64) { - WARN(1, "OOB layout is required when using software BCH on small pages\n"); - return -EINVAL; - } - - mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); - - } - /* * We can only maximize ECC config when the default layout is * used, otherwise we don't know how many bytes can really be * used. */ - if (mtd->ooblayout == nand_get_large_page_ooblayout() && - nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { - int steps, bytes; - - /* Always prefer 1k blocks over 512bytes ones */ - ecc->size = 1024; - steps = mtd->writesize / ecc->size; + if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH && + mtd->ooblayout != nand_get_large_page_ooblayout()) + nanddev->ecc.user_conf.flags &= ~NAND_ECC_MAXIMIZE_STRENGTH; - /* Reserve 2 bytes for the BBM */ - bytes = (mtd->oobsize - 2) / steps; - ecc->strength = bytes * 8 / fls(8 * ecc->size); - } - - /* See the software BCH ECC initialization for details */ - ecc->bytes = 0; ret = rawnand_sw_bch_init(chip); if (ret) { WARN(1, "BCH ECC initialization failed!\n"); diff --git a/include/linux/mtd/nand-ecc-sw-bch.h b/include/linux/mtd/nand-ecc-sw-bch.h index ce005528e55f..22c92073b3dd 100644 --- a/include/linux/mtd/nand-ecc-sw-bch.h +++ b/include/linux/mtd/nand-ecc-sw-bch.h @@ -13,8 +13,8 @@ /** * struct nand_ecc_sw_bch_conf - private software BCH ECC engine structure - * @reqooblen: Save the actual user OOB length requested before overwriting it - * @spare_oobbuf: Spare OOB buffer if none is provided + * @req_ctx: Save request context and tweak the original request to fit the + * engine needs * @code_size: Number of bytes needed to store a code (one code per step) * @nsteps: Number of steps * @calc_buf: Buffer to use when calculating ECC bytes @@ -24,8 +24,7 @@ * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid */ struct nand_ecc_sw_bch_conf { - unsigned int reqooblen; - void *spare_oobbuf; + struct nand_ecc_req_tweak_ctx req_ctx; unsigned int code_size; unsigned int nsteps; u8 *calc_buf; @@ -41,8 +40,9 @@ int nand_ecc_sw_bch_calculate(struct nand_device *nand, const unsigned char *buf, unsigned char *code); int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc); -int nand_ecc_sw_bch_init(struct nand_device *nand); -void nand_ecc_sw_bch_cleanup(struct nand_device *nand); +int nand_ecc_sw_bch_init_ctx(struct nand_device *nand); +void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand); +struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void); #else /* !CONFIG_MTD_NAND_ECC_SW_BCH */ @@ -61,12 +61,12 @@ static inline int nand_ecc_sw_bch_correct(struct nand_device *nand, return -ENOTSUPP; } -static inline int nand_ecc_sw_bch_init(struct nand_device *nand) +static inline int nand_ecc_sw_bch_init_ctx(struct nand_device *nand) { return -ENOTSUPP; } -static inline void nand_ecc_sw_bch_cleanup(struct nand_device *nand) {} +static inline void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand) {} #endif /* CONFIG_MTD_NAND_ECC_SW_BCH */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 36e4fe08d0ea..df8548187713 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -278,6 +278,15 @@ int nand_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req); bool nand_ecc_is_strong_enough(struct nand_device *nand); +#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) +struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void); +#else +static inline struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void) +{ + return NULL; +} +#endif /* CONFIG_MTD_NAND_ECC_SW_BCH */ + /** * struct nand_ecc_req_tweak_ctx - Help for automatically tweaking requests * @orig_req: Pointer to the original IO request -- cgit v1.2.3 From cbd87780bed580b585d2992f29077ac44950cb66 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:14 +0200 Subject: mtd: rawnand: Get rid of chip->ecc.priv nand_ecc_ctrl embeds a private pointer which only has a meaning in the sunxi driver. This structure will soon be deprecated, but as this field is actually not needed, let's just drop it. Cc: Maxime Ripard Cc: Chen-Yu Tsai Signed-off-by: Miquel Raynal Acked-by: Maxime Ripard Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-11-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/sunxi_nand.c | 31 +++++++++++++++++-------------- include/linux/mtd/rawnand.h | 2 -- 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 2a7ca3072f35..540529540ade 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -182,6 +182,7 @@ struct sunxi_nand_hw_ecc { * * @node: used to store NAND chips into a list * @nand: base NAND chip structure + * @ecc: ECC controller structure * @clk_rate: clk_rate required for this NAND chip * @timing_cfg: TIMING_CFG register value for this NAND chip * @timing_ctl: TIMING_CTL register value for this NAND chip @@ -191,6 +192,7 @@ struct sunxi_nand_hw_ecc { struct sunxi_nand_chip { struct list_head node; struct nand_chip nand; + struct sunxi_nand_hw_ecc *ecc; unsigned long clk_rate; u32 timing_cfg; u32 timing_ctl; @@ -676,15 +678,15 @@ static void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf, static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - struct sunxi_nand_hw_ecc *data = nand->ecc.priv; u32 ecc_ctl; ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); - ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION | - NFC_ECC_PIPELINE; + ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc->mode) | + NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE; if (nand->ecc.size == 512) ecc_ctl |= NFC_ECC_BLOCK_512; @@ -1597,9 +1599,9 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { .free = sunxi_nand_ooblayout_free, }; -static void sunxi_nand_hw_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) +static void sunxi_nand_hw_ecc_ctrl_cleanup(struct sunxi_nand_chip *sunxi_nand) { - kfree(ecc->priv); + kfree(sunxi_nand->ecc); } static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, @@ -1607,10 +1609,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, struct device_node *np) { static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct mtd_info *mtd = nand_to_mtd(nand); struct nand_device *nanddev = mtd_to_nanddev(mtd); - struct sunxi_nand_hw_ecc *data; int nsectors; int ret; int i; @@ -1647,8 +1649,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, if (ecc->size != 512 && ecc->size != 1024) return -EINVAL; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) + sunxi_nand->ecc = kzalloc(sizeof(*sunxi_nand->ecc), GFP_KERNEL); + if (!sunxi_nand->ecc) return -ENOMEM; /* Prefer 1k ECC chunk over 512 ones */ @@ -1675,7 +1677,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, goto err; } - data->mode = i; + sunxi_nand->ecc->mode = i; /* HW ECC always request ECC bytes for 1024 bytes blocks */ ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); @@ -1693,7 +1695,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); - ecc->priv = data; if (nfc->dmac) { ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; @@ -1714,16 +1715,18 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, return 0; err: - kfree(data); + kfree(sunxi_nand->ecc); return ret; } -static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) +static void sunxi_nand_ecc_cleanup(struct sunxi_nand_chip *sunxi_nand) { + struct nand_ecc_ctrl *ecc = &sunxi_nand->nand.ecc; + switch (ecc->engine_type) { case NAND_ECC_ENGINE_TYPE_ON_HOST: - sunxi_nand_hw_ecc_ctrl_cleanup(ecc); + sunxi_nand_hw_ecc_ctrl_cleanup(sunxi_nand); break; case NAND_ECC_ENGINE_TYPE_NONE: default: @@ -2053,7 +2056,7 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) ret = mtd_device_unregister(nand_to_mtd(chip)); WARN_ON(ret); nand_cleanup(chip); - sunxi_nand_ecc_cleanup(&chip->ecc); + sunxi_nand_ecc_cleanup(sunxi_nand); list_del(&sunxi_nand->node); } } diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 23623beaad1d..15743c2c2cab 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -302,7 +302,6 @@ static const struct nand_ecc_caps __name = { \ * @prepad: padding information for syndrome based ECC generators * @postpad: padding information for syndrome based ECC generators * @options: ECC specific options (see NAND_ECC_XXX flags defined above) - * @priv: pointer to private ECC control data * @calc_buf: buffer for calculated ECC, size is oobsize. * @code_buf: buffer for ECC read from flash, size is oobsize. * @hwctl: function to control hardware ECC generator. Must only @@ -355,7 +354,6 @@ struct nand_ecc_ctrl { int prepad; int postpad; unsigned int options; - void *priv; u8 *calc_buf; u8 *code_buf; void (*hwctl)(struct nand_chip *chip, int mode); -- cgit v1.2.3 From e5acf9c862974041f7b2f581d1a40ccd29769add Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:15 +0200 Subject: mtd: nand: ecc-hamming: Move Hamming code to the generic NAND layer Hamming ECC code might be later re-used by the SPI NAND layer. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-12-miquel.raynal@bootlin.com --- arch/arm/mach-s3c/common-smdk-s3c24xx.c | 2 +- arch/arm/mach-s3c/mach-anubis.c | 2 +- arch/arm/mach-s3c/mach-at2440evb.c | 2 +- arch/arm/mach-s3c/mach-bast.c | 2 +- arch/arm/mach-s3c/mach-gta02.c | 2 +- arch/arm/mach-s3c/mach-jive.c | 2 +- arch/arm/mach-s3c/mach-mini2440.c | 2 +- arch/arm/mach-s3c/mach-osiris.c | 2 +- arch/arm/mach-s3c/mach-qt2410.c | 2 +- arch/arm/mach-s3c/mach-rx3715.c | 2 +- arch/arm/mach-s3c/mach-vstms.c | 2 +- drivers/mtd/Kconfig | 1 + drivers/mtd/nand/Kconfig | 11 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/ecc-sw-hamming.c | 484 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/Kconfig | 11 - drivers/mtd/nand/raw/Makefile | 1 - drivers/mtd/nand/raw/cs553x_nand.c | 2 +- drivers/mtd/nand/raw/fsl_elbc_nand.c | 2 +- drivers/mtd/nand/raw/fsl_ifc_nand.c | 2 +- drivers/mtd/nand/raw/fsl_upm.c | 2 +- drivers/mtd/nand/raw/fsmc_nand.c | 2 +- drivers/mtd/nand/raw/lpc32xx_mlc.c | 2 +- drivers/mtd/nand/raw/lpc32xx_slc.c | 2 +- drivers/mtd/nand/raw/mxic_nand.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 2 +- drivers/mtd/nand/raw/nand_ecc.c | 484 -------------------------------- drivers/mtd/nand/raw/ndfc.c | 2 +- drivers/mtd/nand/raw/pasemi_nand.c | 2 +- drivers/mtd/nand/raw/s3c2410.c | 2 +- drivers/mtd/nand/raw/sharpsl.c | 2 +- drivers/mtd/nand/raw/tmio_nand.c | 2 +- drivers/mtd/nand/raw/txx9ndfmc.c | 2 +- drivers/mtd/sm_ftl.c | 2 +- drivers/mtd/tests/mtd_nandecctest.c | 2 +- include/linux/mtd/nand-ecc-sw-hamming.h | 39 +++ include/linux/mtd/nand_ecc.h | 39 --- include/linux/mtd/sharpsl.h | 2 +- 38 files changed, 565 insertions(+), 564 deletions(-) create mode 100644 drivers/mtd/nand/ecc-sw-hamming.c delete mode 100644 drivers/mtd/nand/raw/nand_ecc.c create mode 100644 include/linux/mtd/nand-ecc-sw-hamming.h delete mode 100644 include/linux/mtd/nand_ecc.h (limited to 'drivers/mtd') diff --git a/arch/arm/mach-s3c/common-smdk-s3c24xx.c b/arch/arm/mach-s3c/common-smdk-s3c24xx.c index f860d8bcba0e..6d124bbd384c 100644 --- a/arch/arm/mach-s3c/common-smdk-s3c24xx.c +++ b/arch/arm/mach-s3c/common-smdk-s3c24xx.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-s3c/mach-anubis.c b/arch/arm/mach-s3c/mach-anubis.c index 90e3fd98a3ac..04147cc0adcc 100644 --- a/arch/arm/mach-s3c/mach-anubis.c +++ b/arch/arm/mach-s3c/mach-anubis.c @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-s3c/mach-at2440evb.c b/arch/arm/mach-s3c/mach-at2440evb.c index 5fa49d4e2650..c6a5a51d84aa 100644 --- a/arch/arm/mach-s3c/mach-at2440evb.c +++ b/arch/arm/mach-s3c/mach-at2440evb.c @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include "devs.h" diff --git a/arch/arm/mach-s3c/mach-bast.c b/arch/arm/mach-s3c/mach-bast.c index 328f5d9ae9f9..27e8d5950228 100644 --- a/arch/arm/mach-s3c/mach-bast.c +++ b/arch/arm/mach-s3c/mach-bast.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-s3c/mach-gta02.c b/arch/arm/mach-s3c/mach-gta02.c index 3c75c7d112ea..aec8b451c016 100644 --- a/arch/arm/mach-s3c/mach-gta02.c +++ b/arch/arm/mach-s3c/mach-gta02.c @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-s3c/mach-jive.c b/arch/arm/mach-s3c/mach-jive.c index 2a29c3eca559..0785638a9069 100644 --- a/arch/arm/mach-s3c/mach-jive.c +++ b/arch/arm/mach-s3c/mach-jive.c @@ -40,7 +40,7 @@ #include #include -#include +#include #include #include "gpio-cfg.h" diff --git a/arch/arm/mach-s3c/mach-mini2440.c b/arch/arm/mach-s3c/mach-mini2440.c index dc22ab839b95..4100905dfbd0 100644 --- a/arch/arm/mach-s3c/mach-mini2440.c +++ b/arch/arm/mach-s3c/mach-mini2440.c @@ -44,7 +44,7 @@ #include #include -#include +#include #include #include "gpio-cfg.h" diff --git a/arch/arm/mach-s3c/mach-osiris.c b/arch/arm/mach-s3c/mach-osiris.c index 81744ca67d1d..3aefb9d22340 100644 --- a/arch/arm/mach-s3c/mach-osiris.c +++ b/arch/arm/mach-s3c/mach-osiris.c @@ -33,7 +33,7 @@ #include #include -#include +#include #include #include "cpu.h" diff --git a/arch/arm/mach-s3c/mach-qt2410.c b/arch/arm/mach-s3c/mach-qt2410.c index 151e8e373d40..f88b961798fd 100644 --- a/arch/arm/mach-s3c/mach-qt2410.c +++ b/arch/arm/mach-s3c/mach-qt2410.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-s3c/mach-rx3715.c b/arch/arm/mach-s3c/mach-rx3715.c index a03662a47b38..9fd2d9dc3689 100644 --- a/arch/arm/mach-s3c/mach-rx3715.c +++ b/arch/arm/mach-s3c/mach-rx3715.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-s3c/mach-vstms.c b/arch/arm/mach-s3c/mach-vstms.c index 05f19f5ffabb..ec024af7b0ce 100644 --- a/arch/arm/mach-s3c/mach-vstms.c +++ b/arch/arm/mach-s3c/mach-vstms.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 6ddab796216d..8bab6f8718a9 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -152,6 +152,7 @@ config SM_FTL tristate "SmartMedia/xD new translation layer" depends on BLOCK select MTD_BLKDEVS + select MTD_NAND_CORE select MTD_NAND_ECC_SW_HAMMING help This enables EXPERIMENTAL R/W support for SmartMedia/xD diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 55c17fb0dee1..306c33ca3448 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -15,6 +15,17 @@ config MTD_NAND_ECC bool depends on MTD_NAND_CORE +config MTD_NAND_ECC_SW_HAMMING + bool + +config MTD_NAND_ECC_SW_HAMMING_SMC + bool "NAND ECC Smart Media byte order" + depends on MTD_NAND_ECC_SW_HAMMING + default n + help + Software ECC according to the Smart Media Specification. + The original Linux implementation had byte 0 and 1 swapped. + config MTD_NAND_ECC_SW_BCH bool "Software BCH ECC engine" select BCH diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index c7179ff23753..1c0b46960eb1 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -8,4 +8,5 @@ obj-y += raw/ obj-y += spi/ nandcore-$(CONFIG_MTD_NAND_ECC) += ecc.o +nandcore-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += ecc-sw-hamming.o nandcore-$(CONFIG_MTD_NAND_ECC_SW_BCH) += ecc-sw-bch.o diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c new file mode 100644 index 000000000000..76966f5ff13f --- /dev/null +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file contains an ECC algorithm that detects and corrects 1 bit + * errors in a 256 byte block of data. + * + * Copyright © 2008 Koninklijke Philips Electronics NV. + * Author: Frans Meulenbroeks + * + * Completely replaces the previous ECC implementation which was written by: + * Steven J. Hill (sjhill@realitydiluted.com) + * Thomas Gleixner (tglx@linutronix.de) + * + * Information on how this algorithm works and how it was developed + * can be found in Documentation/driver-api/mtd/nand_ecc.rst + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * invparity is a 256 byte table that contains the odd parity + * for each byte. So if the number of bits in a byte is even, + * the array element is 1, and when the number of bits is odd + * the array eleemnt is 0. + */ +static const char invparity[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +/* + * bitsperbyte contains the number of bits per byte + * this is only used for testing and repairing parity + * (a precalculated value slightly improves performance) + */ +static const char bitsperbyte[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; + +/* + * addressbits is a lookup table to filter out the bits from the xor-ed + * ECC data that identify the faulty location. + * this is only used for repairing parity + * see the comments in nand_correct_data for more details + */ +static const char addressbits[256] = { + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f +}; + +/** + * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * block + * @buf: input buffer with raw data + * @eccsize: data bytes per ECC step (256 or 512) + * @code: output buffer with ECC + * @sm_order: Smart Media byte ordering + */ +void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, + unsigned char *code, bool sm_order) +{ + int i; + const uint32_t *bp = (uint32_t *)buf; + /* 256 or 512 bytes/ecc */ + const uint32_t eccsize_mult = eccsize >> 8; + uint32_t cur; /* current value in buffer */ + /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ + uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; + uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16; + uint32_t rp17; + uint32_t par; /* the cumulative parity for all data */ + uint32_t tmppar; /* the cumulative parity for this iteration; + for rp12, rp14 and rp16 at the end of the + loop */ + + par = 0; + rp4 = 0; + rp6 = 0; + rp8 = 0; + rp10 = 0; + rp12 = 0; + rp14 = 0; + rp16 = 0; + + /* + * The loop is unrolled a number of times; + * This avoids if statements to decide on which rp value to update + * Also we process the data by longwords. + * Note: passing unaligned data might give a performance penalty. + * It is assumed that the buffers are aligned. + * tmppar is the cumulative sum of this iteration. + * needed for calculating rp12, rp14, rp16 and par + * also used as a performance improvement for rp6, rp8 and rp10 + */ + for (i = 0; i < eccsize_mult << 2; i++) { + cur = *bp++; + tmppar = cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= tmppar; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= tmppar; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp10 ^= tmppar; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= cur; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + + par ^= tmppar; + if ((i & 0x1) == 0) + rp12 ^= tmppar; + if ((i & 0x2) == 0) + rp14 ^= tmppar; + if (eccsize_mult == 2 && (i & 0x4) == 0) + rp16 ^= tmppar; + } + + /* + * handle the fact that we use longword operations + * we'll bring rp4..rp14..rp16 back to single byte entities by + * shifting and xoring first fold the upper and lower 16 bits, + * then the upper and lower 8 bits. + */ + rp4 ^= (rp4 >> 16); + rp4 ^= (rp4 >> 8); + rp4 &= 0xff; + rp6 ^= (rp6 >> 16); + rp6 ^= (rp6 >> 8); + rp6 &= 0xff; + rp8 ^= (rp8 >> 16); + rp8 ^= (rp8 >> 8); + rp8 &= 0xff; + rp10 ^= (rp10 >> 16); + rp10 ^= (rp10 >> 8); + rp10 &= 0xff; + rp12 ^= (rp12 >> 16); + rp12 ^= (rp12 >> 8); + rp12 &= 0xff; + rp14 ^= (rp14 >> 16); + rp14 ^= (rp14 >> 8); + rp14 &= 0xff; + if (eccsize_mult == 2) { + rp16 ^= (rp16 >> 16); + rp16 ^= (rp16 >> 8); + rp16 &= 0xff; + } + + /* + * we also need to calculate the row parity for rp0..rp3 + * This is present in par, because par is now + * rp3 rp3 rp2 rp2 in little endian and + * rp2 rp2 rp3 rp3 in big endian + * as well as + * rp1 rp0 rp1 rp0 in little endian and + * rp0 rp1 rp0 rp1 in big endian + * First calculate rp2 and rp3 + */ +#ifdef __BIG_ENDIAN + rp2 = (par >> 16); + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; + rp3 = par & 0xffff; + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; +#else + rp3 = (par >> 16); + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; + rp2 = par & 0xffff; + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; +#endif + + /* reduce par to 16 bits then calculate rp1 and rp0 */ + par ^= (par >> 16); +#ifdef __BIG_ENDIAN + rp0 = (par >> 8) & 0xff; + rp1 = (par & 0xff); +#else + rp1 = (par >> 8) & 0xff; + rp0 = (par & 0xff); +#endif + + /* finally reduce par to 8 bits */ + par ^= (par >> 8); + par &= 0xff; + + /* + * and calculate rp5..rp15..rp17 + * note that par = rp4 ^ rp5 and due to the commutative property + * of the ^ operator we can say: + * rp5 = (par ^ rp4); + * The & 0xff seems superfluous, but benchmarking learned that + * leaving it out gives slightly worse results. No idea why, probably + * it has to do with the way the pipeline in pentium is organized. + */ + rp5 = (par ^ rp4) & 0xff; + rp7 = (par ^ rp6) & 0xff; + rp9 = (par ^ rp8) & 0xff; + rp11 = (par ^ rp10) & 0xff; + rp13 = (par ^ rp12) & 0xff; + rp15 = (par ^ rp14) & 0xff; + if (eccsize_mult == 2) + rp17 = (par ^ rp16) & 0xff; + + /* + * Finally calculate the ECC bits. + * Again here it might seem that there are performance optimisations + * possible, but benchmarks showed that on the system this is developed + * the code below is the fastest + */ + if (sm_order) { + code[0] = (invparity[rp7] << 7) | (invparity[rp6] << 6) | + (invparity[rp5] << 5) | (invparity[rp4] << 4) | + (invparity[rp3] << 3) | (invparity[rp2] << 2) | + (invparity[rp1] << 1) | (invparity[rp0]); + code[1] = (invparity[rp15] << 7) | (invparity[rp14] << 6) | + (invparity[rp13] << 5) | (invparity[rp12] << 4) | + (invparity[rp11] << 3) | (invparity[rp10] << 2) | + (invparity[rp9] << 1) | (invparity[rp8]); + } else { + code[1] = (invparity[rp7] << 7) | (invparity[rp6] << 6) | + (invparity[rp5] << 5) | (invparity[rp4] << 4) | + (invparity[rp3] << 3) | (invparity[rp2] << 2) | + (invparity[rp1] << 1) | (invparity[rp0]); + code[0] = (invparity[rp15] << 7) | (invparity[rp14] << 6) | + (invparity[rp13] << 5) | (invparity[rp12] << 4) | + (invparity[rp11] << 3) | (invparity[rp10] << 2) | + (invparity[rp9] << 1) | (invparity[rp8]); + } + + if (eccsize_mult == 1) + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + 3; + else + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + (invparity[rp17] << 1) | + (invparity[rp16] << 0); +} +EXPORT_SYMBOL(__nand_calculate_ecc); + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * block + * @chip: NAND chip object + * @buf: input buffer with raw data + * @code: output buffer with ECC + */ +int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, + unsigned char *code) +{ + bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; + + __nand_calculate_ecc(buf, chip->ecc.size, code, sm_order); + + return 0; +} +EXPORT_SYMBOL(nand_calculate_ecc); + +/** + * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * @eccsize: data bytes per ECC step (256 or 512) + * @sm_order: Smart Media byte order + * + * Detect and correct a 1 bit error for eccsize byte block + */ +int __nand_correct_data(unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc, + unsigned int eccsize, bool sm_order) +{ + unsigned char b0, b1, b2, bit_addr; + unsigned int byte_addr; + /* 256 or 512 bytes/ecc */ + const uint32_t eccsize_mult = eccsize >> 8; + + /* + * b0 to b2 indicate which bit is faulty (if any) + * we might need the xor result more than once, + * so keep them in a local var + */ + if (sm_order) { + b0 = read_ecc[0] ^ calc_ecc[0]; + b1 = read_ecc[1] ^ calc_ecc[1]; + } else { + b0 = read_ecc[1] ^ calc_ecc[1]; + b1 = read_ecc[0] ^ calc_ecc[0]; + } + + b2 = read_ecc[2] ^ calc_ecc[2]; + + /* check if there are any bitfaults */ + + /* repeated if statements are slightly more efficient than switch ... */ + /* ordered in order of likelihood */ + + if ((b0 | b1 | b2) == 0) + return 0; /* no error */ + + if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && + (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && + ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) || + (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) { + /* single bit error */ + /* + * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty + * byte, cp 5/3/1 indicate the faulty bit. + * A lookup table (called addressbits) is used to filter + * the bits from the byte they are in. + * A marginal optimisation is possible by having three + * different lookup tables. + * One as we have now (for b0), one for b2 + * (that would avoid the >> 1), and one for b1 (with all values + * << 4). However it was felt that introducing two more tables + * hardly justify the gain. + * + * The b2 shift is there to get rid of the lowest two bits. + * We could also do addressbits[b2] >> 1 but for the + * performance it does not make any difference + */ + if (eccsize_mult == 1) + byte_addr = (addressbits[b1] << 4) + addressbits[b0]; + else + byte_addr = (addressbits[b2 & 0x3] << 8) + + (addressbits[b1] << 4) + addressbits[b0]; + bit_addr = addressbits[b2 >> 2]; + /* flip the bit */ + buf[byte_addr] ^= (1 << bit_addr); + return 1; + + } + /* count nr of bits; use table lookup, faster than calculating it */ + if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) + return 1; /* error in ECC data; no action needed */ + + pr_err("%s: uncorrectable ECC error\n", __func__); + return -EBADMSG; +} +EXPORT_SYMBOL(__nand_correct_data); + +/** + * nand_correct_data - [NAND Interface] Detect and correct bit error(s) + * @chip: NAND chip object + * @buf: raw data read from the chip + * @read_ecc: ECC from the chip + * @calc_ecc: the ECC calculated from raw data + * + * Detect and correct a 1 bit error for 256/512 byte block + */ +int nand_correct_data(struct nand_chip *chip, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; + + return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size, + sm_order); +} +EXPORT_SYMBOL(nand_correct_data); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Frans Meulenbroeks "); +MODULE_DESCRIPTION("Generic NAND ECC support"); diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index b73860aa77c6..6149096e32cb 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -1,15 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -config MTD_NAND_ECC_SW_HAMMING - tristate - -config MTD_NAND_ECC_SW_HAMMING_SMC - bool "NAND ECC Smart Media byte order" - depends on MTD_NAND_ECC_SW_HAMMING - default n - help - Software ECC according to the Smart Media Specification. - The original Linux implementation had byte 0 and 1 swapped. - menuconfig MTD_RAW_NAND tristate "Raw/Parallel NAND Device Support" select MTD_NAND_CORE diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 76904305d091..dc38c087c693 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_MTD_RAW_NAND) += nand.o -obj-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += nand_ecc.o obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index 282203debd0c..9a2bdb45a771 100644 --- a/drivers/mtd/nand/raw/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index b2af7f81fdf8..eb255cf83e32 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index e345f9d9f8e8..d2f84917fa8b 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c index d5813b9abc8e..3824361928a1 100644 --- a/drivers/mtd/nand/raw/fsl_upm.c +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 984b05e6bd38..e880db5852d8 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 9e728c731795..885b03b7af52 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #define DRV_NAME "lpc32xx_mlc" diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index dc7785e30d2f..0bf9c3fbcd82 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/mxic_nand.c b/drivers/mtd/nand/raw/mxic_nand.c index d66b5b0971fa..da1070993994 100644 --- a/drivers/mtd/nand/raw/mxic_nand.c +++ b/drivers/mtd/nand/raw/mxic_nand.c @@ -12,8 +12,8 @@ #include #include #include +#include #include -#include #include #include "internals.h" diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index ebaf3bbfc1b2..bee7c64a0a4e 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c deleted file mode 100644 index b6a46b1b7781..000000000000 --- a/drivers/mtd/nand/raw/nand_ecc.c +++ /dev/null @@ -1,484 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * This file contains an ECC algorithm that detects and corrects 1 bit - * errors in a 256 byte block of data. - * - * Copyright © 2008 Koninklijke Philips Electronics NV. - * Author: Frans Meulenbroeks - * - * Completely replaces the previous ECC implementation which was written by: - * Steven J. Hill (sjhill@realitydiluted.com) - * Thomas Gleixner (tglx@linutronix.de) - * - * Information on how this algorithm works and how it was developed - * can be found in Documentation/driver-api/mtd/nand_ecc.rst - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * invparity is a 256 byte table that contains the odd parity - * for each byte. So if the number of bits in a byte is even, - * the array element is 1, and when the number of bits is odd - * the array eleemnt is 0. - */ -static const char invparity[256] = { - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 -}; - -/* - * bitsperbyte contains the number of bits per byte - * this is only used for testing and repairing parity - * (a precalculated value slightly improves performance) - */ -static const char bitsperbyte[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, -}; - -/* - * addressbits is a lookup table to filter out the bits from the xor-ed - * ECC data that identify the faulty location. - * this is only used for repairing parity - * see the comments in nand_correct_data for more details - */ -static const char addressbits[256] = { - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, - 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, - 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, - 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f -}; - -/** - * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte - * block - * @buf: input buffer with raw data - * @eccsize: data bytes per ECC step (256 or 512) - * @code: output buffer with ECC - * @sm_order: Smart Media byte ordering - */ -void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, - unsigned char *code, bool sm_order) -{ - int i; - const uint32_t *bp = (uint32_t *)buf; - /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = eccsize >> 8; - uint32_t cur; /* current value in buffer */ - /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ - uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; - uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16; - uint32_t rp17; - uint32_t par; /* the cumulative parity for all data */ - uint32_t tmppar; /* the cumulative parity for this iteration; - for rp12, rp14 and rp16 at the end of the - loop */ - - par = 0; - rp4 = 0; - rp6 = 0; - rp8 = 0; - rp10 = 0; - rp12 = 0; - rp14 = 0; - rp16 = 0; - - /* - * The loop is unrolled a number of times; - * This avoids if statements to decide on which rp value to update - * Also we process the data by longwords. - * Note: passing unaligned data might give a performance penalty. - * It is assumed that the buffers are aligned. - * tmppar is the cumulative sum of this iteration. - * needed for calculating rp12, rp14, rp16 and par - * also used as a performance improvement for rp6, rp8 and rp10 - */ - for (i = 0; i < eccsize_mult << 2; i++) { - cur = *bp++; - tmppar = cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= tmppar; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp8 ^= tmppar; - - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp10 ^= tmppar; - - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp6 ^= cur; - rp8 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= cur; - rp8 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp8 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp8 ^= cur; - - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp6 ^= cur; - cur = *bp++; - tmppar ^= cur; - rp4 ^= cur; - cur = *bp++; - tmppar ^= cur; - - par ^= tmppar; - if ((i & 0x1) == 0) - rp12 ^= tmppar; - if ((i & 0x2) == 0) - rp14 ^= tmppar; - if (eccsize_mult == 2 && (i & 0x4) == 0) - rp16 ^= tmppar; - } - - /* - * handle the fact that we use longword operations - * we'll bring rp4..rp14..rp16 back to single byte entities by - * shifting and xoring first fold the upper and lower 16 bits, - * then the upper and lower 8 bits. - */ - rp4 ^= (rp4 >> 16); - rp4 ^= (rp4 >> 8); - rp4 &= 0xff; - rp6 ^= (rp6 >> 16); - rp6 ^= (rp6 >> 8); - rp6 &= 0xff; - rp8 ^= (rp8 >> 16); - rp8 ^= (rp8 >> 8); - rp8 &= 0xff; - rp10 ^= (rp10 >> 16); - rp10 ^= (rp10 >> 8); - rp10 &= 0xff; - rp12 ^= (rp12 >> 16); - rp12 ^= (rp12 >> 8); - rp12 &= 0xff; - rp14 ^= (rp14 >> 16); - rp14 ^= (rp14 >> 8); - rp14 &= 0xff; - if (eccsize_mult == 2) { - rp16 ^= (rp16 >> 16); - rp16 ^= (rp16 >> 8); - rp16 &= 0xff; - } - - /* - * we also need to calculate the row parity for rp0..rp3 - * This is present in par, because par is now - * rp3 rp3 rp2 rp2 in little endian and - * rp2 rp2 rp3 rp3 in big endian - * as well as - * rp1 rp0 rp1 rp0 in little endian and - * rp0 rp1 rp0 rp1 in big endian - * First calculate rp2 and rp3 - */ -#ifdef __BIG_ENDIAN - rp2 = (par >> 16); - rp2 ^= (rp2 >> 8); - rp2 &= 0xff; - rp3 = par & 0xffff; - rp3 ^= (rp3 >> 8); - rp3 &= 0xff; -#else - rp3 = (par >> 16); - rp3 ^= (rp3 >> 8); - rp3 &= 0xff; - rp2 = par & 0xffff; - rp2 ^= (rp2 >> 8); - rp2 &= 0xff; -#endif - - /* reduce par to 16 bits then calculate rp1 and rp0 */ - par ^= (par >> 16); -#ifdef __BIG_ENDIAN - rp0 = (par >> 8) & 0xff; - rp1 = (par & 0xff); -#else - rp1 = (par >> 8) & 0xff; - rp0 = (par & 0xff); -#endif - - /* finally reduce par to 8 bits */ - par ^= (par >> 8); - par &= 0xff; - - /* - * and calculate rp5..rp15..rp17 - * note that par = rp4 ^ rp5 and due to the commutative property - * of the ^ operator we can say: - * rp5 = (par ^ rp4); - * The & 0xff seems superfluous, but benchmarking learned that - * leaving it out gives slightly worse results. No idea why, probably - * it has to do with the way the pipeline in pentium is organized. - */ - rp5 = (par ^ rp4) & 0xff; - rp7 = (par ^ rp6) & 0xff; - rp9 = (par ^ rp8) & 0xff; - rp11 = (par ^ rp10) & 0xff; - rp13 = (par ^ rp12) & 0xff; - rp15 = (par ^ rp14) & 0xff; - if (eccsize_mult == 2) - rp17 = (par ^ rp16) & 0xff; - - /* - * Finally calculate the ECC bits. - * Again here it might seem that there are performance optimisations - * possible, but benchmarks showed that on the system this is developed - * the code below is the fastest - */ - if (sm_order) { - code[0] = (invparity[rp7] << 7) | (invparity[rp6] << 6) | - (invparity[rp5] << 5) | (invparity[rp4] << 4) | - (invparity[rp3] << 3) | (invparity[rp2] << 2) | - (invparity[rp1] << 1) | (invparity[rp0]); - code[1] = (invparity[rp15] << 7) | (invparity[rp14] << 6) | - (invparity[rp13] << 5) | (invparity[rp12] << 4) | - (invparity[rp11] << 3) | (invparity[rp10] << 2) | - (invparity[rp9] << 1) | (invparity[rp8]); - } else { - code[1] = (invparity[rp7] << 7) | (invparity[rp6] << 6) | - (invparity[rp5] << 5) | (invparity[rp4] << 4) | - (invparity[rp3] << 3) | (invparity[rp2] << 2) | - (invparity[rp1] << 1) | (invparity[rp0]); - code[0] = (invparity[rp15] << 7) | (invparity[rp14] << 6) | - (invparity[rp13] << 5) | (invparity[rp12] << 4) | - (invparity[rp11] << 3) | (invparity[rp10] << 2) | - (invparity[rp9] << 1) | (invparity[rp8]); - } - - if (eccsize_mult == 1) - code[2] = - (invparity[par & 0xf0] << 7) | - (invparity[par & 0x0f] << 6) | - (invparity[par & 0xcc] << 5) | - (invparity[par & 0x33] << 4) | - (invparity[par & 0xaa] << 3) | - (invparity[par & 0x55] << 2) | - 3; - else - code[2] = - (invparity[par & 0xf0] << 7) | - (invparity[par & 0x0f] << 6) | - (invparity[par & 0xcc] << 5) | - (invparity[par & 0x33] << 4) | - (invparity[par & 0xaa] << 3) | - (invparity[par & 0x55] << 2) | - (invparity[rp17] << 1) | - (invparity[rp16] << 0); -} -EXPORT_SYMBOL(__nand_calculate_ecc); - -/** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte - * block - * @chip: NAND chip object - * @buf: input buffer with raw data - * @code: output buffer with ECC - */ -int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, - unsigned char *code) -{ - bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; - - __nand_calculate_ecc(buf, chip->ecc.size, code, sm_order); - - return 0; -} -EXPORT_SYMBOL(nand_calculate_ecc); - -/** - * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * @eccsize: data bytes per ECC step (256 or 512) - * @sm_order: Smart Media byte order - * - * Detect and correct a 1 bit error for eccsize byte block - */ -int __nand_correct_data(unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc, - unsigned int eccsize, bool sm_order) -{ - unsigned char b0, b1, b2, bit_addr; - unsigned int byte_addr; - /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = eccsize >> 8; - - /* - * b0 to b2 indicate which bit is faulty (if any) - * we might need the xor result more than once, - * so keep them in a local var - */ - if (sm_order) { - b0 = read_ecc[0] ^ calc_ecc[0]; - b1 = read_ecc[1] ^ calc_ecc[1]; - } else { - b0 = read_ecc[1] ^ calc_ecc[1]; - b1 = read_ecc[0] ^ calc_ecc[0]; - } - - b2 = read_ecc[2] ^ calc_ecc[2]; - - /* check if there are any bitfaults */ - - /* repeated if statements are slightly more efficient than switch ... */ - /* ordered in order of likelihood */ - - if ((b0 | b1 | b2) == 0) - return 0; /* no error */ - - if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && - (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && - ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) || - (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) { - /* single bit error */ - /* - * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty - * byte, cp 5/3/1 indicate the faulty bit. - * A lookup table (called addressbits) is used to filter - * the bits from the byte they are in. - * A marginal optimisation is possible by having three - * different lookup tables. - * One as we have now (for b0), one for b2 - * (that would avoid the >> 1), and one for b1 (with all values - * << 4). However it was felt that introducing two more tables - * hardly justify the gain. - * - * The b2 shift is there to get rid of the lowest two bits. - * We could also do addressbits[b2] >> 1 but for the - * performance it does not make any difference - */ - if (eccsize_mult == 1) - byte_addr = (addressbits[b1] << 4) + addressbits[b0]; - else - byte_addr = (addressbits[b2 & 0x3] << 8) + - (addressbits[b1] << 4) + addressbits[b0]; - bit_addr = addressbits[b2 >> 2]; - /* flip the bit */ - buf[byte_addr] ^= (1 << bit_addr); - return 1; - - } - /* count nr of bits; use table lookup, faster than calculating it */ - if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) - return 1; /* error in ECC data; no action needed */ - - pr_err("%s: uncorrectable ECC error\n", __func__); - return -EBADMSG; -} -EXPORT_SYMBOL(__nand_correct_data); - -/** - * nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @chip: NAND chip object - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * - * Detect and correct a 1 bit error for 256/512 byte block - */ -int nand_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; - - return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size, - sm_order); -} -EXPORT_SYMBOL(nand_correct_data); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Frans Meulenbroeks "); -MODULE_DESCRIPTION("Generic NAND ECC support"); diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index 0fb4ba93c41e..8cda6633280b 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -18,7 +18,7 @@ */ #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c index 4dfff34800f4..2dddbdca9e94 100644 --- a/drivers/mtd/nand/raw/pasemi_nand.c +++ b/drivers/mtd/nand/raw/pasemi_nand.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index fbd0fa48e063..f7eb3b5fa792 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c index af98bcc9d689..2fae6ade8b13 100644 --- a/drivers/mtd/nand/raw/sharpsl.c +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index aa6c7e7bbf1b..fadca0b9e21f 100644 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index fe8ed2441588..68b920eab6c8 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index b9f272408c4d..db430ae6a1a2 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "nand/raw/sm_common.h" #include "sm_ftl.h" diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c index 13bca9ea0cae..e92e3fb287b6 100644 --- a/drivers/mtd/tests/mtd_nandecctest.c +++ b/drivers/mtd/tests/mtd_nandecctest.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include "mtd_test.h" diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h new file mode 100644 index 000000000000..30a0cfa50eed --- /dev/null +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2000-2010 Steven J. Hill + * David Woodhouse + * Thomas Gleixner + * + * This file is the header for the ECC algorithm. + */ + +#ifndef __MTD_NAND_ECC_SW_HAMMING_H__ +#define __MTD_NAND_ECC_SW_HAMMING_H__ + +struct nand_chip; + +/* + * Calculate 3 byte ECC code for eccsize byte block + */ +void __nand_calculate_ecc(const u_char *dat, unsigned int eccsize, + u_char *ecc_code, bool sm_order); + +/* + * Calculate 3 byte ECC code for 256/512 byte block + */ +int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat, + u_char *ecc_code); + +/* + * Detect and correct a 1 bit error for eccsize byte block + */ +int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc, + unsigned int eccsize, bool sm_order); + +/* + * Detect and correct a 1 bit error for 256/512 byte block + */ +int nand_correct_data(struct nand_chip *chip, u_char *dat, u_char *read_ecc, + u_char *calc_ecc); + +#endif /* __MTD_NAND_ECC_SW_HAMMING_H__ */ diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h deleted file mode 100644 index d423916b94f0..000000000000 --- a/include/linux/mtd/nand_ecc.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2000-2010 Steven J. Hill - * David Woodhouse - * Thomas Gleixner - * - * This file is the header for the ECC algorithm. - */ - -#ifndef __MTD_NAND_ECC_H__ -#define __MTD_NAND_ECC_H__ - -struct nand_chip; - -/* - * Calculate 3 byte ECC code for eccsize byte block - */ -void __nand_calculate_ecc(const u_char *dat, unsigned int eccsize, - u_char *ecc_code, bool sm_order); - -/* - * Calculate 3 byte ECC code for 256/512 byte block - */ -int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat, - u_char *ecc_code); - -/* - * Detect and correct a 1 bit error for eccsize byte block - */ -int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc, - unsigned int eccsize, bool sm_order); - -/* - * Detect and correct a 1 bit error for 256/512 byte block - */ -int nand_correct_data(struct nand_chip *chip, u_char *dat, u_char *read_ecc, - u_char *calc_ecc); - -#endif /* __MTD_NAND_ECC_H__ */ diff --git a/include/linux/mtd/sharpsl.h b/include/linux/mtd/sharpsl.h index d2c3cf29e0d1..3762a90077d6 100644 --- a/include/linux/mtd/sharpsl.h +++ b/include/linux/mtd/sharpsl.h @@ -9,7 +9,7 @@ #define _MTD_SHARPSL_H #include -#include +#include #include struct sharpsl_nand_platform_data { -- cgit v1.2.3 From 2dbe0192efa02f2f405e193f4de84bf07c7f91fb Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:16 +0200 Subject: mtd: nand: ecc-hamming: Clarify the driver descriptions The include file pretends being the header for "ECC algorithm", while it is just the header for the Hamming implementation. Make this clear by rewording the sentence. Do the same with the module description. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-13-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 2 +- include/linux/mtd/nand-ecc-sw-hamming.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index 76966f5ff13f..a17faed374a8 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -481,4 +481,4 @@ EXPORT_SYMBOL(nand_correct_data); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Frans Meulenbroeks "); -MODULE_DESCRIPTION("Generic NAND ECC support"); +MODULE_DESCRIPTION("NAND software Hamming ECC support"); diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h index 30a0cfa50eed..85e9a929b5f9 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -4,7 +4,7 @@ * David Woodhouse * Thomas Gleixner * - * This file is the header for the ECC algorithm. + * This file is the header for the NAND Hamming ECC implementation. */ #ifndef __MTD_NAND_ECC_SW_HAMMING_H__ -- cgit v1.2.3 From c50e7f3c86730c7de00209542899795199a4066c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:17 +0200 Subject: mtd: nand: ecc-hamming: Drop/fix the kernel doc Some functions should never have been exported (the ones prefixed by __*), in this case simply drop the documentation, we never want anybody to use this function from the outside. For the other functions, enhance the style. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-14-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index a17faed374a8..2f6f98557e82 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -112,14 +112,6 @@ static const char addressbits[256] = { 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f }; -/** - * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte - * block - * @buf: input buffer with raw data - * @eccsize: data bytes per ECC step (256 or 512) - * @code: output buffer with ECC - * @sm_order: Smart Media byte ordering - */ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, unsigned char *code, bool sm_order) { @@ -360,11 +352,10 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, EXPORT_SYMBOL(__nand_calculate_ecc); /** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte - * block - * @chip: NAND chip object - * @buf: input buffer with raw data - * @code: output buffer with ECC + * nand_calculate_ecc - Calculate 3-byte ECC for 256/512-byte block + * @chip: NAND chip object + * @buf: Input buffer with raw data + * @code: Output buffer with ECC */ int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, unsigned char *code) @@ -377,16 +368,6 @@ int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, } EXPORT_SYMBOL(nand_calculate_ecc); -/** - * __nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * @eccsize: data bytes per ECC step (256 or 512) - * @sm_order: Smart Media byte order - * - * Detect and correct a 1 bit error for eccsize byte block - */ int __nand_correct_data(unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc, unsigned int eccsize, bool sm_order) @@ -461,13 +442,13 @@ int __nand_correct_data(unsigned char *buf, EXPORT_SYMBOL(__nand_correct_data); /** - * nand_correct_data - [NAND Interface] Detect and correct bit error(s) - * @chip: NAND chip object - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data + * nand_correct_data - Detect and correct bit error(s) + * @chip: NAND chip object + * @buf: Raw data read from the chip + * @read_ecc: ECC bytes read from the chip + * @calc_ecc: ECC calculated from the raw data * - * Detect and correct a 1 bit error for 256/512 byte block + * Detect and correct up to 1 bit error per 256/512-byte block. */ int nand_correct_data(struct nand_chip *chip, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) -- cgit v1.2.3 From b551fa3059ffc64d92d6d862c0045c1fd2dc2f31 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:18 +0200 Subject: mtd: nand: ecc-hamming: Cleanup and style fixes Various style fixes. There is not functional change. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-15-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index 2f6f98557e82..771e27ed8883 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -115,19 +115,18 @@ static const char addressbits[256] = { void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, unsigned char *code, bool sm_order) { + const u32 *bp = (uint32_t *)buf; + const u32 eccsize_mult = eccsize >> 8; + /* current value in buffer */ + u32 cur; + /* rp0..rp17 are the various accumulated parities (per byte) */ + u32 rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9, rp10, rp11, rp12, + rp13, rp14, rp15, rp16, rp17; + /* Cumulative parity for all data */ + u32 par; + /* Cumulative parity at the end of the loop (rp12, rp14, rp16) */ + u32 tmppar; int i; - const uint32_t *bp = (uint32_t *)buf; - /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = eccsize >> 8; - uint32_t cur; /* current value in buffer */ - /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ - uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; - uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16; - uint32_t rp17; - uint32_t par; /* the cumulative parity for all data */ - uint32_t tmppar; /* the cumulative parity for this iteration; - for rp12, rp14 and rp16 at the end of the - loop */ par = 0; rp4 = 0; @@ -372,10 +371,9 @@ int __nand_correct_data(unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc, unsigned int eccsize, bool sm_order) { + const u32 eccsize_mult = eccsize >> 8; unsigned char b0, b1, b2, bit_addr; unsigned int byte_addr; - /* 256 or 512 bytes/ecc */ - const uint32_t eccsize_mult = eccsize >> 8; /* * b0 to b2 indicate which bit is faulty (if any) -- cgit v1.2.3 From 90ccf0a0192f7fa06e52de80cb528c5217e3e297 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:19 +0200 Subject: mtd: nand: ecc-hamming: Rename the exported functions Prefix by ecc_sw_hamming_ the functions which should be internal only but are exported for "raw" operations. Prefix by nand_ecc_sw_hamming_ the other functions which will be used in the context of the declaration of an Hamming proper ECC engine object. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-16-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 53 +++++++++++++++++---------------- drivers/mtd/nand/raw/cs553x_nand.c | 3 +- drivers/mtd/nand/raw/fsmc_nand.c | 2 +- drivers/mtd/nand/raw/lpc32xx_slc.c | 2 +- drivers/mtd/nand/raw/nand_base.c | 25 ++++++++++++++-- drivers/mtd/nand/raw/ndfc.c | 3 +- drivers/mtd/nand/raw/sharpsl.c | 2 +- drivers/mtd/nand/raw/tmio_nand.c | 6 ++-- drivers/mtd/nand/raw/txx9ndfmc.c | 4 +-- drivers/mtd/sm_ftl.c | 28 ++++++++--------- drivers/mtd/tests/mtd_nandecctest.c | 29 +++++++++--------- include/linux/mtd/nand-ecc-sw-hamming.h | 36 ++++++++-------------- include/linux/mtd/rawnand.h | 7 +++++ 13 files changed, 108 insertions(+), 92 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index 771e27ed8883..7ee0387ecb29 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -75,7 +75,7 @@ static const char bitsperbyte[256] = { * addressbits is a lookup table to filter out the bits from the xor-ed * ECC data that identify the faulty location. * this is only used for repairing parity - * see the comments in nand_correct_data for more details + * see the comments in nand_ecc_sw_hamming_correct for more details */ static const char addressbits[256] = { 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, @@ -112,11 +112,11 @@ static const char addressbits[256] = { 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f }; -void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, - unsigned char *code, bool sm_order) +int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, + unsigned char *code, bool sm_order) { const u32 *bp = (uint32_t *)buf; - const u32 eccsize_mult = eccsize >> 8; + const u32 eccsize_mult = step_size >> 8; /* current value in buffer */ u32 cur; /* rp0..rp17 are the various accumulated parities (per byte) */ @@ -347,31 +347,32 @@ void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize, (invparity[par & 0x55] << 2) | (invparity[rp17] << 1) | (invparity[rp16] << 0); + + return 0; } -EXPORT_SYMBOL(__nand_calculate_ecc); +EXPORT_SYMBOL(ecc_sw_hamming_calculate); /** - * nand_calculate_ecc - Calculate 3-byte ECC for 256/512-byte block - * @chip: NAND chip object + * nand_ecc_sw_hamming_calculate - Calculate 3-byte ECC for 256/512-byte block + * @nand: NAND device * @buf: Input buffer with raw data * @code: Output buffer with ECC */ -int nand_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, - unsigned char *code) +int nand_ecc_sw_hamming_calculate(struct nand_device *nand, + const unsigned char *buf, unsigned char *code) { + struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; - __nand_calculate_ecc(buf, chip->ecc.size, code, sm_order); - - return 0; + return ecc_sw_hamming_calculate(buf, chip->ecc.size, code, sm_order); } -EXPORT_SYMBOL(nand_calculate_ecc); +EXPORT_SYMBOL(nand_ecc_sw_hamming_calculate); -int __nand_correct_data(unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc, - unsigned int eccsize, bool sm_order) +int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc, + unsigned char *calc_ecc, unsigned int step_size, + bool sm_order) { - const u32 eccsize_mult = eccsize >> 8; + const u32 eccsize_mult = step_size >> 8; unsigned char b0, b1, b2, bit_addr; unsigned int byte_addr; @@ -437,26 +438,28 @@ int __nand_correct_data(unsigned char *buf, pr_err("%s: uncorrectable ECC error\n", __func__); return -EBADMSG; } -EXPORT_SYMBOL(__nand_correct_data); +EXPORT_SYMBOL(ecc_sw_hamming_correct); /** - * nand_correct_data - Detect and correct bit error(s) - * @chip: NAND chip object + * nand_ecc_sw_hamming_correct - Detect and correct bit error(s) + * @nand: NAND device * @buf: Raw data read from the chip * @read_ecc: ECC bytes read from the chip * @calc_ecc: ECC calculated from the raw data * * Detect and correct up to 1 bit error per 256/512-byte block. */ -int nand_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) +int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc) { + struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; - return __nand_correct_data(buf, read_ecc, calc_ecc, chip->ecc.size, - sm_order); + return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, chip->ecc.size, + sm_order); } -EXPORT_SYMBOL(nand_correct_data); +EXPORT_SYMBOL(nand_ecc_sw_hamming_correct); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Frans Meulenbroeks "); diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index 9a2bdb45a771..6edf78c16fc8 100644 --- a/drivers/mtd/nand/raw/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -252,7 +251,7 @@ static int cs553x_attach_chip(struct nand_chip *chip) chip->ecc.bytes = 3; chip->ecc.hwctl = cs_enable_hwecc; chip->ecc.calculate = cs_calculate_ecc; - chip->ecc.correct = nand_correct_data; + chip->ecc.correct = rawnand_sw_hamming_correct; chip->ecc.strength = 1; return 0; diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index e880db5852d8..1e2ed45be2f1 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -918,7 +918,7 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) case NAND_ECC_ENGINE_TYPE_ON_HOST: dev_info(host->dev, "Using 1-bit HW ECC scheme\n"); nand->ecc.calculate = fsmc_read_hwecc_ecc1; - nand->ecc.correct = nand_correct_data; + nand->ecc.correct = rawnand_sw_hamming_correct; nand->ecc.hwctl = fsmc_enable_hwecc; nand->ecc.bytes = 3; nand->ecc.strength = 1; diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 0bf9c3fbcd82..beecabfa5181 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -803,7 +803,7 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; chip->ecc.calculate = lpc32xx_nand_ecc_calculate; - chip->ecc.correct = nand_correct_data; + chip->ecc.correct = rawnand_sw_hamming_correct; chip->ecc.hwctl = lpc32xx_nand_ecc_enable; /* diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index bee7c64a0a4e..32e9870a2cf8 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5139,6 +5139,27 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) kfree(chip->parameters.onfi); } +int rawnand_sw_hamming_calculate(struct nand_chip *chip, + const unsigned char *buf, + unsigned char *code) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_hamming_calculate(base, buf, code); +} +EXPORT_SYMBOL(rawnand_sw_hamming_calculate); + +int rawnand_sw_hamming_correct(struct nand_chip *chip, + unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_hamming_correct(base, buf, read_ecc, calc_ecc); +} +EXPORT_SYMBOL(rawnand_sw_hamming_correct); + int rawnand_sw_bch_init(struct nand_chip *chip) { struct nand_device *base = &chip->base; @@ -5263,8 +5284,8 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) switch (ecc->algo) { case NAND_ECC_ALGO_HAMMING: - ecc->calculate = nand_calculate_ecc; - ecc->correct = nand_correct_data; + ecc->calculate = rawnand_sw_hamming_calculate; + ecc->correct = rawnand_sw_hamming_correct; ecc->read_page = nand_read_page_swecc; ecc->read_subpage = nand_read_subpage; ecc->write_page = nand_write_page_swecc; diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index 8cda6633280b..338d6b1a189e 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -18,7 +18,6 @@ */ #include #include -#include #include #include #include @@ -146,7 +145,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->controller = &ndfc->ndfc_control; chip->legacy.read_buf = ndfc_read_buf; chip->legacy.write_buf = ndfc_write_buf; - chip->ecc.correct = nand_correct_data; + chip->ecc.correct = rawnand_sw_hamming_correct; chip->ecc.hwctl = ndfc_enable_hwecc; chip->ecc.calculate = ndfc_calculate_ecc; chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c index 2fae6ade8b13..3f55b523aead 100644 --- a/drivers/mtd/nand/raw/sharpsl.c +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -107,7 +107,7 @@ static int sharpsl_attach_chip(struct nand_chip *chip) chip->ecc.strength = 1; chip->ecc.hwctl = sharpsl_nand_enable_hwecc; chip->ecc.calculate = sharpsl_nand_calculate_ecc; - chip->ecc.correct = nand_correct_data; + chip->ecc.correct = rawnand_sw_hamming_correct; return 0; } diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index fadca0b9e21f..29edacbec826 100644 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -293,11 +293,11 @@ static int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf, int r0, r1; /* assume ecc.size = 512 and ecc.bytes = 6 */ - r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256, false); + r0 = rawnand_sw_hamming_correct(chip, buf, read_ecc, calc_ecc); if (r0 < 0) return r0; - r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256, - false); + r1 = rawnand_sw_hamming_correct(chip, buf + 256, read_ecc + 3, + calc_ecc + 3); if (r1 < 0) return r1; return r0 + r1; diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 68b920eab6c8..a705523eb916 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -194,8 +194,8 @@ static int txx9ndfmc_correct_data(struct nand_chip *chip, unsigned char *buf, int stat; for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) { - stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256, - false); + stat = rawnand_sw_hamming_correct(chip, buf, read_ecc, + calc_ecc); if (stat < 0) return stat; corrected += stat; diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index db430ae6a1a2..4d1ae25507ab 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -216,20 +216,19 @@ static void sm_break_offset(struct sm_ftl *ftl, loff_t loffset, static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) { + bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); uint8_t ecc[3]; - __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); - if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0) + ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order); + if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc1, SM_SMALL_PAGE, + sm_order) < 0) return -EIO; buffer += SM_SMALL_PAGE; - __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); - if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) < 0) + ecc_sw_hamming_calculate(buffer, SM_SMALL_PAGE, ecc, sm_order); + if (ecc_sw_hamming_correct(buffer, ecc, oob->ecc2, SM_SMALL_PAGE, + sm_order) < 0) return -EIO; return 0; } @@ -369,6 +368,7 @@ static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf, int zone, int block, int lba, unsigned long invalid_bitmap) { + bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); struct sm_oob oob; int boffset; int retry = 0; @@ -395,13 +395,13 @@ restart: } if (ftl->smallpagenand) { - __nand_calculate_ecc(buf + boffset, SM_SMALL_PAGE, - oob.ecc1, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); + ecc_sw_hamming_calculate(buf + boffset, + SM_SMALL_PAGE, oob.ecc1, + sm_order); - __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE, - SM_SMALL_PAGE, oob.ecc2, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); + ecc_sw_hamming_calculate(buf + boffset + SM_SMALL_PAGE, + SM_SMALL_PAGE, oob.ecc2, + sm_order); } if (!sm_write_sector(ftl, zone, block, boffset, buf + boffset, &oob)) diff --git a/drivers/mtd/tests/mtd_nandecctest.c b/drivers/mtd/tests/mtd_nandecctest.c index e92e3fb287b6..c4f271314f52 100644 --- a/drivers/mtd/tests/mtd_nandecctest.c +++ b/drivers/mtd/tests/mtd_nandecctest.c @@ -119,13 +119,13 @@ static void no_bit_error(void *error_data, void *error_ecc, static int no_bit_error_verify(void *error_data, void *error_ecc, void *correct_data, const size_t size) { + bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); unsigned char calc_ecc[3]; int ret; - __nand_calculate_ecc(error_data, size, calc_ecc, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); - ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); + ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order); + ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size, + sm_order); if (ret == 0 && !memcmp(correct_data, error_data, size)) return 0; @@ -149,13 +149,13 @@ static void single_bit_error_in_ecc(void *error_data, void *error_ecc, static int single_bit_error_correct(void *error_data, void *error_ecc, void *correct_data, const size_t size) { + bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); unsigned char calc_ecc[3]; int ret; - __nand_calculate_ecc(error_data, size, calc_ecc, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); - ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); + ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order); + ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size, + sm_order); if (ret == 1 && !memcmp(correct_data, error_data, size)) return 0; @@ -186,13 +186,13 @@ static void double_bit_error_in_ecc(void *error_data, void *error_ecc, static int double_bit_error_detect(void *error_data, void *error_ecc, void *correct_data, const size_t size) { + bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); unsigned char calc_ecc[3]; int ret; - __nand_calculate_ecc(error_data, size, calc_ecc, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); - ret = __nand_correct_data(error_data, error_ecc, calc_ecc, size, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); + ecc_sw_hamming_calculate(error_data, size, calc_ecc, sm_order); + ret = ecc_sw_hamming_correct(error_data, error_ecc, calc_ecc, size, + sm_order); return (ret == -EBADMSG) ? 0 : -EINVAL; } @@ -248,6 +248,7 @@ static void dump_data_ecc(void *error_data, void *error_ecc, void *correct_data, static int nand_ecc_test_run(const size_t size) { + bool sm_order = IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC); int i; int err = 0; void *error_data; @@ -266,9 +267,7 @@ static int nand_ecc_test_run(const size_t size) } prandom_bytes(correct_data, size); - __nand_calculate_ecc(correct_data, size, correct_ecc, - IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)); - + ecc_sw_hamming_calculate(correct_data, size, correct_ecc, sm_order); for (i = 0; i < ARRAY_SIZE(nand_ecc_test); i++) { nand_ecc_test[i].prepare(error_data, error_ecc, correct_data, correct_ecc, size); diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h index 85e9a929b5f9..84a123bf45f3 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -10,30 +10,18 @@ #ifndef __MTD_NAND_ECC_SW_HAMMING_H__ #define __MTD_NAND_ECC_SW_HAMMING_H__ -struct nand_chip; +#include -/* - * Calculate 3 byte ECC code for eccsize byte block - */ -void __nand_calculate_ecc(const u_char *dat, unsigned int eccsize, - u_char *ecc_code, bool sm_order); - -/* - * Calculate 3 byte ECC code for 256/512 byte block - */ -int nand_calculate_ecc(struct nand_chip *chip, const u_char *dat, - u_char *ecc_code); - -/* - * Detect and correct a 1 bit error for eccsize byte block - */ -int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc, - unsigned int eccsize, bool sm_order); - -/* - * Detect and correct a 1 bit error for 256/512 byte block - */ -int nand_correct_data(struct nand_chip *chip, u_char *dat, u_char *read_ecc, - u_char *calc_ecc); +int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, + unsigned char *code, bool sm_order); +int nand_ecc_sw_hamming_calculate(struct nand_device *nand, + const unsigned char *buf, + unsigned char *code); +int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc, + unsigned char *calc_ecc, unsigned int step_size, + bool sm_order); +int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc); #endif /* __MTD_NAND_ECC_SW_HAMMING_H__ */ diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 15743c2c2cab..685d24557e95 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1301,6 +1301,13 @@ static inline int nand_opcode_8bits(unsigned int command) return 0; } +int rawnand_sw_hamming_calculate(struct nand_chip *chip, + const unsigned char *buf, + unsigned char *code); +int rawnand_sw_hamming_correct(struct nand_chip *chip, + unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc); int rawnand_sw_bch_init(struct nand_chip *chip); int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc); -- cgit v1.2.3 From 19b2ce184b9f404d6620adf667a9019e6abcae51 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:20 +0200 Subject: mtd: nand: ecc-hamming: Stop using raw NAND structures This code is meant to be reused by the SPI-NAND core. Now that the driver has been cleaned and reorganized, use a generic ECC engine object to store the driver's data instead of accessing members of the nand_chip structure. This means adding proper init/cleanup helpers. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-17-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 17 +++++---- drivers/mtd/nand/raw/nand_base.c | 63 +++++++++++++++++++++++++++++++-- include/linux/mtd/nand-ecc-sw-hamming.h | 20 +++++++++++ include/linux/mtd/rawnand.h | 2 ++ 4 files changed, 90 insertions(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index 7ee0387ecb29..9cd70b4637c1 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include #include @@ -361,10 +359,11 @@ EXPORT_SYMBOL(ecc_sw_hamming_calculate); int nand_ecc_sw_hamming_calculate(struct nand_device *nand, const unsigned char *buf, unsigned char *code) { - struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); - bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int step_size = nand->ecc.ctx.conf.step_size; - return ecc_sw_hamming_calculate(buf, chip->ecc.size, code, sm_order); + return ecc_sw_hamming_calculate(buf, step_size, code, + engine_conf->sm_order); } EXPORT_SYMBOL(nand_ecc_sw_hamming_calculate); @@ -453,11 +452,11 @@ int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { - struct nand_chip *chip = mtd_to_nand(nanddev_to_mtd(nand)); - bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int step_size = nand->ecc.ctx.conf.step_size; - return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, chip->ecc.size, - sm_order); + return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, step_size, + engine_conf->sm_order); } EXPORT_SYMBOL(nand_ecc_sw_hamming_correct); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 32e9870a2cf8..c36d2d4a72d5 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5139,6 +5139,46 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) kfree(chip->parameters.onfi); } +int rawnand_sw_hamming_init(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_sw_hamming_conf *engine_conf; + struct nand_device *base = &chip->base; + + base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + base->ecc.user_conf.algo = NAND_ECC_ALGO_HAMMING; + base->ecc.user_conf.strength = chip->ecc.strength; + base->ecc.user_conf.step_size = chip->ecc.size; + + if (base->ecc.user_conf.strength != 1 || + (base->ecc.user_conf.step_size != 256 && + base->ecc.user_conf.step_size != 512)) { + pr_err("%s: unsupported strength or step size\n", __func__); + return -EINVAL; + } + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + engine_conf->code_size = 3; + engine_conf->nsteps = mtd->writesize / base->ecc.user_conf.step_size; + + if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER) + engine_conf->sm_order = true; + + base->ecc.ctx.priv = engine_conf; + + chip->ecc.size = base->ecc.ctx.conf.step_size; + chip->ecc.strength = base->ecc.ctx.conf.strength; + chip->ecc.total = base->ecc.ctx.total; + chip->ecc.steps = engine_conf->nsteps; + chip->ecc.bytes = engine_conf->code_size; + + return 0; +} +EXPORT_SYMBOL(rawnand_sw_hamming_init); + int rawnand_sw_hamming_calculate(struct nand_chip *chip, const unsigned char *buf, unsigned char *code) @@ -5160,6 +5200,14 @@ int rawnand_sw_hamming_correct(struct nand_chip *chip, } EXPORT_SYMBOL(rawnand_sw_hamming_correct); +void rawnand_sw_hamming_cleanup(struct nand_chip *chip) +{ + struct nand_device *base = &chip->base; + + kfree(base->ecc.ctx.priv); +} +EXPORT_SYMBOL(rawnand_sw_hamming_cleanup); + int rawnand_sw_bch_init(struct nand_chip *chip) { struct nand_device *base = &chip->base; @@ -5303,6 +5351,12 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER; + ret = rawnand_sw_hamming_init(chip); + if (ret) { + WARN(1, "Hamming ECC initialization failed!\n"); + return ret; + } + return 0; case NAND_ECC_ALGO_BCH: if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) { @@ -5996,9 +6050,12 @@ EXPORT_SYMBOL(nand_scan_with_ids); */ void nand_cleanup(struct nand_chip *chip) { - if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && - chip->ecc.algo == NAND_ECC_ALGO_BCH) - rawnand_sw_bch_cleanup(chip); + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT) { + if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING) + rawnand_sw_hamming_cleanup(chip); + else if (chip->ecc.algo == NAND_ECC_ALGO_BCH) + rawnand_sw_bch_cleanup(chip); + } nanddev_cleanup(&chip->base); diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h index 84a123bf45f3..9d4b4d623741 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -12,6 +12,26 @@ #include +/** + * struct nand_ecc_sw_hamming_conf - private software Hamming ECC engine structure + * @reqooblen: Save the actual user OOB length requested before overwriting it + * @spare_oobbuf: Spare OOB buffer if none is provided + * @code_size: Number of bytes needed to store a code (one code per step) + * @nsteps: Number of steps + * @calc_buf: Buffer to use when calculating ECC bytes + * @code_buf: Buffer to use when reading (raw) ECC bytes from the chip + * @sm_order: Smart Media special ordering + */ +struct nand_ecc_sw_hamming_conf { + unsigned int reqooblen; + void *spare_oobbuf; + unsigned int code_size; + unsigned int nsteps; + u8 *calc_buf; + u8 *code_buf; + unsigned int sm_order; +}; + int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, unsigned char *code, bool sm_order); int nand_ecc_sw_hamming_calculate(struct nand_device *nand, diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 685d24557e95..0a90a8792d18 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1301,6 +1301,7 @@ static inline int nand_opcode_8bits(unsigned int command) return 0; } +int rawnand_sw_hamming_init(struct nand_chip *chip); int rawnand_sw_hamming_calculate(struct nand_chip *chip, const unsigned char *buf, unsigned char *code); @@ -1308,6 +1309,7 @@ int rawnand_sw_hamming_correct(struct nand_chip *chip, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc); +void rawnand_sw_hamming_cleanup(struct nand_chip *chip); int rawnand_sw_bch_init(struct nand_chip *chip); int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc); -- cgit v1.2.3 From eb08376a5dd943cf2a7360f236fe20bbd709fa95 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:21 +0200 Subject: mtd: nand: ecc-hamming: Remove useless includes Most of the includes are simply useless, drop them. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-18-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 1 - drivers/mtd/nand/raw/fsl_ifc_nand.c | 1 - drivers/mtd/nand/raw/fsl_upm.c | 1 - drivers/mtd/nand/raw/fsmc_nand.c | 1 - drivers/mtd/nand/raw/lpc32xx_mlc.c | 1 - drivers/mtd/nand/raw/lpc32xx_slc.c | 1 - drivers/mtd/nand/raw/pasemi_nand.c | 1 - drivers/mtd/nand/raw/s3c2410.c | 1 - drivers/mtd/nand/raw/sharpsl.c | 1 - drivers/mtd/nand/raw/tmio_nand.c | 1 - drivers/mtd/nand/raw/txx9ndfmc.c | 1 - include/linux/mtd/sharpsl.h | 1 - 12 files changed, 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index eb255cf83e32..aab93b9e6052 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -22,7 +22,6 @@ #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index d2f84917fa8b..02d500176838 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c index 3824361928a1..b3cc427100a2 100644 --- a/drivers/mtd/nand/raw/fsl_upm.c +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 1e2ed45be2f1..0101c0fab50a 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 885b03b7af52..452ecaf7775a 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -31,7 +31,6 @@ #include #include #include -#include #define DRV_NAME "lpc32xx_mlc" diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index beecabfa5181..6b7269cfb7d8 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c index 2dddbdca9e94..5741887d7a24 100644 --- a/drivers/mtd/nand/raw/pasemi_nand.c +++ b/drivers/mtd/nand/raw/pasemi_nand.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index f7eb3b5fa792..cb2d1b4e278c 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -30,7 +30,6 @@ #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c index 3f55b523aead..5612ee628425 100644 --- a/drivers/mtd/nand/raw/sharpsl.c +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index 29edacbec826..de8e919d0ebe 100644 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index a705523eb916..1a9449e53bf9 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/include/linux/mtd/sharpsl.h b/include/linux/mtd/sharpsl.h index 3762a90077d6..231bd1c3f408 100644 --- a/include/linux/mtd/sharpsl.h +++ b/include/linux/mtd/sharpsl.h @@ -9,7 +9,6 @@ #define _MTD_SHARPSL_H #include -#include #include struct sharpsl_nand_platform_data { -- cgit v1.2.3 From 5180a62c12497aa491a7c79c062a9e3a884c9762 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:22 +0200 Subject: mtd: nand: ecc-hamming: Let the software Hamming ECC engine be unselected There is no reason to always embed the software Hamming ECC engine implementation. By default it is (with raw NAND), but we can let the user decide. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-19-miquel.raynal@bootlin.com --- drivers/mtd/nand/Kconfig | 11 +++++++++- drivers/mtd/nand/raw/Kconfig | 2 +- include/linux/mtd/nand-ecc-sw-hamming.h | 36 +++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 306c33ca3448..15ffe1a1b863 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -16,7 +16,16 @@ config MTD_NAND_ECC depends on MTD_NAND_CORE config MTD_NAND_ECC_SW_HAMMING - bool + bool "Software Hamming ECC engine" + default y if MTD_RAW_NAND + select MTD_NAND_ECC + help + This enables support for software Hamming error + correction. This correction can correct up to 1 bit error + per chunk and detect up to 2 bit errors. While it used to be + widely used with old parts, newer NAND chips usually require + more strength correction and in this case BCH or RS will be + preferred. config MTD_NAND_ECC_SW_HAMMING_SMC bool "NAND ECC Smart Media byte order" diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 6149096e32cb..cc86d6e4e042 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -3,7 +3,6 @@ menuconfig MTD_RAW_NAND tristate "Raw/Parallel NAND Device Support" select MTD_NAND_CORE select MTD_NAND_ECC - select MTD_NAND_ECC_SW_HAMMING help This enables support for accessing all type of raw/parallel NAND flash devices. For further information see @@ -72,6 +71,7 @@ config MTD_NAND_AU1550 config MTD_NAND_NDFC tristate "IBM/MCC 4xx NAND controller" depends on 4xx + select MTD_NAND_ECC_SW_HAMMING select MTD_NAND_ECC_SW_HAMMING_SMC help NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h index 9d4b4d623741..5a39e96c3546 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -32,6 +32,8 @@ struct nand_ecc_sw_hamming_conf { unsigned int sm_order; }; +#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING) + int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, unsigned char *code, bool sm_order); int nand_ecc_sw_hamming_calculate(struct nand_device *nand, @@ -44,4 +46,38 @@ int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc); +#else /* !CONFIG_MTD_NAND_ECC_SW_HAMMING */ + +static inline int ecc_sw_hamming_calculate(const unsigned char *buf, + unsigned int step_size, + unsigned char *code, bool sm_order) +{ + return -ENOTSUPP; +} + +static inline int nand_ecc_sw_hamming_calculate(struct nand_device *nand, + const unsigned char *buf, + unsigned char *code) +{ + return -ENOTSUPP; +} + +static inline int ecc_sw_hamming_correct(unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc, + unsigned int step_size, bool sm_order) +{ + return -ENOTSUPP; +} + +static inline int nand_ecc_sw_hamming_correct(struct nand_device *nand, + unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc) +{ + return -ENOTSUPP; +} + +#endif /* CONFIG_MTD_NAND_ECC_SW_HAMMING */ + #endif /* __MTD_NAND_ECC_SW_HAMMING_H__ */ -- cgit v1.2.3 From 35fe1b98a0082ad3f576bcc420c74dab435da307 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:23 +0200 Subject: mtd: nand: ecc-hamming: Create the software Hamming engine Let's continue introducing the generic ECC engine abstraction in the NAND subsystem by instantiating a second ECC engine: software Hamming. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-20-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 193 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/nand_base.c | 26 ++--- include/linux/mtd/nand-ecc-sw-hamming.h | 16 ++- include/linux/mtd/nand.h | 9 ++ 4 files changed, 223 insertions(+), 21 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index 9cd70b4637c1..c95aadf1eb43 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -17,7 +17,9 @@ #include #include #include +#include #include +#include #include /* @@ -460,6 +462,197 @@ int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, } EXPORT_SYMBOL(nand_ecc_sw_hamming_correct); +int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) +{ + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + struct nand_ecc_sw_hamming_conf *engine_conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int ret; + + if (!mtd->ooblayout) { + switch (mtd->oobsize) { + case 8: + case 16: + mtd_set_ooblayout(mtd, nand_get_small_page_ooblayout()); + break; + case 64: + case 128: + mtd_set_ooblayout(mtd, + nand_get_large_page_hamming_ooblayout()); + break; + default: + return -ENOTSUPP; + } + } + + conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + conf->algo = NAND_ECC_ALGO_HAMMING; + conf->step_size = nand->ecc.user_conf.step_size; + conf->strength = 1; + + /* Use the strongest configuration by default */ + if (conf->step_size != 256 && conf->step_size != 512) + conf->step_size = 256; + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand); + if (ret) + goto free_engine_conf; + + engine_conf->code_size = 3; + engine_conf->nsteps = mtd->writesize / conf->step_size; + engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!engine_conf->calc_buf || !engine_conf->code_buf) { + ret = -ENOMEM; + goto free_bufs; + } + + nand->ecc.ctx.priv = engine_conf; + nand->ecc.ctx.total = engine_conf->nsteps * engine_conf->code_size; + + return 0; + +free_bufs: + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); +free_engine_conf: + kfree(engine_conf); + + return ret; +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_init_ctx); + +void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + + if (engine_conf) { + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); + kfree(engine_conf); + } +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_cleanup_ctx); + +static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int eccbytes = engine_conf->code_size; + int eccsteps = engine_conf->nsteps; + int total = nand->ecc.ctx.total; + u8 *ecccalc = engine_conf->calc_buf; + const u8 *data; + int i; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + nand_ecc_tweak_req(&engine_conf->req_ctx, req); + + /* No more preparation for page read */ + if (req->type == NAND_PAGE_READ) + return 0; + + /* Preparation for page write: derive the ECC bytes and place them */ + for (i = 0, data = req->databuf.out; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]); + + return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out, + 0, total); +} + +static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int total = nand->ecc.ctx.total; + int eccbytes = engine_conf->code_size; + int eccsteps = engine_conf->nsteps; + u8 *ecccalc = engine_conf->calc_buf; + u8 *ecccode = engine_conf->code_buf; + unsigned int max_bitflips = 0; + u8 *data = req->databuf.in; + int i, ret; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + /* No more preparation for page write */ + if (req->type == NAND_PAGE_WRITE) { + nand_ecc_restore_req(&engine_conf->req_ctx, req); + return 0; + } + + /* Finish a page read: retrieve the (raw) ECC bytes*/ + ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0, + total); + if (ret) + return ret; + + /* Calculate the ECC bytes */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]); + + /* Finish a page read: compare and correct */ + for (eccsteps = engine_conf->nsteps, i = 0, data = req->databuf.in; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) { + int stat = nand_ecc_sw_hamming_correct(nand, data, + &ecccode[i], + &ecccalc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + nand_ecc_restore_req(&engine_conf->req_ctx, req); + + return max_bitflips; +} + +static struct nand_ecc_engine_ops nand_ecc_sw_hamming_engine_ops = { + .init_ctx = nand_ecc_sw_hamming_init_ctx, + .cleanup_ctx = nand_ecc_sw_hamming_cleanup_ctx, + .prepare_io_req = nand_ecc_sw_hamming_prepare_io_req, + .finish_io_req = nand_ecc_sw_hamming_finish_io_req, +}; + +static struct nand_ecc_engine nand_ecc_sw_hamming_engine = { + .ops = &nand_ecc_sw_hamming_engine_ops, +}; + +struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void) +{ + return &nand_ecc_sw_hamming_engine; +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_get_engine); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Frans Meulenbroeks "); MODULE_DESCRIPTION("NAND software Hamming ECC support"); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c36d2d4a72d5..c33fa1b1847f 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5141,34 +5141,24 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) int rawnand_sw_hamming_init(struct nand_chip *chip) { - struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_sw_hamming_conf *engine_conf; struct nand_device *base = &chip->base; + int ret; base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; base->ecc.user_conf.algo = NAND_ECC_ALGO_HAMMING; base->ecc.user_conf.strength = chip->ecc.strength; base->ecc.user_conf.step_size = chip->ecc.size; - if (base->ecc.user_conf.strength != 1 || - (base->ecc.user_conf.step_size != 256 && - base->ecc.user_conf.step_size != 512)) { - pr_err("%s: unsupported strength or step size\n", __func__); - return -EINVAL; - } - - engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); - if (!engine_conf) - return -ENOMEM; + ret = nand_ecc_sw_hamming_init_ctx(base); + if (ret) + return ret; - engine_conf->code_size = 3; - engine_conf->nsteps = mtd->writesize / base->ecc.user_conf.step_size; + engine_conf = base->ecc.ctx.priv; if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER) engine_conf->sm_order = true; - base->ecc.ctx.priv = engine_conf; - chip->ecc.size = base->ecc.ctx.conf.step_size; chip->ecc.strength = base->ecc.ctx.conf.strength; chip->ecc.total = base->ecc.ctx.total; @@ -5204,7 +5194,7 @@ void rawnand_sw_hamming_cleanup(struct nand_chip *chip) { struct nand_device *base = &chip->base; - kfree(base->ecc.ctx.priv); + nand_ecc_sw_hamming_cleanup_ctx(base); } EXPORT_SYMBOL(rawnand_sw_hamming_cleanup); @@ -5733,7 +5723,9 @@ static int nand_scan_tail(struct nand_chip *chip) */ if (!mtd->ooblayout && !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT && - ecc->algo == NAND_ECC_ALGO_BCH)) { + ecc->algo == NAND_ECC_ALGO_BCH) && + !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT && + ecc->algo == NAND_ECC_ALGO_HAMMING)) { switch (mtd->oobsize) { case 8: case 16: diff --git a/include/linux/mtd/nand-ecc-sw-hamming.h b/include/linux/mtd/nand-ecc-sw-hamming.h index 5a39e96c3546..9f9073d86ff3 100644 --- a/include/linux/mtd/nand-ecc-sw-hamming.h +++ b/include/linux/mtd/nand-ecc-sw-hamming.h @@ -14,8 +14,8 @@ /** * struct nand_ecc_sw_hamming_conf - private software Hamming ECC engine structure - * @reqooblen: Save the actual user OOB length requested before overwriting it - * @spare_oobbuf: Spare OOB buffer if none is provided + * @req_ctx: Save request context and tweak the original request to fit the + * engine needs * @code_size: Number of bytes needed to store a code (one code per step) * @nsteps: Number of steps * @calc_buf: Buffer to use when calculating ECC bytes @@ -23,8 +23,7 @@ * @sm_order: Smart Media special ordering */ struct nand_ecc_sw_hamming_conf { - unsigned int reqooblen; - void *spare_oobbuf; + struct nand_ecc_req_tweak_ctx req_ctx; unsigned int code_size; unsigned int nsteps; u8 *calc_buf; @@ -34,6 +33,8 @@ struct nand_ecc_sw_hamming_conf { #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING) +int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand); +void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand); int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, unsigned char *code, bool sm_order); int nand_ecc_sw_hamming_calculate(struct nand_device *nand, @@ -48,6 +49,13 @@ int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, #else /* !CONFIG_MTD_NAND_ECC_SW_HAMMING */ +static inline int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) +{ + return -ENOTSUPP; +} + +static inline void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand) {} + static inline int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, unsigned char *code, bool sm_order) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index df8548187713..3616fa27eaa1 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -278,6 +278,15 @@ int nand_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req); bool nand_ecc_is_strong_enough(struct nand_device *nand); +#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING) +struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void); +#else +static inline struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void) +{ + return NULL; +} +#endif /* CONFIG_MTD_NAND_ECC_SW_HAMMING */ + #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH) struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void); #else -- cgit v1.2.3 From 53fbdeeb57a0168a88547e22f8d433810c531169 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 01:01:24 +0200 Subject: mtd: nand: Let software ECC engines be retrieved from the NAND core Before making use of the ECC engines, we must retrieve them. Add the boilerplate for the ones already available: software engines (Hamming and BCH). Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200929230124.31491-21-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc.c | 20 ++++++++++++++++++++ include/linux/mtd/nand.h | 1 + 2 files changed, 21 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c index 541137736fd3..57bc8c5ef08c 100644 --- a/drivers/mtd/nand/ecc.c +++ b/drivers/mtd/nand/ecc.c @@ -585,6 +585,26 @@ void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx, } EXPORT_SYMBOL_GPL(nand_ecc_restore_req); +struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand) +{ + unsigned int algo = nand->ecc.user_conf.algo; + + if (algo == NAND_ECC_ALGO_UNKNOWN) + algo = nand->ecc.defaults.algo; + + switch (algo) { + case NAND_ECC_ALGO_HAMMING: + return nand_ecc_sw_hamming_get_engine(); + case NAND_ECC_ALGO_BCH: + return nand_ecc_sw_bch_get_engine(); + default: + break; + } + + return NULL; +} +EXPORT_SYMBOL(nand_ecc_get_sw_engine); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Miquel Raynal "); MODULE_DESCRIPTION("Generic ECC engine"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 3616fa27eaa1..f78f61c9a9ee 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -277,6 +277,7 @@ int nand_ecc_prepare_io_req(struct nand_device *nand, int nand_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req); bool nand_ecc_is_strong_enough(struct nand_device *nand); +struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand); #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING) struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void); -- cgit v1.2.3 From 93afb10e226ec13619a48096ef095c2b1fec3f32 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 17:41:05 +0200 Subject: mtd: spinand: Fix typo in comment One comment in the SPI-NAND core is not very clear, fix it to ease the understanding of what the block does. Signed-off-by: Miquel Raynal Reviewed-by: Boris Brezillon Link: https://lore.kernel.org/linux-mtd/20200930154109.3922-2-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index c35221794645..6285f45d8272 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1068,7 +1068,7 @@ static int spinand_init(struct spinand_device *spinand) /* * Right now, we don't support ECC, so let the whole oob - * area is available for user. + * area available for the user. */ mtd->_read_oob = spinand_mtd_read; mtd->_write_oob = spinand_mtd_write; -- cgit v1.2.3 From 55a1a71a7f5d9a85dbe9d2ab4d67208f49cba522 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 17:41:06 +0200 Subject: mtd: spinand: Move ECC related definitions earlier in the driver Prepare the creation of a SPI-NAND on-die ECC engine by gathering the ECC-related code earlier enough in the core to avoid the need for forward declarations. The next step is to actually create that engine by implementing the generic ECC interface. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200930154109.3922-3-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 106 ++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 53 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 6285f45d8272..b2830e19a7f6 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -193,6 +193,59 @@ static int spinand_ecc_enable(struct spinand_device *spinand, enable ? CFG_ECC_ENABLE : 0); } +static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status) +{ + struct nand_device *nand = spinand_to_nand(spinand); + + if (spinand->eccinfo.get_status) + return spinand->eccinfo.get_status(spinand, status); + + switch (status & STATUS_ECC_MASK) { + case STATUS_ECC_NO_BITFLIPS: + return 0; + + case STATUS_ECC_HAS_BITFLIPS: + /* + * We have no way to know exactly how many bitflips have been + * fixed, so let's return the maximum possible value so that + * wear-leveling layers move the data immediately. + */ + return nanddev_get_ecc_conf(nand)->strength; + + case STATUS_ECC_UNCOR_ERROR: + return -EBADMSG; + + default: + break; + } + + return -EINVAL; +} + +static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + return -ERANGE; +} + +static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section) + return -ERANGE; + + /* Reserve 2 bytes for the BBM. */ + region->offset = 2; + region->length = 62; + + return 0; +} + +static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = { + .ecc = spinand_noecc_ooblayout_ecc, + .free = spinand_noecc_ooblayout_free, +}; + static int spinand_write_enable_op(struct spinand_device *spinand) { struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true); @@ -402,35 +455,6 @@ static int spinand_lock_block(struct spinand_device *spinand, u8 lock) return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock); } -static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status) -{ - struct nand_device *nand = spinand_to_nand(spinand); - - if (spinand->eccinfo.get_status) - return spinand->eccinfo.get_status(spinand, status); - - switch (status & STATUS_ECC_MASK) { - case STATUS_ECC_NO_BITFLIPS: - return 0; - - case STATUS_ECC_HAS_BITFLIPS: - /* - * We have no way to know exactly how many bitflips have been - * fixed, so let's return the maximum possible value so that - * wear-leveling layers move the data immediately. - */ - return nanddev_get_ecc_conf(nand)->strength; - - case STATUS_ECC_UNCOR_ERROR: - return -EBADMSG; - - default: - break; - } - - return -EINVAL; -} - static int spinand_read_page(struct spinand_device *spinand, const struct nand_page_io_req *req, bool ecc_enabled) @@ -965,30 +989,6 @@ static int spinand_detect(struct spinand_device *spinand) return 0; } -static int spinand_noecc_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) -{ - return -ERANGE; -} - -static int spinand_noecc_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *region) -{ - if (section) - return -ERANGE; - - /* Reserve 2 bytes for the BBM. */ - region->offset = 2; - region->length = 62; - - return 0; -} - -static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = { - .ecc = spinand_noecc_ooblayout_ecc, - .free = spinand_noecc_ooblayout_free, -}; - static int spinand_init(struct spinand_device *spinand) { struct device *dev = &spinand->spimem->spi->dev; -- cgit v1.2.3 From 945845b54c9cf61809d1963492bb728ce8937964 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 17:41:07 +0200 Subject: mtd: spinand: Instantiate a SPI-NAND on-die ECC engine Make use of the existing functions taken from the SPI-NAND core to instantiate an on-die ECC engine specific to the SPI-NAND core. The next step will be to tweak the core to use this object instead of calling the helpers directly. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200930154109.3922-4-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 9 ++++++ 2 files changed, 76 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index b2830e19a7f6..069ebf85dd3f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -246,6 +246,73 @@ static const struct mtd_ooblayout_ops spinand_noecc_ooblayout = { .free = spinand_noecc_ooblayout_free, }; +static int spinand_ondie_ecc_init_ctx(struct nand_device *nand) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct spinand_ondie_ecc_conf *engine_conf; + + nand->ecc.ctx.conf.engine_type = NAND_ECC_ENGINE_TYPE_ON_DIE; + nand->ecc.ctx.conf.step_size = nand->ecc.requirements.step_size; + nand->ecc.ctx.conf.strength = nand->ecc.requirements.strength; + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + nand->ecc.ctx.priv = engine_conf; + + if (spinand->eccinfo.ooblayout) + mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout); + else + mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout); + + return 0; +} + +static void spinand_ondie_ecc_cleanup_ctx(struct nand_device *nand) +{ + kfree(nand->ecc.ctx.priv); +} + +static int spinand_ondie_ecc_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct spinand_device *spinand = nand_to_spinand(nand); + bool enable = (req->mode != MTD_OPS_RAW); + + /* Only enable or disable the engine */ + return spinand_ecc_enable(spinand, enable); +} + +static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv; + struct spinand_device *spinand = nand_to_spinand(nand); + + if (req->mode == MTD_OPS_RAW) + return 0; + + /* Nothing to do when finishing a page write */ + if (req->type == NAND_PAGE_WRITE) + return 0; + + /* Finish a page write: check the status, report errors/bitflips */ + return spinand_check_ecc_status(spinand, engine_conf->status); +} + +static struct nand_ecc_engine_ops spinand_ondie_ecc_engine_ops = { + .init_ctx = spinand_ondie_ecc_init_ctx, + .cleanup_ctx = spinand_ondie_ecc_cleanup_ctx, + .prepare_io_req = spinand_ondie_ecc_prepare_io_req, + .finish_io_req = spinand_ondie_ecc_finish_io_req, +}; + +static __maybe_unused struct nand_ecc_engine spinand_ondie_ecc_engine = { + .ops = &spinand_ondie_ecc_engine_ops, +}; + static int spinand_write_enable_op(struct spinand_device *spinand) { struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true); diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 7b78c4ba9b3e..6bb92f26833e 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -286,6 +286,15 @@ struct spinand_ecc_info { #define SPINAND_HAS_QE_BIT BIT(0) #define SPINAND_HAS_CR_FEAT_BIT BIT(1) +/** + * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure + * @status: status of the last wait operation that will be used in case + * ->get_status() is not populated by the spinand device. + */ +struct spinand_ondie_ecc_conf { + u8 status; +}; + /** * struct spinand_info - Structure used to describe SPI NAND chips * @model: model name -- cgit v1.2.3 From da429b9615803b6f19e5734c4c4d99136e1e3bfd Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 17:41:08 +0200 Subject: mtd: nand: Let on-die ECC engines be retrieved from the NAND core Before making use of the ECC engines, we must retrieve them. Add the necessary boilerplate. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200930154109.3922-5-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc.c | 6 ++++++ include/linux/mtd/nand.h | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c index 57bc8c5ef08c..9afdde70824a 100644 --- a/drivers/mtd/nand/ecc.c +++ b/drivers/mtd/nand/ecc.c @@ -605,6 +605,12 @@ struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand) } EXPORT_SYMBOL(nand_ecc_get_sw_engine); +struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand) +{ + return nand->ecc.ondie_engine; +} +EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Miquel Raynal "); MODULE_DESCRIPTION("Generic ECC engine"); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index f78f61c9a9ee..6c6f91c03c42 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -278,6 +278,7 @@ int nand_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req); bool nand_ecc_is_strong_enough(struct nand_device *nand); struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand); +struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand); #if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING) struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void); -- cgit v1.2.3 From c8efe010283ae0bef8593211e39bf7e7e185b93e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Wed, 30 Sep 2020 17:41:09 +0200 Subject: mtd: spinand: Fill a default ECC provider/algorithm The SPI-NAND layer default is on-die ECC because until now it was the only one supported. New SPI-NAND chip flavors might use something else as ECC engine provider but this will always be the default if the user does not choose explicitly something else. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200930154109.3922-6-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 069ebf85dd3f..f58cea52c781 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -309,7 +309,7 @@ static struct nand_ecc_engine_ops spinand_ondie_ecc_engine_ops = { .finish_io_req = spinand_ondie_ecc_finish_io_req, }; -static __maybe_unused struct nand_ecc_engine spinand_ondie_ecc_engine = { +static struct nand_ecc_engine spinand_ondie_ecc_engine = { .ops = &spinand_ondie_ecc_engine_ops, }; @@ -1133,6 +1133,10 @@ static int spinand_init(struct spinand_device *spinand) if (ret) goto err_manuf_cleanup; + /* SPI-NAND default ECC engine is on-die */ + nand->ecc.defaults.engine_type = NAND_ECC_ENGINE_TYPE_ON_DIE; + nand->ecc.ondie_engine = &spinand_ondie_ecc_engine; + /* * Right now, we don't support ECC, so let the whole oob * area available for the user. -- cgit v1.2.3 From 6b0c3b84156125e029956e46d2b44e72f513a9fa Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 1 Oct 2020 12:20:09 +0200 Subject: mtd: nand: Add helpers to manage ECC engines and configurations Add the logic in the NAND core to find the right ECC engine depending on the NAND chip requirements and the user desires. Right now, the choice may be made between (more will come): * software Hamming * software BCH * on-die (SPI-NAND devices only) Once the ECC engine has been found, the ECC engine must be configured. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201001102014.20100-2-miquel.raynal@bootlin.com --- drivers/mtd/nand/core.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 4 ++ 2 files changed, 128 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c index b6de955ac8bf..5e13a03d2b32 100644 --- a/drivers/mtd/nand/core.c +++ b/drivers/mtd/nand/core.c @@ -207,6 +207,130 @@ int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len) } EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks); +/** + * nanddev_get_ecc_engine() - Find and get a suitable ECC engine + * @nand: NAND device + */ +static int nanddev_get_ecc_engine(struct nand_device *nand) +{ + int engine_type; + + /* Read the user desires in terms of ECC engine/configuration */ + of_get_nand_ecc_user_config(nand); + + engine_type = nand->ecc.user_conf.engine_type; + if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID) + engine_type = nand->ecc.defaults.engine_type; + + switch (engine_type) { + case NAND_ECC_ENGINE_TYPE_NONE: + return 0; + case NAND_ECC_ENGINE_TYPE_SOFT: + nand->ecc.engine = nand_ecc_get_sw_engine(nand); + break; + case NAND_ECC_ENGINE_TYPE_ON_DIE: + nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand); + break; + case NAND_ECC_ENGINE_TYPE_ON_HOST: + pr_err("On-host hardware ECC engines not supported yet\n"); + break; + default: + pr_err("Missing ECC engine type\n"); + } + + if (!nand->ecc.engine) + return -EINVAL; + + return 0; +} + +/** + * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine + * @nand: NAND device + */ +static int nanddev_put_ecc_engine(struct nand_device *nand) +{ + switch (nand->ecc.ctx.conf.engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: + pr_err("On-host hardware ECC engines not supported yet\n"); + break; + case NAND_ECC_ENGINE_TYPE_NONE: + case NAND_ECC_ENGINE_TYPE_SOFT: + case NAND_ECC_ENGINE_TYPE_ON_DIE: + default: + break; + } + + return 0; +} + +/** + * nanddev_find_ecc_configuration() - Find a suitable ECC configuration + * @nand: NAND device + */ +static int nanddev_find_ecc_configuration(struct nand_device *nand) +{ + int ret; + + if (!nand->ecc.engine) + return -ENOTSUPP; + + ret = nand_ecc_init_ctx(nand); + if (ret) + return ret; + + if (!nand_ecc_is_strong_enough(nand)) + pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", + nand->mtd.name); + + return 0; +} + +/** + * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip + * @nand: NAND device + */ +int nanddev_ecc_engine_init(struct nand_device *nand) +{ + int ret; + + /* Look for the ECC engine to use */ + ret = nanddev_get_ecc_engine(nand); + if (ret) { + pr_err("No ECC engine found\n"); + return ret; + } + + /* No ECC engine requested */ + if (!nand->ecc.engine) + return 0; + + /* Configure the engine: balance user input and chip requirements */ + ret = nanddev_find_ecc_configuration(nand); + if (ret) { + pr_err("No suitable ECC configuration\n"); + nanddev_put_ecc_engine(nand); + + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init); + +/** + * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations + * @nand: NAND device + */ +void nanddev_ecc_engine_cleanup(struct nand_device *nand) +{ + if (nand->ecc.engine) + nand_ecc_cleanup_ctx(nand); + + nanddev_put_ecc_engine(nand); +} +EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup); + /** * nanddev_init() - Initialize a NAND device * @nand: NAND device diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 6c6f91c03c42..414f8a4d2853 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -936,6 +936,10 @@ bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos); int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos); int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos); +/* ECC related functions */ +int nanddev_ecc_engine_init(struct nand_device *nand); +void nanddev_ecc_engine_cleanup(struct nand_device *nand); + /* BBT related functions */ enum nand_bbt_block_status { NAND_BBT_BLOCK_STATUS_UNKNOWN, -- cgit v1.2.3 From 3d1f08b032dc4e168f3aefed1e07a63c3c080325 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 1 Oct 2020 12:20:11 +0200 Subject: mtd: spinand: Use the external ECC engine logic Now that all the logic is available in the NAND core, let's use it from the SPI-NAND core. Right now there is no functional change as the default ECC engine for SPI-NANDs is set to 'on-die', but user can now use software correction if they want to by just setting the right properties in the DT. Also note that the OOB layout handling is removed from the SPI-NAND core as each ECC engine is supposed to handle it by it's own; users should not be aware of that. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201001102014.20100-4-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/Kconfig | 1 + drivers/mtd/nand/spi/core.c | 97 ++++++++++++++++++++++++-------------------- 2 files changed, 54 insertions(+), 44 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/Kconfig b/drivers/mtd/nand/spi/Kconfig index da89b250df7c..3d7649a2dd72 100644 --- a/drivers/mtd/nand/spi/Kconfig +++ b/drivers/mtd/nand/spi/Kconfig @@ -2,6 +2,7 @@ menuconfig MTD_SPI_NAND tristate "SPI NAND device Support" select MTD_NAND_CORE + select MTD_NAND_ECC depends on SPI_MASTER select SPI_MEM help diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index f58cea52c781..8a4d20e30edb 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -313,6 +313,15 @@ static struct nand_ecc_engine spinand_ondie_ecc_engine = { .ops = &spinand_ondie_ecc_engine_ops, }; +static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status) +{ + struct spinand_ondie_ecc_conf *engine_conf = nand->ecc.ctx.priv; + + if (nand->ecc.ctx.conf.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE && + engine_conf) + engine_conf->status = status; +} + static int spinand_write_enable_op(struct spinand_device *spinand) { struct spi_mem_op op = SPINAND_WR_EN_DIS_OP(true); @@ -334,7 +343,6 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, const struct nand_page_io_req *req) { struct nand_device *nand = spinand_to_nand(spinand); - struct mtd_info *mtd = nanddev_to_mtd(nand); struct spi_mem_dirmap_desc *rdesc; unsigned int nbytes = 0; void *buf = NULL; @@ -374,17 +382,6 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, memcpy(req->databuf.in, spinand->databuf + req->dataoffs, req->datalen); - if (req->ooblen) { - if (req->mode == MTD_OPS_AUTO_OOB) - mtd_ooblayout_get_databytes(mtd, req->oobbuf.in, - spinand->oobbuf, - req->ooboffs, - req->ooblen); - else - memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs, - req->ooblen); - } - return 0; } @@ -392,7 +389,7 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, const struct nand_page_io_req *req) { struct nand_device *nand = spinand_to_nand(spinand); - struct mtd_info *mtd = nanddev_to_mtd(nand); + struct mtd_info *mtd = spinand_to_mtd(spinand); struct spi_mem_dirmap_desc *wdesc; unsigned int nbytes, column = 0; void *buf = spinand->databuf; @@ -404,9 +401,12 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, * must fill the page cache entirely even if we only want to program * the data portion of the page, otherwise we might corrupt the BBM or * user data previously programmed in OOB area. + * + * Only reset the data buffer manually, the OOB buffer is prepared by + * ECC engines ->prepare_io_req() callback. */ nbytes = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - memset(spinand->databuf, 0xff, nbytes); + memset(spinand->databuf, 0xff, nanddev_page_size(nand)); if (req->datalen) memcpy(spinand->databuf + req->dataoffs, req->databuf.out, @@ -523,12 +523,16 @@ static int spinand_lock_block(struct spinand_device *spinand, u8 lock) } static int spinand_read_page(struct spinand_device *spinand, - const struct nand_page_io_req *req, - bool ecc_enabled) + const struct nand_page_io_req *req) { + struct nand_device *nand = spinand_to_nand(spinand); u8 status; int ret; + ret = nand_ecc_prepare_io_req(nand, (struct nand_page_io_req *)req); + if (ret) + return ret; + ret = spinand_load_page_op(spinand, req); if (ret) return ret; @@ -537,22 +541,26 @@ static int spinand_read_page(struct spinand_device *spinand, if (ret < 0) return ret; + spinand_ondie_ecc_save_status(nand, status); + ret = spinand_read_from_cache_op(spinand, req); if (ret) return ret; - if (!ecc_enabled) - return 0; - - return spinand_check_ecc_status(spinand, status); + return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req); } static int spinand_write_page(struct spinand_device *spinand, const struct nand_page_io_req *req) { + struct nand_device *nand = spinand_to_nand(spinand); u8 status; int ret; + ret = nand_ecc_prepare_io_req(nand, (struct nand_page_io_req *)req); + if (ret) + return ret; + ret = spinand_write_enable_op(spinand); if (ret) return ret; @@ -567,9 +575,9 @@ static int spinand_write_page(struct spinand_device *spinand, ret = spinand_wait(spinand, &status); if (!ret && (status & STATUS_PROG_FAILED)) - ret = -EIO; + return -EIO; - return ret; + return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req); } static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, @@ -579,25 +587,24 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, struct nand_device *nand = mtd_to_nanddev(mtd); unsigned int max_bitflips = 0; struct nand_io_iter iter; - bool enable_ecc = false; + bool disable_ecc = false; bool ecc_failed = false; int ret = 0; - if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout) - enable_ecc = true; + if (ops->mode == MTD_OPS_RAW || !spinand->eccinfo.ooblayout) + disable_ecc = true; mutex_lock(&spinand->lock); nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) { - ret = spinand_select_target(spinand, iter.req.pos.target); - if (ret) - break; + if (disable_ecc) + iter.req.mode = MTD_OPS_RAW; - ret = spinand_ecc_enable(spinand, enable_ecc); + ret = spinand_select_target(spinand, iter.req.pos.target); if (ret) break; - ret = spinand_read_page(spinand, &iter.req, enable_ecc); + ret = spinand_read_page(spinand, &iter.req); if (ret < 0 && ret != -EBADMSG) break; @@ -628,20 +635,19 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to, struct spinand_device *spinand = mtd_to_spinand(mtd); struct nand_device *nand = mtd_to_nanddev(mtd); struct nand_io_iter iter; - bool enable_ecc = false; + bool disable_ecc = false; int ret = 0; - if (ops->mode != MTD_OPS_RAW && mtd->ooblayout) - enable_ecc = true; + if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout) + disable_ecc = true; mutex_lock(&spinand->lock); nanddev_io_for_each_page(nand, NAND_PAGE_WRITE, to, ops, &iter) { - ret = spinand_select_target(spinand, iter.req.pos.target); - if (ret) - break; + if (disable_ecc) + iter.req.mode = MTD_OPS_RAW; - ret = spinand_ecc_enable(spinand, enable_ecc); + ret = spinand_select_target(spinand, iter.req.pos.target); if (ret) break; @@ -671,7 +677,7 @@ static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos) }; spinand_select_target(spinand, pos->target); - spinand_read_page(spinand, &req, false); + spinand_read_page(spinand, &req); if (marker[0] != 0xff || marker[1] != 0xff) return true; @@ -1137,6 +1143,11 @@ static int spinand_init(struct spinand_device *spinand) nand->ecc.defaults.engine_type = NAND_ECC_ENGINE_TYPE_ON_DIE; nand->ecc.ondie_engine = &spinand_ondie_ecc_engine; + spinand_ecc_enable(spinand, false); + ret = nanddev_ecc_engine_init(nand); + if (ret) + goto err_cleanup_nanddev; + /* * Right now, we don't support ECC, so let the whole oob * area available for the user. @@ -1149,14 +1160,9 @@ static int spinand_init(struct spinand_device *spinand) mtd->_erase = spinand_mtd_erase; mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks; - if (spinand->eccinfo.ooblayout) - mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout); - else - mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout); - ret = mtd_ooblayout_count_freebytes(mtd); if (ret < 0) - goto err_cleanup_nanddev; + goto err_cleanup_ecc_engine; mtd->oobavail = ret; @@ -1166,6 +1172,9 @@ static int spinand_init(struct spinand_device *spinand) return 0; +err_cleanup_ecc_engine: + nanddev_ecc_engine_cleanup(nand); + err_cleanup_nanddev: nanddev_cleanup(nand); -- cgit v1.2.3 From 00c15b78b4b46bcd9253bf4ab4ef05fb746ac4af Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 1 Oct 2020 12:20:12 +0200 Subject: mtd: spinand: Allow the case where there is no ECC engine Even if this is not supposed to happen, there is no reason to fail the probe if it was explicitly requested to use no ECC engine at all (for instance, during development). This condition is met by just commenting out the error on the OOB free bytes count after the assignation of an ECC engine if none was provided (any other situation would error out much earlier anyway). Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201001102014.20100-5-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc.c | 8 ++++---- drivers/mtd/nand/spi/core.c | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c index 9afdde70824a..6c43dfda01d4 100644 --- a/drivers/mtd/nand/ecc.c +++ b/drivers/mtd/nand/ecc.c @@ -105,7 +105,7 @@ */ int nand_ecc_init_ctx(struct nand_device *nand) { - if (!nand->ecc.engine->ops->init_ctx) + if (!nand->ecc.engine || !nand->ecc.engine->ops->init_ctx) return 0; return nand->ecc.engine->ops->init_ctx(nand); @@ -118,7 +118,7 @@ EXPORT_SYMBOL(nand_ecc_init_ctx); */ void nand_ecc_cleanup_ctx(struct nand_device *nand) { - if (nand->ecc.engine->ops->cleanup_ctx) + if (nand->ecc.engine && nand->ecc.engine->ops->cleanup_ctx) nand->ecc.engine->ops->cleanup_ctx(nand); } EXPORT_SYMBOL(nand_ecc_cleanup_ctx); @@ -131,7 +131,7 @@ EXPORT_SYMBOL(nand_ecc_cleanup_ctx); int nand_ecc_prepare_io_req(struct nand_device *nand, struct nand_page_io_req *req) { - if (!nand->ecc.engine->ops->prepare_io_req) + if (!nand->ecc.engine || !nand->ecc.engine->ops->prepare_io_req) return 0; return nand->ecc.engine->ops->prepare_io_req(nand, req); @@ -146,7 +146,7 @@ EXPORT_SYMBOL(nand_ecc_prepare_io_req); int nand_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req) { - if (!nand->ecc.engine->ops->finish_io_req) + if (!nand->ecc.engine || !nand->ecc.engine->ops->finish_io_req) return 0; return nand->ecc.engine->ops->finish_io_req(nand, req); diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 8a4d20e30edb..4a4c86d8eeb6 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1160,9 +1160,11 @@ static int spinand_init(struct spinand_device *spinand) mtd->_erase = spinand_mtd_erase; mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks; - ret = mtd_ooblayout_count_freebytes(mtd); - if (ret < 0) - goto err_cleanup_ecc_engine; + if (nand->ecc.engine) { + ret = mtd_ooblayout_count_freebytes(mtd); + if (ret < 0) + goto err_cleanup_ecc_engine; + } mtd->oobavail = ret; -- cgit v1.2.3 From 868cbe2a6dcee451bd8f87cbbb2a73cf463b57e5 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 1 Oct 2020 12:20:13 +0200 Subject: mtd: spinand: Fix OOB read So far OOB have never been used in SPI-NAND, add the missing memcpy to make it work properly. Fixes: 7529df465248 ("mtd: nand: Add core infrastructure to support SPI NANDs") Cc: stable@vger.kernel.org Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201001102014.20100-6-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4a4c86d8eeb6..bec8c2a57f24 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -382,6 +382,10 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, memcpy(req->databuf.in, spinand->databuf + req->dataoffs, req->datalen); + if (req->ooblen) + memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs, + req->ooblen); + return 0; } -- cgit v1.2.3 From a8c1dc9dc6fe081492e125cc92fc402d91f17efc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 1 Oct 2020 12:20:14 +0200 Subject: mtd: spinand: Remove outdated comment This comment is no longer true so drop it. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201001102014.20100-7-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index bec8c2a57f24..8ea545bb924d 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1152,10 +1152,6 @@ static int spinand_init(struct spinand_device *spinand) if (ret) goto err_cleanup_nanddev; - /* - * Right now, we don't support ECC, so let the whole oob - * area available for the user. - */ mtd->_read_oob = spinand_mtd_read; mtd->_write_oob = spinand_mtd_write; mtd->_block_isbad = spinand_mtd_block_isbad; -- cgit v1.2.3 From efd50ff127b59d9a0f5f41ebf842d0d6ae8e4f6d Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 7 Oct 2020 15:45:33 +0200 Subject: mtd: rawnand: gpmi: cleanup makefile The extra gpmi_nand.o object is not needed anymore since commit 3045f8e36963 ("mtd: rawnand: gpmi: move all driver code into single file"). Signed-off-by: Marco Felsch Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201007134533.31390-1-m.felsch@pengutronix.de --- drivers/mtd/nand/raw/gpmi-nand/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/Makefile b/drivers/mtd/nand/raw/gpmi-nand/Makefile index 9bd81a31e02e..247cbfceaa19 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/Makefile +++ b/drivers/mtd/nand/raw/gpmi-nand/Makefile @@ -1,3 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi_nand.o -gpmi_nand-objs += gpmi-nand.o +obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand.o -- cgit v1.2.3 From bc3686021122de953858a5be4cbf6e3f1d821e79 Mon Sep 17 00:00:00 2001 From: Praveenkumar I Date: Fri, 9 Oct 2020 13:37:52 +0530 Subject: mtd: rawnand: qcom: Fix DMA sync on FLASH_STATUS register read After each codeword NAND_FLASH_STATUS is read for possible operational failures. But there is no DMA sync for CPU operation before reading it and this leads to incorrect or older copy of DMA buffer in reg_read_buf. This patch adds the DMA sync on reg_read_buf for CPU before reading it. Fixes: 5bc36b2bf6e2 ("mtd: rawnand: qcom: check for operation errors in case of raw read") Cc: stable@vger.kernel.org Signed-off-by: Praveenkumar I Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1602230872-25616-1-git-send-email-ipkumar@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 777fb0de0680..dfc17a28a06b 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -1570,6 +1570,8 @@ static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt) struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); int i; + nandc_read_buffer_sync(nandc, true); + for (i = 0; i < cw_cnt; i++) { u32 flash = le32_to_cpu(nandc->reg_read_buf[i]); -- cgit v1.2.3 From 62858625441edd28c4cb4087d55c4dabf947f85b Mon Sep 17 00:00:00 2001 From: Kathiravan T Date: Tue, 13 Oct 2020 10:45:23 +0530 Subject: mtd: rawnand: qcom: Support for IPQ6018 QPIC NAND controller Add the compatible string for IPQ6018 QPIC NAND controller version 1.5.0. It's properties are same as IPQ8074, so reuse the same. Signed-off-by: Kathiravan T Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1602566124-13456-3-git-send-email-kathirav@codeaurora.org --- drivers/mtd/nand/raw/qcom_nandc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index dfc17a28a06b..f69457c80be7 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -3065,6 +3065,10 @@ static const struct of_device_id qcom_nandc_of_match[] = { .compatible = "qcom,ipq4019-nand", .data = &ipq4019_nandc_props, }, + { + .compatible = "qcom,ipq6018-nand", + .data = &ipq8074_nandc_props, + }, { .compatible = "qcom,ipq8074-nand", .data = &ipq8074_nandc_props, -- cgit v1.2.3 From 7998d89875177a5fac9f963e230dbb828c218cb9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 23 Oct 2020 18:33:04 +0200 Subject: mtd: rawnand: fix a kernel-doc markup Some identifiers have different names between their prototypes and the kernel-doc markup. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/9ed47a57d12c40e73a9b01612ee119d39baa6236.1603469755.git.mchehab+huawei@kernel.org --- drivers/mtd/nand/raw/nand_bbt.c | 2 +- include/linux/mtd/rawnand.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index 344a24fd2ca8..dced32a126d9 100644 --- a/drivers/mtd/nand/raw/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c @@ -1087,7 +1087,7 @@ static int nand_update_bbt(struct nand_chip *this, loff_t offs) } /** - * mark_bbt_regions - [GENERIC] mark the bad block table regions + * mark_bbt_region - [GENERIC] mark the bad block table regions * @this: the NAND device * @td: bad block table descriptor * diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 0a90a8792d18..6b3240e44310 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1284,7 +1284,8 @@ static inline bool nand_is_slc(struct nand_chip *chip) } /** - * Check if the opcode's address should be sent only on the lower 8 bits + * nand_opcode_8bits - Check if the opcode's address should be sent only on the + * lower 8 bits * @command: opcode to check */ static inline int nand_opcode_8bits(unsigned int command) -- cgit v1.2.3 From c13d845e9a69580424d40b7b101c37d4f71bcd63 Mon Sep 17 00:00:00 2001 From: Sergei Antonov Date: Wed, 28 Oct 2020 12:49:40 +0300 Subject: mtd: rawnand: meson: fix meson_nfc_dma_buffer_release() arguments Arguments 'infolen' and 'datalen' to meson_nfc_dma_buffer_release() were mixed up. Fixes: 8fae856c53500 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller") Cc: stable@vger.kernel.org Signed-off-by: Sergei Antonov Acked-by: Liang Yang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201028094940.11765-1-saproj@gmail.com --- drivers/mtd/nand/raw/meson_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 48e6dac96be6..a76afea6ea77 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -510,7 +510,7 @@ static int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf, } static void meson_nfc_dma_buffer_release(struct nand_chip *nand, - int infolen, int datalen, + int datalen, int infolen, enum dma_data_direction dir) { struct meson_nfc *nfc = nand_get_controller_data(nand); -- cgit v1.2.3 From 910ef7a4b39c39c135b4f0e80c64fc8f68226a8d Mon Sep 17 00:00:00 2001 From: Manuel Dipolt Date: Mon, 12 Oct 2020 17:41:22 +0200 Subject: mtd: rawnand: sunxi: Add MDMA support This patch enables NAND MDMA (MBUS DMA) mode for the Allwinner SoCs A23/A33/H3. The DMA transfer method gets sets now to MBUS DMA as default for the sun8i-a23-nand-controller (till now DMA transfer was executed via the shared DMA engine). The main advantage is more bandwidth for the users of the shared DMA engine and also that the MBUS DMA setup requires less configuration effort. For example you don't need to define a dedicated DMA channel in the device-tree any more. Reviewed-by: Boris Brezillon Signed-off-by: Manuel Dipolt Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/154840787.280672.1602517282173.JavaMail.zimbra@robart.cc --- drivers/mtd/nand/raw/sunxi_nand.c | 117 ++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 44 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 540529540ade..1ec672bbd459 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -51,6 +51,7 @@ #define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) #define NFC_REG_SPARE_AREA 0x00A0 #define NFC_REG_PAT_ID 0x00A4 +#define NFC_REG_MDMA_ADDR 0x00C0 #define NFC_REG_MDMA_CNT 0x00C4 #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -209,13 +210,13 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) * NAND Controller capabilities structure: stores NAND controller capabilities * for distinction between compatible strings. * - * @extra_mbus_conf: Contrary to A10, A10s and A13, accessing internal RAM + * @has_mdma: Use mbus dma mode, otherwise general dma * through MBUS on A23/A33 needs extra configuration. * @reg_io_data: I/O data register * @dma_maxburst: DMA maxburst */ struct sunxi_nfc_caps { - bool extra_mbus_conf; + bool has_mdma; unsigned int reg_io_data; unsigned int dma_maxburst; }; @@ -365,24 +366,31 @@ static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, if (!ret) return -ENOMEM; - dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); - if (!dmad) { - ret = -EINVAL; - goto err_unmap_buf; + if (!nfc->caps->has_mdma) { + dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); + if (!dmad) { + ret = -EINVAL; + goto err_unmap_buf; + } } writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, nfc->regs + NFC_REG_CTL); writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); writel(chunksize, nfc->regs + NFC_REG_CNT); - if (nfc->caps->extra_mbus_conf) - writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT); - dmat = dmaengine_submit(dmad); + if (nfc->caps->has_mdma) { + writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL, + nfc->regs + NFC_REG_CTL); + writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT); + writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR); + } else { + dmat = dmaengine_submit(dmad); - ret = dma_submit_error(dmat); - if (ret) - goto err_clr_dma_flag; + ret = dma_submit_error(dmat); + if (ret) + goto err_clr_dma_flag; + } return 0; @@ -913,7 +921,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf unsigned int max_bitflips = 0; int ret, i, raw_mode = 0; struct scatterlist sg; - u32 status; + u32 status, wait; ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); if (ret) @@ -931,13 +939,18 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); - dma_async_issue_pending(nfc->dmac); + wait = NFC_CMD_INT_FLAG; + + if (nfc->caps->has_mdma) + wait |= NFC_DMA_INT_FLAG; + else + dma_async_issue_pending(nfc->dmac); writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, nfc->regs + NFC_REG_CMD); - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); - if (ret) + ret = sunxi_nfc_wait_events(nfc, wait, false, 0); + if (ret && !nfc->caps->has_mdma) dmaengine_terminate_all(nfc->dmac); sunxi_nfc_randomizer_disable(nand); @@ -1278,6 +1291,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; struct scatterlist sg; + u32 wait; int ret, i; sunxi_nfc_select_chip(nand, nand->cur_cs); @@ -1306,14 +1320,19 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, nfc->regs + NFC_REG_WCMD_SET); - dma_async_issue_pending(nfc->dmac); + wait = NFC_CMD_INT_FLAG; + + if (nfc->caps->has_mdma) + wait |= NFC_DMA_INT_FLAG; + else + dma_async_issue_pending(nfc->dmac); writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS | NFC_ACCESS_DIR, nfc->regs + NFC_REG_CMD); - ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); - if (ret) + ret = sunxi_nfc_wait_events(nfc, wait, false, 0); + if (ret && !nfc->caps->has_mdma) dmaengine_terminate_all(nfc->dmac); sunxi_nfc_randomizer_disable(nand); @@ -1696,7 +1715,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); - if (nfc->dmac) { + if (nfc->dmac || nfc->caps->has_mdma) { ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; @@ -2061,6 +2080,36 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) } } +static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r) +{ + int ret; + + if (nfc->caps->has_mdma) + return 0; + + nfc->dmac = dma_request_chan(nfc->dev, "rxtx"); + if (IS_ERR(nfc->dmac)) { + ret = PTR_ERR(nfc->dmac); + if (ret == -EPROBE_DEFER) + return ret; + + /* Ignore errors to fall back to PIO mode */ + dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret); + nfc->dmac = NULL; + } else { + struct dma_slave_config dmac_cfg = { }; + + dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data; + dmac_cfg.dst_addr = dmac_cfg.src_addr; + dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; + dmac_cfg.src_maxburst = nfc->caps->dma_maxburst; + dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst; + dmaengine_slave_config(nfc->dmac, &dmac_cfg); + } + return 0; +} + static int sunxi_nfc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -2135,30 +2184,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (ret) goto out_ahb_reset_reassert; - nfc->dmac = dma_request_chan(dev, "rxtx"); - if (IS_ERR(nfc->dmac)) { - ret = PTR_ERR(nfc->dmac); - if (ret == -EPROBE_DEFER) - goto out_ahb_reset_reassert; + ret = sunxi_nfc_dma_init(nfc, r); - /* Ignore errors to fall back to PIO mode */ - dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret); - nfc->dmac = NULL; - } else { - struct dma_slave_config dmac_cfg = { }; - - dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data; - dmac_cfg.dst_addr = dmac_cfg.src_addr; - dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; - dmac_cfg.src_maxburst = nfc->caps->dma_maxburst; - dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst; - dmaengine_slave_config(nfc->dmac, &dmac_cfg); - - if (nfc->caps->extra_mbus_conf) - writel(readl(nfc->regs + NFC_REG_CTL) | - NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL); - } + if (ret) + goto out_ahb_reset_reassert; platform_set_drvdata(pdev, nfc); @@ -2205,7 +2234,7 @@ static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { }; static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { - .extra_mbus_conf = true, + .has_mdma = true, .reg_io_data = NFC_REG_A23_IO_DATA, .dma_maxburst = 8, }; -- cgit v1.2.3 From 1771af5cce2d041e6cdd24521e07959691b72401 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 30 Oct 2020 18:23:33 +0100 Subject: mtd: nand: ecc-hamming: Clarify the logic around rp17 This code has been written in 2008 and is fine, but in order to keep robots happy, I think it's time to change a little bit this code just to clarify the different possible values of eccsize_mult. Indeed, this variable may only take the value 1 or 2 because step_size, in the case of the software Hamming ECC engine may only be 256 or 512. Depending on the value of eccsize_mult, an extra rp17 variable is set, or not and triggers the following warning: smatch warnings: ecc_sw_hamming_calculate() error: uninitialized symbol 'rp17'. As highlighted by Dan Carpenter, if the only possible values for eccsize_mult are 1 and 2, then the code is fine, but "it's hard to tell just from looking". So instead of shifting step_size, let's use a ternary condition to assign to eccsize_mult the only two possible values and clarify the driver's logic. Now that the situation is clarified for humans, ensure rp17 is initialized to 0 to keep compilers and robots silent as well. Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201030172333.28390-1-miquel.raynal@bootlin.com --- drivers/mtd/nand/ecc-sw-hamming.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c index c95aadf1eb43..6334d1d7735d 100644 --- a/drivers/mtd/nand/ecc-sw-hamming.c +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -116,7 +116,7 @@ int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, unsigned char *code, bool sm_order) { const u32 *bp = (uint32_t *)buf; - const u32 eccsize_mult = step_size >> 8; + const u32 eccsize_mult = (step_size == 256) ? 1 : 2; /* current value in buffer */ u32 cur; /* rp0..rp17 are the various accumulated parities (per byte) */ @@ -136,6 +136,7 @@ int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, rp12 = 0; rp14 = 0; rp16 = 0; + rp17 = 0; /* * The loop is unrolled a number of times; -- cgit v1.2.3 From 1f0c4ea95ed4bcb872d3751a95e51f02e2822243 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Sat, 31 Oct 2020 18:54:39 +0800 Subject: mtd: rawnand: ingenic: remove redundant get_device() in ingenic_ecc_get() of_find_device_by_node() already takes a reference to the device, and ingenic_ecc_release() will drop the reference. So, the get_device() in ingenic_ecc_get() is redundand. Fixes: 15de8c6efd0e("mtd: rawnand: ingenic: Separate top-level and SoC specific code") Signed-off-by: Yu Kuai Acked-by: Paul Cercueil Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201031105439.2304211-1-yukuai3@huawei.com --- drivers/mtd/nand/raw/ingenic/ingenic_ecc.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c index 8e22cd6ec71f..efe0ffe4f1ab 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c @@ -71,8 +71,6 @@ static struct ingenic_ecc *ingenic_ecc_get(struct device_node *np) if (!pdev || !platform_get_drvdata(pdev)) return ERR_PTR(-EPROBE_DEFER); - get_device(&pdev->dev); - ecc = platform_get_drvdata(pdev); clk_prepare_enable(ecc->clk); -- cgit v1.2.3 From 0f6b791955a6365b5ebe8b6a5b01de69a47ee92e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Nov 2020 09:19:08 -0300 Subject: mtd: rawnand: mxc: Remove platform data support i.MX is a devicetree-only platform now and the existing platform data support in this driver was only useful for old non-devicetree platforms. Get rid of the platform data support since it is no longer used. Signed-off-by: Fabio Estevam Reviewed-by: Sascha Hauer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201110121908.19400-1-festevam@gmail.com --- drivers/mtd/nand/raw/Kconfig | 2 +- drivers/mtd/nand/raw/mxc_nand.c | 75 ++---------------------------- include/linux/platform_data/mtd-mxc_nand.h | 19 -------- 3 files changed, 5 insertions(+), 91 deletions(-) delete mode 100644 include/linux/platform_data/mtd-mxc_nand.h (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index cc86d6e4e042..b93e6cd23259 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -292,7 +292,7 @@ config MTD_NAND_VF610_NFC config MTD_NAND_MXC tristate "Freescale MXC NAND controller" depends on ARCH_MXC || COMPILE_TEST - depends on HAS_IOMEM + depends on HAS_IOMEM && OF help This enables the driver for the NAND flash controller on the MXC processors. diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 684c51e5e60d..f896364968d8 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -21,7 +21,6 @@ #include #include #include -#include #define DRIVER_NAME "mxc_nand" @@ -184,7 +183,6 @@ struct mxc_nand_host { unsigned int buf_start; const struct mxc_nand_devtype_data *devtype_data; - struct mxc_nand_platform_data pdata; }; static const char * const part_probes[] = { @@ -1611,29 +1609,6 @@ static inline int is_imx53_nfc(struct mxc_nand_host *host) return host->devtype_data == &imx53_nand_devtype_data; } -static const struct platform_device_id mxcnd_devtype[] = { - { - .name = "imx21-nand", - .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data, - }, { - .name = "imx27-nand", - .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data, - }, { - .name = "imx25-nand", - .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data, - }, { - .name = "imx51-nand", - .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data, - }, { - .name = "imx53-nand", - .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mxcnd_devtype); - -#ifdef CONFIG_OF static const struct of_device_id mxcnd_dt_ids[] = { { .compatible = "fsl,imx21-nand", @@ -1655,26 +1630,6 @@ static const struct of_device_id mxcnd_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); -static int mxcnd_probe_dt(struct mxc_nand_host *host) -{ - struct device_node *np = host->dev->of_node; - const struct of_device_id *of_id = - of_match_device(mxcnd_dt_ids, host->dev); - - if (!np) - return 1; - - host->devtype_data = of_id->data; - - return 0; -} -#else -static int mxcnd_probe_dt(struct mxc_nand_host *host) -{ - return 1; -} -#endif - static int mxcnd_attach_chip(struct nand_chip *chip) { struct mtd_info *mtd = nand_to_mtd(chip); @@ -1759,6 +1714,7 @@ static const struct nand_controller_ops mxcnd_controller_ops = { static int mxcnd_probe(struct platform_device *pdev) { + const struct of_device_id *of_id; struct nand_chip *this; struct mtd_info *mtd; struct mxc_nand_host *host; @@ -1800,20 +1756,8 @@ static int mxcnd_probe(struct platform_device *pdev) if (IS_ERR(host->clk)) return PTR_ERR(host->clk); - err = mxcnd_probe_dt(host); - if (err > 0) { - struct mxc_nand_platform_data *pdata = - dev_get_platdata(&pdev->dev); - if (pdata) { - host->pdata = *pdata; - host->devtype_data = (struct mxc_nand_devtype_data *) - pdev->id_entry->driver_data; - } else { - err = -ENODEV; - } - } - if (err < 0) - return err; + of_id = of_match_device(mxcnd_dt_ids, host->dev); + host->devtype_data = of_id->data; if (!host->devtype_data->setup_interface) this->options |= NAND_KEEP_TIMINGS; @@ -1843,14 +1787,6 @@ static int mxcnd_probe(struct platform_device *pdev) this->legacy.select_chip = host->devtype_data->select_chip; - /* NAND bus width determines access functions used by upper layer */ - if (host->pdata.width == 2) - this->options |= NAND_BUSWIDTH_16; - - /* update flash based bbt */ - if (host->pdata.flash_bbt) - this->bbt_options |= NAND_BBT_USE_FLASH; - init_completion(&host->op_completion); host->irq = platform_get_irq(pdev, 0); @@ -1891,9 +1827,7 @@ static int mxcnd_probe(struct platform_device *pdev) goto escan; /* Register the partitions */ - err = mtd_device_parse_register(mtd, part_probes, NULL, - host->pdata.parts, - host->pdata.nr_parts); + err = mtd_device_parse_register(mtd, part_probes, NULL, NULL, 0); if (err) goto cleanup_nand; @@ -1930,7 +1864,6 @@ static struct platform_driver mxcnd_driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(mxcnd_dt_ids), }, - .id_table = mxcnd_devtype, .probe = mxcnd_probe, .remove = mxcnd_remove, }; diff --git a/include/linux/platform_data/mtd-mxc_nand.h b/include/linux/platform_data/mtd-mxc_nand.h deleted file mode 100644 index d1230030c6db..000000000000 --- a/include/linux/platform_data/mtd-mxc_nand.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2008 Sascha Hauer, kernel@pengutronix.de - */ - -#ifndef __ASM_ARCH_NAND_H -#define __ASM_ARCH_NAND_H - -#include - -struct mxc_nand_platform_data { - unsigned int width; /* data bus width in bytes */ - unsigned int hw_ecc:1; /* 0 if suppress hardware ECC */ - unsigned int flash_bbt:1; /* set to 1 to use a flash based bbt */ - struct mtd_partition *parts; /* partition table */ - int nr_parts; /* size of parts */ -}; -#endif /* __ASM_ARCH_NAND_H */ -- cgit v1.2.3 From 928f0736e9aa19488e030e408dde308507fe8bc1 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:39:45 +0100 Subject: mtd: nand: Change dependency between the NAND and ECC cores The NAND ECC core is included in the generic NAND core when it is compiled in. Different software ECC engines drivers will select the NAND ECC core and thus also have a dependency on the NAND core. Using a "depends on" between the two leads to possible cases (not real cases, but created by robots) where one is still unselected because of the "select does not verifies depends on" game: WARNING: unmet direct dependencies detected for MTD_NAND_ECC Depends on [n]: MTD [=m] && MTD_NAND_CORE [=n] Selected by [m]: - MTD_NAND_ECC_SW_HAMMING [=y] && MTD [=m] - MTD_NAND_ECC_SW_BCH [=y] && MTD [=m] Fix this by using a select instead. Reported-by: Randy Dunlap Signed-off-by: Miquel Raynal Acked-by: Randy Dunlap # build-tested Link: https://lore.kernel.org/linux-mtd/20201113123945.32592-1-miquel.raynal@bootlin.com --- drivers/mtd/nand/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 15ffe1a1b863..b40455234cbd 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -13,7 +13,7 @@ menu "ECC engine support" config MTD_NAND_ECC bool - depends on MTD_NAND_CORE + select MTD_NAND_CORE config MTD_NAND_ECC_SW_HAMMING bool "Software Hamming ECC engine" -- cgit v1.2.3 From d59df005ed6870c4a8914489a8520b9f339ac62e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:40:21 +0100 Subject: mtd: rawnand: au1550: Ensure the presence of the right includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While working a bit on this driver I dropped the platform includes and commented a few lines just to verify the correctness of my changes. It appeared the following: drivers/mtd/nand/raw/au1550nd.c: In function ‘au1550nd_waitrdy’: drivers/mtd/nand/raw/au1550nd.c:130:3: error: implicit declaration of function ‘usleep_range’ [-Werror=implicit-function-declaration] usleep_range(10, 100); ^~~~~~~~~~~~ drivers/mtd/nand/raw/au1550nd.c: In function ‘au1550nd_exec_instr’: drivers/mtd/nand/raw/au1550nd.c:188:3: error: implicit declaration of function ‘ndelay’ [-Werror=implicit-function-declaration] ndelay(instr->delay_ns); ^~~~~~ I think the delay.h header should be included in this file and not come from one of its platform includes, so let's add it here. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113124021.32675-1-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/au1550nd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index 7892022bd6dd..188132c3b7a0 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -3,6 +3,7 @@ * Copyright (C) 2004 Embedded Edge, LLC */ +#include #include #include #include -- cgit v1.2.3 From b75e17b00f2c0add86524737f2842d5ec19e539a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:40:45 +0100 Subject: mtd: rawnand: davinci: Do not use extra dereferencing When the nand_chip structure is already available, there is no need to dereference it through the info pointer. Use the chip pointer directly in this case. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113124045.32743-1-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/davinci_nand.c | 38 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index f8c36d19ab47..118da9944e3b 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -586,10 +586,10 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) return PTR_ERR(pdata); /* Use board-specific ECC config */ - info->chip.ecc.engine_type = pdata->engine_type; - info->chip.ecc.placement = pdata->ecc_placement; + chip->ecc.engine_type = pdata->engine_type; + chip->ecc.placement = pdata->ecc_placement; - switch (info->chip.ecc.engine_type) { + switch (chip->ecc.engine_type) { case NAND_ECC_ENGINE_TYPE_NONE: pdata->ecc_bits = 0; break; @@ -601,7 +601,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) * NAND_ECC_ALGO_HAMMING to avoid adding an extra ->ecc_algo * field to davinci_nand_pdata. */ - info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; break; case NAND_ECC_ENGINE_TYPE_ON_HOST: if (pdata->ecc_bits == 4) { @@ -628,12 +628,12 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) if (ret == -EBUSY) return ret; - info->chip.ecc.calculate = nand_davinci_calculate_4bit; - info->chip.ecc.correct = nand_davinci_correct_4bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; - info->chip.ecc.bytes = 10; - info->chip.ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; - info->chip.ecc.algo = NAND_ECC_ALGO_BCH; + chip->ecc.calculate = nand_davinci_calculate_4bit; + chip->ecc.correct = nand_davinci_correct_4bit; + chip->ecc.hwctl = nand_davinci_hwctl_4bit; + chip->ecc.bytes = 10; + chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; + chip->ecc.algo = NAND_ECC_ALGO_BCH; /* * Update ECC layout if needed ... for 1-bit HW ECC, the @@ -651,20 +651,20 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) } else if (chunks == 4 || chunks == 8) { mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); - info->chip.ecc.read_page = nand_davinci_read_page_hwecc_oob_first; + chip->ecc.read_page = nand_davinci_read_page_hwecc_oob_first; } else { return -EIO; } } else { /* 1bit ecc hamming */ - info->chip.ecc.calculate = nand_davinci_calculate_1bit; - info->chip.ecc.correct = nand_davinci_correct_1bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; - info->chip.ecc.bytes = 3; - info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; + chip->ecc.calculate = nand_davinci_calculate_1bit; + chip->ecc.correct = nand_davinci_correct_1bit; + chip->ecc.hwctl = nand_davinci_hwctl_1bit; + chip->ecc.bytes = 3; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; } - info->chip.ecc.size = 512; - info->chip.ecc.strength = pdata->ecc_bits; + chip->ecc.size = 512; + chip->ecc.strength = pdata->ecc_bits; break; default: return -EINVAL; @@ -899,7 +899,7 @@ static int nand_davinci_remove(struct platform_device *pdev) int ret; spin_lock_irq(&davinci_nand_lock); - if (info->chip.ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED) + if (chip->ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED) ecc4_busy = false; spin_unlock_irq(&davinci_nand_lock); -- cgit v1.2.3 From 62e5c6c50992d1418eb9a6a8eaa51fa0b203b691 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 13 Nov 2020 13:41:14 +0100 Subject: mtd: rawnand: marvell: Drop useless line The raw NAND core now declares the on host ECC engine being the default if none is provided in the DT. Drop this line doing exactly the same from the Marvell driver. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113124114.449-1-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/marvell_nand.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index f5ca2002d08e..42d4881d598d 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2678,12 +2678,6 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, mtd = nand_to_mtd(chip); mtd->dev.parent = dev; - /* - * Default to HW ECC engine mode. If the nand-ecc-mode property is given - * in the DT node, this entry will be overwritten in nand_scan_ident(). - */ - chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - /* * Save a reference value for timing registers before * ->setup_interface() is called. -- cgit v1.2.3 From 875330f87a057a7d9831cd6a9dabf39185d15a92 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 13 Nov 2020 15:14:22 +0100 Subject: mtd: onenand: Use mtd->oops_panic_write as condition struct mtd_info has a flag oops_panic_write which is set when the write operation is issued via the panic_write() callback. That allows controller drivers to distinguish the panic write from a regular write. Replace the open coded 'in_interrupt() | oops_in_progress' checks with a check for that flag. in_interrupt() is an unrealiable indicator anyway as it covers all sorts of atomic contexts not only hard and soft interrupt service routines. Signed-off-by: Thomas Gleixner Signed-off-by: Sebastian Andrzej Siewior Cc: Vignesh Raghavendra Cc: Tudor Ambarus Cc: Richard Weinberger Cc: Kyungmin Park Cc: Boris Brezillon Cc: linux-mtd@lists.infradead.org Cc: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113141422.2214771-1-bigeasy@linutronix.de --- drivers/mtd/nand/onenand/onenand_omap2.c | 16 ++++++++-------- drivers/mtd/nand/raw/nand_legacy.c | 9 +++++---- 2 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/onenand/onenand_omap2.c b/drivers/mtd/nand/onenand/onenand_omap2.c index d8c0bd002c2b..12825eb97938 100644 --- a/drivers/mtd/nand/onenand/onenand_omap2.c +++ b/drivers/mtd/nand/onenand/onenand_omap2.c @@ -371,12 +371,12 @@ static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; /* - * If the buffer address is not DMA-able, len is not long enough to make - * DMA transfers profitable or panic_write() may be in an interrupt - * context fallback to PIO mode. + * If the buffer address is not DMA-able, len is not long enough to + * make DMA transfers profitable or if invoked from panic_write() + * fallback to PIO mode. */ if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 || - count < 384 || in_interrupt() || oops_in_progress) + count < 384 || mtd->oops_panic_write) goto out_copy; xtra = count & 3; @@ -418,12 +418,12 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; /* - * If the buffer address is not DMA-able, len is not long enough to make - * DMA transfers profitable or panic_write() may be in an interrupt - * context fallback to PIO mode. + * If the buffer address is not DMA-able, len is not long enough to + * make DMA transfers profitable or if invoked from panic_write() + * fallback to PIO mode. */ if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 || - count < 384 || in_interrupt() || oops_in_progress) + count < 384 || mtd->oops_panic_write) goto out_copy; dma_src = dma_map_single(dev, buf, count, DMA_TO_DEVICE); diff --git a/drivers/mtd/nand/raw/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c index 2bcc03714432..eccc18b266d5 100644 --- a/drivers/mtd/nand/raw/nand_legacy.c +++ b/drivers/mtd/nand/raw/nand_legacy.c @@ -192,9 +192,10 @@ static void panic_nand_wait_ready(struct nand_chip *chip, unsigned long timeo) */ void nand_wait_ready(struct nand_chip *chip) { + struct mtd_info *mtd = nand_to_mtd(chip); unsigned long timeo = 400; - if (in_interrupt() || oops_in_progress) + if (mtd->oops_panic_write) return panic_nand_wait_ready(chip, timeo); /* Wait until command is processed or timeout occurs */ @@ -531,7 +532,7 @@ EXPORT_SYMBOL(nand_get_set_features_notsupp); */ static int nand_wait(struct nand_chip *chip) { - + struct mtd_info *mtd = nand_to_mtd(chip); unsigned long timeo = 400; u8 status; int ret; @@ -546,9 +547,9 @@ static int nand_wait(struct nand_chip *chip) if (ret) return ret; - if (in_interrupt() || oops_in_progress) + if (mtd->oops_panic_write) { panic_nand_wait(chip, timeo); - else { + } else { timeo = jiffies + msecs_to_jiffies(timeo); do { if (chip->legacy.dev_ready) { -- cgit v1.2.3 From 8c293f545419c0d3da9a2a70df0311aa4027a820 Mon Sep 17 00:00:00 2001 From: Baskov Evgeiny Date: Fri, 13 Nov 2020 19:05:37 +0300 Subject: mtd: plat-ram: correctly free memory on error path in platram_probe() If an error happens in mtd_device_parse_register or mtd_device_register, memory allocated for struct platram_info is leaked. Make platram_probe() call platram_remove() on all error paths after struct platram_info allocation to correctly free resources. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Baskov Evgeiny Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201113160537.899-1-baskov@ispras.ru --- drivers/mtd/maps/plat-ram.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 311742c78155..0bec7c791d17 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -177,8 +177,12 @@ static int platram_probe(struct platform_device *pdev) err = mtd_device_parse_register(info->mtd, pdata->probes, NULL, pdata->partitions, pdata->nr_partitions); - if (!err) - dev_info(&pdev->dev, "registered mtd device\n"); + if (err) { + dev_err(&pdev->dev, "failed to register mtd device\n"); + goto exit_free; + } + + dev_info(&pdev->dev, "registered mtd device\n"); if (pdata->nr_partitions) { /* add the whole device. */ @@ -186,10 +190,11 @@ static int platram_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "failed to register the entire device\n"); + goto exit_free; } } - return err; + return 0; exit_free: platram_remove(pdev); -- cgit v1.2.3 From 5ece78de88739b4c68263e9f2582380c1fd8314f Mon Sep 17 00:00:00 2001 From: YouChing Lin Date: Thu, 5 Nov 2020 15:23:40 +0800 Subject: mtd: spinand: macronix: Add support for MX35LFxGE4AD The Macronix MX35LF2GE4AD / MX35LF4GE4AD are 3V, 2G / 4Gbit serial SLC NAND flash device (with on-die ECC). Validated by read, erase, read back, write, read back and nandtest on Xilinx Zynq PicoZed FPGA board which included Macronix SPI Host (drivers/spi/spi-mxic.c). Signed-off-by: YouChing Lin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1604561020-13499-1-git-send-email-ycllin@mxic.com.tw --- drivers/mtd/nand/spi/macronix.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 8e801e4c3a00..3786b1b03b3b 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -119,6 +119,26 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF2GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26), + NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF4GE4AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37), + NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), SPINAND_INFO("MX31LF1GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), -- cgit v1.2.3 From 1b391c7f2e863985668d705f525af3ceb55bc800 Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Sat, 7 Nov 2020 19:05:52 +0800 Subject: mtd: rawnand: gpmi: fix reference count leak in gpmi ops pm_runtime_get_sync() will increment pm usage at first and it will resume the device later. If runtime of the device has error or device is in inaccessible state(or other error state), resume operation will fail. If we do not call put operation to decrease the reference, it will result in reference leak in the two functions(gpmi_init and gpmi_nfc_exec_op). Moreover, this device cannot enter the idle state and always stay busy or other non-idle state later. So we fixed it through adding pm_runtime_put_noidle. Fixes: 5bc6bb603b4d0 ("mtd: rawnand: gpmi: Fix suspend/resume problem") Signed-off-by: Zhang Qilong Acked-by: Han Xu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201107110552.1568742-1-zhangqilong3@huawei.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index dc8104e67506..b5f46f214a58 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -149,8 +149,10 @@ static int gpmi_init(struct gpmi_nand_data *this) int ret; ret = pm_runtime_get_sync(this->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(this->dev); return ret; + } ret = gpmi_reset_block(r->gpmi_regs, false); if (ret) @@ -2263,8 +2265,10 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip, this->transfers[i].direction = DMA_NONE; ret = pm_runtime_get_sync(this->dev); - if (ret < 0) + if (ret < 0) { + pm_runtime_put_noidle(this->dev); return ret; + } /* * This driver currently supports only one NAND chip. Plus, dies share -- cgit v1.2.3 From bdb84a22b02b0c2ca76bb3e3e16942338f67999b Mon Sep 17 00:00:00 2001 From: Thirumalesha Narasimhappa Date: Sun, 8 Nov 2020 19:37:34 +0800 Subject: mtd: spinand: micron: Use more specific names Rename the read/write/update of SPINAND_OP_VARIANTS() to more specialized names. Signed-off-by: Thirumalesha Narasimhappa Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201108113735.2533-2-nthirumalesha7@gmail.com --- drivers/mtd/nand/spi/micron.c | 60 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 5d370cfcdaaa..afe3ba37dcfb 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -28,7 +28,7 @@ #define MICRON_SELECT_DIE(x) ((x) << 6) -static SPINAND_OP_VARIANTS(read_cache_variants, +static SPINAND_OP_VARIANTS(quadio_read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), @@ -36,11 +36,11 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); -static SPINAND_OP_VARIANTS(write_cache_variants, +static SPINAND_OP_VARIANTS(x4_write_cache_variants, SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), SPINAND_PROG_LOAD(true, 0, NULL, 0)); -static SPINAND_OP_VARIANTS(update_cache_variants, +static SPINAND_OP_VARIANTS(x4_update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); @@ -120,9 +120,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), @@ -131,9 +131,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), @@ -142,9 +142,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), @@ -153,9 +153,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), @@ -164,9 +164,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), NAND_MEMORG(1, 2048, 128, 64, 2048, 80, 2, 1, 2), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), 0, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status), @@ -176,9 +176,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), @@ -187,9 +187,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status)), @@ -198,9 +198,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status), @@ -210,9 +210,9 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 2), NAND_ECCREQ(8, 512), - SPINAND_INFO_OP_VARIANTS(&read_cache_variants, - &write_cache_variants, - &update_cache_variants), + SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants, + &x4_write_cache_variants, + &x4_update_cache_variants), SPINAND_HAS_CR_FEAT_BIT, SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status), -- cgit v1.2.3 From 8c573d9419bf61f7b66b6114f1171f3a8a4a0e38 Mon Sep 17 00:00:00 2001 From: Thirumalesha Narasimhappa Date: Sun, 8 Nov 2020 19:37:35 +0800 Subject: mtd: spinand: micron: Add support for MT29F2G01AAAED The MT29F2G01AAAED is a single die, 2Gb Micron SPI NAND Flash with 4-bit ECC Signed-off-by: Thirumalesha Narasimhappa Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201108113735.2533-3-nthirumalesha7@gmail.com --- drivers/mtd/nand/spi/micron.c | 64 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index afe3ba37dcfb..50b7295bc922 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -44,6 +44,19 @@ static SPINAND_OP_VARIANTS(x4_update_cache_variants, SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), SPINAND_PROG_LOAD(false, 0, NULL, 0)); +/* Micron MT29F2G01AAAED Device */ +static SPINAND_OP_VARIANTS(x4_read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(x1_write_cache_variants, + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(x1_update_cache_variants, + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + static int micron_8_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { @@ -74,6 +87,47 @@ static const struct mtd_ooblayout_ops micron_8_ooblayout = { .free = micron_8_ooblayout_free, }; +static int micron_4_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + + if (section >= spinand->base.memorg.pagesize / + mtd->ecc_step_size) + return -ERANGE; + + region->offset = (section * 16) + 8; + region->length = 8; + + return 0; +} + +static int micron_4_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + + if (section >= spinand->base.memorg.pagesize / + mtd->ecc_step_size) + return -ERANGE; + + if (section) { + region->offset = 16 * section; + region->length = 8; + } else { + /* section 0 has two bytes reserved for the BBM */ + region->offset = 2; + region->length = 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops micron_4_ooblayout = { + .ecc = micron_4_ooblayout_ecc, + .free = micron_4_ooblayout_free, +}; + static int micron_select_target(struct spinand_device *spinand, unsigned int target) { @@ -217,6 +271,16 @@ static const struct spinand_info micron_spinand_table[] = { SPINAND_ECCINFO(µn_8_ooblayout, micron_8_ecc_get_status), SPINAND_SELECT_TARGET(micron_select_target)), + /* M69A 2Gb 3.3V */ + SPINAND_INFO("MT29F2G01AAAED", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9F), + NAND_MEMORG(1, 2048, 64, 64, 2048, 80, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&x4_read_cache_variants, + &x1_write_cache_variants, + &x1_update_cache_variants), + 0, + SPINAND_ECCINFO(µn_4_ooblayout, NULL)), }; static int micron_spinand_init(struct spinand_device *spinand) -- cgit v1.2.3 From 0b1039f016e8a37c779a4aee362cb2100ebb1cfd Mon Sep 17 00:00:00 2001 From: Ramuthevar Vadivel Murugan Date: Tue, 10 Nov 2020 09:23:33 +0800 Subject: mtd: rawnand: Add NAND controller support on Intel LGM SoC This patch adds the new IP of Nand Flash Controller(NFC) support on Intel's Lightning Mountain(LGM) SoC. DMA is used for burst data transfer operation, also DMA HW supports aligned 32bit memory address and aligned data access by default. DMA burst of 8 supported. Data register used to support the read/write operation from/to device. Signed-off-by: Ramuthevar Vadivel Murugan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201110012333.18647-3-vadivel.muruganx.ramuthevar@linux.intel.com --- drivers/mtd/nand/raw/Kconfig | 8 + drivers/mtd/nand/raw/Makefile | 1 + drivers/mtd/nand/raw/intel-nand-controller.c | 721 +++++++++++++++++++++++++++ 3 files changed, 730 insertions(+) create mode 100644 drivers/mtd/nand/raw/intel-nand-controller.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index b93e6cd23259..c321442f4ea6 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -441,6 +441,14 @@ config MTD_NAND_ARASAN Enables the driver for the Arasan NAND flash controller on Zynq Ultrascale+ MPSoC. +config MTD_NAND_INTEL_LGM + tristate "Support for NAND controller on Intel LGM SoC" + depends on OF || COMPILE_TEST + depends on HAS_IOMEM + help + Enables support for NAND Flash chips on Intel's LGM SoC. + NAND flash controller interfaced through the External Bus Unit. + comment "Misc" config MTD_SM_COMMON diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index dc38c087c693..9a63174df1b9 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.o obj-$(CONFIG_MTD_NAND_ARASAN) += arasan-nand-controller.o +obj-$(CONFIG_MTD_NAND_INTEL_LGM) += intel-nand-controller.o nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o nand-objs += nand_onfi.o diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c new file mode 100644 index 000000000000..fdb112e8a90d --- /dev/null +++ b/drivers/mtd/nand/raw/intel-nand-controller.c @@ -0,0 +1,721 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Copyright (c) 2020 Intel Corporation. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define EBU_CLC 0x000 +#define EBU_CLC_RST 0x00000000u + +#define EBU_ADDR_SEL(n) (0x020 + (n) * 4) +/* 5 bits 26:22 included for comparison in the ADDR_SELx */ +#define EBU_ADDR_MASK(x) ((x) << 4) +#define EBU_ADDR_SEL_REGEN 0x1 + +#define EBU_BUSCON(n) (0x060 + (n) * 4) +#define EBU_BUSCON_CMULT_V4 0x1 +#define EBU_BUSCON_RECOVC(n) ((n) << 2) +#define EBU_BUSCON_HOLDC(n) ((n) << 4) +#define EBU_BUSCON_WAITRDC(n) ((n) << 6) +#define EBU_BUSCON_WAITWRC(n) ((n) << 8) +#define EBU_BUSCON_BCGEN_CS 0x0 +#define EBU_BUSCON_SETUP_EN BIT(22) +#define EBU_BUSCON_ALEC 0xC000 + +#define EBU_CON 0x0B0 +#define EBU_CON_NANDM_EN BIT(0) +#define EBU_CON_NANDM_DIS 0x0 +#define EBU_CON_CSMUX_E_EN BIT(1) +#define EBU_CON_ALE_P_LOW BIT(2) +#define EBU_CON_CLE_P_LOW BIT(3) +#define EBU_CON_CS_P_LOW BIT(4) +#define EBU_CON_SE_P_LOW BIT(5) +#define EBU_CON_WP_P_LOW BIT(6) +#define EBU_CON_PRE_P_LOW BIT(7) +#define EBU_CON_IN_CS_S(n) ((n) << 8) +#define EBU_CON_OUT_CS_S(n) ((n) << 10) +#define EBU_CON_LAT_EN_CS_P ((0x3D) << 18) + +#define EBU_WAIT 0x0B4 +#define EBU_WAIT_RDBY BIT(0) +#define EBU_WAIT_WR_C BIT(3) + +#define HSNAND_CTL1 0x110 +#define HSNAND_CTL1_ADDR_SHIFT 24 + +#define HSNAND_CTL2 0x114 +#define HSNAND_CTL2_ADDR_SHIFT 8 +#define HSNAND_CTL2_CYC_N_V5 (0x2 << 16) + +#define HSNAND_INT_MSK_CTL 0x124 +#define HSNAND_INT_MSK_CTL_WR_C BIT(4) + +#define HSNAND_INT_STA 0x128 +#define HSNAND_INT_STA_WR_C BIT(4) + +#define HSNAND_CTL 0x130 +#define HSNAND_CTL_ENABLE_ECC BIT(0) +#define HSNAND_CTL_GO BIT(2) +#define HSNAND_CTL_CE_SEL_CS(n) BIT(3 + (n)) +#define HSNAND_CTL_RW_READ 0x0 +#define HSNAND_CTL_RW_WRITE BIT(10) +#define HSNAND_CTL_ECC_OFF_V8TH BIT(11) +#define HSNAND_CTL_CKFF_EN 0x0 +#define HSNAND_CTL_MSG_EN BIT(17) + +#define HSNAND_PARA0 0x13c +#define HSNAND_PARA0_PAGE_V8192 0x3 +#define HSNAND_PARA0_PIB_V256 (0x3 << 4) +#define HSNAND_PARA0_BYP_EN_NP 0x0 +#define HSNAND_PARA0_BYP_DEC_NP 0x0 +#define HSNAND_PARA0_TYPE_ONFI BIT(18) +#define HSNAND_PARA0_ADEP_EN BIT(21) + +#define HSNAND_CMSG_0 0x150 +#define HSNAND_CMSG_1 0x154 + +#define HSNAND_ALE_OFFS BIT(2) +#define HSNAND_CLE_OFFS BIT(3) +#define HSNAND_CS_OFFS BIT(4) + +#define HSNAND_ECC_OFFSET 0x008 + +#define NAND_DATA_IFACE_CHECK_ONLY -1 + +#define MAX_CS 2 + +#define HZ_PER_MHZ 1000000L +#define USEC_PER_SEC 1000000L + +struct ebu_nand_cs { + void __iomem *chipaddr; + dma_addr_t nand_pa; + u32 addr_sel; +}; + +struct ebu_nand_controller { + struct nand_controller controller; + struct nand_chip chip; + struct device *dev; + void __iomem *ebu; + void __iomem *hsnand; + struct dma_chan *dma_tx; + struct dma_chan *dma_rx; + struct completion dma_access_complete; + unsigned long clk_rate; + struct clk *clk; + u32 nd_para0; + u8 cs_num; + struct ebu_nand_cs cs[MAX_CS]; +}; + +static inline struct ebu_nand_controller *nand_to_ebu(struct nand_chip *chip) +{ + return container_of(chip, struct ebu_nand_controller, chip); +} + +static int ebu_nand_waitrdy(struct nand_chip *chip, int timeout_ms) +{ + struct ebu_nand_controller *ctrl = nand_to_ebu(chip); + u32 status; + + return readl_poll_timeout(ctrl->ebu + EBU_WAIT, status, + (status & EBU_WAIT_RDBY) || + (status & EBU_WAIT_WR_C), 20, timeout_ms); +} + +static u8 ebu_nand_readb(struct nand_chip *chip) +{ + struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); + u8 cs_num = ebu_host->cs_num; + u8 val; + + val = readb(ebu_host->cs[cs_num].chipaddr + HSNAND_CS_OFFS); + ebu_nand_waitrdy(chip, 1000); + return val; +} + +static void ebu_nand_writeb(struct nand_chip *chip, u32 offset, u8 value) +{ + struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); + u8 cs_num = ebu_host->cs_num; + + writeb(value, ebu_host->cs[cs_num].chipaddr + offset); + ebu_nand_waitrdy(chip, 1000); +} + +static void ebu_read_buf(struct nand_chip *chip, u_char *buf, unsigned int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = ebu_nand_readb(chip); +} + +static void ebu_write_buf(struct nand_chip *chip, const u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + ebu_nand_writeb(chip, HSNAND_CS_OFFS, buf[i]); +} + +static void ebu_nand_disable(struct nand_chip *chip) +{ + struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); + + writel(0, ebu_host->ebu + EBU_CON); +} + +static void ebu_select_chip(struct nand_chip *chip) +{ + struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); + void __iomem *nand_con = ebu_host->ebu + EBU_CON; + u32 cs = ebu_host->cs_num; + + writel(EBU_CON_NANDM_EN | EBU_CON_CSMUX_E_EN | EBU_CON_CS_P_LOW | + EBU_CON_SE_P_LOW | EBU_CON_WP_P_LOW | EBU_CON_PRE_P_LOW | + EBU_CON_IN_CS_S(cs) | EBU_CON_OUT_CS_S(cs) | + EBU_CON_LAT_EN_CS_P, nand_con); +} + +static int ebu_nand_set_timings(struct nand_chip *chip, int csline, + const struct nand_interface_config *conf) +{ + struct ebu_nand_controller *ctrl = nand_to_ebu(chip); + unsigned int rate = clk_get_rate(ctrl->clk) / HZ_PER_MHZ; + unsigned int period = DIV_ROUND_UP(USEC_PER_SEC, rate); + const struct nand_sdr_timings *timings; + u32 trecov, thold, twrwait, trdwait; + u32 reg = 0; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + trecov = DIV_ROUND_UP(max(timings->tREA_max, timings->tREH_min), + period); + reg |= EBU_BUSCON_RECOVC(trecov); + + thold = DIV_ROUND_UP(max(timings->tDH_min, timings->tDS_min), period); + reg |= EBU_BUSCON_HOLDC(thold); + + trdwait = DIV_ROUND_UP(max(timings->tRC_min, timings->tREH_min), + period); + reg |= EBU_BUSCON_WAITRDC(trdwait); + + twrwait = DIV_ROUND_UP(max(timings->tWC_min, timings->tWH_min), period); + reg |= EBU_BUSCON_WAITWRC(twrwait); + + reg |= EBU_BUSCON_CMULT_V4 | EBU_BUSCON_BCGEN_CS | EBU_BUSCON_ALEC | + EBU_BUSCON_SETUP_EN; + + writel(reg, ctrl->ebu + EBU_BUSCON(ctrl->cs_num)); + + return 0; +} + +static int ebu_nand_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = HSNAND_ECC_OFFSET; + oobregion->length = chip->ecc.total; + + return 0; +} + +static int ebu_nand_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = chip->ecc.total + HSNAND_ECC_OFFSET; + oobregion->length = mtd->oobsize - oobregion->offset; + + return 0; +} + +static const struct mtd_ooblayout_ops ebu_nand_ooblayout_ops = { + .ecc = ebu_nand_ooblayout_ecc, + .free = ebu_nand_ooblayout_free, +}; + +static void ebu_dma_rx_callback(void *cookie) +{ + struct ebu_nand_controller *ebu_host = cookie; + + dmaengine_terminate_async(ebu_host->dma_rx); + + complete(&ebu_host->dma_access_complete); +} + +static void ebu_dma_tx_callback(void *cookie) +{ + struct ebu_nand_controller *ebu_host = cookie; + + dmaengine_terminate_async(ebu_host->dma_tx); + + complete(&ebu_host->dma_access_complete); +} + +static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir, + const u8 *buf, u32 len) +{ + struct dma_async_tx_descriptor *tx; + struct completion *dma_completion; + dma_async_tx_callback callback; + struct dma_chan *chan; + dma_cookie_t cookie; + unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + dma_addr_t buf_dma; + int ret; + u32 timeout; + + if (dir == DMA_DEV_TO_MEM) { + chan = ebu_host->dma_rx; + dma_completion = &ebu_host->dma_access_complete; + callback = ebu_dma_rx_callback; + } else { + chan = ebu_host->dma_tx; + dma_completion = &ebu_host->dma_access_complete; + callback = ebu_dma_tx_callback; + } + + buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir); + if (dma_mapping_error(chan->device->dev, buf_dma)) { + dev_err(ebu_host->dev, "Failed to map DMA buffer\n"); + ret = -EIO; + goto err_unmap; + } + + tx = dmaengine_prep_slave_single(chan, buf_dma, len, dir, flags); + if (!tx) + return -ENXIO; + + tx->callback = callback; + tx->callback_param = ebu_host; + cookie = tx->tx_submit(tx); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(ebu_host->dev, "dma_submit_error %d\n", cookie); + ret = -EIO; + goto err_unmap; + } + + init_completion(dma_completion); + dma_async_issue_pending(chan); + + /* Wait DMA to finish the data transfer.*/ + timeout = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000)); + if (!timeout) { + dev_err(ebu_host->dev, "I/O Error in DMA RX (status %d)\n", + dmaengine_tx_status(chan, cookie, NULL)); + dmaengine_terminate_sync(chan); + ret = -ETIMEDOUT; + goto err_unmap; + } + + return 0; + +err_unmap: + dma_unmap_single(ebu_host->dev, buf_dma, len, dir); + + return ret; +} + +static void ebu_nand_trigger(struct ebu_nand_controller *ebu_host, + int page, u32 cmd) +{ + unsigned int val; + + val = cmd | (page & 0xFF) << HSNAND_CTL1_ADDR_SHIFT; + writel(val, ebu_host->hsnand + HSNAND_CTL1); + val = (page & 0xFFFF00) >> 8 | HSNAND_CTL2_CYC_N_V5; + writel(val, ebu_host->hsnand + HSNAND_CTL2); + + writel(ebu_host->nd_para0, ebu_host->hsnand + HSNAND_PARA0); + + /* clear first, will update later */ + writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_0); + writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_1); + + writel(HSNAND_INT_MSK_CTL_WR_C, + ebu_host->hsnand + HSNAND_INT_MSK_CTL); + + if (!cmd) + val = HSNAND_CTL_RW_READ; + else + val = HSNAND_CTL_RW_WRITE; + + writel(HSNAND_CTL_MSG_EN | HSNAND_CTL_CKFF_EN | + HSNAND_CTL_ECC_OFF_V8TH | HSNAND_CTL_CE_SEL_CS(ebu_host->cs_num) | + HSNAND_CTL_ENABLE_ECC | HSNAND_CTL_GO | val, + ebu_host->hsnand + HSNAND_CTL); +} + +static int ebu_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); + int ret, reg_data; + + ebu_nand_trigger(ebu_host, page, NAND_CMD_READ0); + + ret = ebu_dma_start(ebu_host, DMA_DEV_TO_MEM, buf, mtd->writesize); + if (ret) + return ret; + + if (oob_required) + chip->ecc.read_oob(chip, page); + + reg_data = readl(ebu_host->hsnand + HSNAND_CTL); + reg_data &= ~HSNAND_CTL_GO; + writel(reg_data, ebu_host->hsnand + HSNAND_CTL); + + return 0; +} + +static int ebu_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf, + int oob_required, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); + void __iomem *int_sta = ebu_host->hsnand + HSNAND_INT_STA; + int reg_data, ret, val; + u32 reg; + + ebu_nand_trigger(ebu_host, page, NAND_CMD_SEQIN); + + ret = ebu_dma_start(ebu_host, DMA_MEM_TO_DEV, buf, mtd->writesize); + if (ret) + return ret; + + if (oob_required) { + reg = get_unaligned_le32(chip->oob_poi); + writel(reg, ebu_host->hsnand + HSNAND_CMSG_0); + + reg = get_unaligned_le32(chip->oob_poi + 4); + writel(reg, ebu_host->hsnand + HSNAND_CMSG_1); + } + + ret = readl_poll_timeout_atomic(int_sta, val, !(val & HSNAND_INT_STA_WR_C), + 10, 1000); + if (ret) + return ret; + + reg_data = readl(ebu_host->hsnand + HSNAND_CTL); + reg_data &= ~HSNAND_CTL_GO; + writel(reg_data, ebu_host->hsnand + HSNAND_CTL); + + return 0; +} + +static const u8 ecc_strength[] = { 1, 1, 4, 8, 24, 32, 40, 60, }; + +static int ebu_nand_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); + u32 ecc_steps, ecc_bytes, ecc_total, pagesize, pg_per_blk; + u32 ecc_strength_ds = chip->ecc.strength; + u32 ecc_size = chip->ecc.size; + u32 writesize = mtd->writesize; + u32 blocksize = mtd->erasesize; + int bch_algo, start, val; + + /* Default to an ECC size of 512 */ + if (!chip->ecc.size) + chip->ecc.size = 512; + + switch (ecc_size) { + case 512: + start = 1; + if (!ecc_strength_ds) + ecc_strength_ds = 4; + break; + case 1024: + start = 4; + if (!ecc_strength_ds) + ecc_strength_ds = 32; + break; + default: + return -EINVAL; + } + + /* BCH ECC algorithm Settings for number of bits per 512B/1024B */ + bch_algo = round_up(start + 1, 4); + for (val = start; val < bch_algo; val++) { + if (ecc_strength_ds == ecc_strength[val]) + break; + } + if (val == bch_algo) + return -EINVAL; + + if (ecc_strength_ds == 8) + ecc_bytes = 14; + else + ecc_bytes = DIV_ROUND_UP(ecc_strength_ds * fls(8 * ecc_size), 8); + + ecc_steps = writesize / ecc_size; + ecc_total = ecc_steps * ecc_bytes; + if ((ecc_total + 8) > mtd->oobsize) + return -ERANGE; + + chip->ecc.total = ecc_total; + pagesize = fls(writesize >> 11); + if (pagesize > HSNAND_PARA0_PAGE_V8192) + return -ERANGE; + + pg_per_blk = fls((blocksize / writesize) >> 6) / 8; + if (pg_per_blk > HSNAND_PARA0_PIB_V256) + return -ERANGE; + + ebu_host->nd_para0 = pagesize | pg_per_blk | HSNAND_PARA0_BYP_EN_NP | + HSNAND_PARA0_BYP_DEC_NP | HSNAND_PARA0_ADEP_EN | + HSNAND_PARA0_TYPE_ONFI | (val << 29); + + mtd_set_ooblayout(mtd, &ebu_nand_ooblayout_ops); + chip->ecc.read_page = ebu_nand_read_page_hwecc; + chip->ecc.write_page = ebu_nand_write_page_hwecc; + + return 0; +} + +static int ebu_nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, bool check_only) +{ + const struct nand_op_instr *instr = NULL; + unsigned int op_id; + int i, timeout_ms, ret = 0; + + if (check_only) + return 0; + + ebu_select_chip(chip); + for (op_id = 0; op_id < op->ninstrs; op_id++) { + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + ebu_nand_writeb(chip, HSNAND_CLE_OFFS | HSNAND_CS_OFFS, + instr->ctx.cmd.opcode); + break; + + case NAND_OP_ADDR_INSTR: + for (i = 0; i < instr->ctx.addr.naddrs; i++) + ebu_nand_writeb(chip, + HSNAND_ALE_OFFS | HSNAND_CS_OFFS, + instr->ctx.addr.addrs[i]); + break; + + case NAND_OP_DATA_IN_INSTR: + ebu_read_buf(chip, instr->ctx.data.buf.in, + instr->ctx.data.len); + break; + + case NAND_OP_DATA_OUT_INSTR: + ebu_write_buf(chip, instr->ctx.data.buf.out, + instr->ctx.data.len); + break; + + case NAND_OP_WAITRDY_INSTR: + timeout_ms = instr->ctx.waitrdy.timeout_ms * 1000; + ret = ebu_nand_waitrdy(chip, timeout_ms); + break; + } + } + + return ret; +} + +static const struct nand_controller_ops ebu_nand_controller_ops = { + .attach_chip = ebu_nand_attach_chip, + .setup_interface = ebu_nand_set_timings, + .exec_op = ebu_nand_exec_op, +}; + +static void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host) +{ + if (ebu_host->dma_rx) + dma_release_channel(ebu_host->dma_rx); + + if (ebu_host->dma_tx) + dma_release_channel(ebu_host->dma_tx); +} + +static int ebu_nand_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ebu_nand_controller *ebu_host; + struct nand_chip *nand; + struct mtd_info *mtd = NULL; + struct resource *res; + char *resname; + int ret; + u32 cs; + + ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL); + if (!ebu_host) + return -ENOMEM; + + ebu_host->dev = dev; + nand_controller_init(&ebu_host->controller); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ebunand"); + ebu_host->ebu = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ebu_host->ebu)) + return PTR_ERR(ebu_host->ebu); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsnand"); + ebu_host->hsnand = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ebu_host->hsnand)) + return PTR_ERR(ebu_host->hsnand); + + ret = device_property_read_u32(dev, "reg", &cs); + if (ret) { + dev_err(dev, "failed to get chip select: %d\n", ret); + return ret; + } + ebu_host->cs_num = cs; + + resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname); + ebu_host->cs[cs].chipaddr = devm_ioremap_resource(dev, res); + ebu_host->cs[cs].nand_pa = res->start; + if (IS_ERR(ebu_host->cs[cs].chipaddr)) + return PTR_ERR(ebu_host->cs[cs].chipaddr); + + ebu_host->clk = devm_clk_get(dev, NULL); + if (IS_ERR(ebu_host->clk)) + return dev_err_probe(dev, PTR_ERR(ebu_host->clk), + "failed to get clock\n"); + + ret = clk_prepare_enable(ebu_host->clk); + if (ret) { + dev_err(dev, "failed to enable clock: %d\n", ret); + return ret; + } + ebu_host->clk_rate = clk_get_rate(ebu_host->clk); + + ebu_host->dma_tx = dma_request_chan(dev, "tx"); + if (IS_ERR(ebu_host->dma_tx)) + return dev_err_probe(dev, PTR_ERR(ebu_host->dma_tx), + "failed to request DMA tx chan!.\n"); + + ebu_host->dma_rx = dma_request_chan(dev, "rx"); + if (IS_ERR(ebu_host->dma_rx)) + return dev_err_probe(dev, PTR_ERR(ebu_host->dma_rx), + "failed to request DMA rx chan!.\n"); + + resname = devm_kasprintf(dev, GFP_KERNEL, "addr_sel%d", cs); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname); + if (!res) + return -EINVAL; + ebu_host->cs[cs].addr_sel = res->start; + writel(ebu_host->cs[cs].addr_sel | EBU_ADDR_MASK(5) | EBU_ADDR_SEL_REGEN, + ebu_host->ebu + EBU_ADDR_SEL(cs)); + + nand_set_flash_node(&ebu_host->chip, dev->of_node); + if (!mtd->name) { + dev_err(ebu_host->dev, "NAND label property is mandatory\n"); + return -EINVAL; + } + + mtd = nand_to_mtd(&ebu_host->chip); + mtd->dev.parent = dev; + ebu_host->dev = dev; + + platform_set_drvdata(pdev, ebu_host); + nand_set_controller_data(&ebu_host->chip, ebu_host); + + nand = &ebu_host->chip; + nand->controller = &ebu_host->controller; + nand->controller->ops = &ebu_nand_controller_ops; + + /* Scan to find existence of the device */ + ret = nand_scan(&ebu_host->chip, 1); + if (ret) + goto err_cleanup_dma; + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) + goto err_clean_nand; + + return 0; + +err_clean_nand: + nand_cleanup(&ebu_host->chip); +err_cleanup_dma: + ebu_dma_cleanup(ebu_host); + clk_disable_unprepare(ebu_host->clk); + + return ret; +} + +static int ebu_nand_remove(struct platform_device *pdev) +{ + struct ebu_nand_controller *ebu_host = platform_get_drvdata(pdev); + int ret; + + ret = mtd_device_unregister(nand_to_mtd(&ebu_host->chip)); + WARN_ON(ret); + nand_cleanup(&ebu_host->chip); + ebu_nand_disable(&ebu_host->chip); + ebu_dma_cleanup(ebu_host); + clk_disable_unprepare(ebu_host->clk); + + return 0; +} + +static const struct of_device_id ebu_nand_match[] = { + { .compatible = "intel,nand-controller" }, + { .compatible = "intel,lgm-ebunand" }, + {} +}; +MODULE_DEVICE_TABLE(of, ebu_nand_match); + +static struct platform_driver ebu_nand_driver = { + .probe = ebu_nand_probe, + .remove = ebu_nand_remove, + .driver = { + .name = "intel-nand-controller", + .of_match_table = ebu_nand_match, + }, + +}; +module_platform_driver(ebu_nand_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Vadivel Murugan R "); +MODULE_DESCRIPTION("Intel's LGM External Bus NAND Controller driver"); -- cgit v1.2.3 From d1c3ede6a3374b8046d6b6cccdecf8645292bf39 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 26 Nov 2020 00:09:46 -0300 Subject: mtd: rawnand: gpmi: Use of_device_get_match_data() The retrieval of driver data via of_device_get_match_data() can make the code simpler. Use of_device_get_match_data() to simplify the code. Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201126030946.2058-1-festevam@gmail.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index b5f46f214a58..0d30f3fbb045 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -2487,21 +2487,13 @@ MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); static int gpmi_nand_probe(struct platform_device *pdev) { struct gpmi_nand_data *this; - const struct of_device_id *of_id; int ret; this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); if (!this) return -ENOMEM; - of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); - if (of_id) { - this->devdata = of_id->data; - } else { - dev_err(&pdev->dev, "Failed to find the right device id.\n"); - return -ENODEV; - } - + this->devdata = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, this); this->pdev = pdev; this->dev = &pdev->dev; -- cgit v1.2.3 From ad8566d3555c4731e6b48823b92d3929b0394c14 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 3 Dec 2020 11:39:48 +0300 Subject: mtd: rawnand: meson: Fix a resource leak in init Call clk_disable_unprepare(nfc->phase_rx) if the clk_set_rate() function fails to avoid a resource leak. Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller") Signed-off-by: Dan Carpenter Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/X8ikVCnUsfTpffFB@mwanda --- drivers/mtd/nand/raw/meson_nand.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index a76afea6ea77..817bddccb775 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1044,9 +1044,12 @@ static int meson_nfc_clk_init(struct meson_nfc *nfc) ret = clk_set_rate(nfc->device_clk, 24000000); if (ret) - goto err_phase_rx; + goto err_disable_rx; return 0; + +err_disable_rx: + clk_disable_unprepare(nfc->phase_rx); err_phase_rx: clk_disable_unprepare(nfc->phase_tx); err_phase_tx: -- cgit v1.2.3 From 5876f2d93d195be552eacefb34905b9cc8d451b0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 4 Dec 2020 11:58:17 -0300 Subject: mtd: rawnand: mxc: Use device_get_match_data() The retrieval of driver data can be a bit simplified by using device_get_match_data(), so switch to it. Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201204145818.32739-1-festevam@gmail.com --- drivers/mtd/nand/raw/mxc_nand.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index f896364968d8..d5c445eb93ad 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1714,7 +1714,6 @@ static const struct nand_controller_ops mxcnd_controller_ops = { static int mxcnd_probe(struct platform_device *pdev) { - const struct of_device_id *of_id; struct nand_chip *this; struct mtd_info *mtd; struct mxc_nand_host *host; @@ -1756,8 +1755,7 @@ static int mxcnd_probe(struct platform_device *pdev) if (IS_ERR(host->clk)) return PTR_ERR(host->clk); - of_id = of_match_device(mxcnd_dt_ids, host->dev); - host->devtype_data = of_id->data; + host->devtype_data = device_get_match_data(&pdev->dev); if (!host->devtype_data->setup_interface) this->options |= NAND_KEEP_TIMINGS; -- cgit v1.2.3 From 5e214b2554f8b8e44eed62f62196406cbfe3caa8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 4 Dec 2020 11:58:18 -0300 Subject: mtd: rawnand: mxc: Use a single line for of_device_id The .compatible and .data pairs can be stored in a single line, which makes the code more concise. Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201204145818.32739-2-festevam@gmail.com --- drivers/mtd/nand/raw/mxc_nand.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index d5c445eb93ad..fd705dd1768d 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -1610,22 +1610,11 @@ static inline int is_imx53_nfc(struct mxc_nand_host *host) } static const struct of_device_id mxcnd_dt_ids[] = { - { - .compatible = "fsl,imx21-nand", - .data = &imx21_nand_devtype_data, - }, { - .compatible = "fsl,imx27-nand", - .data = &imx27_nand_devtype_data, - }, { - .compatible = "fsl,imx25-nand", - .data = &imx25_nand_devtype_data, - }, { - .compatible = "fsl,imx51-nand", - .data = &imx51_nand_devtype_data, - }, { - .compatible = "fsl,imx53-nand", - .data = &imx53_nand_devtype_data, - }, + { .compatible = "fsl,imx21-nand", .data = &imx21_nand_devtype_data, }, + { .compatible = "fsl,imx27-nand", .data = &imx27_nand_devtype_data, }, + { .compatible = "fsl,imx25-nand", .data = &imx25_nand_devtype_data, }, + { .compatible = "fsl,imx51-nand", .data = &imx51_nand_devtype_data, }, + { .compatible = "fsl,imx53-nand", .data = &imx53_nand_devtype_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); -- cgit v1.2.3 From b1209582fb08897ab9da47076d0637ed64e7a4c3 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 26 Nov 2020 14:27:05 +0530 Subject: mtd: rawnand: qcom: Add NAND controller support for SDX55 SDX55 uses QPIC version 2.0.0 IP for the NAND controller support. In this version, DEV_CMD_* registers are moved to operational state, hence CPU access in BAM mode is restricted. So, skip accessing these registers and also use a different config for reading ONFI parameters. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201126085705.48399-3-manivannan.sadhasivam@linaro.org --- drivers/mtd/nand/raw/qcom_nandc.c | 68 +++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 17 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index f69457c80be7..667e4bfe369f 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -145,6 +145,7 @@ #define OP_PAGE_READ 0x2 #define OP_PAGE_READ_WITH_ECC 0x3 #define OP_PAGE_READ_WITH_ECC_SPARE 0x4 +#define OP_PAGE_READ_ONFI_READ 0x5 #define OP_PROGRAM_PAGE 0x6 #define OP_PAGE_PROGRAM_WITH_ECC 0x7 #define OP_PROGRAM_PAGE_SPARE 0x9 @@ -460,12 +461,14 @@ struct qcom_nand_host { * @ecc_modes - ecc mode for NAND * @is_bam - whether NAND controller is using BAM * @is_qpic - whether NAND CTRL is part of qpic IP + * @qpic_v2 - flag to indicate QPIC IP version 2 * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset */ struct qcom_nandc_props { u32 ecc_modes; bool is_bam; bool is_qpic; + bool qpic_v2; u32 dev_cmd_reg_start; }; @@ -1164,7 +1167,13 @@ static int nandc_param(struct qcom_nand_host *host) * in use. we configure the controller to perform a raw read of 512 * bytes to read onfi params */ - nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE); + if (nandc->props->qpic_v2) + nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ_ONFI_READ | + PAGE_ACC | LAST_PAGE); + else + nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | + PAGE_ACC | LAST_PAGE); + nandc_set_reg(nandc, NAND_ADDR0, 0); nandc_set_reg(nandc, NAND_ADDR1, 0); nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE @@ -1180,21 +1189,28 @@ static int nandc_param(struct qcom_nand_host *host) | 1 << DEV0_CFG1_ECC_DISABLE); nandc_set_reg(nandc, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); - /* configure CMD1 and VLD for ONFI param probing */ - nandc_set_reg(nandc, NAND_DEV_CMD_VLD, - (nandc->vld & ~READ_START_VLD)); - nandc_set_reg(nandc, NAND_DEV_CMD1, - (nandc->cmd1 & ~(0xFF << READ_ADDR)) - | NAND_CMD_PARAM << READ_ADDR); + /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ + if (!nandc->props->qpic_v2) { + nandc_set_reg(nandc, NAND_DEV_CMD_VLD, + (nandc->vld & ~READ_START_VLD)); + nandc_set_reg(nandc, NAND_DEV_CMD1, + (nandc->cmd1 & ~(0xFF << READ_ADDR)) + | NAND_CMD_PARAM << READ_ADDR); + } nandc_set_reg(nandc, NAND_EXEC_CMD, 1); - nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); - nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); + if (!nandc->props->qpic_v2) { + nandc_set_reg(nandc, NAND_DEV_CMD1_RESTORE, nandc->cmd1); + nandc_set_reg(nandc, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); + } + nandc_set_read_loc(nandc, 0, 0, 512, 1); - write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); - write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + if (!nandc->props->qpic_v2) { + write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + } nandc->buf_count = 512; memset(nandc->data_buffer, 0xff, nandc->buf_count); @@ -1205,8 +1221,10 @@ static int nandc_param(struct qcom_nand_host *host) nandc->buf_count, 0); /* restore CMD1 and VLD regs */ - write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); - write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); + if (!nandc->props->qpic_v2) { + write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); + } return 0; } @@ -2772,8 +2790,10 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) /* kill onenand */ if (!nandc->props->is_qpic) nandc_write(nandc, SFLASHC_BURST_CFG, 0); - nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), - NAND_DEV_CMD_VLD_VAL); + + if (!nandc->props->qpic_v2) + nandc_write(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD_VLD), + NAND_DEV_CMD_VLD_VAL); /* enable ADM or BAM DMA */ if (nandc->props->is_bam) { @@ -2793,8 +2813,10 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) } /* save the original values of these registers */ - nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); - nandc->vld = NAND_DEV_CMD_VLD_VAL; + if (!nandc->props->qpic_v2) { + nandc->cmd1 = nandc_read(nandc, dev_cmd_reg_addr(nandc, NAND_DEV_CMD1)); + nandc->vld = NAND_DEV_CMD_VLD_VAL; + } return 0; } @@ -3052,6 +3074,14 @@ static const struct qcom_nandc_props ipq8074_nandc_props = { .dev_cmd_reg_start = 0x7000, }; +static const struct qcom_nandc_props sdx55_nandc_props = { + .ecc_modes = (ECC_BCH_4BIT | ECC_BCH_8BIT), + .is_bam = true, + .is_qpic = true, + .qpic_v2 = true, + .dev_cmd_reg_start = 0x7000, +}; + /* * data will hold a struct pointer containing more differences once we support * more controller variants @@ -3073,6 +3103,10 @@ static const struct of_device_id qcom_nandc_of_match[] = { .compatible = "qcom,ipq8074-nand", .data = &ipq8074_nandc_props, }, + { + .compatible = "qcom,sdx55-nand", + .data = &sdx55_nandc_props, + }, {} }; MODULE_DEVICE_TABLE(of, qcom_nandc_of_match); -- cgit v1.2.3 From 46337d158262465a89f3568c94410ea553aa15b9 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Tue, 8 Dec 2020 21:51:03 -0600 Subject: mtd: rawnand: gpmi: Fix the driver only sense CS0 R/B issue Set the GPMI CTRL1 GANGED_RDYBUSY bit so driver can sense the R/B signal from all CS. For the NAND chip MT29F64G08AFAAAWP, only the first chip detected without the patch. [ 3.764118] nand: device found, Manufacturer ID: 0x2c, Chip ID: 0x68 [ 3.770613] nand: Micron MT29F64G08AFAAAWP [ 3.774752] nand: 4096 MiB, SLC, erase size: 1024 KiB, page size: 8192, OOB size: 448 [ 3.786421] Bad block table found at page 524160, version 0x01 [ 3.792730] Bad block table found at page 524032, version 0x01 After applying the patch [ 3.764445] nand: device found, Manufacturer ID: 0x2c, Chip ID: 0x68 [ 3.770941] nand: Micron MT29F64G08AFAAAWP [ 3.775080] nand: 4096 MiB, SLC, erase size: 1024 KiB, page size: 8192, OOB size: 448 [ 3.784390] nand: 2 chips detected [ 3.790900] Bad block table found at page 524160, version 0x01 [ 3.796776] Bad block table found at page 1048448, version 0x01 Signed-off-by: Han Xu Acked-by: Sascha Hauer Reviewed-by: Fabio Estevam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201209035104.22679-2-han.xu@nxp.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 6 ++++-- drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 0d30f3fbb045..b06e202cdd0d 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -181,9 +181,11 @@ static int gpmi_init(struct gpmi_nand_data *this) /* * Decouple the chip select from dma channel. We use dma0 for all - * the chips. + * the chips, force all NAND RDY_BUSY inputs to be sourced from + * RDY_BUSY0. */ - writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); + writel(BM_GPMI_CTRL1_DECOUPLE_CS | BM_GPMI_CTRL1_GANGED_RDYBUSY, + r->gpmi_regs + HW_GPMI_CTRL1_SET); err_out: pm_runtime_mark_last_busy(this->dev); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h index f5e4f26c34da..fc31fd084dcf 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-regs.h @@ -107,6 +107,7 @@ #define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2 #define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3 +#define BM_GPMI_CTRL1_GANGED_RDYBUSY (1 << 19) #define BM_GPMI_CTRL1_BCH_MODE (1 << 18) #define BP_GPMI_CTRL1_DLL_ENABLE 17 -- cgit v1.2.3 From 7671edeb193910482a9b0c22cd32176e7de7b2ed Mon Sep 17 00:00:00 2001 From: Han Xu Date: Tue, 8 Dec 2020 21:51:04 -0600 Subject: mtd: rawnand: gpmi: Fix the random DMA timeout issue To get better performance, current gpmi driver collected and chained all small DMA transfers in gpmi_nfc_exec_op, the whole chain triggered and wait for complete at the end. But some random DMA timeout found in this new driver, with the help of ftrace, we found the root cause is as follows: Take gpmi_ecc_read_page() as an example, gpmi_nfc_exec_op collected 6 DMA transfers and the DMA chain triggered at the end. It waits for bch completion and check jiffies if it's timeout. The typical function graph shown below, 63.216351 | 1) | gpmi_ecc_read_page() { 63.216352 | 1) 0.750 us | gpmi_bch_layout_std(); 63.216354 | 1) | gpmi_nfc_exec_op() { 63.216355 | 1) | gpmi_chain_command() { 63.216356 | 1) | mxs_dma_prep_slave_sg() { 63.216357 | 1) | /* mxs chan ccw idx: 0 */ 63.216358 | 1) 1.750 us | } 63.216359 | 1) | mxs_dma_prep_slave_sg() { 63.216360 | 1) | /* mxs chan ccw idx: 1 */ 63.216361 | 1) 2.000 us | } 63.216361 | 1) 6.500 us | } 63.216362 | 1) | gpmi_chain_command() { 63.216363 | 1) | mxs_dma_prep_slave_sg() { 63.216364 | 1) | /* mxs chan ccw idx: 2 */ 63.216365 | 1) 1.750 us | } 63.216366 | 1) | mxs_dma_prep_slave_sg() { 63.216367 | 1) | /* mxs chan ccw idx: 3 */ 63.216367 | 1) 1.750 us | } 63.216368 | 1) 5.875 us | } 63.216369 | 1) | /* gpmi_chain_wait_ready */ 63.216370 | 1) | mxs_dma_prep_slave_sg() { 63.216372 | 1) | /* mxs chan ccw idx: 4 */ 63.216373 | 1) 3.000 us | } 63.216374 | 1) | /* gpmi_chain_data_read */ 63.216376 | 1) | mxs_dma_prep_slave_sg() { 63.216377 | 1) | /* mxs chan ccw idx: 5 */ 63.216378 | 1) 2.000 us | } 63.216379 | 1) 1.125 us | mxs_dma_tx_submit(); 63.216381 | 1) 1.000 us | mxs_dma_enable_chan(); 63.216712 | 0) 2.625 us | mxs_dma_int_handler(); 63.216717 | 0) 4.250 us | bch_irq(); 63.216723 | 0) 1.250 us | mxs_dma_tasklet(); 63.216723 | 1) | /* jiffies left 250 */ 63.216725 | 1) ! 372.000 us | } 63.216726 | 1) 2.625 us | gpmi_count_bitflips(); 63.216730 | 1) ! 379.125 us | } but it's not gurantee that bch irq handled always after dma irq handled, sometimes bch_irq comes first and gpmi_nfc_exec_op won't wait anymore, another gpmi_nfc_exec_op may get invoked before last DMA chain IRQ handled, this messed up the next DMA chain and causes DMA timeout. Check the trace log when issue happened. 63.218923 | 1) | gpmi_ecc_read_page() { 63.218924 | 1) 0.625 us | gpmi_bch_layout_std(); 63.218926 | 1) | gpmi_nfc_exec_op() { 63.218927 | 1) | gpmi_chain_command() { 63.218928 | 1) | mxs_dma_prep_slave_sg() { 63.218929 | 1) | /* mxs chan ccw idx: 0 */ 63.218929 | 1) 1.625 us | } 63.218931 | 1) | mxs_dma_prep_slave_sg() { 63.218931 | 1) | /* mxs chan ccw idx: 1 */ 63.218932 | 1) 1.750 us | } 63.218933 | 1) 5.875 us | } 63.218934 | 1) | gpmi_chain_command() { 63.218934 | 1) | mxs_dma_prep_slave_sg() { 63.218935 | 1) | /* mxs chan ccw idx: 2 */ 63.218936 | 1) 1.875 us | } 63.218937 | 1) | mxs_dma_prep_slave_sg() { 63.218938 | 1) | /* mxs chan ccw idx: 3 */ 63.218939 | 1) 1.625 us | } 63.218939 | 1) 5.875 us | } 63.218940 | 1) | /* gpmi_chain_wait_ready */ 63.218941 | 1) | mxs_dma_prep_slave_sg() { 63.218942 | 1) | /* mxs chan ccw idx: 4 */ 63.218942 | 1) 1.625 us | } 63.218943 | 1) | /* gpmi_chain_data_read */ 63.218944 | 1) | mxs_dma_prep_slave_sg() { 63.218945 | 1) | /* mxs chan ccw idx: 5 */ 63.218947 | 1) 2.375 us | } 63.218948 | 1) 0.625 us | mxs_dma_tx_submit(); 63.218949 | 1) 1.000 us | mxs_dma_enable_chan(); 63.219276 | 0) 5.125 us | bch_irq(); <---- 63.219283 | 1) | /* jiffies left 250 */ 63.219285 | 1) ! 358.625 us | } 63.219286 | 1) 2.750 us | gpmi_count_bitflips(); 63.219289 | 1) ! 366.000 us | } 63.219290 | 1) | gpmi_ecc_read_page() { 63.219291 | 1) 0.750 us | gpmi_bch_layout_std(); 63.219293 | 1) | gpmi_nfc_exec_op() { 63.219294 | 1) | gpmi_chain_command() { 63.219295 | 1) | mxs_dma_prep_slave_sg() { 63.219295 | 0) 1.875 us | mxs_dma_int_handler(); <---- 63.219296 | 1) | /* mxs chan ccw idx: 6 */ 63.219297 | 1) 2.250 us | } 63.219298 | 1) | mxs_dma_prep_slave_sg() { 63.219298 | 0) 1.000 us | mxs_dma_tasklet(); 63.219299 | 1) | /* mxs chan ccw idx: 0 */ 63.219300 | 1) 1.625 us | } 63.219300 | 1) 6.375 us | } 63.219301 | 1) | gpmi_chain_command() { 63.219302 | 1) | mxs_dma_prep_slave_sg() { 63.219303 | 1) | /* mxs chan ccw idx: 1 */ 63.219304 | 1) 1.625 us | } 63.219305 | 1) | mxs_dma_prep_slave_sg() { 63.219306 | 1) | /* mxs chan ccw idx: 2 */ 63.219306 | 1) 1.875 us | } 63.219307 | 1) 6.000 us | } 63.219308 | 1) | /* gpmi_chain_wait_ready */ 63.219308 | 1) | mxs_dma_prep_slave_sg() { 63.219309 | 1) | /* mxs chan ccw idx: 3 */ 63.219310 | 1) 2.000 us | } 63.219311 | 1) | /* gpmi_chain_data_read */ 63.219312 | 1) | mxs_dma_prep_slave_sg() { 63.219313 | 1) | /* mxs chan ccw idx: 4 */ 63.219314 | 1) 1.750 us | } 63.219315 | 1) 0.625 us | mxs_dma_tx_submit(); 63.219316 | 1) 0.875 us | mxs_dma_enable_chan(); 64.224227 | 1) | /* jiffies left 0 */ In the first gpmi_nfc_exec_op, bch_irq comes first and gpmi_nfc_exec_op exits, but DMA IRQ still not happened yet until the middle of following gpmi_nfc_exec_op, the first DMA transfer index get messed and DMA get timeout. To fix the issue, when there is bch ops in DMA chain, the gpmi_nfc_exec_op should wait for both completions rather than bch completion only. Fixes: ef347c0cfd61 ("mtd: rawnand: gpmi: Implement exec_op") Signed-off-by: Han Xu Reviewed-by: Sascha Hauer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201209035104.22679-3-han.xu@nxp.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index b06e202cdd0d..aaae000f9169 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -2256,7 +2256,7 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip, void *buf_read = NULL; const void *buf_write = NULL; bool direct = false; - struct completion *completion; + struct completion *dma_completion, *bch_completion; unsigned long to; if (check_only) @@ -2353,22 +2353,24 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip, this->resources.bch_regs + HW_BCH_FLASH0LAYOUT1); } + desc->callback = dma_irq_callback; + desc->callback_param = this; + dma_completion = &this->dma_done; + bch_completion = NULL; + + init_completion(dma_completion); + if (this->bch && buf_read) { writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, this->resources.bch_regs + HW_BCH_CTRL_SET); - completion = &this->bch_done; - } else { - desc->callback = dma_irq_callback; - desc->callback_param = this; - completion = &this->dma_done; + bch_completion = &this->bch_done; + init_completion(bch_completion); } - init_completion(completion); - dmaengine_submit(desc); dma_async_issue_pending(get_dma_chan(this)); - to = wait_for_completion_timeout(completion, msecs_to_jiffies(1000)); + to = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000)); if (!to) { dev_err(this->dev, "DMA timeout, last DMA\n"); gpmi_dump_info(this); @@ -2376,6 +2378,16 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip, goto unmap; } + if (this->bch && buf_read) { + to = wait_for_completion_timeout(bch_completion, msecs_to_jiffies(1000)); + if (!to) { + dev_err(this->dev, "BCH timeout, last DMA\n"); + gpmi_dump_info(this); + ret = -ETIMEDOUT; + goto unmap; + } + } + writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, this->resources.bch_regs + HW_BCH_CTRL_CLR); gpmi_clear_bch(this); -- cgit v1.2.3 From ea7110b87bf9c32eb57311da8011b464d18d80cd Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 8 Dec 2020 19:12:43 -0300 Subject: mtd: rawnand: gpmi: Use a single line for of_device_id The .compatible and .data pairs can be stored in a single line, which makes the code more concise. Signed-off-by: Fabio Estevam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201208221243.3255-1-festevam@gmail.com --- drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index aaae000f9169..5cdf05bcbf8f 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -2479,22 +2479,12 @@ err_out: } static const struct of_device_id gpmi_nand_id_table[] = { - { - .compatible = "fsl,imx23-gpmi-nand", - .data = &gpmi_devdata_imx23, - }, { - .compatible = "fsl,imx28-gpmi-nand", - .data = &gpmi_devdata_imx28, - }, { - .compatible = "fsl,imx6q-gpmi-nand", - .data = &gpmi_devdata_imx6q, - }, { - .compatible = "fsl,imx6sx-gpmi-nand", - .data = &gpmi_devdata_imx6sx, - }, { - .compatible = "fsl,imx7d-gpmi-nand", - .data = &gpmi_devdata_imx7d, - }, {} + { .compatible = "fsl,imx23-gpmi-nand", .data = &gpmi_devdata_imx23, }, + { .compatible = "fsl,imx28-gpmi-nand", .data = &gpmi_devdata_imx28, }, + { .compatible = "fsl,imx6q-gpmi-nand", .data = &gpmi_devdata_imx6q, }, + { .compatible = "fsl,imx6sx-gpmi-nand", .data = &gpmi_devdata_imx6sx, }, + { .compatible = "fsl,imx7d-gpmi-nand", .data = &gpmi_devdata_imx7d,}, + {} }; MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); -- cgit v1.2.3 From 058e0e847d54944c5dc9ec6d29727e1449feb131 Mon Sep 17 00:00:00 2001 From: Yifeng Zhao Date: Thu, 10 Dec 2020 08:21:32 +0800 Subject: mtd: rawnand: rockchip: NFC driver for RK3308, RK2928 and others This driver supports Rockchip NFC (NAND Flash Controller) found on RK3308, RK2928, RKPX30, RV1108 and other SOCs. The driver has been tested using 8-bit NAND interface on the ARM based RK3308 platform. Support Rockchip SoCs and NFC versions: - PX30 and RK3326(NFCv900). ECC: 16/40/60/70 bits/1KB. CLOCK: ahb and nfc. - RK3308 and RV1108(NFCv800). ECC: 16 bits/1KB. CLOCK: ahb and nfc. - RK3036 and RK3128(NFCv622). ECC: 16/24/40/60 bits/1KB. CLOCK: ahb and nfc. - RK3066, RK3188 and RK2928(NFCv600). ECC: 16/24/40/60 bits/1KB. CLOCK: ahb. Supported features: - Read full page data by DMA. - Support HW ECC(one step is 1KB). - Support 2 - 32K page size. - Support 8 CS(depend on SoCs) Limitations: - No support for the ecc step size is 512. - Untested on some SoCs. - No support for subpages. - No support for the builtin randomizer. - The original bad block mask is not supported. It is recommended to use the BBT(bad block table). Suggested-by: Johan Jonker Signed-off-by: Yifeng Zhao Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20201210002134.5686-3-yifeng.zhao@rock-chips.com --- drivers/mtd/nand/raw/Kconfig | 12 + drivers/mtd/nand/raw/Makefile | 1 + drivers/mtd/nand/raw/rockchip-nand-controller.c | 1495 +++++++++++++++++++++++ 3 files changed, 1508 insertions(+) create mode 100644 drivers/mtd/nand/raw/rockchip-nand-controller.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index c321442f4ea6..442a039b92f3 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -449,6 +449,18 @@ config MTD_NAND_INTEL_LGM Enables support for NAND Flash chips on Intel's LGM SoC. NAND flash controller interfaced through the External Bus Unit. +config MTD_NAND_ROCKCHIP + tristate "Rockchip NAND controller" + depends on ARCH_ROCKCHIP && HAS_IOMEM + help + Enables support for NAND controller on Rockchip SoCs. + There are four different versions of NAND FLASH Controllers, + including: + NFC v600: RK2928, RK3066, RK3188 + NFC v622: RK3036, RK3128 + NFC v800: RK3308, RV1108 + NFC v900: PX30, RK3326 + comment "Misc" config MTD_SM_COMMON diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index 9a63174df1b9..32475a28d8f8 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o obj-$(CONFIG_MTD_NAND_CADENCE) += cadence-nand-controller.o obj-$(CONFIG_MTD_NAND_ARASAN) += arasan-nand-controller.o obj-$(CONFIG_MTD_NAND_INTEL_LGM) += intel-nand-controller.o +obj-$(CONFIG_MTD_NAND_ROCKCHIP) += rockchip-nand-controller.o nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o nand-objs += nand_onfi.o diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c new file mode 100644 index 000000000000..796b678cb108 --- /dev/null +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -0,0 +1,1495 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Rockchip NAND Flash controller driver. + * Copyright (C) 2020 Rockchip Inc. + * Author: Yifeng Zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * NFC Page Data Layout: + * 1024 bytes data + 4Bytes sys data + 28Bytes~124Bytes ECC data + + * 1024 bytes data + 4Bytes sys data + 28Bytes~124Bytes ECC data + + * ...... + * NAND Page Data Layout: + * 1024 * n data + m Bytes oob + * Original Bad Block Mask Location: + * First byte of oob(spare). + * nand_chip->oob_poi data layout: + * 4Bytes sys data + .... + 4Bytes sys data + ECC data. + */ + +/* NAND controller register definition */ +#define NFC_READ (0) +#define NFC_WRITE (1) + +#define NFC_FMCTL (0x00) +#define FMCTL_CE_SEL_M 0xFF +#define FMCTL_CE_SEL(x) (1 << (x)) +#define FMCTL_WP BIT(8) +#define FMCTL_RDY BIT(9) + +#define NFC_FMWAIT (0x04) +#define FLCTL_RST BIT(0) +#define FLCTL_WR (1) /* 0: read, 1: write */ +#define FLCTL_XFER_ST BIT(2) +#define FLCTL_XFER_EN BIT(3) +#define FLCTL_ACORRECT BIT(10) /* Auto correct error bits. */ +#define FLCTL_XFER_READY BIT(20) +#define FLCTL_XFER_SECTOR (22) +#define FLCTL_TOG_FIX BIT(29) + +#define BCHCTL_BANK_M (7 << 5) +#define BCHCTL_BANK (5) + +#define DMA_ST BIT(0) +#define DMA_WR (1) /* 0: write, 1: read */ +#define DMA_EN BIT(2) +#define DMA_AHB_SIZE (3) /* 0: 1, 1: 2, 2: 4 */ +#define DMA_BURST_SIZE (6) /* 0: 1, 3: 4, 5: 8, 7: 16 */ +#define DMA_INC_NUM (9) /* 1 - 16 */ + +#define ECC_ERR_CNT(x, e) ((((x) >> (e).low) & (e).low_mask) |\ + (((x) >> (e).high) & (e).high_mask) << (e).low_bn) +#define INT_DMA BIT(0) +#define NFC_BANK (0x800) +#define NFC_BANK_STEP (0x100) +#define BANK_DATA (0x00) +#define BANK_ADDR (0x04) +#define BANK_CMD (0x08) +#define NFC_SRAM0 (0x1000) +#define NFC_SRAM1 (0x1400) +#define NFC_SRAM_SIZE (0x400) +#define NFC_TIMEOUT (500000) +#define NFC_MAX_OOB_PER_STEP 128 +#define NFC_MIN_OOB_PER_STEP 64 +#define MAX_DATA_SIZE 0xFFFC +#define MAX_ADDRESS_CYC 6 +#define NFC_ECC_MAX_MODES 4 +#define NFC_MAX_NSELS (8) /* Some Socs only have 1 or 2 CSs. */ +#define NFC_SYS_DATA_SIZE (4) /* 4 bytes sys data in oob pre 1024 data.*/ +#define RK_DEFAULT_CLOCK_RATE (150 * 1000 * 1000) /* 150 Mhz */ +#define ACCTIMING(csrw, rwpw, rwcs) ((csrw) << 12 | (rwpw) << 5 | (rwcs)) + +enum nfc_type { + NFC_V6, + NFC_V8, + NFC_V9, +}; + +/** + * struct rk_ecc_cnt_status: represent a ecc status data. + * @err_flag_bit: error flag bit index at register. + * @low: ECC count low bit index at register. + * @low_mask: mask bit. + * @low_bn: ECC count low bit number. + * @high: ECC count high bit index at register. + * @high_mask: mask bit + */ +struct ecc_cnt_status { + u8 err_flag_bit; + u8 low; + u8 low_mask; + u8 low_bn; + u8 high; + u8 high_mask; +}; + +/** + * @type: NFC version + * @ecc_strengths: ECC strengths + * @ecc_cfgs: ECC config values + * @flctl_off: FLCTL register offset + * @bchctl_off: BCHCTL register offset + * @dma_data_buf_off: DMA_DATA_BUF register offset + * @dma_oob_buf_off: DMA_OOB_BUF register offset + * @dma_cfg_off: DMA_CFG register offset + * @dma_st_off: DMA_ST register offset + * @bch_st_off: BCG_ST register offset + * @randmz_off: RANDMZ register offset + * @int_en_off: interrupt enable register offset + * @int_clr_off: interrupt clean register offset + * @int_st_off: interrupt status register offset + * @oob0_off: oob0 register offset + * @oob1_off: oob1 register offset + * @ecc0: represent ECC0 status data + * @ecc1: represent ECC1 status data + */ +struct nfc_cfg { + enum nfc_type type; + u8 ecc_strengths[NFC_ECC_MAX_MODES]; + u32 ecc_cfgs[NFC_ECC_MAX_MODES]; + u32 flctl_off; + u32 bchctl_off; + u32 dma_cfg_off; + u32 dma_data_buf_off; + u32 dma_oob_buf_off; + u32 dma_st_off; + u32 bch_st_off; + u32 randmz_off; + u32 int_en_off; + u32 int_clr_off; + u32 int_st_off; + u32 oob0_off; + u32 oob1_off; + struct ecc_cnt_status ecc0; + struct ecc_cnt_status ecc1; +}; + +struct rk_nfc_nand_chip { + struct list_head node; + struct nand_chip chip; + + u16 boot_blks; + u16 metadata_size; + u32 boot_ecc; + u32 timing; + + u8 nsels; + u8 sels[0]; + /* Nothing after this field. */ +}; + +struct rk_nfc { + struct nand_controller controller; + const struct nfc_cfg *cfg; + struct device *dev; + + struct clk *nfc_clk; + struct clk *ahb_clk; + void __iomem *regs; + + u32 selected_bank; + u32 band_offset; + u32 cur_ecc; + u32 cur_timing; + + struct completion done; + struct list_head chips; + + u8 *page_buf; + u32 *oob_buf; + u32 page_buf_size; + u32 oob_buf_size; + + unsigned long assigned_cs; +}; + +static inline struct rk_nfc_nand_chip *rk_nfc_to_rknand(struct nand_chip *chip) +{ + return container_of(chip, struct rk_nfc_nand_chip, chip); +} + +static inline u8 *rk_nfc_buf_to_data_ptr(struct nand_chip *chip, const u8 *p, int i) +{ + return (u8 *)p + i * chip->ecc.size; +} + +static inline u8 *rk_nfc_buf_to_oob_ptr(struct nand_chip *chip, int i) +{ + u8 *poi; + + poi = chip->oob_poi + i * NFC_SYS_DATA_SIZE; + + return poi; +} + +static inline u8 *rk_nfc_buf_to_oob_ecc_ptr(struct nand_chip *chip, int i) +{ + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + u8 *poi; + + poi = chip->oob_poi + rknand->metadata_size + chip->ecc.bytes * i; + + return poi; +} + +static inline int rk_nfc_data_len(struct nand_chip *chip) +{ + return chip->ecc.size + chip->ecc.bytes + NFC_SYS_DATA_SIZE; +} + +static inline u8 *rk_nfc_data_ptr(struct nand_chip *chip, int i) +{ + struct rk_nfc *nfc = nand_get_controller_data(chip); + + return nfc->page_buf + i * rk_nfc_data_len(chip); +} + +static inline u8 *rk_nfc_oob_ptr(struct nand_chip *chip, int i) +{ + struct rk_nfc *nfc = nand_get_controller_data(chip); + + return nfc->page_buf + i * rk_nfc_data_len(chip) + chip->ecc.size; +} + +static int rk_nfc_hw_ecc_setup(struct nand_chip *chip, u32 strength) +{ + struct rk_nfc *nfc = nand_get_controller_data(chip); + u32 reg, i; + + for (i = 0; i < NFC_ECC_MAX_MODES; i++) { + if (strength == nfc->cfg->ecc_strengths[i]) { + reg = nfc->cfg->ecc_cfgs[i]; + break; + } + } + + if (i >= NFC_ECC_MAX_MODES) + return -EINVAL; + + writel(reg, nfc->regs + nfc->cfg->bchctl_off); + + /* Save chip ECC setting */ + nfc->cur_ecc = strength; + + return 0; +} + +static void rk_nfc_select_chip(struct nand_chip *chip, int cs) +{ + struct rk_nfc *nfc = nand_get_controller_data(chip); + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + u32 val; + + if (cs < 0) { + nfc->selected_bank = -1; + /* Deselect the currently selected target. */ + val = readl_relaxed(nfc->regs + NFC_FMCTL); + val &= ~FMCTL_CE_SEL_M; + writel(val, nfc->regs + NFC_FMCTL); + return; + } + + nfc->selected_bank = rknand->sels[cs]; + nfc->band_offset = NFC_BANK + nfc->selected_bank * NFC_BANK_STEP; + + val = readl_relaxed(nfc->regs + NFC_FMCTL); + val &= ~FMCTL_CE_SEL_M; + val |= FMCTL_CE_SEL(nfc->selected_bank); + + writel(val, nfc->regs + NFC_FMCTL); + + /* + * Compare current chip timing with selected chip timing and + * change if needed. + */ + if (nfc->cur_timing != rknand->timing) { + writel(rknand->timing, nfc->regs + NFC_FMWAIT); + nfc->cur_timing = rknand->timing; + } + + /* + * Compare current chip ECC setting with selected chip ECC setting and + * change if needed. + */ + if (nfc->cur_ecc != ecc->strength) + rk_nfc_hw_ecc_setup(chip, ecc->strength); +} + +static inline int rk_nfc_wait_ioready(struct rk_nfc *nfc) +{ + int rc; + u32 val; + + rc = readl_relaxed_poll_timeout(nfc->regs + NFC_FMCTL, val, + val & FMCTL_RDY, 10, NFC_TIMEOUT); + + return rc; +} + +static void rk_nfc_read_buf(struct rk_nfc *nfc, u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = readb_relaxed(nfc->regs + nfc->band_offset + + BANK_DATA); +} + +static void rk_nfc_write_buf(struct rk_nfc *nfc, const u8 *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + writeb(buf[i], nfc->regs + nfc->band_offset + BANK_DATA); +} + +static int rk_nfc_cmd(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct rk_nfc *nfc = nand_get_controller_data(chip); + unsigned int i, j, remaining, start; + int reg_offset = nfc->band_offset; + u8 *inbuf = NULL; + const u8 *outbuf; + u32 cnt = 0; + int ret = 0; + + for (i = 0; i < subop->ninstrs; i++) { + const struct nand_op_instr *instr = &subop->instrs[i]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + writeb(instr->ctx.cmd.opcode, + nfc->regs + reg_offset + BANK_CMD); + break; + + case NAND_OP_ADDR_INSTR: + remaining = nand_subop_get_num_addr_cyc(subop, i); + start = nand_subop_get_addr_start_off(subop, i); + + for (j = 0; j < 8 && j + start < remaining; j++) + writeb(instr->ctx.addr.addrs[j + start], + nfc->regs + reg_offset + BANK_ADDR); + break; + + case NAND_OP_DATA_IN_INSTR: + case NAND_OP_DATA_OUT_INSTR: + start = nand_subop_get_data_start_off(subop, i); + cnt = nand_subop_get_data_len(subop, i); + + if (instr->type == NAND_OP_DATA_OUT_INSTR) { + outbuf = instr->ctx.data.buf.out + start; + rk_nfc_write_buf(nfc, outbuf, cnt); + } else { + inbuf = instr->ctx.data.buf.in + start; + rk_nfc_read_buf(nfc, inbuf, cnt); + } + break; + + case NAND_OP_WAITRDY_INSTR: + if (rk_nfc_wait_ioready(nfc) < 0) { + ret = -ETIMEDOUT; + dev_err(nfc->dev, "IO not ready\n"); + } + break; + } + } + + return ret; +} + +static const struct nand_op_parser rk_nfc_op_parser = NAND_OP_PARSER( + NAND_OP_PARSER_PATTERN( + rk_nfc_cmd, + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYC), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, MAX_DATA_SIZE)), + NAND_OP_PARSER_PATTERN( + rk_nfc_cmd, + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYC), + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, MAX_DATA_SIZE), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), +); + +static int rk_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + if (!check_only) + rk_nfc_select_chip(chip, op->cs); + + return nand_op_parser_exec_op(chip, &rk_nfc_op_parser, op, + check_only); +} + +static int rk_nfc_setup_interface(struct nand_chip *chip, int target, + const struct nand_interface_config *conf) +{ + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + struct rk_nfc *nfc = nand_get_controller_data(chip); + const struct nand_sdr_timings *timings; + u32 rate, tc2rw, trwpw, trw2c; + u32 temp; + + if (target < 0) + return 0; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -EOPNOTSUPP; + + if (IS_ERR(nfc->nfc_clk)) + rate = clk_get_rate(nfc->ahb_clk); + else + rate = clk_get_rate(nfc->nfc_clk); + + /* Turn clock rate into kHz. */ + rate /= 1000; + + tc2rw = 1; + trw2c = 1; + + trwpw = max(timings->tWC_min, timings->tRC_min) / 1000; + trwpw = DIV_ROUND_UP(trwpw * rate, 1000000); + + temp = timings->tREA_max / 1000; + temp = DIV_ROUND_UP(temp * rate, 1000000); + + if (trwpw < temp) + trwpw = temp; + + /* + * ACCON: access timing control register + * ------------------------------------- + * 31:18: reserved + * 17:12: csrw, clock cycles from the falling edge of CSn to the + * falling edge of RDn or WRn + * 11:11: reserved + * 10:05: rwpw, the width of RDn or WRn in processor clock cycles + * 04:00: rwcs, clock cycles from the rising edge of RDn or WRn to the + * rising edge of CSn + */ + + /* Save chip timing */ + rknand->timing = ACCTIMING(tc2rw, trwpw, trw2c); + + return 0; +} + +static void rk_nfc_xfer_start(struct rk_nfc *nfc, u8 rw, u8 n_KB, + dma_addr_t dma_data, dma_addr_t dma_oob) +{ + u32 dma_reg, fl_reg, bch_reg; + + dma_reg = DMA_ST | ((!rw) << DMA_WR) | DMA_EN | (2 << DMA_AHB_SIZE) | + (7 << DMA_BURST_SIZE) | (16 << DMA_INC_NUM); + + fl_reg = (rw << FLCTL_WR) | FLCTL_XFER_EN | FLCTL_ACORRECT | + (n_KB << FLCTL_XFER_SECTOR) | FLCTL_TOG_FIX; + + if (nfc->cfg->type == NFC_V6 || nfc->cfg->type == NFC_V8) { + bch_reg = readl_relaxed(nfc->regs + nfc->cfg->bchctl_off); + bch_reg = (bch_reg & (~BCHCTL_BANK_M)) | + (nfc->selected_bank << BCHCTL_BANK); + writel(bch_reg, nfc->regs + nfc->cfg->bchctl_off); + } + + writel(dma_reg, nfc->regs + nfc->cfg->dma_cfg_off); + writel((u32)dma_data, nfc->regs + nfc->cfg->dma_data_buf_off); + writel((u32)dma_oob, nfc->regs + nfc->cfg->dma_oob_buf_off); + writel(fl_reg, nfc->regs + nfc->cfg->flctl_off); + fl_reg |= FLCTL_XFER_ST; + writel(fl_reg, nfc->regs + nfc->cfg->flctl_off); +} + +static int rk_nfc_wait_for_xfer_done(struct rk_nfc *nfc) +{ + void __iomem *ptr; + u32 reg; + + ptr = nfc->regs + nfc->cfg->flctl_off; + + return readl_relaxed_poll_timeout(ptr, reg, + reg & FLCTL_XFER_READY, + 10, NFC_TIMEOUT); +} + +static int rk_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf, + int oob_on, int page) +{ + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + struct rk_nfc *nfc = nand_get_controller_data(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int i, pages_per_blk; + + pages_per_blk = mtd->erasesize / mtd->writesize; + if ((chip->options & NAND_IS_BOOT_MEDIUM) && + (page < (pages_per_blk * rknand->boot_blks)) && + rknand->boot_ecc != ecc->strength) { + /* + * There's currently no method to notify the MTD framework that + * a different ECC strength is in use for the boot blocks. + */ + return -EIO; + } + + if (!buf) + memset(nfc->page_buf, 0xff, mtd->writesize + mtd->oobsize); + + for (i = 0; i < ecc->steps; i++) { + /* Copy data to the NFC buffer. */ + if (buf) + memcpy(rk_nfc_data_ptr(chip, i), + rk_nfc_buf_to_data_ptr(chip, buf, i), + ecc->size); + /* + * The first four bytes of OOB are reserved for the + * boot ROM. In some debugging cases, such as with a + * read, erase and write back test these 4 bytes stored + * in OOB also need to be written back. + * + * The function nand_block_bad detects bad blocks like: + * + * bad = chip->oob_poi[chip->badblockpos]; + * + * chip->badblockpos == 0 for a large page NAND Flash, + * so chip->oob_poi[0] is the bad block mask (BBM). + * + * The OOB data layout on the NFC is: + * + * PA0 PA1 PA2 PA3 | BBM OOB1 OOB2 OOB3 | ... + * + * or + * + * 0xFF 0xFF 0xFF 0xFF | BBM OOB1 OOB2 OOB3 | ... + * + * The code here just swaps the first 4 bytes with the last + * 4 bytes without losing any data. + * + * The chip->oob_poi data layout: + * + * BBM OOB1 OOB2 OOB3 |......| PA0 PA1 PA2 PA3 + * + * The rk_nfc_ooblayout_free() function already has reserved + * these 4 bytes with: + * + * oob_region->offset = NFC_SYS_DATA_SIZE + 2; + */ + if (!i) + memcpy(rk_nfc_oob_ptr(chip, i), + rk_nfc_buf_to_oob_ptr(chip, ecc->steps - 1), + NFC_SYS_DATA_SIZE); + else + memcpy(rk_nfc_oob_ptr(chip, i), + rk_nfc_buf_to_oob_ptr(chip, i - 1), + NFC_SYS_DATA_SIZE); + /* Copy ECC data to the NFC buffer. */ + memcpy(rk_nfc_oob_ptr(chip, i) + NFC_SYS_DATA_SIZE, + rk_nfc_buf_to_oob_ecc_ptr(chip, i), + ecc->bytes); + } + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + rk_nfc_write_buf(nfc, buf, mtd->writesize + mtd->oobsize); + return nand_prog_page_end_op(chip); +} + +static int rk_nfc_write_page_hwecc(struct nand_chip *chip, const u8 *buf, + int oob_on, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct rk_nfc *nfc = nand_get_controller_data(chip); + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int oob_step = (ecc->bytes > 60) ? NFC_MAX_OOB_PER_STEP : + NFC_MIN_OOB_PER_STEP; + int pages_per_blk = mtd->erasesize / mtd->writesize; + int ret = 0, i, boot_rom_mode = 0; + dma_addr_t dma_data, dma_oob; + u32 reg; + u8 *oob; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + if (buf) + memcpy(nfc->page_buf, buf, mtd->writesize); + else + memset(nfc->page_buf, 0xFF, mtd->writesize); + + /* + * The first blocks (4, 8 or 16 depending on the device) are used + * by the boot ROM and the first 32 bits of OOB need to link to + * the next page address in the same block. We can't directly copy + * OOB data from the MTD framework, because this page address + * conflicts for example with the bad block marker (BBM), + * so we shift all OOB data including the BBM with 4 byte positions. + * As a consequence the OOB size available to the MTD framework is + * also reduced with 4 bytes. + * + * PA0 PA1 PA2 PA3 | BBM OOB1 OOB2 OOB3 | ... + * + * If a NAND is not a boot medium or the page is not a boot block, + * the first 4 bytes are left untouched by writing 0xFF to them. + * + * 0xFF 0xFF 0xFF 0xFF | BBM OOB1 OOB2 OOB3 | ... + * + * Configure the ECC algorithm supported by the boot ROM. + */ + if ((page < (pages_per_blk * rknand->boot_blks)) && + (chip->options & NAND_IS_BOOT_MEDIUM)) { + boot_rom_mode = 1; + if (rknand->boot_ecc != ecc->strength) + rk_nfc_hw_ecc_setup(chip, rknand->boot_ecc); + } + + for (i = 0; i < ecc->steps; i++) { + if (!i) { + reg = 0xFFFFFFFF; + } else { + oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE; + reg = oob[0] | oob[1] << 8 | oob[2] << 16 | + oob[3] << 24; + } + + if (!i && boot_rom_mode) + reg = (page & (pages_per_blk - 1)) * 4; + + if (nfc->cfg->type == NFC_V9) + nfc->oob_buf[i] = reg; + else + nfc->oob_buf[i * (oob_step / 4)] = reg; + } + + dma_data = dma_map_single(nfc->dev, (void *)nfc->page_buf, + mtd->writesize, DMA_TO_DEVICE); + dma_oob = dma_map_single(nfc->dev, nfc->oob_buf, + ecc->steps * oob_step, + DMA_TO_DEVICE); + + reinit_completion(&nfc->done); + writel(INT_DMA, nfc->regs + nfc->cfg->int_en_off); + + rk_nfc_xfer_start(nfc, NFC_WRITE, ecc->steps, dma_data, + dma_oob); + ret = wait_for_completion_timeout(&nfc->done, + msecs_to_jiffies(100)); + if (!ret) + dev_warn(nfc->dev, "write: wait dma done timeout.\n"); + /* + * Whether the DMA transfer is completed or not. The driver + * needs to check the NFC`s status register to see if the data + * transfer was completed. + */ + ret = rk_nfc_wait_for_xfer_done(nfc); + + dma_unmap_single(nfc->dev, dma_data, mtd->writesize, + DMA_TO_DEVICE); + dma_unmap_single(nfc->dev, dma_oob, ecc->steps * oob_step, + DMA_TO_DEVICE); + + if (boot_rom_mode && rknand->boot_ecc != ecc->strength) + rk_nfc_hw_ecc_setup(chip, ecc->strength); + + if (ret) { + dev_err(nfc->dev, "write: wait transfer done timeout.\n"); + return -ETIMEDOUT; + } + + return nand_prog_page_end_op(chip); +} + +static int rk_nfc_write_oob(struct nand_chip *chip, int page) +{ + return rk_nfc_write_page_hwecc(chip, NULL, 1, page); +} + +static int rk_nfc_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_on, + int page) +{ + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + struct rk_nfc *nfc = nand_get_controller_data(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int i, pages_per_blk; + + pages_per_blk = mtd->erasesize / mtd->writesize; + if ((chip->options & NAND_IS_BOOT_MEDIUM) && + (page < (pages_per_blk * rknand->boot_blks)) && + rknand->boot_ecc != ecc->strength) { + /* + * There's currently no method to notify the MTD framework that + * a different ECC strength is in use for the boot blocks. + */ + return -EIO; + } + + nand_read_page_op(chip, page, 0, NULL, 0); + rk_nfc_read_buf(nfc, nfc->page_buf, mtd->writesize + mtd->oobsize); + for (i = 0; i < ecc->steps; i++) { + /* + * The first four bytes of OOB are reserved for the + * boot ROM. In some debugging cases, such as with a read, + * erase and write back test, these 4 bytes also must be + * saved somewhere, otherwise this information will be + * lost during a write back. + */ + if (!i) + memcpy(rk_nfc_buf_to_oob_ptr(chip, ecc->steps - 1), + rk_nfc_oob_ptr(chip, i), + NFC_SYS_DATA_SIZE); + else + memcpy(rk_nfc_buf_to_oob_ptr(chip, i - 1), + rk_nfc_oob_ptr(chip, i), + NFC_SYS_DATA_SIZE); + + /* Copy ECC data from the NFC buffer. */ + memcpy(rk_nfc_buf_to_oob_ecc_ptr(chip, i), + rk_nfc_oob_ptr(chip, i) + NFC_SYS_DATA_SIZE, + ecc->bytes); + + /* Copy data from the NFC buffer. */ + if (buf) + memcpy(rk_nfc_buf_to_data_ptr(chip, buf, i), + rk_nfc_data_ptr(chip, i), + ecc->size); + } + + return 0; +} + +static int rk_nfc_read_page_hwecc(struct nand_chip *chip, u8 *buf, int oob_on, + int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct rk_nfc *nfc = nand_get_controller_data(chip); + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int oob_step = (ecc->bytes > 60) ? NFC_MAX_OOB_PER_STEP : + NFC_MIN_OOB_PER_STEP; + int pages_per_blk = mtd->erasesize / mtd->writesize; + dma_addr_t dma_data, dma_oob; + int ret = 0, i, cnt, boot_rom_mode = 0; + int max_bitflips = 0, bch_st, ecc_fail = 0; + u8 *oob; + u32 tmp; + + nand_read_page_op(chip, page, 0, NULL, 0); + + dma_data = dma_map_single(nfc->dev, nfc->page_buf, + mtd->writesize, + DMA_FROM_DEVICE); + dma_oob = dma_map_single(nfc->dev, nfc->oob_buf, + ecc->steps * oob_step, + DMA_FROM_DEVICE); + + /* + * The first blocks (4, 8 or 16 depending on the device) + * are used by the boot ROM. + * Configure the ECC algorithm supported by the boot ROM. + */ + if ((page < (pages_per_blk * rknand->boot_blks)) && + (chip->options & NAND_IS_BOOT_MEDIUM)) { + boot_rom_mode = 1; + if (rknand->boot_ecc != ecc->strength) + rk_nfc_hw_ecc_setup(chip, rknand->boot_ecc); + } + + reinit_completion(&nfc->done); + writel(INT_DMA, nfc->regs + nfc->cfg->int_en_off); + rk_nfc_xfer_start(nfc, NFC_READ, ecc->steps, dma_data, + dma_oob); + ret = wait_for_completion_timeout(&nfc->done, + msecs_to_jiffies(100)); + if (!ret) + dev_warn(nfc->dev, "read: wait dma done timeout.\n"); + /* + * Whether the DMA transfer is completed or not. The driver + * needs to check the NFC`s status register to see if the data + * transfer was completed. + */ + ret = rk_nfc_wait_for_xfer_done(nfc); + + dma_unmap_single(nfc->dev, dma_data, mtd->writesize, + DMA_FROM_DEVICE); + dma_unmap_single(nfc->dev, dma_oob, ecc->steps * oob_step, + DMA_FROM_DEVICE); + + if (ret) { + ret = -ETIMEDOUT; + dev_err(nfc->dev, "read: wait transfer done timeout.\n"); + goto timeout_err; + } + + for (i = 1; i < ecc->steps; i++) { + oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE; + if (nfc->cfg->type == NFC_V9) + tmp = nfc->oob_buf[i]; + else + tmp = nfc->oob_buf[i * (oob_step / 4)]; + *oob++ = (u8)tmp; + *oob++ = (u8)(tmp >> 8); + *oob++ = (u8)(tmp >> 16); + *oob++ = (u8)(tmp >> 24); + } + + for (i = 0; i < (ecc->steps / 2); i++) { + bch_st = readl_relaxed(nfc->regs + + nfc->cfg->bch_st_off + i * 4); + if (bch_st & BIT(nfc->cfg->ecc0.err_flag_bit) || + bch_st & BIT(nfc->cfg->ecc1.err_flag_bit)) { + mtd->ecc_stats.failed++; + ecc_fail = 1; + } else { + cnt = ECC_ERR_CNT(bch_st, nfc->cfg->ecc0); + mtd->ecc_stats.corrected += cnt; + max_bitflips = max_t(u32, max_bitflips, cnt); + + cnt = ECC_ERR_CNT(bch_st, nfc->cfg->ecc1); + mtd->ecc_stats.corrected += cnt; + max_bitflips = max_t(u32, max_bitflips, cnt); + } + } + + if (buf) + memcpy(buf, nfc->page_buf, mtd->writesize); + +timeout_err: + if (boot_rom_mode && rknand->boot_ecc != ecc->strength) + rk_nfc_hw_ecc_setup(chip, ecc->strength); + + if (ret) + return ret; + + if (ecc_fail) { + dev_err(nfc->dev, "read page: %x ecc error!\n", page); + return 0; + } + + return max_bitflips; +} + +static int rk_nfc_read_oob(struct nand_chip *chip, int page) +{ + return rk_nfc_read_page_hwecc(chip, NULL, 1, page); +} + +static inline void rk_nfc_hw_init(struct rk_nfc *nfc) +{ + /* Disable flash wp. */ + writel(FMCTL_WP, nfc->regs + NFC_FMCTL); + /* Config default timing 40ns at 150 Mhz NFC clock. */ + writel(0x1081, nfc->regs + NFC_FMWAIT); + nfc->cur_timing = 0x1081; + /* Disable randomizer and DMA. */ + writel(0, nfc->regs + nfc->cfg->randmz_off); + writel(0, nfc->regs + nfc->cfg->dma_cfg_off); + writel(FLCTL_RST, nfc->regs + nfc->cfg->flctl_off); +} + +static irqreturn_t rk_nfc_irq(int irq, void *id) +{ + struct rk_nfc *nfc = id; + u32 sta, ien; + + sta = readl_relaxed(nfc->regs + nfc->cfg->int_st_off); + ien = readl_relaxed(nfc->regs + nfc->cfg->int_en_off); + + if (!(sta & ien)) + return IRQ_NONE; + + writel(sta, nfc->regs + nfc->cfg->int_clr_off); + writel(~sta & ien, nfc->regs + nfc->cfg->int_en_off); + + complete(&nfc->done); + + return IRQ_HANDLED; +} + +static int rk_nfc_enable_clks(struct device *dev, struct rk_nfc *nfc) +{ + int ret; + + if (!IS_ERR(nfc->nfc_clk)) { + ret = clk_prepare_enable(nfc->nfc_clk); + if (ret) { + dev_err(dev, "failed to enable NFC clk\n"); + return ret; + } + } + + ret = clk_prepare_enable(nfc->ahb_clk); + if (ret) { + dev_err(dev, "failed to enable ahb clk\n"); + if (!IS_ERR(nfc->nfc_clk)) + clk_disable_unprepare(nfc->nfc_clk); + return ret; + } + + return 0; +} + +static void rk_nfc_disable_clks(struct rk_nfc *nfc) +{ + if (!IS_ERR(nfc->nfc_clk)) + clk_disable_unprepare(nfc->nfc_clk); + clk_disable_unprepare(nfc->ahb_clk); +} + +static int rk_nfc_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oob_region) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + + if (section) + return -ERANGE; + + /* + * The beginning of the OOB area stores the reserved data for the NFC, + * the size of the reserved data is NFC_SYS_DATA_SIZE bytes. + */ + oob_region->length = rknand->metadata_size - NFC_SYS_DATA_SIZE - 2; + oob_region->offset = NFC_SYS_DATA_SIZE + 2; + + return 0; +} + +static int rk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oob_region) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + + if (section) + return -ERANGE; + + oob_region->length = mtd->oobsize - rknand->metadata_size; + oob_region->offset = rknand->metadata_size; + + return 0; +} + +static const struct mtd_ooblayout_ops rk_nfc_ooblayout_ops = { + .free = rk_nfc_ooblayout_free, + .ecc = rk_nfc_ooblayout_ecc, +}; + +static int rk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct rk_nfc *nfc = nand_get_controller_data(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + const u8 *strengths = nfc->cfg->ecc_strengths; + u8 max_strength, nfc_max_strength; + int i; + + nfc_max_strength = nfc->cfg->ecc_strengths[0]; + /* If optional dt settings not present. */ + if (!ecc->size || !ecc->strength || + ecc->strength > nfc_max_strength) { + chip->ecc.size = 1024; + ecc->steps = mtd->writesize / ecc->size; + + /* + * HW ECC always requests the number of ECC bytes per 1024 byte + * blocks. The first 4 OOB bytes are reserved for sys data. + */ + max_strength = ((mtd->oobsize / ecc->steps) - 4) * 8 / + fls(8 * 1024); + if (max_strength > nfc_max_strength) + max_strength = nfc_max_strength; + + for (i = 0; i < 4; i++) { + if (max_strength >= strengths[i]) + break; + } + + if (i >= 4) { + dev_err(nfc->dev, "unsupported ECC strength\n"); + return -EOPNOTSUPP; + } + + ecc->strength = strengths[i]; + } + ecc->steps = mtd->writesize / ecc->size; + ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * chip->ecc.size), 8); + + return 0; +} + +static int rk_nfc_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct device *dev = mtd->dev.parent; + struct rk_nfc *nfc = nand_get_controller_data(chip); + struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + int new_page_len, new_oob_len; + void *buf; + int ret; + + if (chip->options & NAND_BUSWIDTH_16) { + dev_err(dev, "16 bits bus width not supported"); + return -EINVAL; + } + + if (ecc->engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + ret = rk_nfc_ecc_init(dev, mtd); + if (ret) + return ret; + + rknand->metadata_size = NFC_SYS_DATA_SIZE * ecc->steps; + + if (rknand->metadata_size < NFC_SYS_DATA_SIZE + 2) { + dev_err(dev, + "driver needs at least %d bytes of meta data\n", + NFC_SYS_DATA_SIZE + 2); + return -EIO; + } + + /* Check buffer first, avoid duplicate alloc buffer. */ + new_page_len = mtd->writesize + mtd->oobsize; + if (nfc->page_buf && new_page_len > nfc->page_buf_size) { + buf = krealloc(nfc->page_buf, new_page_len, + GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + nfc->page_buf = buf; + nfc->page_buf_size = new_page_len; + } + + new_oob_len = ecc->steps * NFC_MAX_OOB_PER_STEP; + if (nfc->oob_buf && new_oob_len > nfc->oob_buf_size) { + buf = krealloc(nfc->oob_buf, new_oob_len, + GFP_KERNEL | GFP_DMA); + if (!buf) { + kfree(nfc->page_buf); + nfc->page_buf = NULL; + return -ENOMEM; + } + nfc->oob_buf = buf; + nfc->oob_buf_size = new_oob_len; + } + + if (!nfc->page_buf) { + nfc->page_buf = kzalloc(new_page_len, GFP_KERNEL | GFP_DMA); + if (!nfc->page_buf) + return -ENOMEM; + nfc->page_buf_size = new_page_len; + } + + if (!nfc->oob_buf) { + nfc->oob_buf = kzalloc(new_oob_len, GFP_KERNEL | GFP_DMA); + if (!nfc->oob_buf) { + kfree(nfc->page_buf); + nfc->page_buf = NULL; + return -ENOMEM; + } + nfc->oob_buf_size = new_oob_len; + } + + chip->ecc.write_page_raw = rk_nfc_write_page_raw; + chip->ecc.write_page = rk_nfc_write_page_hwecc; + chip->ecc.write_oob = rk_nfc_write_oob; + + chip->ecc.read_page_raw = rk_nfc_read_page_raw; + chip->ecc.read_page = rk_nfc_read_page_hwecc; + chip->ecc.read_oob = rk_nfc_read_oob; + + return 0; +} + +static const struct nand_controller_ops rk_nfc_controller_ops = { + .attach_chip = rk_nfc_attach_chip, + .exec_op = rk_nfc_exec_op, + .setup_interface = rk_nfc_setup_interface, +}; + +static int rk_nfc_nand_chip_init(struct device *dev, struct rk_nfc *nfc, + struct device_node *np) +{ + struct rk_nfc_nand_chip *rknand; + struct nand_chip *chip; + struct mtd_info *mtd; + int nsels; + u32 tmp; + int ret; + int i; + + if (!of_get_property(np, "reg", &nsels)) + return -ENODEV; + nsels /= sizeof(u32); + if (!nsels || nsels > NFC_MAX_NSELS) { + dev_err(dev, "invalid reg property size %d\n", nsels); + return -EINVAL; + } + + rknand = devm_kzalloc(dev, sizeof(*rknand) + nsels * sizeof(u8), + GFP_KERNEL); + if (!rknand) + return -ENOMEM; + + rknand->nsels = nsels; + for (i = 0; i < nsels; i++) { + ret = of_property_read_u32_index(np, "reg", i, &tmp); + if (ret) { + dev_err(dev, "reg property failure : %d\n", ret); + return ret; + } + + if (tmp >= NFC_MAX_NSELS) { + dev_err(dev, "invalid CS: %u\n", tmp); + return -EINVAL; + } + + if (test_and_set_bit(tmp, &nfc->assigned_cs)) { + dev_err(dev, "CS %u already assigned\n", tmp); + return -EINVAL; + } + + rknand->sels[i] = tmp; + } + + chip = &rknand->chip; + chip->controller = &nfc->controller; + + nand_set_flash_node(chip, np); + + nand_set_controller_data(chip, nfc); + + chip->options |= NAND_USES_DMA | NAND_NO_SUBPAGE_WRITE; + chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + + /* Set default mode in case dt entry is missing. */ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + + mtd = nand_to_mtd(chip); + mtd->owner = THIS_MODULE; + mtd->dev.parent = dev; + + if (!mtd->name) { + dev_err(nfc->dev, "NAND label property is mandatory\n"); + return -EINVAL; + } + + mtd_set_ooblayout(mtd, &rk_nfc_ooblayout_ops); + rk_nfc_hw_init(nfc); + ret = nand_scan(chip, nsels); + if (ret) + return ret; + + if (chip->options & NAND_IS_BOOT_MEDIUM) { + ret = of_property_read_u32(np, "rockchip,boot-blks", &tmp); + rknand->boot_blks = ret ? 0 : tmp; + + ret = of_property_read_u32(np, "rockchip,boot-ecc-strength", + &tmp); + rknand->boot_ecc = ret ? chip->ecc.strength : tmp; + } + + ret = mtd_device_register(mtd, NULL, 0); + if (ret) { + dev_err(dev, "MTD parse partition error\n"); + nand_cleanup(chip); + return ret; + } + + list_add_tail(&rknand->node, &nfc->chips); + + return 0; +} + +static void rk_nfc_chips_cleanup(struct rk_nfc *nfc) +{ + struct rk_nfc_nand_chip *rknand, *tmp; + struct nand_chip *chip; + int ret; + + list_for_each_entry_safe(rknand, tmp, &nfc->chips, node) { + chip = &rknand->chip; + ret = mtd_device_unregister(nand_to_mtd(chip)); + WARN_ON(ret); + nand_cleanup(chip); + list_del(&rknand->node); + } +} + +static int rk_nfc_nand_chips_init(struct device *dev, struct rk_nfc *nfc) +{ + struct device_node *np = dev->of_node, *nand_np; + int nchips = of_get_child_count(np); + int ret; + + if (!nchips || nchips > NFC_MAX_NSELS) { + dev_err(nfc->dev, "incorrect number of NAND chips (%d)\n", + nchips); + return -EINVAL; + } + + for_each_child_of_node(np, nand_np) { + ret = rk_nfc_nand_chip_init(dev, nfc, nand_np); + if (ret) { + of_node_put(nand_np); + rk_nfc_chips_cleanup(nfc); + return ret; + } + } + + return 0; +} + +static struct nfc_cfg nfc_v6_cfg = { + .type = NFC_V6, + .ecc_strengths = {60, 40, 24, 16}, + .ecc_cfgs = { + 0x00040011, 0x00040001, 0x00000011, 0x00000001, + }, + .flctl_off = 0x08, + .bchctl_off = 0x0C, + .dma_cfg_off = 0x10, + .dma_data_buf_off = 0x14, + .dma_oob_buf_off = 0x18, + .dma_st_off = 0x1C, + .bch_st_off = 0x20, + .randmz_off = 0x150, + .int_en_off = 0x16C, + .int_clr_off = 0x170, + .int_st_off = 0x174, + .oob0_off = 0x200, + .oob1_off = 0x230, + .ecc0 = { + .err_flag_bit = 2, + .low = 3, + .low_mask = 0x1F, + .low_bn = 5, + .high = 27, + .high_mask = 0x1, + }, + .ecc1 = { + .err_flag_bit = 15, + .low = 16, + .low_mask = 0x1F, + .low_bn = 5, + .high = 29, + .high_mask = 0x1, + }, +}; + +static struct nfc_cfg nfc_v8_cfg = { + .type = NFC_V8, + .ecc_strengths = {16, 16, 16, 16}, + .ecc_cfgs = { + 0x00000001, 0x00000001, 0x00000001, 0x00000001, + }, + .flctl_off = 0x08, + .bchctl_off = 0x0C, + .dma_cfg_off = 0x10, + .dma_data_buf_off = 0x14, + .dma_oob_buf_off = 0x18, + .dma_st_off = 0x1C, + .bch_st_off = 0x20, + .randmz_off = 0x150, + .int_en_off = 0x16C, + .int_clr_off = 0x170, + .int_st_off = 0x174, + .oob0_off = 0x200, + .oob1_off = 0x230, + .ecc0 = { + .err_flag_bit = 2, + .low = 3, + .low_mask = 0x1F, + .low_bn = 5, + .high = 27, + .high_mask = 0x1, + }, + .ecc1 = { + .err_flag_bit = 15, + .low = 16, + .low_mask = 0x1F, + .low_bn = 5, + .high = 29, + .high_mask = 0x1, + }, +}; + +static struct nfc_cfg nfc_v9_cfg = { + .type = NFC_V9, + .ecc_strengths = {70, 60, 40, 16}, + .ecc_cfgs = { + 0x00000001, 0x06000001, 0x04000001, 0x02000001, + }, + .flctl_off = 0x10, + .bchctl_off = 0x20, + .dma_cfg_off = 0x30, + .dma_data_buf_off = 0x34, + .dma_oob_buf_off = 0x38, + .dma_st_off = 0x3C, + .bch_st_off = 0x150, + .randmz_off = 0x208, + .int_en_off = 0x120, + .int_clr_off = 0x124, + .int_st_off = 0x128, + .oob0_off = 0x200, + .oob1_off = 0x204, + .ecc0 = { + .err_flag_bit = 2, + .low = 3, + .low_mask = 0x7F, + .low_bn = 7, + .high = 0, + .high_mask = 0x0, + }, + .ecc1 = { + .err_flag_bit = 18, + .low = 19, + .low_mask = 0x7F, + .low_bn = 7, + .high = 0, + .high_mask = 0x0, + }, +}; + +static const struct of_device_id rk_nfc_id_table[] = { + { + .compatible = "rockchip,px30-nfc", + .data = &nfc_v9_cfg + }, + { + .compatible = "rockchip,rk2928-nfc", + .data = &nfc_v6_cfg + }, + { + .compatible = "rockchip,rv1108-nfc", + .data = &nfc_v8_cfg + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rk_nfc_id_table); + +static int rk_nfc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rk_nfc *nfc; + int ret, irq; + + nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nand_controller_init(&nfc->controller); + INIT_LIST_HEAD(&nfc->chips); + nfc->controller.ops = &rk_nfc_controller_ops; + + nfc->cfg = of_device_get_match_data(dev); + nfc->dev = dev; + + init_completion(&nfc->done); + + nfc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(nfc->regs)) { + ret = PTR_ERR(nfc->regs); + goto release_nfc; + } + + nfc->nfc_clk = devm_clk_get(dev, "nfc"); + if (IS_ERR(nfc->nfc_clk)) { + dev_dbg(dev, "no NFC clk\n"); + /* Some earlier models, such as rk3066, have no NFC clk. */ + } + + nfc->ahb_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(nfc->ahb_clk)) { + dev_err(dev, "no ahb clk\n"); + ret = PTR_ERR(nfc->ahb_clk); + goto release_nfc; + } + + ret = rk_nfc_enable_clks(dev, nfc); + if (ret) + goto release_nfc; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no NFC irq resource\n"); + ret = -EINVAL; + goto clk_disable; + } + + writel(0, nfc->regs + nfc->cfg->int_en_off); + ret = devm_request_irq(dev, irq, rk_nfc_irq, 0x0, "rk-nand", nfc); + if (ret) { + dev_err(dev, "failed to request NFC irq\n"); + goto clk_disable; + } + + platform_set_drvdata(pdev, nfc); + + ret = rk_nfc_nand_chips_init(dev, nfc); + if (ret) { + dev_err(dev, "failed to init NAND chips\n"); + goto clk_disable; + } + return 0; + +clk_disable: + rk_nfc_disable_clks(nfc); +release_nfc: + return ret; +} + +static int rk_nfc_remove(struct platform_device *pdev) +{ + struct rk_nfc *nfc = platform_get_drvdata(pdev); + + kfree(nfc->page_buf); + kfree(nfc->oob_buf); + rk_nfc_chips_cleanup(nfc); + rk_nfc_disable_clks(nfc); + + return 0; +} + +static int __maybe_unused rk_nfc_suspend(struct device *dev) +{ + struct rk_nfc *nfc = dev_get_drvdata(dev); + + rk_nfc_disable_clks(nfc); + + return 0; +} + +static int __maybe_unused rk_nfc_resume(struct device *dev) +{ + struct rk_nfc *nfc = dev_get_drvdata(dev); + struct rk_nfc_nand_chip *rknand; + struct nand_chip *chip; + int ret; + u32 i; + + ret = rk_nfc_enable_clks(dev, nfc); + if (ret) + return ret; + + /* Reset NAND chip if VCC was powered off. */ + list_for_each_entry(rknand, &nfc->chips, node) { + chip = &rknand->chip; + for (i = 0; i < rknand->nsels; i++) + nand_reset(chip, i); + } + + return 0; +} + +static const struct dev_pm_ops rk_nfc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rk_nfc_suspend, rk_nfc_resume) +}; + +static struct platform_driver rk_nfc_driver = { + .probe = rk_nfc_probe, + .remove = rk_nfc_remove, + .driver = { + .name = "rockchip-nfc", + .of_match_table = rk_nfc_id_table, + .pm = &rk_nfc_pm_ops, + }, +}; + +module_platform_driver(rk_nfc_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Yifeng Zhao "); +MODULE_DESCRIPTION("Rockchip Nand Flash Controller Driver"); +MODULE_ALIAS("platform:rockchip-nand-controller"); -- cgit v1.2.3 From ee4e0eafa43cfd9008722fe15e17b8bf62fb6e8d Mon Sep 17 00:00:00 2001 From: YouChing Lin Date: Thu, 10 Dec 2020 11:22:09 +0800 Subject: mtd: spinand: macronix: Add support for MX35LFxG24AD The Macronix MX35LF1G24AD(/2G24AD/4G24AD) are 3V, 1G/2G/4Gbit serial SLC NAND flash device (without on-die ECC). Validated by read, erase, read back, write, read back on Xilinx Zynq PicoZed FPGA board which included Macronix SPI Host(drivers/spi/spi-mxic.c) & S/W BCH ecc(drivers/mtd/nand/ecc-sw-bch.c) with bug fixing patch (mtd: nand: ecc-bch: Fix the size of calc_buf/code_buf of the BCH). Signed-off-by: YouChing Lin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1607570529-22341-3-git-send-email-ycllin@mxic.com.tw --- drivers/mtd/nand/spi/macronix.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 3786b1b03b3b..6701aaa21a49 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -139,6 +139,33 @@ static const struct spinand_info macronix_spinand_table[] = { 0, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX35LF1G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF2G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), + SPINAND_INFO("MX35LF4G24AD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), SPINAND_INFO("MX31LF1GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), -- cgit v1.2.3