diff options
Diffstat (limited to 'drivers/mtd')
89 files changed, 2051 insertions, 980 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index f96287c4b789..0f4c2d823de8 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -91,7 +91,7 @@ config MTD_MCHP23K256 config MTD_SPEAR_SMI tristate "SPEAR MTD NOR Support through SMI controller" - depends on PLAT_SPEAR + depends on PLAT_SPEAR || COMPILE_TEST default y help This enable SNOR support on SPEAR platforms using SMI controller diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 56f50d27b7fd..aecd441e4183 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -436,7 +436,10 @@ static int flash_read (struct mtd_info *mtd,loff_t from,size_t len,size_t *retle { int gap = BUSWIDTH - (from & (BUSWIDTH - 1)); - while (len && gap--) *buf++ = read8 (from++), len--; + while (len && gap--) { + *buf++ = read8 (from++); + len--; + } } /* now we read dwords until we reach a non-dword boundary */ @@ -518,7 +521,10 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen i = n = 0; while (gap--) tmp[i++] = 0xFF; - while (len && i < BUSWIDTH) tmp[i++] = buf[n++], len--; + while (len && i < BUSWIDTH) { + tmp[i++] = buf[n++]; + len--; + } while (i < BUSWIDTH) tmp[i++] = 0xFF; if (!write_dword (aligned,*((__u32 *) tmp))) return (-EIO); diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 79dcca16481d..2e00862389dd 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -793,7 +793,7 @@ static int spear_smi_probe_config_dt(struct platform_device *pdev, struct device_node *np) { struct spear_smi_plat_data *pdata = dev_get_platdata(&pdev->dev); - struct device_node *pp = NULL; + struct device_node *pp; const __be32 *addr; u32 val; int len; @@ -812,7 +812,7 @@ static int spear_smi_probe_config_dt(struct platform_device *pdev, return -ENOMEM; /* Fill structs for each subnode (flash device) */ - while ((pp = of_get_next_child(np, pp))) { + for_each_child_of_node(np, pp) { pdata->np[i] = pp; /* Read base-addr and size from DT */ diff --git a/drivers/mtd/hyperbus/Kconfig b/drivers/mtd/hyperbus/Kconfig index a4d8968d133d..46c7e407e378 100644 --- a/drivers/mtd/hyperbus/Kconfig +++ b/drivers/mtd/hyperbus/Kconfig @@ -22,4 +22,11 @@ config HBMC_AM654 This is the driver for HyperBus controller on TI's AM65x and other SoCs +config RPCIF_HYPERBUS + tristate "Renesas RPC-IF HyperBus driver" + depends on RENESAS_RPCIF + depends on MTD_CFI_BE_BYTE_SWAP + help + This option includes Renesas RPC-IF HyperBus support. + endif # MTD_HYPERBUS diff --git a/drivers/mtd/hyperbus/Makefile b/drivers/mtd/hyperbus/Makefile index 8a936e066f48..5fc2b5124542 100644 --- a/drivers/mtd/hyperbus/Makefile +++ b/drivers/mtd/hyperbus/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_MTD_HYPERBUS) += hyperbus-core.o obj-$(CONFIG_HBMC_AM654) += hbmc-am654.o +obj-$(CONFIG_RPCIF_HYPERBUS) += rpc-if.o diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c index e0e33f6bf513..a3439b791eeb 100644 --- a/drivers/mtd/hyperbus/hbmc-am654.c +++ b/drivers/mtd/hyperbus/hbmc-am654.c @@ -3,6 +3,10 @@ // Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com/ // Author: Vignesh Raghavendra <vigneshr@ti.com> +#include <linux/completion.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> @@ -13,11 +17,18 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> +#include <linux/sched/task_stack.h> #include <linux/types.h> #define AM654_HBMC_CALIB_COUNT 25 +struct am654_hbmc_device_priv { + struct completion rx_dma_complete; + phys_addr_t device_base; + struct hyperbus_ctlr *ctlr; + struct dma_chan *rx_chan; +}; + struct am654_hbmc_priv { struct hyperbus_ctlr ctlr; struct hyperbus_device hbdev; @@ -52,13 +63,103 @@ static int am654_hbmc_calibrate(struct hyperbus_device *hbdev) return ret; } +static void am654_hbmc_dma_callback(void *param) +{ + struct am654_hbmc_device_priv *priv = param; + + complete(&priv->rx_dma_complete); +} + +static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to, + unsigned long from, ssize_t len) + +{ + enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + struct dma_chan *rx_chan = priv->rx_chan; + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_dst, dma_src; + dma_cookie_t cookie; + int ret; + + if (!priv->rx_chan || !virt_addr_valid(to) || object_is_on_stack(to)) + return -EINVAL; + + dma_dst = dma_map_single(rx_chan->device->dev, to, len, DMA_FROM_DEVICE); + if (dma_mapping_error(rx_chan->device->dev, dma_dst)) { + dev_dbg(priv->ctlr->dev, "DMA mapping failed\n"); + return -EIO; + } + + dma_src = priv->device_base + from; + tx = dmaengine_prep_dma_memcpy(rx_chan, dma_dst, dma_src, len, flags); + if (!tx) { + dev_err(priv->ctlr->dev, "device_prep_dma_memcpy error\n"); + ret = -EIO; + goto unmap_dma; + } + + reinit_completion(&priv->rx_dma_complete); + tx->callback = am654_hbmc_dma_callback; + tx->callback_param = priv; + cookie = dmaengine_submit(tx); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(priv->ctlr->dev, "dma_submit_error %d\n", cookie); + goto unmap_dma; + } + + dma_async_issue_pending(rx_chan); + if (!wait_for_completion_timeout(&priv->rx_dma_complete, msecs_to_jiffies(len + 1000))) { + dmaengine_terminate_sync(rx_chan); + dev_err(priv->ctlr->dev, "DMA wait_for_completion_timeout\n"); + ret = -ETIMEDOUT; + } + +unmap_dma: + dma_unmap_single(rx_chan->device->dev, dma_dst, len, DMA_FROM_DEVICE); + return ret; +} + +static void am654_hbmc_read(struct hyperbus_device *hbdev, void *to, + unsigned long from, ssize_t len) +{ + struct am654_hbmc_device_priv *priv = hbdev->priv; + + if (len < SZ_1K || am654_hbmc_dma_read(priv, to, from, len)) + memcpy_fromio(to, hbdev->map.virt + from, len); +} + static const struct hyperbus_ops am654_hbmc_ops = { .calibrate = am654_hbmc_calibrate, + .copy_from = am654_hbmc_read, }; +static int am654_hbmc_request_mmap_dma(struct am654_hbmc_device_priv *priv) +{ + struct dma_chan *rx_chan; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + rx_chan = dma_request_chan_by_mask(&mask); + if (IS_ERR(rx_chan)) { + if (PTR_ERR(rx_chan) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(priv->ctlr->dev, "No DMA channel available\n"); + return 0; + } + priv->rx_chan = rx_chan; + init_completion(&priv->rx_dma_complete); + + return 0; +} + static int am654_hbmc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + struct am654_hbmc_device_priv *dev_priv; struct device *dev = &pdev->dev; struct am654_hbmc_priv *priv; struct resource res; @@ -70,7 +171,8 @@ static int am654_hbmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - ret = of_address_to_resource(np, 0, &res); + priv->hbdev.np = of_get_next_child(np, NULL); + ret = of_address_to_resource(priv->hbdev.np, 0, &res); if (ret) return ret; @@ -88,13 +190,6 @@ static int am654_hbmc_probe(struct platform_device *pdev) priv->mux_ctrl = control; } - pm_runtime_enable(dev); - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); - goto disable_pm; - } - priv->hbdev.map.size = resource_size(&res); priv->hbdev.map.virt = devm_ioremap_resource(dev, &res); if (IS_ERR(priv->hbdev.map.virt)) @@ -103,17 +198,32 @@ static int am654_hbmc_probe(struct platform_device *pdev) priv->ctlr.dev = dev; priv->ctlr.ops = &am654_hbmc_ops; priv->hbdev.ctlr = &priv->ctlr; - priv->hbdev.np = of_get_next_child(dev->of_node, NULL); + + dev_priv = devm_kzalloc(dev, sizeof(*dev_priv), GFP_KERNEL); + if (!dev_priv) { + ret = -ENOMEM; + goto disable_mux; + } + + priv->hbdev.priv = dev_priv; + dev_priv->device_base = res.start; + dev_priv->ctlr = &priv->ctlr; + + ret = am654_hbmc_request_mmap_dma(dev_priv); + if (ret) + goto disable_mux; + ret = hyperbus_register_device(&priv->hbdev); if (ret) { dev_err(dev, "failed to register controller\n"); - pm_runtime_put_sync(&pdev->dev); - goto disable_pm; + goto release_dma; } return 0; -disable_pm: - pm_runtime_disable(dev); +release_dma: + if (dev_priv->rx_chan) + dma_release_channel(dev_priv->rx_chan); +disable_mux: if (priv->mux_ctrl) mux_control_deselect(priv->mux_ctrl); return ret; @@ -122,13 +232,15 @@ disable_pm: static int am654_hbmc_remove(struct platform_device *pdev) { struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); + struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv; int ret; ret = hyperbus_unregister_device(&priv->hbdev); if (priv->mux_ctrl) mux_control_deselect(priv->mux_ctrl); - pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); + + if (dev_priv->rx_chan) + dma_release_channel(dev_priv->rx_chan); return ret; } diff --git a/drivers/mtd/hyperbus/rpc-if.c b/drivers/mtd/hyperbus/rpc-if.c new file mode 100644 index 000000000000..ecb050ba95cd --- /dev/null +++ b/drivers/mtd/hyperbus/rpc-if.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Linux driver for RPC-IF HyperFlash + * + * Copyright (C) 2019-2020 Cogent Embedded, Inc. + */ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/hyperbus.h> +#include <linux/mtd/mtd.h> +#include <linux/mux/consumer.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include <memory/renesas-rpc-if.h> + +struct rpcif_hyperbus { + struct rpcif rpc; + struct hyperbus_ctlr ctlr; + struct hyperbus_device hbdev; +}; + +static const struct rpcif_op rpcif_op_tmpl = { + .cmd = { + .buswidth = 8, + .ddr = true, + }, + .ocmd = { + .buswidth = 8, + .ddr = true, + }, + .addr = { + .nbytes = 1, + .buswidth = 8, + .ddr = true, + }, + .data = { + .buswidth = 8, + .ddr = true, + }, +}; + +static void rpcif_hb_prepare_read(struct rpcif *rpc, void *to, + unsigned long from, ssize_t len) +{ + struct rpcif_op op = rpcif_op_tmpl; + + op.cmd.opcode = HYPERBUS_RW_READ | HYPERBUS_AS_MEM; + op.addr.val = from >> 1; + op.dummy.buswidth = 1; + op.dummy.ncycles = 15; + op.data.dir = RPCIF_DATA_IN; + op.data.nbytes = len; + op.data.buf.in = to; + + rpcif_prepare(rpc, &op, NULL, NULL); +} + +static void rpcif_hb_prepare_write(struct rpcif *rpc, unsigned long to, + void *from, ssize_t len) +{ + struct rpcif_op op = rpcif_op_tmpl; + + op.cmd.opcode = HYPERBUS_RW_WRITE | HYPERBUS_AS_MEM; + op.addr.val = to >> 1; + op.data.dir = RPCIF_DATA_OUT; + op.data.nbytes = len; + op.data.buf.out = from; + + rpcif_prepare(rpc, &op, NULL, NULL); +} + +static u16 rpcif_hb_read16(struct hyperbus_device *hbdev, unsigned long addr) +{ + struct rpcif_hyperbus *hyperbus = + container_of(hbdev, struct rpcif_hyperbus, hbdev); + map_word data; + + rpcif_hb_prepare_read(&hyperbus->rpc, &data, addr, 2); + + rpcif_manual_xfer(&hyperbus->rpc); + + return data.x[0]; +} + +static void rpcif_hb_write16(struct hyperbus_device *hbdev, unsigned long addr, + u16 data) +{ + struct rpcif_hyperbus *hyperbus = + container_of(hbdev, struct rpcif_hyperbus, hbdev); + + rpcif_hb_prepare_write(&hyperbus->rpc, addr, &data, 2); + + rpcif_manual_xfer(&hyperbus->rpc); +} + +static void rpcif_hb_copy_from(struct hyperbus_device *hbdev, void *to, + unsigned long from, ssize_t len) +{ + struct rpcif_hyperbus *hyperbus = + container_of(hbdev, struct rpcif_hyperbus, hbdev); + + rpcif_hb_prepare_read(&hyperbus->rpc, to, from, len); + + rpcif_dirmap_read(&hyperbus->rpc, from, len, to); +} + +static const struct hyperbus_ops rpcif_hb_ops = { + .read16 = rpcif_hb_read16, + .write16 = rpcif_hb_write16, + .copy_from = rpcif_hb_copy_from, +}; + +static int rpcif_hb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rpcif_hyperbus *hyperbus; + int error; + + hyperbus = devm_kzalloc(dev, sizeof(*hyperbus), GFP_KERNEL); + if (!hyperbus) + return -ENOMEM; + + rpcif_sw_init(&hyperbus->rpc, pdev->dev.parent); + + platform_set_drvdata(pdev, hyperbus); + + rpcif_enable_rpm(&hyperbus->rpc); + + rpcif_hw_init(&hyperbus->rpc, true); + + hyperbus->hbdev.map.size = hyperbus->rpc.size; + hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; + + hyperbus->ctlr.dev = dev; + hyperbus->ctlr.ops = &rpcif_hb_ops; + hyperbus->hbdev.ctlr = &hyperbus->ctlr; + hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL); + error = hyperbus_register_device(&hyperbus->hbdev); + if (error) + rpcif_disable_rpm(&hyperbus->rpc); + + return error; +} + +static int rpcif_hb_remove(struct platform_device *pdev) +{ + struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev); + int error = hyperbus_unregister_device(&hyperbus->hbdev); + struct rpcif *rpc = dev_get_drvdata(pdev->dev.parent); + + rpcif_disable_rpm(rpc); + return error; +} + +static struct platform_driver rpcif_platform_driver = { + .probe = rpcif_hb_probe, + .remove = rpcif_hb_remove, + .driver = { + .name = "rpc-if-hyperflash", + }, +}; + +module_platform_driver(rpcif_platform_driver); + +MODULE_DESCRIPTION("Renesas RPC-IF HyperFlash driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c index 0f1547f09d08..72f5c7b30079 100644 --- a/drivers/mtd/lpddr/lpddr2_nvm.c +++ b/drivers/mtd/lpddr/lpddr2_nvm.c @@ -393,6 +393,17 @@ static int lpddr2_nvm_lock(struct mtd_info *mtd, loff_t start_add, return lpddr2_nvm_do_block_op(mtd, start_add, len, LPDDR2_NVM_LOCK); } +static const struct mtd_info lpddr2_nvm_mtd_info = { + .type = MTD_RAM, + .writesize = 1, + .flags = (MTD_CAP_NVRAM | MTD_POWERUP_LOCK), + ._read = lpddr2_nvm_read, + ._write = lpddr2_nvm_write, + ._erase = lpddr2_nvm_erase, + ._unlock = lpddr2_nvm_unlock, + ._lock = lpddr2_nvm_lock, +}; + /* * lpddr2_nvm driver probe method */ @@ -433,6 +444,7 @@ static int lpddr2_nvm_probe(struct platform_device *pdev) .pfow_base = OW_BASE_ADDRESS, .fldrv_priv = pcm_data, }; + if (IS_ERR(map->virt)) return PTR_ERR(map->virt); @@ -444,22 +456,13 @@ static int lpddr2_nvm_probe(struct platform_device *pdev) return PTR_ERR(pcm_data->ctl_regs); /* Populate mtd_info data structure */ - *mtd = (struct mtd_info) { - .dev = { .parent = &pdev->dev }, - .name = pdev->dev.init_name, - .type = MTD_RAM, - .priv = map, - .size = resource_size(add_range), - .erasesize = ERASE_BLOCKSIZE * pcm_data->bus_width, - .writesize = 1, - .writebufsize = WRITE_BUFFSIZE * pcm_data->bus_width, - .flags = (MTD_CAP_NVRAM | MTD_POWERUP_LOCK), - ._read = lpddr2_nvm_read, - ._write = lpddr2_nvm_write, - ._erase = lpddr2_nvm_erase, - ._unlock = lpddr2_nvm_unlock, - ._lock = lpddr2_nvm_lock, - }; + *mtd = lpddr2_nvm_mtd_info; + mtd->dev.parent = &pdev->dev; + mtd->name = pdev->dev.init_name; + mtd->priv = map; + mtd->size = resource_size(add_range); + mtd->erasesize = ERASE_BLOCKSIZE * pcm_data->bus_width; + mtd->writebufsize = WRITE_BUFFSIZE * pcm_data->bus_width; /* Verify the presence of the device looking for PFOW string */ if (!lpddr2_nvm_pfow_present(map)) { diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index fb1cbc9a2870..ee063baed136 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -94,6 +94,34 @@ struct mtd_info *lpddr_cmdset(struct map_info *map) } EXPORT_SYMBOL(lpddr_cmdset); +static void print_drs_error(unsigned int dsr) +{ + int prog_status = (dsr & DSR_RPS) >> 8; + + if (!(dsr & DSR_AVAILABLE)) + pr_notice("DSR.15: (0) Device not Available\n"); + if ((prog_status & 0x03) == 0x03) + pr_notice("DSR.9,8: (11) Attempt to program invalid half with 41h command\n"); + else if (prog_status & 0x02) + pr_notice("DSR.9,8: (10) Object Mode Program attempt in region with Control Mode data\n"); + else if (prog_status & 0x01) + pr_notice("DSR.9,8: (01) Program attempt in region with Object Mode data\n"); + if (!(dsr & DSR_READY_STATUS)) + pr_notice("DSR.7: (0) Device is Busy\n"); + if (dsr & DSR_ESS) + pr_notice("DSR.6: (1) Erase Suspended\n"); + if (dsr & DSR_ERASE_STATUS) + pr_notice("DSR.5: (1) Erase/Blank check error\n"); + if (dsr & DSR_PROGRAM_STATUS) + pr_notice("DSR.4: (1) Program Error\n"); + if (dsr & DSR_VPPS) + pr_notice("DSR.3: (1) Vpp low detect, operation aborted\n"); + if (dsr & DSR_PSS) + pr_notice("DSR.2: (1) Program suspended\n"); + if (dsr & DSR_DPS) + pr_notice("DSR.1: (1) Aborted Erase/Program attempt on locked block\n"); +} + static int wait_for_ready(struct map_info *map, struct flchip *chip, unsigned int chip_op_time) { diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index fd37553f1b07..6650acbc961e 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -75,6 +75,17 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree. +config MTD_PHYSMAP_BT1_ROM + bool "Baikal-T1 Boot ROMs OF-based physical memory map handling" + depends on MTD_PHYSMAP_OF + depends on MIPS_BAIKAL_T1 || COMPILE_TEST + select MTD_COMPLEX_MAPPINGS + select MULTIPLEXER + select MUX_MMIO + help + This provides some extra DT physmap parsing for the Baikal-T1 + platforms, some detection and setting up ROMs-specific accessors. + config MTD_PHYSMAP_VERSATILE bool "ARM Versatile OF-based physical memory map handling" depends on MTD_PHYSMAP_OF diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index c0da86a5d26f..79f018cf412f 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o physmap-objs-y += physmap-core.o +physmap-objs-$(CONFIG_MTD_PHYSMAP_BT1_ROM) += physmap-bt1-rom.o physmap-objs-$(CONFIG_MTD_PHYSMAP_VERSATILE) += physmap-versatile.o physmap-objs-$(CONFIG_MTD_PHYSMAP_GEMINI) += physmap-gemini.o physmap-objs-$(CONFIG_MTD_PHYSMAP_IXP4XX) += physmap-ixp4xx.o diff --git a/drivers/mtd/maps/physmap-bt1-rom.c b/drivers/mtd/maps/physmap-bt1-rom.c new file mode 100644 index 000000000000..27cfe1c63a2e --- /dev/null +++ b/drivers/mtd/maps/physmap-bt1-rom.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC + * + * Authors: + * Serge Semin <Sergey.Semin@baikalelectronics.ru> + * + * Baikal-T1 Physically Mapped Internal ROM driver + */ +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mtd/map.h> +#include <linux/mtd/xip.h> +#include <linux/mux/consumer.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "physmap-bt1-rom.h" + +/* + * Baikal-T1 SoC ROMs are only accessible by the dword-aligned instructions. + * We have to take this into account when implementing the data read-methods. + * Note there is no need in bothering with endianness, since both Baikal-T1 + * CPU and MMIO are LE. + */ +static map_word __xipram bt1_rom_map_read(struct map_info *map, + unsigned long ofs) +{ + void __iomem *src = map->virt + ofs; + unsigned long shift; + map_word ret; + u32 data; + + /* Read data within offset dword. */ + shift = (unsigned long)src & 0x3; + data = readl_relaxed(src - shift); + if (!shift) { + ret.x[0] = data; + return ret; + } + ret.x[0] = data >> (shift * BITS_PER_BYTE); + + /* Read data from the next dword. */ + shift = 4 - shift; + if (ofs + shift >= map->size) + return ret; + + data = readl_relaxed(src + shift); + ret.x[0] |= data << (shift * BITS_PER_BYTE); + + return ret; +} + +static void __xipram bt1_rom_map_copy_from(struct map_info *map, + void *to, unsigned long from, + ssize_t len) +{ + void __iomem *src = map->virt + from; + ssize_t shift, chunk; + u32 data; + + if (len <= 0 || from >= map->size) + return; + + /* Make sure we don't go over the map limit. */ + len = min_t(ssize_t, map->size - from, len); + + /* + * Since requested data size can be pretty big we have to implement + * the copy procedure as optimal as possible. That's why it's split + * up into the next three stages: unaligned head, aligned body, + * unaligned tail. + */ + shift = (ssize_t)src & 0x3; + if (shift) { + chunk = min_t(ssize_t, 4 - shift, len); + data = readl_relaxed(src - shift); + memcpy(to, &data + shift, chunk); + src += chunk; + to += chunk; + len -= chunk; + } + + while (len >= 4) { + data = readl_relaxed(src); + memcpy(to, &data, 4); + src += 4; + to += 4; + len -= 4; + } + + if (len) { + data = readl_relaxed(src); + memcpy(to, &data, len); + } +} + +int of_flash_probe_bt1_rom(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + struct device *dev = &pdev->dev; + + /* It's supposed to be read-only MTD. */ + if (!of_device_is_compatible(np, "mtd-rom")) { + dev_info(dev, "No mtd-rom compatible string\n"); + return 0; + } + + /* Multiplatform guard. */ + if (!of_device_is_compatible(np, "baikal,bt1-int-rom")) + return 0; + + /* Sanity check the device parameters retrieved from DTB. */ + if (map->bankwidth != 4) + dev_warn(dev, "Bank width is supposed to be 32 bits wide\n"); + + map->read = bt1_rom_map_read; + map->copy_from = bt1_rom_map_copy_from; + + return 0; +} diff --git a/drivers/mtd/maps/physmap-bt1-rom.h b/drivers/mtd/maps/physmap-bt1-rom.h new file mode 100644 index 000000000000..6782899598a4 --- /dev/null +++ b/drivers/mtd/maps/physmap-bt1-rom.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <linux/mtd/map.h> +#include <linux/of.h> + +#ifdef CONFIG_MTD_PHYSMAP_BT1_ROM +int of_flash_probe_bt1_rom(struct platform_device *pdev, + struct device_node *np, + struct map_info *map); +#else +static inline +int of_flash_probe_bt1_rom(struct platform_device *pdev, + struct device_node *np, + struct map_info *map) +{ + return 0; +} +#endif diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index 8f7f966fa9a7..001ed5deb622 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -41,6 +41,7 @@ #include <linux/pm_runtime.h> #include <linux/gpio/consumer.h> +#include "physmap-bt1-rom.h" #include "physmap-gemini.h" #include "physmap-ixp4xx.h" #include "physmap-versatile.h" @@ -371,6 +372,10 @@ static int physmap_flash_of_init(struct platform_device *dev) info->maps[i].bankwidth = bankwidth; info->maps[i].device_node = dp; + err = of_flash_probe_bt1_rom(dev, dp, &info->maps[i]); + if (err) + return err; + err = of_flash_probe_gemini(dev, dp, &info->maps[i]); if (err) return err; @@ -515,7 +520,8 @@ static int physmap_flash_probe(struct platform_device *dev) dev_notice(&dev->dev, "physmap platform flash device: %pR\n", res); - info->maps[i].name = dev_name(&dev->dev); + if (!info->maps[i].name) + info->maps[i].name = dev_name(&dev->dev); if (!info->maps[i].phys) info->maps[i].phys = res->start; diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c index 177bf134e189..a7ec947a3ebb 100644 --- a/drivers/mtd/maps/vmu-flash.c +++ b/drivers/mtd/maps/vmu-flash.c @@ -40,7 +40,7 @@ struct memcard { u32 blocklen; u32 writecnt; u32 readcnt; - u32 removeable; + u32 removable; int partition; int read; unsigned char *blockread; @@ -619,7 +619,7 @@ static int vmu_connect(struct maple_device *mdev) card->blocklen = ((basic_flash_data >> 16 & 0xFF) + 1) << 5; card->writecnt = basic_flash_data >> 12 & 0xF; card->readcnt = basic_flash_data >> 8 & 0xF; - card->removeable = basic_flash_data >> 7 & 1; + card->removable = basic_flash_data >> 7 & 1; card->partition = 0; @@ -772,7 +772,6 @@ static void vmu_file_error(struct maple_device *mdev, void *recvbuf) static int probe_maple_vmu(struct device *dev) { - int error; struct maple_device *mdev = to_maple_dev(dev); struct maple_driver *mdrv = to_maple_driver(dev->driver); @@ -780,11 +779,7 @@ static int probe_maple_vmu(struct device *dev) mdev->fileerr_handler = vmu_file_error; mdev->driver = mdrv; - error = vmu_connect(mdev); - if (error) - return error; - - return 0; + return vmu_connect(mdev); } static int remove_maple_vmu(struct device *dev) diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 1d6c9e7e7b7d..6e4d0017c0bd 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -103,6 +103,47 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, } static int +concat_panic_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) { + to -= subdev->size; + continue; + } + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + err = mtd_panic_write(subdev, to, size, &retsize, buf); + if (err == -EOPNOTSUPP) { + printk(KERN_ERR "mtdconcat: Cannot write from panic without panic_write\n"); + return err; + } + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + return err; +} + + +static int concat_write(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) { @@ -648,6 +689,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd._block_isbad = concat_block_isbad; if (subdev[0]->_block_markbad) concat->mtd._block_markbad = concat_block_markbad; + if (subdev[0]->_panic_write) + concat->mtd._panic_write = concat_panic_write; concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index b5e5d3140f57..e9e163ae9d86 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -335,7 +335,7 @@ static const struct device_type mtd_devtype = { .release = mtd_release, }; -static int mtd_partid_show(struct seq_file *s, void *p) +static int mtd_partid_debug_show(struct seq_file *s, void *p) { struct mtd_info *mtd = s->private; @@ -344,19 +344,9 @@ static int mtd_partid_show(struct seq_file *s, void *p) return 0; } -static int mtd_partid_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, mtd_partid_show, inode->i_private); -} - -static const struct file_operations mtd_partid_debug_fops = { - .open = mtd_partid_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(mtd_partid_debug); -static int mtd_partname_show(struct seq_file *s, void *p) +static int mtd_partname_debug_show(struct seq_file *s, void *p) { struct mtd_info *mtd = s->private; @@ -365,17 +355,7 @@ static int mtd_partname_show(struct seq_file *s, void *p) return 0; } -static int mtd_partname_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, mtd_partname_show, inode->i_private); -} - -static const struct file_operations mtd_partname_debug_fops = { - .open = mtd_partname_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(mtd_partname_debug); static struct dentry *dfs_dir_mtd; diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 4ced68be7ed7..774970bfcf85 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -279,12 +279,13 @@ static void mtdoops_do_dump(struct kmsg_dumper *dumper, kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE, record_size - MTDOOPS_HEADER_SIZE, NULL); - /* Panics must be written immediately */ - if (reason != KMSG_DUMP_OOPS) + if (reason != KMSG_DUMP_OOPS) { + /* Panics must be written immediately */ mtdoops_write(cxt, 1); - - /* For other cases, schedule work to write it "nicely" */ - schedule_work(&cxt->work_write); + } else { + /* For other cases, schedule work to write it "nicely" */ + schedule_work(&cxt->work_write); + } } static void mtdoops_notify_add(struct mtd_info *mtd) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index c1a45b071165..4a9aed4f0104 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -9,4 +9,12 @@ source "drivers/mtd/nand/onenand/Kconfig" source "drivers/mtd/nand/raw/Kconfig" source "drivers/mtd/nand/spi/Kconfig" +menu "ECC engine support" + +config MTD_NAND_ECC + bool + depends on MTD_NAND_CORE + +endmenu + endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 7ecd80c0a66e..981372953b56 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o obj-y += onenand/ obj-y += raw/ obj-y += spi/ + +nandcore-$(CONFIG_MTD_NAND_ECC) += ecc.o diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c new file mode 100644 index 000000000000..4a56e6c0da67 --- /dev/null +++ b/drivers/mtd/nand/ecc.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generic Error-Correcting Code (ECC) engine + * + * Copyright (C) 2019 Macronix + * Author: + * Miquèl RAYNAL <miquel.raynal@bootlin.com> + * + * + * This file describes the abstraction of any NAND ECC engine. It has been + * designed to fit most cases, including parallel NANDs and SPI-NANDs. + * + * There are three main situations where instantiating this ECC engine makes + * sense: + * - external: The ECC engine is outside the NAND pipeline, typically this + * is a software ECC engine, or an hardware engine that is + * outside the NAND controller pipeline. + * - pipelined: The ECC engine is inside the NAND pipeline, ie. on the + * controller's side. This is the case of most of the raw NAND + * controllers. In the pipeline case, the ECC bytes are + * generated/data corrected on the fly when a page is + * written/read. + * - ondie: The ECC engine is inside the NAND pipeline, on the chip's side. + * Some NAND chips can correct themselves the data. + * + * Besides the initial setup and final cleanups, the interfaces are rather + * simple: + * - prepare: Prepare an I/O request. Enable/disable the ECC engine based on + * the I/O request type. In case of software correction or external + * engine, this step may involve to derive the ECC bytes and place + * them in the OOB area before a write. + * - finish: Finish an I/O request. Correct the data in case of a read + * request and report the number of corrected bits/uncorrectable + * errors. Most likely empty for write operations, unless you have + * hardware specific stuff to do, like shutting down the engine to + * save power. + * + * The I/O request should be enclosed in a prepare()/finish() pair of calls + * and will behave differently depending on the requested I/O type: + * - raw: Correction disabled + * - ecc: Correction enabled + * + * The request direction is impacting the logic as well: + * - read: Load data from the NAND chip + * - write: Store data in the NAND chip + * + * Mixing all this combinations together gives the following behavior. + * Those are just examples, drivers are free to add custom steps in their + * prepare/finish hook. + * + * [external ECC engine] + * - external + prepare + raw + read: do nothing + * - external + finish + raw + read: do nothing + * - external + prepare + raw + write: do nothing + * - external + finish + raw + write: do nothing + * - external + prepare + ecc + read: do nothing + * - external + finish + ecc + read: calculate expected ECC bytes, extract + * ECC bytes from OOB buffer, correct + * and report any bitflip/error + * - external + prepare + ecc + write: calculate ECC bytes and store them at + * the right place in the OOB buffer based + * on the OOB layout + * - external + finish + ecc + write: do nothing + * + * [pipelined ECC engine] + * - pipelined + prepare + raw + read: disable the controller's ECC engine if + * activated + * - pipelined + finish + raw + read: do nothing + * - pipelined + prepare + raw + write: disable the controller's ECC engine if + * activated + * - pipelined + finish + raw + write: do nothing + * - pipelined + prepare + ecc + read: enable the controller's ECC engine if + * deactivated + * - pipelined + finish + ecc + read: check the status, report any + * error/bitflip + * - pipelined + prepare + ecc + write: enable the controller's ECC engine if + * deactivated + * - pipelined + finish + ecc + write: do nothing + * + * [ondie ECC engine] + * - ondie + prepare + raw + read: send commands to disable the on-chip ECC + * engine if activated + * - ondie + finish + raw + read: do nothing + * - ondie + prepare + raw + write: send commands to disable the on-chip ECC + * engine if activated + * - ondie + finish + raw + write: do nothing + * - ondie + prepare + ecc + read: send commands to enable the on-chip ECC + * engine if deactivated + * - ondie + finish + ecc + read: send commands to check the status, report + * any error/bitflip + * - ondie + prepare + ecc + write: send commands to enable the on-chip ECC + * engine if deactivated + * - ondie + finish + ecc + write: do nothing + */ + +#include <linux/module.h> +#include <linux/mtd/nand.h> + +/** + * nand_ecc_init_ctx - Init the ECC engine context + * @nand: the NAND device + * + * On success, the caller is responsible of calling @nand_ecc_cleanup_ctx(). + */ +int nand_ecc_init_ctx(struct nand_device *nand) +{ + if (!nand->ecc.engine->ops->init_ctx) + return 0; + + return nand->ecc.engine->ops->init_ctx(nand); +} +EXPORT_SYMBOL(nand_ecc_init_ctx); + +/** + * nand_ecc_cleanup_ctx - Cleanup the ECC engine context + * @nand: the NAND device + */ +void nand_ecc_cleanup_ctx(struct nand_device *nand) +{ + if (nand->ecc.engine->ops->cleanup_ctx) + nand->ecc.engine->ops->cleanup_ctx(nand); +} +EXPORT_SYMBOL(nand_ecc_cleanup_ctx); + +/** + * nand_ecc_prepare_io_req - Prepare an I/O request + * @nand: the NAND device + * @req: the I/O request + */ +int nand_ecc_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + if (!nand->ecc.engine->ops->prepare_io_req) + return 0; + + return nand->ecc.engine->ops->prepare_io_req(nand, req); +} +EXPORT_SYMBOL(nand_ecc_prepare_io_req); + +/** + * nand_ecc_finish_io_req - Finish an I/O request + * @nand: the NAND device + * @req: the I/O request + */ +int nand_ecc_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + if (!nand->ecc.engine->ops->finish_io_req) + return 0; + + return nand->ecc.engine->ops->finish_io_req(nand, req); +} +EXPORT_SYMBOL(nand_ecc_finish_io_req); + +/* Define default OOB placement schemes for large and small page devices */ +static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section > 1) + return -ERANGE; + + if (!section) { + oobregion->offset = 0; + if (mtd->oobsize == 16) + oobregion->length = 4; + else + oobregion->length = 3; + } else { + if (mtd->oobsize == 8) + return -ERANGE; + + oobregion->offset = 6; + oobregion->length = total_ecc_bytes - 4; + } + + return 0; +} + +static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section > 1) + return -ERANGE; + + if (mtd->oobsize == 16) { + if (section) + return -ERANGE; + + oobregion->length = 8; + oobregion->offset = 8; + } else { + oobregion->length = 2; + if (!section) + oobregion->offset = 3; + else + oobregion->offset = 6; + } + + return 0; +} + +static const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = { + .ecc = nand_ooblayout_ecc_sp, + .free = nand_ooblayout_free_sp, +}; + +const struct mtd_ooblayout_ops *nand_get_small_page_ooblayout(void) +{ + return &nand_ooblayout_sp_ops; +} +EXPORT_SYMBOL_GPL(nand_get_small_page_ooblayout); + +static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section || !total_ecc_bytes) + return -ERANGE; + + oobregion->length = total_ecc_bytes; + oobregion->offset = mtd->oobsize - oobregion->length; + + return 0; +} + +static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section) + return -ERANGE; + + oobregion->length = mtd->oobsize - total_ecc_bytes - 2; + oobregion->offset = 2; + + return 0; +} + +static const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { + .ecc = nand_ooblayout_ecc_lp, + .free = nand_ooblayout_free_lp, +}; + +const struct mtd_ooblayout_ops *nand_get_large_page_ooblayout(void) +{ + return &nand_ooblayout_lp_ops; +} +EXPORT_SYMBOL_GPL(nand_get_large_page_ooblayout); + +/* + * Support the old "large page" layout used for 1-bit Hamming ECC where ECC + * are placed at a fixed offset. + */ +static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + + if (section) + return -ERANGE; + + switch (mtd->oobsize) { + case 64: + oobregion->offset = 40; + break; + case 128: + oobregion->offset = 80; + break; + default: + return -EINVAL; + } + + oobregion->length = total_ecc_bytes; + if (oobregion->offset + oobregion->length > mtd->oobsize) + return -ERANGE; + + return 0; +} + +static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + unsigned int total_ecc_bytes = nand->ecc.ctx.total; + int ecc_offset = 0; + + if (section < 0 || section > 1) + return -ERANGE; + + switch (mtd->oobsize) { + case 64: + ecc_offset = 40; + break; + case 128: + ecc_offset = 80; + break; + default: + return -EINVAL; + } + + if (section == 0) { + oobregion->offset = 2; + oobregion->length = ecc_offset - 2; + } else { + oobregion->offset = ecc_offset + total_ecc_bytes; + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = { + .ecc = nand_ooblayout_ecc_lp_hamming, + .free = nand_ooblayout_free_lp_hamming, +}; + +const struct mtd_ooblayout_ops *nand_get_large_page_hamming_ooblayout(void) +{ + return &nand_ooblayout_lp_hamming_ops; +} +EXPORT_SYMBOL_GPL(nand_get_large_page_hamming_ooblayout); + +static enum nand_ecc_engine_type +of_get_nand_ecc_engine_type(struct device_node *np) +{ + struct device_node *eng_np; + + if (of_property_read_bool(np, "nand-no-ecc-engine")) + return NAND_ECC_ENGINE_TYPE_NONE; + + if (of_property_read_bool(np, "nand-use-soft-ecc-engine")) + return NAND_ECC_ENGINE_TYPE_SOFT; + + eng_np = of_parse_phandle(np, "nand-ecc-engine", 0); + of_node_put(eng_np); + + if (eng_np) { + if (eng_np == np) + return NAND_ECC_ENGINE_TYPE_ON_DIE; + else + return NAND_ECC_ENGINE_TYPE_ON_HOST; + } + + return NAND_ECC_ENGINE_TYPE_INVALID; +} + +static const char * const nand_ecc_placement[] = { + [NAND_ECC_PLACEMENT_OOB] = "oob", + [NAND_ECC_PLACEMENT_INTERLEAVED] = "interleaved", +}; + +static enum nand_ecc_placement of_get_nand_ecc_placement(struct device_node *np) +{ + enum nand_ecc_placement placement; + const char *pm; + int err; + + err = of_property_read_string(np, "nand-ecc-placement", &pm); + if (!err) { + for (placement = NAND_ECC_PLACEMENT_OOB; + placement < ARRAY_SIZE(nand_ecc_placement); placement++) { + if (!strcasecmp(pm, nand_ecc_placement[placement])) + return placement; + } + } + + return NAND_ECC_PLACEMENT_UNKNOWN; +} + +static const char * const nand_ecc_algos[] = { + [NAND_ECC_ALGO_HAMMING] = "hamming", + [NAND_ECC_ALGO_BCH] = "bch", + [NAND_ECC_ALGO_RS] = "rs", +}; + +static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np) +{ + enum nand_ecc_algo ecc_algo; + const char *pm; + int err; + + err = of_property_read_string(np, "nand-ecc-algo", &pm); + if (!err) { + for (ecc_algo = NAND_ECC_ALGO_HAMMING; + ecc_algo < ARRAY_SIZE(nand_ecc_algos); + ecc_algo++) { + if (!strcasecmp(pm, nand_ecc_algos[ecc_algo])) + return ecc_algo; + } + } + + return NAND_ECC_ALGO_UNKNOWN; +} + +static int of_get_nand_ecc_step_size(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-step-size", &val); + return ret ? ret : val; +} + +static int of_get_nand_ecc_strength(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-strength", &val); + return ret ? ret : val; +} + +void of_get_nand_ecc_user_config(struct nand_device *nand) +{ + struct device_node *dn = nanddev_get_of_node(nand); + int strength, size; + + nand->ecc.user_conf.engine_type = of_get_nand_ecc_engine_type(dn); + nand->ecc.user_conf.algo = of_get_nand_ecc_algo(dn); + nand->ecc.user_conf.placement = of_get_nand_ecc_placement(dn); + + strength = of_get_nand_ecc_strength(dn); + if (strength >= 0) + nand->ecc.user_conf.strength = strength; + + size = of_get_nand_ecc_step_size(dn); + if (size >= 0) + nand->ecc.user_conf.step_size = size; + + if (of_property_read_bool(dn, "nand-ecc-maximize")) + nand->ecc.user_conf.flags |= NAND_ECC_MAXIMIZE_STRENGTH; +} +EXPORT_SYMBOL(of_get_nand_ecc_user_config); + +/** + * nand_ecc_is_strong_enough - Check if the chip configuration meets the + * datasheet requirements. + * + * @nand: Device to check + * + * If our configuration corrects A bits per B bytes and the minimum + * required correction level is X bits per Y bytes, then we must ensure + * both of the following are true: + * + * (1) A / B >= X / Y + * (2) A >= X + * + * Requirement (1) ensures we can correct for the required bitflip density. + * Requirement (2) ensures we can correct even when all bitflips are clumped + * in the same sector. + */ +bool nand_ecc_is_strong_enough(struct nand_device *nand) +{ + const struct nand_ecc_props *reqs = nanddev_get_ecc_requirements(nand); + const struct nand_ecc_props *conf = nanddev_get_ecc_conf(nand); + struct mtd_info *mtd = nanddev_to_mtd(nand); + int corr, ds_corr; + + if (conf->step_size == 0 || reqs->step_size == 0) + /* Not enough information */ + return true; + + /* + * We get the number of corrected bits per page to compare + * the correction density. + */ + corr = (mtd->writesize * conf->strength) / conf->step_size; + ds_corr = (mtd->writesize * reqs->strength) / reqs->step_size; + + return corr >= ds_corr && conf->strength >= reqs->strength; +} +EXPORT_SYMBOL(nand_ecc_is_strong_enough); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>"); +MODULE_DESCRIPTION("Generic ECC engine"); diff --git a/drivers/mtd/nand/onenand/onenand_base.c b/drivers/mtd/nand/onenand/onenand_base.c index ec18ade33262..188b8061e1f7 100644 --- a/drivers/mtd/nand/onenand/onenand_base.c +++ b/drivers/mtd/nand/onenand/onenand_base.c @@ -1052,16 +1052,11 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col int thislen) { struct onenand_chip *this = mtd->priv; - int ret; this->read_bufferram(mtd, ONENAND_SPARERAM, this->oob_buf, 0, mtd->oobsize); - ret = mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf, - column, thislen); - if (ret) - return ret; - - return 0; + return mtd_ooblayout_get_databytes(mtd, buf, this->oob_buf, + column, thislen); } /** diff --git a/drivers/mtd/nand/onenand/onenand_omap2.c b/drivers/mtd/nand/onenand/onenand_omap2.c index aa9368bf7a0c..d8c0bd002c2b 100644 --- a/drivers/mtd/nand/onenand/onenand_omap2.c +++ b/drivers/mtd/nand/onenand/onenand_omap2.c @@ -494,11 +494,8 @@ static int omap2_onenand_probe(struct platform_device *pdev) c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN); if (IS_ERR(c->int_gpiod)) { - r = PTR_ERR(c->int_gpiod); /* Just try again if this happens */ - if (r != -EPROBE_DEFER) - dev_err(dev, "error getting gpio: %d\n", r); - return r; + return dev_err_probe(dev, PTR_ERR(c->int_gpiod), "error getting gpio\n"); } if (c->int_gpiod) { diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 1203775023ad..6c46f25b57e2 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -13,6 +13,7 @@ config MTD_NAND_ECC_SW_HAMMING_SMC 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 diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index fdba155416d2..d3c5cc513c8f 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -260,8 +260,8 @@ static int gpio_nand_probe(struct platform_device *pdev) return err; } - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.algo = NAND_ECC_HAMMING; + this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + this->ecc.algo = NAND_ECC_ALGO_HAMMING; platform_set_drvdata(pdev, priv); @@ -400,12 +400,14 @@ static int gpio_nand_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id gpio_nand_of_id_table[] = { { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, gpio_nand_of_id_table); +#endif static const struct platform_device_id gpio_nand_plat_id_table[] = { { diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c index 12c643e97c85..fbb4ea751be8 100644 --- a/drivers/mtd/nand/raw/arasan-nand-controller.c +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c @@ -980,10 +980,10 @@ static int anfc_init_hw_ecc_controller(struct arasan_nfc *nfc, return -EINVAL; } - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); ecc->steps = mtd->writesize / ecc->size; - ecc->algo = NAND_ECC_BCH; + ecc->algo = NAND_ECC_ALGO_BCH; anand->ecc_bits = bch_gf_mag * ecc->strength; ecc->bytes = DIV_ROUND_UP(anand->ecc_bits, 8); anand->ecc_total = DIV_ROUND_UP(anand->ecc_bits * ecc->steps, 8); @@ -1056,17 +1056,17 @@ static int anfc_attach_chip(struct nand_chip *chip) chip->ecc.read_page_raw = nand_monolithic_read_page_raw; chip->ecc.write_page_raw = nand_monolithic_write_page_raw; - switch (chip->ecc.mode) { - case NAND_ECC_NONE: - case NAND_ECC_SOFT: - case NAND_ECC_ON_DIE: + switch (chip->ecc.engine_type) { + case NAND_ECC_ENGINE_TYPE_NONE: + case NAND_ECC_ENGINE_TYPE_SOFT: + case NAND_ECC_ENGINE_TYPE_ON_DIE: break; - case NAND_ECC_HW: + case NAND_ECC_ENGINE_TYPE_ON_HOST: ret = anfc_init_hw_ecc_controller(nfc, chip); break; default: dev_err(nfc->dev, "Unsupported ECC mode: %d\n", - chip->ecc.mode); + chip->ecc.engine_type); return -EINVAL; } diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index c9818f548d07..e6ceec8f50dc 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -202,6 +202,8 @@ struct atmel_nand_controller_ops { int (*ecc_init)(struct nand_chip *chip); int (*setup_interface)(struct atmel_nand *nand, int csline, const struct nand_interface_config *conf); + int (*exec_op)(struct atmel_nand *nand, + const struct nand_operation *op, bool check_only); }; struct atmel_nand_controller_caps { @@ -259,6 +261,7 @@ struct atmel_hsmc_nand_controller { struct regmap *io; struct atmel_nfc_op op; struct completion complete; + u32 cfg; int irq; /* Only used when instantiating from legacy DT bindings. */ @@ -414,29 +417,62 @@ err: return -EIO; } -static u8 atmel_nand_read_byte(struct nand_chip *chip) +static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll) { - struct atmel_nand *nand = to_atmel_nand(chip); + u8 *addrs = nc->op.addrs; + unsigned int op = 0; + u32 addr, val; + int i, ret; - return ioread8(nand->activecs->io.virt); -} + nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE; -static void atmel_nand_write_byte(struct nand_chip *chip, u8 byte) -{ - struct atmel_nand *nand = to_atmel_nand(chip); + for (i = 0; i < nc->op.ncmds; i++) + op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]); - if (chip->options & NAND_BUSWIDTH_16) - iowrite16(byte | (byte << 8), nand->activecs->io.virt); - else - iowrite8(byte, nand->activecs->io.virt); + if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++); + + op |= ATMEL_NFC_CSID(nc->op.cs) | + ATMEL_NFC_ACYCLE(nc->op.naddrs); + + if (nc->op.ncmds > 1) + op |= ATMEL_NFC_VCMD2; + + addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) | + (addrs[3] << 24); + + if (nc->op.data != ATMEL_NFC_NO_DATA) { + op |= ATMEL_NFC_DATAEN; + nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE; + + if (nc->op.data == ATMEL_NFC_WRITE_DATA) + op |= ATMEL_NFC_NFCWR; + } + + /* Clear all flags. */ + regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val); + + /* Send the command. */ + regmap_write(nc->io, op, addr); + + ret = atmel_nfc_wait(nc, poll, 0); + if (ret) + dev_err(nc->base.dev, + "Failed to send NAND command (err = %d)!", + ret); + + /* Reset the op state. */ + memset(&nc->op, 0, sizeof(nc->op)); + + return ret; } -static void atmel_nand_read_buf(struct nand_chip *chip, u8 *buf, int len) +static void atmel_nand_data_in(struct atmel_nand *nand, void *buf, + unsigned int len, bool force_8bit) { - struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_nand_controller *nc; - nc = to_nand_controller(chip->controller); + nc = to_nand_controller(nand->base.controller); /* * If the controller supports DMA, the buffer address is DMA-able and @@ -444,23 +480,23 @@ static void atmel_nand_read_buf(struct nand_chip *chip, u8 *buf, int len) * a DMA transfer. If it fails, fallback to PIO mode. */ if (nc->dmac && virt_addr_valid(buf) && - len >= MIN_DMA_LEN && + len >= MIN_DMA_LEN && !force_8bit && !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len, DMA_FROM_DEVICE)) return; - if (chip->options & NAND_BUSWIDTH_16) + if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit) ioread16_rep(nand->activecs->io.virt, buf, len / 2); else ioread8_rep(nand->activecs->io.virt, buf, len); } -static void atmel_nand_write_buf(struct nand_chip *chip, const u8 *buf, int len) +static void atmel_nand_data_out(struct atmel_nand *nand, const void *buf, + unsigned int len, bool force_8bit) { - struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_nand_controller *nc; - nc = to_nand_controller(chip->controller); + nc = to_nand_controller(nand->base.controller); /* * If the controller supports DMA, the buffer address is DMA-able and @@ -468,179 +504,213 @@ static void atmel_nand_write_buf(struct nand_chip *chip, const u8 *buf, int len) * a DMA transfer. If it fails, fallback to PIO mode. */ if (nc->dmac && virt_addr_valid(buf) && - len >= MIN_DMA_LEN && + len >= MIN_DMA_LEN && !force_8bit && !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma, len, DMA_TO_DEVICE)) return; - if (chip->options & NAND_BUSWIDTH_16) + if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit) iowrite16_rep(nand->activecs->io.virt, buf, len / 2); else iowrite8_rep(nand->activecs->io.virt, buf, len); } -static int atmel_nand_dev_ready(struct nand_chip *chip) +static int atmel_nand_waitrdy(struct atmel_nand *nand, unsigned int timeout_ms) { - struct atmel_nand *nand = to_atmel_nand(chip); + if (nand->activecs->rb.type == ATMEL_NAND_NO_RB) + return nand_soft_waitrdy(&nand->base, timeout_ms); - return gpiod_get_value(nand->activecs->rb.gpio); + return nand_gpio_waitrdy(&nand->base, nand->activecs->rb.gpio, + timeout_ms); } -static void atmel_nand_select_chip(struct nand_chip *chip, int cs) +static int atmel_hsmc_nand_waitrdy(struct atmel_nand *nand, + unsigned int timeout_ms) { - struct atmel_nand *nand = to_atmel_nand(chip); - - if (cs < 0 || cs >= nand->numcs) { - nand->activecs = NULL; - chip->legacy.dev_ready = NULL; - return; - } + struct atmel_hsmc_nand_controller *nc; + u32 status, mask; - nand->activecs = &nand->cs[cs]; + if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) + return atmel_nand_waitrdy(nand, timeout_ms); - if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB) - chip->legacy.dev_ready = atmel_nand_dev_ready; + nc = to_hsmc_nand_controller(nand->base.controller); + mask = ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); + return regmap_read_poll_timeout_atomic(nc->base.smc, ATMEL_HSMC_NFC_SR, + status, status & mask, + 10, timeout_ms * 1000); } -static int atmel_hsmc_nand_dev_ready(struct nand_chip *chip) +static void atmel_nand_select_target(struct atmel_nand *nand, + unsigned int cs) { - struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_hsmc_nand_controller *nc; - u32 status; - - nc = to_hsmc_nand_controller(chip->controller); - - regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status); - - return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); + nand->activecs = &nand->cs[cs]; } -static void atmel_hsmc_nand_select_chip(struct nand_chip *chip, int cs) +static void atmel_hsmc_nand_select_target(struct atmel_nand *nand, + unsigned int cs) { - struct mtd_info *mtd = nand_to_mtd(chip); - struct atmel_nand *nand = to_atmel_nand(chip); + struct mtd_info *mtd = nand_to_mtd(&nand->base); struct atmel_hsmc_nand_controller *nc; + u32 cfg = ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) | + ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) | + ATMEL_HSMC_NFC_CFG_RSPARE; - nc = to_hsmc_nand_controller(chip->controller); - - atmel_nand_select_chip(chip, cs); - - if (!nand->activecs) { - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, - ATMEL_HSMC_NFC_CTRL_DIS); + nand->activecs = &nand->cs[cs]; + nc = to_hsmc_nand_controller(nand->base.controller); + if (nc->cfg == cfg) return; - } - - if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB) - chip->legacy.dev_ready = atmel_hsmc_nand_dev_ready; regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG, ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK | ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK | ATMEL_HSMC_NFC_CFG_RSPARE | ATMEL_HSMC_NFC_CFG_WSPARE, - ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) | - ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) | - ATMEL_HSMC_NFC_CFG_RSPARE); - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, - ATMEL_HSMC_NFC_CTRL_EN); + cfg); + nc->cfg = cfg; } -static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll) +static int atmel_smc_nand_exec_instr(struct atmel_nand *nand, + const struct nand_op_instr *instr) { - u8 *addrs = nc->op.addrs; - unsigned int op = 0; - u32 addr, val; - int i, ret; - - nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE; - - for (i = 0; i < nc->op.ncmds; i++) - op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]); - - if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) - regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++); - - op |= ATMEL_NFC_CSID(nc->op.cs) | - ATMEL_NFC_ACYCLE(nc->op.naddrs); - - if (nc->op.ncmds > 1) - op |= ATMEL_NFC_VCMD2; - - addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) | - (addrs[3] << 24); - - if (nc->op.data != ATMEL_NFC_NO_DATA) { - op |= ATMEL_NFC_DATAEN; - nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE; + struct atmel_nand_controller *nc; + unsigned int i; - if (nc->op.data == ATMEL_NFC_WRITE_DATA) - op |= ATMEL_NFC_NFCWR; + nc = to_nand_controller(nand->base.controller); + switch (instr->type) { + case NAND_OP_CMD_INSTR: + writeb(instr->ctx.cmd.opcode, + nand->activecs->io.virt + nc->caps->cle_offs); + return 0; + case NAND_OP_ADDR_INSTR: + for (i = 0; i < instr->ctx.addr.naddrs; i++) + writeb(instr->ctx.addr.addrs[i], + nand->activecs->io.virt + nc->caps->ale_offs); + return 0; + case NAND_OP_DATA_IN_INSTR: + atmel_nand_data_in(nand, instr->ctx.data.buf.in, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + return 0; + case NAND_OP_DATA_OUT_INSTR: + atmel_nand_data_out(nand, instr->ctx.data.buf.out, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + return 0; + case NAND_OP_WAITRDY_INSTR: + return atmel_nand_waitrdy(nand, + instr->ctx.waitrdy.timeout_ms); + default: + break; } - /* Clear all flags. */ - regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val); + return -EINVAL; +} - /* Send the command. */ - regmap_write(nc->io, op, addr); +static int atmel_smc_nand_exec_op(struct atmel_nand *nand, + const struct nand_operation *op, + bool check_only) +{ + unsigned int i; + int ret = 0; - ret = atmel_nfc_wait(nc, poll, 0); - if (ret) - dev_err(nc->base.dev, - "Failed to send NAND command (err = %d)!", - ret); + if (check_only) + return 0; - /* Reset the op state. */ - memset(&nc->op, 0, sizeof(nc->op)); + atmel_nand_select_target(nand, op->cs); + gpiod_set_value(nand->activecs->csgpio, 0); + for (i = 0; i < op->ninstrs; i++) { + ret = atmel_smc_nand_exec_instr(nand, &op->instrs[i]); + if (ret) + break; + } + gpiod_set_value(nand->activecs->csgpio, 1); return ret; } -static void atmel_hsmc_nand_cmd_ctrl(struct nand_chip *chip, int dat, - unsigned int ctrl) +static int atmel_hsmc_exec_cmd_addr(struct nand_chip *chip, + const struct nand_subop *subop) { struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_hsmc_nand_controller *nc; + unsigned int i, j; nc = to_hsmc_nand_controller(chip->controller); - if (ctrl & NAND_ALE) { - if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) - return; + nc->op.cs = nand->activecs->id; + for (i = 0; i < subop->ninstrs; i++) { + const struct nand_op_instr *instr = &subop->instrs[i]; - nc->op.addrs[nc->op.naddrs++] = dat; - } else if (ctrl & NAND_CLE) { - if (nc->op.ncmds > 1) - return; + if (instr->type == NAND_OP_CMD_INSTR) { + nc->op.cmds[nc->op.ncmds++] = instr->ctx.cmd.opcode; + continue; + } - nc->op.cmds[nc->op.ncmds++] = dat; + for (j = nand_subop_get_addr_start_off(subop, i); + j < nand_subop_get_num_addr_cyc(subop, i); j++) { + nc->op.addrs[nc->op.naddrs] = instr->ctx.addr.addrs[j]; + nc->op.naddrs++; + } } - if (dat == NAND_CMD_NONE) { - nc->op.cs = nand->activecs->id; - atmel_nfc_exec_op(nc, true); - } + return atmel_nfc_exec_op(nc, true); } -static void atmel_nand_cmd_ctrl(struct nand_chip *chip, int cmd, - unsigned int ctrl) +static int atmel_hsmc_exec_rw(struct nand_chip *chip, + const struct nand_subop *subop) { + const struct nand_op_instr *instr = subop->instrs; struct atmel_nand *nand = to_atmel_nand(chip); - struct atmel_nand_controller *nc; - nc = to_nand_controller(chip->controller); + if (instr->type == NAND_OP_DATA_IN_INSTR) + atmel_nand_data_in(nand, instr->ctx.data.buf.in, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + else + atmel_nand_data_out(nand, instr->ctx.data.buf.out, + instr->ctx.data.len, + instr->ctx.data.force_8bit); - if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) { - if (ctrl & NAND_NCE) - gpiod_set_value(nand->activecs->csgpio, 0); - else - gpiod_set_value(nand->activecs->csgpio, 1); - } + return 0; +} + +static int atmel_hsmc_exec_waitrdy(struct nand_chip *chip, + const struct nand_subop *subop) +{ + const struct nand_op_instr *instr = subop->instrs; + struct atmel_nand *nand = to_atmel_nand(chip); - if (ctrl & NAND_ALE) - writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs); - else if (ctrl & NAND_CLE) - writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs); + return atmel_hsmc_nand_waitrdy(nand, instr->ctx.waitrdy.timeout_ms); +} + +static const struct nand_op_parser atmel_hsmc_op_parser = NAND_OP_PARSER( + NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_cmd_addr, + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5), + NAND_OP_PARSER_PAT_CMD_ELEM(true)), + NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw, + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 0)), + NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw, + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 0)), + NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_waitrdy, + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), +); + +static int atmel_hsmc_nand_exec_op(struct atmel_nand *nand, + const struct nand_operation *op, + bool check_only) +{ + int ret; + + if (check_only) + return nand_op_parser_exec_op(&nand->base, + &atmel_hsmc_op_parser, op, true); + + atmel_hsmc_nand_select_target(nand, op->cs); + ret = nand_op_parser_exec_op(&nand->base, &atmel_hsmc_op_parser, op, + false); + + return ret; } static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf, @@ -838,7 +908,7 @@ static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, if (ret) return ret; - atmel_nand_write_buf(chip, buf, mtd->writesize); + nand_write_data_op(chip, buf, mtd->writesize, false); ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); if (ret) { @@ -848,7 +918,7 @@ static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, atmel_nand_pmecc_disable(chip, raw); - atmel_nand_write_buf(chip, chip->oob_poi, mtd->oobsize); + nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); return nand_prog_page_end_op(chip); } @@ -878,11 +948,17 @@ static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, if (ret) return ret; - atmel_nand_read_buf(chip, buf, mtd->writesize); - atmel_nand_read_buf(chip, chip->oob_poi, mtd->oobsize); + ret = nand_read_data_op(chip, buf, mtd->writesize, false, false); + if (ret) + goto out_disable; + + ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, false); + if (ret) + goto out_disable; ret = atmel_nand_pmecc_correct_data(chip, buf, raw); +out_disable: atmel_nand_pmecc_disable(chip, raw); return ret; @@ -907,8 +983,9 @@ static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, struct mtd_info *mtd = nand_to_mtd(chip); struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_hsmc_nand_controller *nc; - int ret, status; + int ret; + atmel_hsmc_nand_select_target(nand, chip->cur_cs); nc = to_hsmc_nand_controller(chip->controller); atmel_nfc_copy_to_sram(chip, buf, false); @@ -939,21 +1016,9 @@ static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, if (ret) return ret; - atmel_nand_write_buf(chip, chip->oob_poi, mtd->oobsize); - - nc->op.cmds[0] = NAND_CMD_PAGEPROG; - nc->op.ncmds = 1; - nc->op.cs = nand->activecs->id; - ret = atmel_nfc_exec_op(nc, false); - if (ret) - dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n", - ret); - - status = chip->legacy.waitfunc(chip); - if (status & NAND_STATUS_FAIL) - return -EIO; + nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); - return ret; + return nand_prog_page_end_op(chip); } static int atmel_hsmc_nand_pmecc_write_page(struct nand_chip *chip, @@ -981,6 +1046,7 @@ static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, struct atmel_hsmc_nand_controller *nc; int ret; + atmel_hsmc_nand_select_target(nand, chip->cur_cs); nc = to_hsmc_nand_controller(chip->controller); /* @@ -988,12 +1054,9 @@ static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, * connected to a native SoC R/B pin. If that's not the case, fallback * to the non-optimized one. */ - if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) { - nand_read_page_op(chip, page, 0, NULL, 0); - + if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, raw); - } nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0; @@ -1043,7 +1106,10 @@ static int atmel_hsmc_nand_pmecc_read_page_raw(struct nand_chip *chip, static int atmel_nand_pmecc_init(struct nand_chip *chip) { + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_device *nanddev = mtd_to_nanddev(mtd); struct atmel_nand *nand = to_atmel_nand(chip); struct atmel_nand_controller *nc; struct atmel_pmecc_user_req req; @@ -1068,19 +1134,19 @@ static int atmel_nand_pmecc_init(struct nand_chip *chip) chip->ecc.size = val; } - if (chip->ecc.options & NAND_ECC_MAXIMIZE) + if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; else if (chip->ecc.strength) req.ecc.strength = chip->ecc.strength; - else if (chip->base.eccreq.strength) - req.ecc.strength = chip->base.eccreq.strength; + else if (requirements->strength) + req.ecc.strength = requirements->strength; else req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; if (chip->ecc.size) req.ecc.sectorsize = chip->ecc.size; - else if (chip->base.eccreq.step_size) - req.ecc.sectorsize = chip->base.eccreq.step_size; + else if (requirements->step_size) + req.ecc.sectorsize = requirements->step_size; else req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO; @@ -1099,14 +1165,14 @@ static int atmel_nand_pmecc_init(struct nand_chip *chip) if (IS_ERR(nand->pmecc)) return PTR_ERR(nand->pmecc); - chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.algo = NAND_ECC_ALGO_BCH; chip->ecc.size = req.ecc.sectorsize; chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors; chip->ecc.strength = req.ecc.strength; chip->options |= NAND_NO_SUBPAGE_WRITE; - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); return 0; } @@ -1118,15 +1184,15 @@ static int atmel_nand_ecc_init(struct nand_chip *chip) nc = to_nand_controller(chip->controller); - switch (chip->ecc.mode) { - case NAND_ECC_NONE: - case NAND_ECC_SOFT: + switch (chip->ecc.engine_type) { + case NAND_ECC_ENGINE_TYPE_NONE: + case NAND_ECC_ENGINE_TYPE_SOFT: /* * Nothing to do, the core will initialize everything for us. */ break; - case NAND_ECC_HW: + case NAND_ECC_ENGINE_TYPE_ON_HOST: ret = atmel_nand_pmecc_init(chip); if (ret) return ret; @@ -1140,7 +1206,7 @@ static int atmel_nand_ecc_init(struct nand_chip *chip) default: /* Other modes are not supported. */ dev_err(nc->dev, "Unsupported ECC mode: %d\n", - chip->ecc.mode); + chip->ecc.engine_type); return -ENOTSUPP; } @@ -1155,7 +1221,7 @@ static int atmel_hsmc_nand_ecc_init(struct nand_chip *chip) if (ret) return ret; - if (chip->ecc.mode != NAND_ECC_HW) + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) return 0; /* Adjust the ECC operations for the HSMC IP. */ @@ -1467,6 +1533,18 @@ static int atmel_nand_setup_interface(struct nand_chip *chip, int csline, return nc->caps->ops->setup_interface(nand, csline, conf); } +static int atmel_nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + + nc = to_nand_controller(nand->base.controller); + + return nc->caps->ops->exec_op(nand, op, check_only); +} + static void atmel_nand_init(struct atmel_nand_controller *nc, struct atmel_nand *nand) { @@ -1476,19 +1554,9 @@ static void atmel_nand_init(struct atmel_nand_controller *nc, mtd->dev.parent = nc->dev; nand->base.controller = &nc->base; - chip->legacy.cmd_ctrl = atmel_nand_cmd_ctrl; - chip->legacy.read_byte = atmel_nand_read_byte; - chip->legacy.write_byte = atmel_nand_write_byte; - chip->legacy.read_buf = atmel_nand_read_buf; - chip->legacy.write_buf = atmel_nand_write_buf; - chip->legacy.select_chip = atmel_nand_select_chip; - if (!nc->mck || !nc->caps->ops->setup_interface) chip->options |= NAND_KEEP_TIMINGS; - /* Some NANDs require a longer delay than the default one (20us). */ - chip->legacy.chip_delay = 40; - /* * Use a bounce buffer when the buffer passed by the MTD user is not * suitable for DMA. @@ -1498,7 +1566,7 @@ static void atmel_nand_init(struct atmel_nand_controller *nc, /* Default to HW ECC if pmecc is available. */ if (nc->pmecc) - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; } static void atmel_smc_nand_init(struct atmel_nand_controller *nc, @@ -1527,18 +1595,6 @@ static void atmel_smc_nand_init(struct atmel_nand_controller *nc, smc_nc->ebi_csa->nfd0_on_d16); } -static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc, - struct atmel_nand *nand) -{ - struct nand_chip *chip = &nand->base; - - atmel_nand_init(nc, nand); - - /* Overload some methods for the HSMC controller. */ - chip->legacy.cmd_ctrl = atmel_hsmc_nand_cmd_ctrl; - chip->legacy.select_chip = atmel_hsmc_nand_select_chip; -} - static int atmel_nand_controller_remove_nand(struct atmel_nand *nand) { struct nand_chip *chip = &nand->base; @@ -1957,6 +2013,7 @@ static int atmel_nand_attach_chip(struct nand_chip *chip) static const struct nand_controller_ops atmel_nand_controller_ops = { .attach_chip = atmel_nand_attach_chip, .setup_interface = atmel_nand_setup_interface, + .exec_op = atmel_nand_exec_op, }; static int atmel_nand_controller_init(struct atmel_nand_controller *nc, @@ -1976,13 +2033,9 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, platform_set_drvdata(pdev, nc); nc->pmecc = devm_atmel_pmecc_get(dev); - if (IS_ERR(nc->pmecc)) { - ret = PTR_ERR(nc->pmecc); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Could not get PMECC object (err = %d)\n", - ret); - return ret; - } + if (IS_ERR(nc->pmecc)) + return dev_err_probe(dev, PTR_ERR(nc->pmecc), + "Could not get PMECC object\n"); if (nc->caps->has_dma && !atmel_nand_avoid_dma) { dma_cap_mask_t mask; @@ -2248,6 +2301,9 @@ atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc) return ret; hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base); + regmap_write(hsmc_nc->base.smc, ATMEL_HSMC_NFC_CTRL, + ATMEL_HSMC_NFC_CTRL_DIS); + if (hsmc_nc->sram.pool) gen_pool_free(hsmc_nc->sram.pool, (unsigned long)hsmc_nc->sram.virt, @@ -2300,6 +2356,8 @@ static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev, /* Initial NFC configuration. */ regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG, ATMEL_HSMC_NFC_CFG_DTO_MAX); + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, + ATMEL_HSMC_NFC_CTRL_EN); ret = atmel_nand_controller_add_nands(&nc->base); if (ret) @@ -2317,8 +2375,9 @@ static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = { .probe = atmel_hsmc_nand_controller_probe, .remove = atmel_hsmc_nand_controller_remove, .ecc_init = atmel_hsmc_nand_ecc_init, - .nand_init = atmel_hsmc_nand_init, + .nand_init = atmel_nand_init, .setup_interface = atmel_hsmc_nand_setup_interface, + .exec_op = atmel_hsmc_nand_exec_op, }; static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = { @@ -2385,6 +2444,7 @@ static const struct atmel_nand_controller_ops at91rm9200_nc_ops = { .remove = atmel_smc_nand_controller_remove, .ecc_init = atmel_nand_ecc_init, .nand_init = atmel_smc_nand_init, + .exec_op = atmel_smc_nand_exec_op, }; static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = { @@ -2400,6 +2460,7 @@ static const struct atmel_nand_controller_ops atmel_smc_nc_ops = { .ecc_init = atmel_nand_ecc_init, .nand_init = atmel_smc_nand_init, .setup_interface = atmel_smc_nand_setup_interface, + .exec_op = atmel_smc_nand_exec_op, }; static const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = { diff --git a/drivers/mtd/nand/raw/au1550nd.c b/drivers/mtd/nand/raw/au1550nd.c index d865200ccd08..79b057400fe9 100644 --- a/drivers/mtd/nand/raw/au1550nd.c +++ b/drivers/mtd/nand/raw/au1550nd.c @@ -294,8 +294,8 @@ static int au1550nd_probe(struct platform_device *pdev) nand_controller_init(&ctx->controller); ctx->controller.ops = &au1550nd_ops; this->controller = &ctx->controller; - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.algo = NAND_ECC_HAMMING; + this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + this->ecc.algo = NAND_ECC_ALGO_HAMMING; if (pd->devwidth) this->options |= NAND_BUSWIDTH_16; diff --git a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c index 591775173034..8bb17c5a66c3 100644 --- a/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c @@ -391,7 +391,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) nand_chip->legacy.chip_delay = 50; b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; - b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */ + /* TODO: implement ECC */ + b47n->nand_chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_NONE; /* Enable NAND flash access */ bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG, diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index a4033d32a710..2da39ab89286 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -2532,6 +2532,8 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) { struct mtd_info *mtd = nand_to_mtd(&host->chip); struct nand_chip *chip = &host->chip; + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); struct brcmnand_controller *ctrl = host->ctrl; struct brcmnand_cfg *cfg = &host->hwcfg; char msg[128]; @@ -2565,34 +2567,34 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) cfg->col_adr_bytes = 2; cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize); - if (chip->ecc.mode != NAND_ECC_HW) { + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) { dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n", - chip->ecc.mode); + chip->ecc.engine_type); return -EINVAL; } - if (chip->ecc.algo == NAND_ECC_UNKNOWN) { + if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) { if (chip->ecc.strength == 1 && chip->ecc.size == 512) /* Default to Hamming for 1-bit ECC, if unspecified */ - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; else /* Otherwise, BCH */ - chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.algo = NAND_ECC_ALGO_BCH; } - if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 || - chip->ecc.size != 512)) { + if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING && + (chip->ecc.strength != 1 || chip->ecc.size != 512)) { dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n", chip->ecc.strength, chip->ecc.size); return -EINVAL; } - if (chip->ecc.mode != NAND_ECC_NONE && + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_NONE && (!chip->ecc.size || !chip->ecc.strength)) { - if (chip->base.eccreq.step_size && chip->base.eccreq.strength) { + if (requirements->step_size && requirements->strength) { /* use detected ECC parameters */ - chip->ecc.size = chip->base.eccreq.step_size; - chip->ecc.strength = chip->base.eccreq.strength; + chip->ecc.size = requirements->step_size; + chip->ecc.strength = requirements->strength; dev_info(ctrl->dev, "Using ECC step-size %d, strength %d\n", chip->ecc.size, chip->ecc.strength); } @@ -2600,7 +2602,7 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) switch (chip->ecc.size) { case 512: - if (chip->ecc.algo == NAND_ECC_HAMMING) + if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING) cfg->ecc_level = 15; else cfg->ecc_level = chip->ecc.strength; @@ -2728,7 +2730,7 @@ static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn) chip->legacy.read_buf = brcmnand_read_buf; chip->legacy.write_buf = brcmnand_write_buf; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.read_page = brcmnand_read_page; chip->ecc.write_page = brcmnand_write_page; chip->ecc.read_page_raw = brcmnand_read_page_raw; diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index 71516af85f23..b46786cd53e0 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -2611,7 +2611,7 @@ static int cadence_nand_attach_chip(struct nand_chip *chip) chip->bbt_options |= NAND_BBT_USE_FLASH; chip->bbt_options |= NAND_BBT_NO_OOB; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->options |= NAND_NO_SUBPAGE_WRITE; @@ -2757,7 +2757,7 @@ static int cadence_nand_chip_init(struct cdns_nand_ctrl *cdns_ctrl, * 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.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; ret = nand_scan(chip, cdns_chip->nsels); if (ret) { @@ -2980,18 +2980,14 @@ static int cadence_nand_dt_probe(struct platform_device *ofdev) dev_info(cdns_ctrl->dev, "IRQ: nr %d\n", cdns_ctrl->irq); cdns_ctrl->reg = devm_platform_ioremap_resource(ofdev, 0); - if (IS_ERR(cdns_ctrl->reg)) { - dev_err(&ofdev->dev, "devm_ioremap_resource res 0 failed\n"); + if (IS_ERR(cdns_ctrl->reg)) return PTR_ERR(cdns_ctrl->reg); - } res = platform_get_resource(ofdev, IORESOURCE_MEM, 1); cdns_ctrl->io.dma = res->start; cdns_ctrl->io.virt = devm_ioremap_resource(&ofdev->dev, res); - if (IS_ERR(cdns_ctrl->io.virt)) { - dev_err(cdns_ctrl->dev, "devm_ioremap_resource res 1 failed\n"); + if (IS_ERR(cdns_ctrl->io.virt)) return PTR_ERR(cdns_ctrl->io.virt); - } dt->clk = devm_clk_get(cdns_ctrl->dev, "nf_clk"); if (IS_ERR(dt->clk)) diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index 92173790f20b..2b94f385a1a8 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -629,7 +629,8 @@ static int cafe_nand_attach_chip(struct nand_chip *chip) goto out_free_dma; } - cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + cafe->nand.ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + cafe->nand.ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; cafe->nand.ecc.size = mtd->writesize; cafe->nand.ecc.bytes = 14; cafe->nand.ecc.strength = 4; diff --git a/drivers/mtd/nand/raw/cs553x_nand.c b/drivers/mtd/nand/raw/cs553x_nand.c index 9472bf798ed5..b7f3f6347761 100644 --- a/drivers/mtd/nand/raw/cs553x_nand.c +++ b/drivers/mtd/nand/raw/cs553x_nand.c @@ -286,7 +286,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) goto out_mtd; } - this->ecc.mode = NAND_ECC_HW; + this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; this->ecc.size = 256; this->ecc.bytes = 3; this->ecc.hwctl = cs_enable_hwecc; diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index d975a62caaa5..427f320fb79b 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -168,7 +168,7 @@ static int nand_davinci_correct_1bit(struct nand_chip *chip, u_char *dat, /* * 4-bit hardware ECC ... context maintained over entire AEMIF * - * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME + * This is a syndrome engine, but we avoid NAND_ECC_PLACEMENT_INTERLEAVED * since that forces use of a problematic "infix OOB" layout. * Among other things, it trashes manufacturer bad block markers. * Also, and specific to this hardware, it ECC-protects the "prepad" @@ -530,11 +530,11 @@ static struct davinci_nand_pdata if (!of_property_read_string(pdev->dev.of_node, "ti,davinci-ecc-mode", &mode)) { if (!strncmp("none", mode, 4)) - pdata->ecc_mode = NAND_ECC_NONE; + pdata->engine_type = NAND_ECC_ENGINE_TYPE_NONE; if (!strncmp("soft", mode, 4)) - pdata->ecc_mode = NAND_ECC_SOFT; + pdata->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; if (!strncmp("hw", mode, 2)) - pdata->ecc_mode = NAND_ECC_HW; + pdata->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; } if (!of_property_read_u32(pdev->dev.of_node, "ti,davinci-ecc-bits", &prop)) @@ -585,21 +585,21 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) if (IS_ERR(pdata)) return PTR_ERR(pdata); - switch (info->chip.ecc.mode) { - case NAND_ECC_NONE: + switch (info->chip.ecc.engine_type) { + case NAND_ECC_ENGINE_TYPE_NONE: pdata->ecc_bits = 0; break; - case NAND_ECC_SOFT: + case NAND_ECC_ENGINE_TYPE_SOFT: pdata->ecc_bits = 0; /* - * This driver expects Hamming based ECC when ecc_mode is set - * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to - * avoid adding an extra ->ecc_algo field to - * davinci_nand_pdata. + * This driver expects Hamming based ECC when engine_type is set + * to NAND_ECC_ENGINE_TYPE_SOFT. Force ecc.algo to + * NAND_ECC_ALGO_HAMMING to avoid adding an extra ->ecc_algo + * field to davinci_nand_pdata. */ - info->chip.ecc.algo = NAND_ECC_HAMMING; + info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; break; - case NAND_ECC_HW: + case NAND_ECC_ENGINE_TYPE_ON_HOST: if (pdata->ecc_bits == 4) { int chunks = mtd->writesize / 512; @@ -629,7 +629,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) 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_BCH; + info->chip.ecc.algo = NAND_ECC_ALGO_BCH; /* * Update ECC layout if needed ... for 1-bit HW ECC, the @@ -645,7 +645,8 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) mtd_set_ooblayout(mtd, &hwecc4_small_ooblayout_ops); } else if (chunks == 4 || chunks == 8) { - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + mtd_set_ooblayout(mtd, + nand_get_large_page_ooblayout()); info->chip.ecc.read_page = nand_davinci_read_page_hwecc_oob_first; } else { return -EIO; @@ -656,7 +657,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip) 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_HAMMING; + info->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; } info->chip.ecc.size = 512; info->chip.ecc.strength = pdata->ecc_bits; @@ -850,7 +851,8 @@ static int nand_davinci_probe(struct platform_device *pdev) info->mask_cle = pdata->mask_cle ? : MASK_CLE; /* Use board-specific ECC config */ - info->chip.ecc.mode = pdata->ecc_mode; + info->chip.ecc.engine_type = pdata->engine_type; + info->chip.ecc.placement = pdata->ecc_placement; spin_lock_irq(&davinci_nand_lock); @@ -897,7 +899,7 @@ static int nand_davinci_remove(struct platform_device *pdev) int ret; spin_lock_irq(&davinci_nand_lock); - if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) + if (info->chip.ecc.placement == NAND_ECC_PLACEMENT_INTERLEAVED) ecc4_busy = false; spin_unlock_irq(&davinci_nand_lock); diff --git a/drivers/mtd/nand/raw/denali.c b/drivers/mtd/nand/raw/denali.c index 9d99dade95ce..fa2439cb4daa 100644 --- a/drivers/mtd/nand/raw/denali.c +++ b/drivers/mtd/nand/raw/denali.c @@ -1237,7 +1237,8 @@ int denali_chip_init(struct denali_controller *denali, chip->bbt_options |= NAND_BBT_USE_FLASH; chip->bbt_options |= NAND_BBT_NO_OOB; chip->options |= NAND_NO_SUBPAGE_WRITE; - chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; chip->ecc.read_page = denali_read_page; chip->ecc.write_page = denali_write_page; chip->ecc.read_page_raw = denali_read_page_raw; diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c index 2f77ee55e1bf..20c085a30adc 100644 --- a/drivers/mtd/nand/raw/denali_pci.c +++ b/drivers/mtd/nand/raw/denali_pci.c @@ -100,7 +100,7 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto out_remove_denali; } - dchip->chip.ecc.options |= NAND_ECC_MAXIMIZE; + dchip->chip.base.ecc.user_conf.flags |= NAND_ECC_MAXIMIZE_STRENGTH; dchip->nsels = nsels; diff --git a/drivers/mtd/nand/raw/diskonchip.c b/drivers/mtd/nand/raw/diskonchip.c index 43721863a0d8..94432a453e5e 100644 --- a/drivers/mtd/nand/raw/diskonchip.c +++ b/drivers/mtd/nand/raw/diskonchip.c @@ -1456,7 +1456,8 @@ static int __init doc_probe(unsigned long physadr) nand->ecc.calculate = doc200x_calculate_ecc; nand->ecc.correct = doc200x_correct_data; - nand->ecc.mode = NAND_ECC_HW_SYNDROME; + 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; diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index 088692b2e27a..b2af7f81fdf8 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -244,7 +244,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) return -EIO; } - if (chip->ecc.mode != NAND_ECC_HW) + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) return 0; elbc_fcm_ctrl->max_bitflips = 0; @@ -727,12 +727,12 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip) struct fsl_lbc_regs __iomem *lbc = ctrl->regs; unsigned int al; - switch (chip->ecc.mode) { + switch (chip->ecc.engine_type) { /* * if ECC was not chosen in DT, decide whether to use HW or SW ECC from * CS Base Register */ - case NAND_ECC_NONE: + case NAND_ECC_ENGINE_TYPE_NONE: /* If CS Base Register selects full hardware ECC then use it */ if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == BR_DECC_CHK_GEN) { @@ -740,23 +740,23 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip) chip->ecc.write_page = fsl_elbc_write_page; chip->ecc.write_subpage = fsl_elbc_write_subpage; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops); chip->ecc.size = 512; chip->ecc.bytes = 3; chip->ecc.strength = 1; } else { /* otherwise fall back to default software ECC */ - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; } break; /* if SW ECC was chosen in DT, we do not need to set anything here */ - case NAND_ECC_SOFT: + case NAND_ECC_ENGINE_TYPE_SOFT: break; - /* should we also implement NAND_ECC_HW to do as the code above? */ + /* should we also implement *_ECC_ENGINE_CONTROLLER to do as above? */ default: return -EINVAL; } @@ -786,8 +786,8 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip) chip->page_shift); dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", chip->phys_erase_shift); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", - chip->ecc.mode); + dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.engine_type = %d\n", + chip->ecc.engine_type); dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", chip->ecc.steps); dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index 00ae7a910b03..0e7a9b64301e 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -309,7 +309,7 @@ static void fsl_ifc_cmdfunc(struct nand_chip *chip, unsigned int command, ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; ifc_nand_ctrl->index += column; - if (chip->ecc.mode == NAND_ECC_HW) + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) ifc_nand_ctrl->eccread = 1; fsl_ifc_do_read(chip, 0, mtd); @@ -724,8 +724,8 @@ static int fsl_ifc_attach_chip(struct nand_chip *chip) chip->page_shift); dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, chip->phys_erase_shift); - dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, - chip->ecc.mode); + dev_dbg(priv->dev, "%s: nand->ecc.engine_type = %d\n", __func__, + chip->ecc.engine_type); dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, chip->ecc.steps); dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, @@ -912,7 +912,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ if (csor & CSOR_NAND_ECC_DEC_EN) { - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); /* Hardware generates ECC per 512 Bytes */ @@ -925,8 +925,8 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->ecc.strength = 8; } } else { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; } ret = fsl_ifc_sram_init(priv); diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c index 197850aeb261..d5813b9abc8e 100644 --- a/drivers/mtd/nand/raw/fsl_upm.c +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -47,8 +47,8 @@ static int fun_chip_init(struct fsl_upm_nand *fun, int ret; struct device_node *flash_np; - fun->chip.ecc.mode = NAND_ECC_SOFT; - fun->chip.ecc.algo = NAND_ECC_HAMMING; + fun->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + fun->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; fun->chip.controller = &fun->base; mtd->dev.parent = fun->dev; diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 92ddc41d0ff0..4191831df182 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -900,8 +900,8 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) return 0; } - switch (nand->ecc.mode) { - case NAND_ECC_HW: + switch (nand->ecc.engine_type) { + 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; @@ -910,14 +910,14 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand) nand->ecc.options |= NAND_ECC_SOFT_HAMMING_SM_ORDER; break; - case NAND_ECC_SOFT: - if (nand->ecc.algo == NAND_ECC_BCH) { + case NAND_ECC_ENGINE_TYPE_SOFT: + if (nand->ecc.algo == NAND_ECC_ALGO_BCH) { dev_info(host->dev, "Using 4-bit SW BCH ECC scheme\n"); break; } - case NAND_ECC_ON_DIE: + case NAND_ECC_ENGINE_TYPE_ON_DIE: break; default: @@ -929,7 +929,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. */ - if (nand->ecc.mode == NAND_ECC_HW) { + if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { switch (mtd->oobsize) { case 16: case 64: @@ -1059,7 +1059,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) * 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.mode = NAND_ECC_HW; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; nand->ecc.hwctl = fsmc_enable_hwecc; nand->ecc.size = 512; nand->badblockbits = 7; diff --git a/drivers/mtd/nand/raw/gpio.c b/drivers/mtd/nand/raw/gpio.c index 3bd847ccc3f3..4ec0a1e10867 100644 --- a/drivers/mtd/nand/raw/gpio.c +++ b/drivers/mtd/nand/raw/gpio.c @@ -342,8 +342,8 @@ 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.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + 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; diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 5d4aee46cc55..dc8104e67506 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -272,8 +272,8 @@ static int set_geometry_by_ecc_info(struct gpmi_nand_data *this, default: dev_err(this->dev, "unsupported nand chip. ecc bits : %d, ecc size : %d\n", - chip->base.eccreq.strength, - chip->base.eccreq.step_size); + nanddev_get_ecc_requirements(&chip->base)->strength, + nanddev_get_ecc_requirements(&chip->base)->step_size); return -EINVAL; } geo->ecc_chunk_size = ecc_step; @@ -510,6 +510,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) static int common_nfc_set_geometry(struct gpmi_nand_data *this) { struct nand_chip *chip = &this->nand; + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); if (chip->ecc.strength > 0 && chip->ecc.size > 0) return set_geometry_by_ecc_info(this, chip->ecc.strength, @@ -517,13 +519,12 @@ static int common_nfc_set_geometry(struct gpmi_nand_data *this) if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) || legacy_set_geometry(this)) { - if (!(chip->base.eccreq.strength > 0 && - chip->base.eccreq.step_size > 0)) + if (!(requirements->strength > 0 && requirements->step_size > 0)) return -EINVAL; return set_geometry_by_ecc_info(this, - chip->base.eccreq.strength, - chip->base.eccreq.step_size); + requirements->strength, + requirements->step_size); } return 0; @@ -1003,10 +1004,8 @@ static int acquire_dma_channels(struct gpmi_nand_data *this) /* request dma channel */ dma_chan = dma_request_chan(&pdev->dev, "rx-tx"); if (IS_ERR(dma_chan)) { - ret = PTR_ERR(dma_chan); - if (ret != -EPROBE_DEFER) - dev_err(this->dev, "DMA channel request failed: %d\n", - ret); + ret = dev_err_probe(this->dev, PTR_ERR(dma_chan), + "DMA channel request failed\n"); release_dma_channels(this); } else { this->dma_chans[0] = dma_chan; @@ -2032,7 +2031,7 @@ static int gpmi_init_last(struct gpmi_nand_data *this) ecc->write_page_raw = gpmi_ecc_write_page_raw; ecc->read_oob_raw = gpmi_ecc_read_oob_raw; ecc->write_oob_raw = gpmi_ecc_write_oob_raw; - ecc->mode = NAND_ECC_HW; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; ecc->size = bch_geo->ecc_chunk_size; ecc->strength = bch_geo->ecc_strength; mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); diff --git a/drivers/mtd/nand/raw/hisi504_nand.c b/drivers/mtd/nand/raw/hisi504_nand.c index b84238e2268a..8b2122ce6ec3 100644 --- a/drivers/mtd/nand/raw/hisi504_nand.c +++ b/drivers/mtd/nand/raw/hisi504_nand.c @@ -186,7 +186,7 @@ static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev) hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA); hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB); - if (chip->ecc.mode == NAND_ECC_NONE) { + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_NONE) { hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK) << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN); @@ -468,7 +468,7 @@ static void hisi_nfc_cmdfunc(struct nand_chip *chip, unsigned command, case NAND_CMD_STATUS: flag = hinfc_read(host, HINFC504_CON); - if (chip->ecc.mode == NAND_ECC_HW) + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) hinfc_write(host, flag & ~(HINFC504_CON_ECCTYPE_MASK << HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON); @@ -721,7 +721,7 @@ static int hisi_nfc_attach_chip(struct nand_chip *chip) } hinfc_write(host, flag, HINFC504_CON); - if (chip->ecc.mode == NAND_ECC_HW) + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) hisi_nfc_ecc_probe(host); return 0; diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c index 69423bb29adb..0e9d426fe4f2 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -194,8 +194,8 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip) (chip->ecc.strength / 8); } - switch (chip->ecc.mode) { - case NAND_ECC_HW: + switch (chip->ecc.engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: if (!nfc->ecc) { dev_err(nfc->dev, "HW ECC selected, but ECC controller not found\n"); return -ENODEV; @@ -205,22 +205,22 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip) chip->ecc.calculate = ingenic_nand_ecc_calculate; chip->ecc.correct = ingenic_nand_ecc_correct; fallthrough; - case NAND_ECC_SOFT: + case NAND_ECC_ENGINE_TYPE_SOFT: dev_info(nfc->dev, "using %s (strength %d, size %d, bytes %d)\n", (nfc->ecc) ? "hardware ECC" : "software ECC", chip->ecc.strength, chip->ecc.size, chip->ecc.bytes); break; - case NAND_ECC_NONE: + case NAND_ECC_ENGINE_TYPE_NONE: dev_info(nfc->dev, "not using ECC\n"); break; default: dev_err(nfc->dev, "ECC mode %d not supported\n", - chip->ecc.mode); + chip->ecc.engine_type); return -EINVAL; } /* The NAND core will generate the ECC layout for SW ECC */ - if (chip->ecc.mode != NAND_ECC_HW) + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) return 0; /* Generate ECC layout. ECC codes are right aligned in the OOB area. */ @@ -243,8 +243,10 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip) /* For legacy reasons we use a different layout on the qi,lb60 board. */ if (of_machine_is_compatible("qi,lb60")) mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops); - else + else if (nfc->soc_info->oob_layout) mtd_set_ooblayout(mtd, nfc->soc_info->oob_layout); + else + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); return 0; } @@ -404,7 +406,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev, mtd->dev.parent = dev; chip->options = NAND_NO_SUBPAGE_WRITE; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->controller = &nfc->controller; nand_set_flash_node(chip, np); @@ -532,7 +534,6 @@ static const struct jz_soc_info jz4740_soc_info = { .data_offset = 0x00000000, .cmd_offset = 0x00008000, .addr_offset = 0x00010000, - .oob_layout = &nand_ooblayout_lp_ops, }; static const struct jz_soc_info jz4725b_soc_info = { @@ -546,7 +547,6 @@ static const struct jz_soc_info jz4780_soc_info = { .data_offset = 0x00000000, .cmd_offset = 0x00400000, .addr_offset = 0x00800000, - .oob_layout = &nand_ooblayout_lp_ops, }; static const struct of_device_id ingenic_nand_dt_match[] = { diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 7521038af2ef..4940bb2e3c07 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -656,7 +656,7 @@ static int lpc32xx_nand_attach_chip(struct nand_chip *chip) if (!host->dummy_buf) return -ENOMEM; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); host->mlcsubpages = mtd->writesize / 512; diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index b151fd000815..6db9d2ed6881 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -881,7 +881,8 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); /* NAND callbacks for LPC32xx SLC hardware */ - chip->ecc.mode = NAND_ECC_HW_SYNDROME; + 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; diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 8482d3bd8b1f..f5ca2002d08e 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -227,6 +227,8 @@ #define XTYPE_MASK 7 /** + * struct marvell_hw_ecc_layout - layout of Marvell ECC + * * Marvell ECC engine works differently than the others, in order to limit the * size of the IP, hardware engineers chose to set a fixed strength at 16 bits * per subpage, and depending on a the desired strength needed by the NAND chip, @@ -292,6 +294,8 @@ static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = { }; /** + * struct marvell_nand_chip_sel - CS line description + * * The Nand Flash Controller has up to 4 CE and 2 RB pins. The CE selection * is made by a field in NDCB0 register, and in another field in NDCB2 register. * The datasheet describes the logic with an error: ADDR5 field is once @@ -312,14 +316,15 @@ struct marvell_nand_chip_sel { }; /** - * NAND chip structure: stores NAND chip device related information + * struct marvell_nand_chip - stores NAND chip device related information * * @chip: Base NAND chip structure * @node: Used to store NAND chips into a list - * @layout NAND layout when using hardware ECC + * @layout: NAND layout when using hardware ECC * @ndcr: Controller register value for this NAND chip * @ndtr0: Timing registers 0 value for this NAND chip * @ndtr1: Timing registers 1 value for this NAND chip + * @addr_cyc: Amount of cycles needed to pass column address * @selected_die: Current active CS * @nsels: Number of CS lines required by the NAND chip * @sels: Array of CS lines descriptions @@ -349,7 +354,8 @@ static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip } /** - * NAND controller capabilities for distinction between compatible strings + * struct marvell_nfc_caps - NAND controller capabilities for distinction + * between compatible strings * * @max_cs_nb: Number of Chip Select lines available * @max_rb_nb: Number of Ready/Busy lines available @@ -372,7 +378,7 @@ struct marvell_nfc_caps { }; /** - * NAND controller structure: stores Marvell NAND controller information + * struct marvell_nfc - stores Marvell NAND controller information * * @controller: Base controller structure * @dev: Parent device (used to print error messages) @@ -383,7 +389,9 @@ struct marvell_nfc_caps { * @assigned_cs: Bitmask describing already assigned CS lines * @chips: List containing all the NAND chips attached to * this NAND controller + * @selected_chip: Currently selected target chip * @caps: NAND controller capabilities for each compatible string + * @use_dma: Whetner DMA is used * @dma_chan: DMA channel (NFCv1 only) * @dma_buf: 32-bit aligned buffer for DMA transfers (NFCv1 only) */ @@ -411,7 +419,8 @@ static inline struct marvell_nfc *to_marvell_nfc(struct nand_controller *ctrl) } /** - * NAND controller timings expressed in NAND Controller clock cycles + * struct marvell_nfc_timings - NAND controller timings expressed in NAND + * Controller clock cycles * * @tRP: ND_nRE pulse width * @tRH: ND_nRE high duration @@ -455,8 +464,8 @@ struct marvell_nfc_timings { period_ns)) /** - * NAND driver structure filled during the parsing of the ->exec_op() subop - * subset of instructions. + * struct marvell_nfc_op - filled during the parsing of the ->exec_op() + * subop subset of instructions. * * @ndcb: Array of values written to NDCBx registers * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle @@ -685,9 +694,31 @@ static int marvell_nfc_wait_cmdd(struct nand_chip *chip) return marvell_nfc_end_cmd(chip, cs_flag, "CMDD"); } +static int marvell_nfc_poll_status(struct marvell_nfc *nfc, u32 mask, + u32 expected_val, unsigned long timeout_ms) +{ + unsigned long limit; + u32 st; + + limit = jiffies + msecs_to_jiffies(timeout_ms); + do { + st = readl_relaxed(nfc->regs + NDSR); + if (st & NDSR_RDY(1)) + st |= NDSR_RDY(0); + + if ((st & mask) == expected_val) + return 0; + + cpu_relax(); + } while (time_after(limit, jiffies)); + + return -ETIMEDOUT; +} + static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) { struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); + struct mtd_info *mtd = nand_to_mtd(chip); u32 pending; int ret; @@ -695,12 +726,18 @@ static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms) if (!timeout_ms) timeout_ms = IRQ_TIMEOUT; - init_completion(&nfc->complete); + if (mtd->oops_panic_write) { + ret = marvell_nfc_poll_status(nfc, NDSR_RDY(0), + NDSR_RDY(0), + timeout_ms); + } else { + init_completion(&nfc->complete); - marvell_nfc_enable_int(nfc, NDCR_RDYM); - ret = wait_for_completion_timeout(&nfc->complete, - msecs_to_jiffies(timeout_ms)); - marvell_nfc_disable_int(nfc, NDCR_RDYM); + marvell_nfc_enable_int(nfc, NDCR_RDYM); + ret = wait_for_completion_timeout(&nfc->complete, + msecs_to_jiffies(timeout_ms)); + marvell_nfc_disable_int(nfc, NDCR_RDYM); + } pending = marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1)); /* @@ -780,7 +817,7 @@ static void marvell_nfc_enable_hw_ecc(struct nand_chip *chip) * When enabling BCH, set threshold to 0 to always know the * number of corrected bitflips. */ - if (chip->ecc.algo == NAND_ECC_BCH) + if (chip->ecc.algo == NAND_ECC_ALGO_BCH) writel_relaxed(NDECCCTRL_BCH_EN, nfc->regs + NDECCCTRL); } } @@ -792,7 +829,7 @@ static void marvell_nfc_disable_hw_ecc(struct nand_chip *chip) if (ndcr & NDCR_ECC_EN) { writel_relaxed(ndcr & ~NDCR_ECC_EN, nfc->regs + NDCR); - if (chip->ecc.algo == NAND_ECC_BCH) + if (chip->ecc.algo == NAND_ECC_ALGO_BCH) writel_relaxed(0, nfc->regs + NDECCCTRL); } } @@ -966,7 +1003,7 @@ static int marvell_nfc_hw_ecc_check_bitflips(struct nand_chip *chip, if (ndsr & NDSR_CORERR) { writel_relaxed(ndsr, nfc->regs + NDSR); - if (chip->ecc.algo == NAND_ECC_BCH) + if (chip->ecc.algo == NAND_ECC_ALGO_BCH) bf = NDSR_ERRCNT(ndsr); else bf = 1; @@ -2218,7 +2255,7 @@ static int marvell_nand_hw_ecc_controller_init(struct mtd_info *mtd, ecc->size = l->data_bytes; if (ecc->strength == 1) { - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; ecc->read_page_raw = marvell_nfc_hw_ecc_hmg_read_page_raw; ecc->read_page = marvell_nfc_hw_ecc_hmg_read_page; ecc->read_oob_raw = marvell_nfc_hw_ecc_hmg_read_oob_raw; @@ -2228,7 +2265,7 @@ static int marvell_nand_hw_ecc_controller_init(struct mtd_info *mtd, ecc->write_oob_raw = marvell_nfc_hw_ecc_hmg_write_oob_raw; ecc->write_oob = ecc->write_oob_raw; } else { - chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.algo = NAND_ECC_ALGO_BCH; ecc->strength = 16; ecc->read_page_raw = marvell_nfc_hw_ecc_bch_read_page_raw; ecc->read_page = marvell_nfc_hw_ecc_bch_read_page; @@ -2247,13 +2284,16 @@ static int marvell_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { struct nand_chip *chip = mtd_to_nand(mtd); + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); struct marvell_nfc *nfc = to_marvell_nfc(chip->controller); int ret; - if (ecc->mode != NAND_ECC_NONE && (!ecc->size || !ecc->strength)) { - if (chip->base.eccreq.step_size && chip->base.eccreq.strength) { - ecc->size = chip->base.eccreq.step_size; - ecc->strength = chip->base.eccreq.strength; + if (ecc->engine_type != NAND_ECC_ENGINE_TYPE_NONE && + (!ecc->size || !ecc->strength)) { + if (requirements->step_size && requirements->strength) { + ecc->size = requirements->step_size; + ecc->strength = requirements->strength; } else { dev_info(nfc->dev, "No minimum ECC strength, using 1b/512B\n"); @@ -2262,15 +2302,15 @@ static int marvell_nand_ecc_init(struct mtd_info *mtd, } } - switch (ecc->mode) { - case NAND_ECC_HW: + switch (ecc->engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: ret = marvell_nand_hw_ecc_controller_init(mtd, ecc); if (ret) return ret; break; - case NAND_ECC_NONE: - case NAND_ECC_SOFT: - case NAND_ECC_ON_DIE: + case NAND_ECC_ENGINE_TYPE_NONE: + case NAND_ECC_ENGINE_TYPE_SOFT: + case NAND_ECC_ENGINE_TYPE_ON_DIE: if (!nfc->caps->is_nfcv2 && mtd->writesize != SZ_512 && mtd->writesize != SZ_2K) { dev_err(nfc->dev, "NFCv1 cannot write %d bytes pages\n", @@ -2467,7 +2507,7 @@ static int marvell_nand_attach_chip(struct nand_chip *chip) return ret; } - if (chip->ecc.mode == NAND_ECC_HW) { + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { /* * Subpage write not available with hardware ECC, prohibit also * subpage read as in userspace subpage access would still be @@ -2642,7 +2682,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc, * 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.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; /* * Save a reference value for timing registers before @@ -2759,10 +2799,7 @@ static int marvell_nfc_init_dma(struct marvell_nfc *nfc) if (IS_ERR(nfc->dma_chan)) { ret = PTR_ERR(nfc->dma_chan); nfc->dma_chan = NULL; - if (ret != -EPROBE_DEFER) - dev_err(nfc->dev, "DMA channel request failed: %d\n", - ret); - return ret; + return dev_err_probe(nfc->dev, ret, "DMA channel request failed\n"); } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 0e5829a2b54f..48e6dac96be6 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1197,7 +1197,7 @@ static int meson_nand_attach_chip(struct nand_chip *nand) if (ret) return -EINVAL; - nand->ecc.mode = NAND_ECC_HW; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; nand->ecc.write_page_raw = meson_nfc_write_page_raw; nand->ecc.write_page = meson_nfc_write_page_hwecc; nand->ecc.write_oob_raw = nand_write_oob_std; diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index 18ecb096a32d..dfd0d3ed5ed0 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -688,8 +688,8 @@ 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.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + 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")) { diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index ad1b55dab211..57f1f1708994 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1253,21 +1253,23 @@ static int mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&nand->base); struct mtk_nfc *nfc = nand_get_controller_data(nand); u32 spare; int free, ret; /* support only ecc hw mode */ - if (nand->ecc.mode != NAND_ECC_HW) { - dev_err(dev, "ecc.mode not supported\n"); + if (nand->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) { + dev_err(dev, "ecc.engine_type not supported\n"); return -EINVAL; } /* if optional dt settings not present */ if (!nand->ecc.size || !nand->ecc.strength) { /* use datasheet requirements */ - nand->ecc.strength = nand->base.eccreq.strength; - nand->ecc.size = nand->base.eccreq.step_size; + nand->ecc.strength = requirements->strength; + nand->ecc.size = requirements->step_size; /* * align eccstrength and eccsize @@ -1416,7 +1418,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, nand->options |= NAND_USES_DMA | NAND_SUBPAGE_READ; /* set default mode in case dt entry is missing */ - nand->ecc.mode = NAND_ECC_HW; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc; nand->ecc.write_page_raw = mtk_nfc_write_page_raw; diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index a043d76b48cb..d4200eb2ad32 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -669,7 +669,7 @@ static void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) struct mxc_nand_host *host = nand_get_controller_data(chip); uint16_t config1; - if (chip->ecc.mode != NAND_ECC_HW) + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) return; config1 = readw(NFC_V1_V2_CONFIG1); @@ -687,7 +687,7 @@ static void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) struct mxc_nand_host *host = nand_get_controller_data(chip); uint32_t config2; - if (chip->ecc.mode != NAND_ECC_HW) + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) return; config2 = readl(NFC_V3_CONFIG2); @@ -1117,7 +1117,8 @@ static void preset_v1(struct mtd_info *mtd) struct mxc_nand_host *host = nand_get_controller_data(nand_chip); uint16_t config1 = 0; - if (nand_chip->ecc.mode == NAND_ECC_HW && mtd->writesize) + if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST && + mtd->writesize) config1 |= NFC_V1_V2_CONFIG1_ECC_EN; if (!host->devtype_data->irqpending_quirk) @@ -1227,7 +1228,7 @@ static void preset_v2(struct mtd_info *mtd) if (mtd->writesize) { uint16_t pages_per_block = mtd->erasesize / mtd->writesize; - if (nand_chip->ecc.mode == NAND_ECC_HW) + if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) config1 |= NFC_V1_V2_CONFIG1_ECC_EN; host->eccsize = get_eccsize(mtd); @@ -1303,7 +1304,7 @@ static void preset_v3(struct mtd_info *mtd) } if (mtd->writesize) { - if (chip->ecc.mode == NAND_ECC_HW) + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) config2 |= NFC_V3_CONFIG2_ECC_EN; config2 |= NFC_V3_CONFIG2_PPB( @@ -1680,8 +1681,8 @@ 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; - switch (chip->ecc.mode) { - case NAND_ECC_HW: + switch (chip->ecc.engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: chip->ecc.read_page = mxc_nand_read_page; chip->ecc.read_page_raw = mxc_nand_read_page_raw; chip->ecc.read_oob = mxc_nand_read_oob; @@ -1690,7 +1691,7 @@ static int mxcnd_attach_chip(struct nand_chip *chip) chip->ecc.write_oob = mxc_nand_write_oob; break; - case NAND_ECC_SOFT: + case NAND_ECC_ENGINE_TYPE_SOFT: break; default: @@ -1728,7 +1729,7 @@ static int mxcnd_attach_chip(struct nand_chip *chip) */ host->used_oobsize = min(mtd->oobsize, 218U); - if (chip->ecc.mode == NAND_ECC_HW) { + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { if (is_imx21_nfc(host) || is_imx27_nfc(host)) chip->ecc.strength = 1; else @@ -1843,10 +1844,10 @@ static int mxcnd_probe(struct platform_device *pdev) mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); if (host->pdata.hw_ecc) { - this->ecc.mode = NAND_ECC_HW; + this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; } else { - this->ecc.mode = NAND_ECC_SOFT; - this->ecc.algo = NAND_ECC_HAMMING; + 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 */ diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 0c768cb88f96..1f0d542d5923 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -34,6 +34,7 @@ #include <linux/mm.h> #include <linux/types.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/nand_bch.h> #include <linux/interrupt.h> @@ -45,166 +46,6 @@ #include "internals.h" -/* Define default oob placement schemes for large and small page devices */ -static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section > 1) - return -ERANGE; - - if (!section) { - oobregion->offset = 0; - if (mtd->oobsize == 16) - oobregion->length = 4; - else - oobregion->length = 3; - } else { - if (mtd->oobsize == 8) - return -ERANGE; - - oobregion->offset = 6; - oobregion->length = ecc->total - 4; - } - - return 0; -} - -static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - if (section > 1) - return -ERANGE; - - if (mtd->oobsize == 16) { - if (section) - return -ERANGE; - - oobregion->length = 8; - oobregion->offset = 8; - } else { - oobregion->length = 2; - if (!section) - oobregion->offset = 3; - else - oobregion->offset = 6; - } - - return 0; -} - -const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = { - .ecc = nand_ooblayout_ecc_sp, - .free = nand_ooblayout_free_sp, -}; -EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops); - -static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section || !ecc->total) - return -ERANGE; - - oobregion->length = ecc->total; - oobregion->offset = mtd->oobsize - oobregion->length; - - return 0; -} - -static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section) - return -ERANGE; - - oobregion->length = mtd->oobsize - ecc->total - 2; - oobregion->offset = 2; - - return 0; -} - -const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = { - .ecc = nand_ooblayout_ecc_lp, - .free = nand_ooblayout_free_lp, -}; -EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops); - -/* - * Support the old "large page" layout used for 1-bit Hamming ECC where ECC - * are placed at a fixed offset. - */ -static int nand_ooblayout_ecc_lp_hamming(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - - if (section) - return -ERANGE; - - switch (mtd->oobsize) { - case 64: - oobregion->offset = 40; - break; - case 128: - oobregion->offset = 80; - break; - default: - return -EINVAL; - } - - oobregion->length = ecc->total; - if (oobregion->offset + oobregion->length > mtd->oobsize) - return -ERANGE; - - return 0; -} - -static int nand_ooblayout_free_lp_hamming(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct nand_chip *chip = mtd_to_nand(mtd); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int ecc_offset = 0; - - if (section < 0 || section > 1) - return -ERANGE; - - switch (mtd->oobsize) { - case 64: - ecc_offset = 40; - break; - case 128: - ecc_offset = 80; - break; - default: - return -EINVAL; - } - - if (section == 0) { - oobregion->offset = 2; - oobregion->length = ecc_offset - 2; - } else { - oobregion->offset = ecc_offset + ecc->total; - oobregion->length = mtd->oobsize - oobregion->offset; - } - - return 0; -} - -static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = { - .ecc = nand_ooblayout_ecc_lp_hamming, - .free = nand_ooblayout_free_lp_hamming, -}; - static int nand_pairing_dist3_get_info(struct mtd_info *mtd, int page, struct mtd_pairing_info *info) { @@ -4750,6 +4591,8 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type) static bool find_full_id_nand(struct nand_chip *chip, struct nand_flash_dev *type) { + struct nand_device *base = &chip->base; + struct nand_ecc_props requirements; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_memory_organization *memorg; u8 *id_data = chip->id.data; @@ -4771,8 +4614,9 @@ static bool find_full_id_nand(struct nand_chip *chip, memorg->pagesize * memorg->pages_per_eraseblock); chip->options |= type->options; - chip->base.eccreq.strength = NAND_ECC_STRENGTH(type); - chip->base.eccreq.step_size = NAND_ECC_STEP(type); + requirements.strength = NAND_ECC_STRENGTH(type); + requirements.step_size = NAND_ECC_STEP(type); + nanddev_set_ecc_requirements(base, &requirements); chip->parameters.model = kstrdup(type->name, GFP_KERNEL); if (!chip->parameters.model) @@ -5033,91 +4877,101 @@ free_detect_allocation: return ret; } -static const char * const nand_ecc_modes[] = { - [NAND_ECC_NONE] = "none", - [NAND_ECC_SOFT] = "soft", - [NAND_ECC_HW] = "hw", - [NAND_ECC_HW_SYNDROME] = "hw_syndrome", - [NAND_ECC_ON_DIE] = "on-die", -}; - -static int of_get_nand_ecc_mode(struct device_node *np) +static enum nand_ecc_engine_type +of_get_rawnand_ecc_engine_type_legacy(struct device_node *np) { + enum nand_ecc_legacy_mode { + NAND_ECC_INVALID, + NAND_ECC_NONE, + NAND_ECC_SOFT, + NAND_ECC_SOFT_BCH, + NAND_ECC_HW, + NAND_ECC_HW_SYNDROME, + NAND_ECC_ON_DIE, + }; + const char * const nand_ecc_legacy_modes[] = { + [NAND_ECC_NONE] = "none", + [NAND_ECC_SOFT] = "soft", + [NAND_ECC_SOFT_BCH] = "soft_bch", + [NAND_ECC_HW] = "hw", + [NAND_ECC_HW_SYNDROME] = "hw_syndrome", + [NAND_ECC_ON_DIE] = "on-die", + }; + enum nand_ecc_legacy_mode eng_type; const char *pm; - int err, i; + int err; err = of_property_read_string(np, "nand-ecc-mode", &pm); - if (err < 0) - return err; - - for (i = NAND_ECC_NONE; i < ARRAY_SIZE(nand_ecc_modes); i++) - if (!strcasecmp(pm, nand_ecc_modes[i])) - return i; - - /* - * For backward compatibility we support few obsoleted values that don't - * have their mappings into the nand_ecc_mode enum anymore (they were - * merged with other enums). - */ - if (!strcasecmp(pm, "soft_bch")) - return NAND_ECC_SOFT; + if (err) + return NAND_ECC_ENGINE_TYPE_INVALID; + + for (eng_type = NAND_ECC_NONE; + eng_type < ARRAY_SIZE(nand_ecc_legacy_modes); eng_type++) { + if (!strcasecmp(pm, nand_ecc_legacy_modes[eng_type])) { + switch (eng_type) { + case NAND_ECC_NONE: + return NAND_ECC_ENGINE_TYPE_NONE; + case NAND_ECC_SOFT: + case NAND_ECC_SOFT_BCH: + return NAND_ECC_ENGINE_TYPE_SOFT; + case NAND_ECC_HW: + case NAND_ECC_HW_SYNDROME: + return NAND_ECC_ENGINE_TYPE_ON_HOST; + case NAND_ECC_ON_DIE: + return NAND_ECC_ENGINE_TYPE_ON_DIE; + default: + break; + } + } + } - return -ENODEV; + return NAND_ECC_ENGINE_TYPE_INVALID; } -static const char * const nand_ecc_algos[] = { - [NAND_ECC_HAMMING] = "hamming", - [NAND_ECC_BCH] = "bch", - [NAND_ECC_RS] = "rs", -}; - -static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np) +static enum nand_ecc_placement +of_get_rawnand_ecc_placement_legacy(struct device_node *np) { - enum nand_ecc_algo ecc_algo; const char *pm; int err; - err = of_property_read_string(np, "nand-ecc-algo", &pm); + err = of_property_read_string(np, "nand-ecc-mode", &pm); if (!err) { - for (ecc_algo = NAND_ECC_HAMMING; - ecc_algo < ARRAY_SIZE(nand_ecc_algos); - ecc_algo++) { - if (!strcasecmp(pm, nand_ecc_algos[ecc_algo])) - return ecc_algo; - } + if (!strcasecmp(pm, "hw_syndrome")) + return NAND_ECC_PLACEMENT_INTERLEAVED; } - /* - * For backward compatibility we also read "nand-ecc-mode" checking - * for some obsoleted values that were specifying ECC algorithm. - */ + return NAND_ECC_PLACEMENT_UNKNOWN; +} + +static enum nand_ecc_algo of_get_rawnand_ecc_algo_legacy(struct device_node *np) +{ + const char *pm; + int err; + err = of_property_read_string(np, "nand-ecc-mode", &pm); if (!err) { if (!strcasecmp(pm, "soft")) - return NAND_ECC_HAMMING; + return NAND_ECC_ALGO_HAMMING; else if (!strcasecmp(pm, "soft_bch")) - return NAND_ECC_BCH; + return NAND_ECC_ALGO_BCH; } - return NAND_ECC_UNKNOWN; + return NAND_ECC_ALGO_UNKNOWN; } -static int of_get_nand_ecc_step_size(struct device_node *np) +static void of_get_nand_ecc_legacy_user_config(struct nand_chip *chip) { - int ret; - u32 val; + struct device_node *dn = nand_get_flash_node(chip); + struct nand_ecc_props *user_conf = &chip->base.ecc.user_conf; - ret = of_property_read_u32(np, "nand-ecc-step-size", &val); - return ret ? ret : val; -} + if (user_conf->engine_type == NAND_ECC_ENGINE_TYPE_INVALID) + user_conf->engine_type = of_get_rawnand_ecc_engine_type_legacy(dn); -static int of_get_nand_ecc_strength(struct device_node *np) -{ - int ret; - u32 val; + if (user_conf->algo == NAND_ECC_ALGO_UNKNOWN) + user_conf->algo = of_get_rawnand_ecc_algo_legacy(dn); - ret = of_property_read_u32(np, "nand-ecc-strength", &val); - return ret ? ret : val; + if (user_conf->placement == NAND_ECC_PLACEMENT_UNKNOWN) + user_conf->placement = of_get_rawnand_ecc_placement_legacy(dn); } static int of_get_nand_bus_width(struct device_node *np) @@ -5141,11 +4995,10 @@ static bool of_get_nand_on_flash_bbt(struct device_node *np) return of_property_read_bool(np, "nand-on-flash-bbt"); } -static int nand_dt_init(struct nand_chip *chip) +static int rawnand_dt_init(struct nand_chip *chip) { + struct nand_device *nand = mtd_to_nanddev(nand_to_mtd(chip)); struct device_node *dn = nand_get_flash_node(chip); - enum nand_ecc_algo ecc_algo; - int ecc_mode, ecc_strength, ecc_step; if (!dn) return 0; @@ -5159,25 +5012,29 @@ static int nand_dt_init(struct nand_chip *chip) if (of_get_nand_on_flash_bbt(dn)) chip->bbt_options |= NAND_BBT_USE_FLASH; - ecc_mode = of_get_nand_ecc_mode(dn); - ecc_algo = of_get_nand_ecc_algo(dn); - ecc_strength = of_get_nand_ecc_strength(dn); - ecc_step = of_get_nand_ecc_step_size(dn); - - if (ecc_mode >= 0) - chip->ecc.mode = ecc_mode; + of_get_nand_ecc_user_config(nand); + of_get_nand_ecc_legacy_user_config(chip); - if (ecc_algo != NAND_ECC_UNKNOWN) - chip->ecc.algo = ecc_algo; - - if (ecc_strength >= 0) - chip->ecc.strength = ecc_strength; + /* + * If neither the user nor the NAND controller have requested a specific + * ECC engine type, we will default to NAND_ECC_ENGINE_TYPE_ON_HOST. + */ + nand->ecc.defaults.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; - if (ecc_step > 0) - chip->ecc.size = ecc_step; + /* + * Use the user requested engine type, unless there is none, in this + * case default to the NAND controller choice, otherwise fallback to + * the raw NAND default one. + */ + if (nand->ecc.user_conf.engine_type != NAND_ECC_ENGINE_TYPE_INVALID) + chip->ecc.engine_type = nand->ecc.user_conf.engine_type; + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) + chip->ecc.engine_type = nand->ecc.defaults.engine_type; - if (of_property_read_bool(dn, "nand-ecc-maximize")) - chip->ecc.options |= NAND_ECC_MAXIMIZE; + chip->ecc.placement = nand->ecc.user_conf.placement; + chip->ecc.algo = nand->ecc.user_conf.algo; + chip->ecc.strength = nand->ecc.user_conf.strength; + chip->ecc.size = nand->ecc.user_conf.step_size; return 0; } @@ -5215,7 +5072,7 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips, /* Enforce the right timings for reset/detection */ chip->current_interface_config = nand_get_reset_interface_config(); - ret = nand_dt_init(chip); + ret = rawnand_dt_init(chip); if (ret) return ret; @@ -5282,16 +5139,76 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) kfree(chip->parameters.onfi); } +static int nand_set_ecc_on_host_ops(struct nand_chip *chip) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + + switch (ecc->placement) { + case NAND_ECC_PLACEMENT_UNKNOWN: + case NAND_ECC_PLACEMENT_OOB: + /* Use standard hwecc read page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc; + if (!ecc->write_page) + ecc->write_page = nand_write_page_hwecc; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_std; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_std; + if (!ecc->read_subpage) + ecc->read_subpage = nand_read_subpage; + if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) + ecc->write_subpage = nand_write_subpage_hwecc; + fallthrough; + + case NAND_ECC_PLACEMENT_INTERLEAVED: + if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && + (!ecc->read_page || + ecc->read_page == nand_read_page_hwecc || + !ecc->write_page || + ecc->write_page == nand_write_page_hwecc)) { + WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); + return -EINVAL; + } + /* Use standard syndrome read/write page function? */ + if (!ecc->read_page) + ecc->read_page = nand_read_page_syndrome; + if (!ecc->write_page) + ecc->write_page = nand_write_page_syndrome; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw_syndrome; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw_syndrome; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_syndrome; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_syndrome; + break; + + default: + pr_warn("Invalid NAND_ECC_PLACEMENT %d\n", + ecc->placement); + return -EINVAL; + } + + return 0; +} + 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; - if (WARN_ON(ecc->mode != NAND_ECC_SOFT)) + if (WARN_ON(ecc->engine_type != NAND_ECC_ENGINE_TYPE_SOFT)) return -EINVAL; switch (ecc->algo) { - case NAND_ECC_HAMMING: + case NAND_ECC_ALGO_HAMMING: ecc->calculate = nand_calculate_ecc; ecc->correct = nand_correct_data; ecc->read_page = nand_read_page_swecc; @@ -5312,7 +5229,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER; return 0; - case NAND_ECC_BCH: + case NAND_ECC_ALGO_BCH: if (!mtd_nand_has_bch()) { WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n"); return -EINVAL; @@ -5350,7 +5267,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) return -EINVAL; } - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); } @@ -5359,8 +5276,8 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) * used, otherwise we don't know how many bytes can really be * used. */ - if (mtd->ooblayout == &nand_ooblayout_lp_ops && - ecc->options & NAND_ECC_MAXIMIZE) { + 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 */ @@ -5454,10 +5371,12 @@ static int nand_match_ecc_req(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail) { + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); struct mtd_info *mtd = nand_to_mtd(chip); const struct nand_ecc_step_info *stepinfo; - int req_step = chip->base.eccreq.step_size; - int req_strength = chip->base.eccreq.strength; + int req_step = requirements->step_size; + int req_strength = requirements->strength; int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total; int best_step, best_strength, best_ecc_bytes; int best_ecc_bytes_total = INT_MAX; @@ -5598,11 +5517,12 @@ nand_maximize_ecc(struct nand_chip *chip, * @caps: ECC engine caps info structure * @oobavail: OOB size that the ECC engine can use * - * Choose the ECC configuration according to following logic + * Choose the ECC configuration according to following logic. * * 1. If both ECC step size and ECC strength are already set (usually by DT) * then check if it is supported by this controller. - * 2. If NAND_ECC_MAXIMIZE is set, then select maximum ECC strength. + * 2. If the user provided the nand-ecc-maximize property, then select maximum + * ECC strength. * 3. Otherwise, try to match the ECC step size and ECC strength closest * to the chip's requirement. If available OOB size can't fit the chip * requirement then fallback to the maximum ECC step size and ECC strength. @@ -5613,6 +5533,7 @@ int nand_ecc_choose_conf(struct nand_chip *chip, const struct nand_ecc_caps *caps, int oobavail) { struct mtd_info *mtd = nand_to_mtd(chip); + struct nand_device *nanddev = mtd_to_nanddev(mtd); if (WARN_ON(oobavail < 0 || oobavail > mtd->oobsize)) return -EINVAL; @@ -5620,7 +5541,7 @@ int nand_ecc_choose_conf(struct nand_chip *chip, if (chip->ecc.size && chip->ecc.strength) return nand_check_ecc_caps(chip, caps, oobavail); - if (chip->ecc.options & NAND_ECC_MAXIMIZE) + if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) return nand_maximize_ecc(chip, caps, oobavail); if (!nand_match_ecc_req(chip, caps, oobavail)) @@ -5630,41 +5551,6 @@ int nand_ecc_choose_conf(struct nand_chip *chip, } EXPORT_SYMBOL_GPL(nand_ecc_choose_conf); -/* - * Check if the chip configuration meet the datasheet requirements. - - * If our configuration corrects A bits per B bytes and the minimum - * required correction level is X bits per Y bytes, then we must ensure - * both of the following are true: - * - * (1) A / B >= X / Y - * (2) A >= X - * - * Requirement (1) ensures we can correct for the required bitflip density. - * Requirement (2) ensures we can correct even when all bitflips are clumped - * in the same sector. - */ -static bool nand_ecc_strength_good(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int corr, ds_corr; - - if (ecc->size == 0 || chip->base.eccreq.step_size == 0) - /* Not enough information */ - return true; - - /* - * We get the number of corrected bits per page to compare - * the correction density. - */ - corr = (mtd->writesize * ecc->strength) / ecc->size; - ds_corr = (mtd->writesize * chip->base.eccreq.strength) / - chip->base.eccreq.step_size; - - return corr >= ds_corr && ecc->strength >= chip->base.eccreq.strength; -} - static int rawnand_erase(struct nand_device *nand, const struct nand_pos *pos) { struct nand_chip *chip = container_of(nand, struct nand_chip, @@ -5752,15 +5638,17 @@ static int nand_scan_tail(struct nand_chip *chip) * If no default placement scheme is given, select an appropriate one. */ if (!mtd->ooblayout && - !(ecc->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_BCH)) { + !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT && + ecc->algo == NAND_ECC_ALGO_BCH)) { switch (mtd->oobsize) { case 8: case 16: - mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops); + mtd_set_ooblayout(mtd, nand_get_small_page_ooblayout()); break; case 64: case 128: - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops); + mtd_set_ooblayout(mtd, + nand_get_large_page_hamming_ooblayout()); break; default: /* @@ -5770,9 +5658,9 @@ static int nand_scan_tail(struct nand_chip *chip) * page with ECC layout when ->oobsize <= 128 for * compatibility reasons. */ - if (ecc->mode == NAND_ECC_NONE) { + if (ecc->engine_type == NAND_ECC_ENGINE_TYPE_NONE) { mtd_set_ooblayout(mtd, - &nand_ooblayout_lp_ops); + nand_get_large_page_ooblayout()); break; } @@ -5788,49 +5676,11 @@ static int nand_scan_tail(struct nand_chip *chip) * selected and we have 256 byte pagesize fallback to software ECC */ - switch (ecc->mode) { - case NAND_ECC_HW: - /* Use standard hwecc read page function? */ - if (!ecc->read_page) - ecc->read_page = nand_read_page_hwecc; - if (!ecc->write_page) - ecc->write_page = nand_write_page_hwecc; - if (!ecc->read_page_raw) - ecc->read_page_raw = nand_read_page_raw; - if (!ecc->write_page_raw) - ecc->write_page_raw = nand_write_page_raw; - if (!ecc->read_oob) - ecc->read_oob = nand_read_oob_std; - if (!ecc->write_oob) - ecc->write_oob = nand_write_oob_std; - if (!ecc->read_subpage) - ecc->read_subpage = nand_read_subpage; - if (!ecc->write_subpage && ecc->hwctl && ecc->calculate) - ecc->write_subpage = nand_write_subpage_hwecc; - fallthrough; - case NAND_ECC_HW_SYNDROME: - if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && - (!ecc->read_page || - ecc->read_page == nand_read_page_hwecc || - !ecc->write_page || - ecc->write_page == nand_write_page_hwecc)) { - WARN(1, "No ECC functions supplied; hardware ECC not possible\n"); - ret = -EINVAL; + switch (ecc->engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: + ret = nand_set_ecc_on_host_ops(chip); + if (ret) goto err_nand_manuf_cleanup; - } - /* Use standard syndrome read/write page function? */ - if (!ecc->read_page) - ecc->read_page = nand_read_page_syndrome; - if (!ecc->write_page) - ecc->write_page = nand_write_page_syndrome; - if (!ecc->read_page_raw) - ecc->read_page_raw = nand_read_page_raw_syndrome; - if (!ecc->write_page_raw) - ecc->write_page_raw = nand_write_page_raw_syndrome; - if (!ecc->read_oob) - ecc->read_oob = nand_read_oob_syndrome; - if (!ecc->write_oob) - ecc->write_oob = nand_write_oob_syndrome; if (mtd->writesize >= ecc->size) { if (!ecc->strength) { @@ -5842,18 +5692,17 @@ static int nand_scan_tail(struct nand_chip *chip) } pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", ecc->size, mtd->writesize); - ecc->mode = NAND_ECC_SOFT; - ecc->algo = NAND_ECC_HAMMING; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + ecc->algo = NAND_ECC_ALGO_HAMMING; fallthrough; - case NAND_ECC_SOFT: + + case NAND_ECC_ENGINE_TYPE_SOFT: ret = nand_set_ecc_soft_ops(chip); - if (ret) { - ret = -EINVAL; + if (ret) goto err_nand_manuf_cleanup; - } break; - case NAND_ECC_ON_DIE: + case NAND_ECC_ENGINE_TYPE_ON_DIE: if (!ecc->read_page || !ecc->write_page) { WARN(1, "No ECC functions supplied; on-die ECC not possible\n"); ret = -EINVAL; @@ -5865,8 +5714,8 @@ static int nand_scan_tail(struct nand_chip *chip) ecc->write_oob = nand_write_oob_std; break; - case NAND_ECC_NONE: - pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n"); + case NAND_ECC_ENGINE_TYPE_NONE: + pr_warn("NAND_ECC_ENGINE_TYPE_NONE selected by board driver. This is not recommended!\n"); ecc->read_page = nand_read_page_raw; ecc->write_page = nand_write_page_raw; ecc->read_oob = nand_read_oob_std; @@ -5879,7 +5728,7 @@ static int nand_scan_tail(struct nand_chip *chip) break; default: - WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode); + WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->engine_type); ret = -EINVAL; goto err_nand_manuf_cleanup; } @@ -5913,7 +5762,10 @@ static int nand_scan_tail(struct nand_chip *chip) ret = -EINVAL; goto err_nand_manuf_cleanup; } + 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"); ret = -EINVAL; @@ -5931,11 +5783,11 @@ static int nand_scan_tail(struct nand_chip *chip) mtd->oobavail = ret; /* ECC sanity check: warn if it's too weak */ - if (!nand_ecc_strength_good(chip)) + if (!nand_ecc_is_strong_enough(&chip->base)) pr_warn("WARNING: %s: the ECC used on your system (%db/%dB) is too weak compared to the one required by the NAND chip (%db/%dB)\n", mtd->name, chip->ecc.strength, chip->ecc.size, - chip->base.eccreq.strength, - chip->base.eccreq.step_size); + nanddev_get_ecc_requirements(&chip->base)->strength, + nanddev_get_ecc_requirements(&chip->base)->step_size); /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { @@ -5956,8 +5808,8 @@ static int nand_scan_tail(struct nand_chip *chip) chip->pagecache.page = -1; /* Large page NAND with SOFT_ECC should support subpage reads */ - switch (ecc->mode) { - case NAND_ECC_SOFT: + switch (ecc->engine_type) { + case NAND_ECC_ENGINE_TYPE_SOFT: if (chip->page_shift > 9) chip->options |= NAND_SUBPAGE_READ; break; @@ -6101,8 +5953,8 @@ EXPORT_SYMBOL(nand_scan_with_ids); */ void nand_cleanup(struct nand_chip *chip) { - if (chip->ecc.mode == NAND_ECC_SOFT && - chip->ecc.algo == NAND_ECC_BCH) + 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); nanddev_cleanup(&chip->base); diff --git a/drivers/mtd/nand/raw/nand_bch.c b/drivers/mtd/nand/raw/nand_bch.c index d5af8c5fd02f..9d19ac14c196 100644 --- a/drivers/mtd/nand/raw/nand_bch.c +++ b/drivers/mtd/nand/raw/nand_bch.c @@ -165,6 +165,7 @@ struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) */ 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; diff --git a/drivers/mtd/nand/raw/nand_esmt.c b/drivers/mtd/nand/raw/nand_esmt.c index 3338c68aaaf1..4412c407aef3 100644 --- a/drivers/mtd/nand/raw/nand_esmt.c +++ b/drivers/mtd/nand/raw/nand_esmt.c @@ -10,27 +10,32 @@ static void esmt_nand_decode_id(struct nand_chip *chip) { + struct nand_device *base = &chip->base; + struct nand_ecc_props requirements = {}; + nand_decode_ext_id(chip); /* Extract ECC requirements from 5th id byte. */ if (chip->id.len >= 5 && nand_is_slc(chip)) { - chip->base.eccreq.step_size = 512; + requirements.step_size = 512; switch (chip->id.data[4] & 0x3) { case 0x0: - chip->base.eccreq.strength = 4; + requirements.strength = 4; break; case 0x1: - chip->base.eccreq.strength = 2; + requirements.strength = 2; break; case 0x2: - chip->base.eccreq.strength = 1; + requirements.strength = 1; break; default: WARN(1, "Could not get ECC info"); - chip->base.eccreq.step_size = 0; + requirements.step_size = 0; break; } } + + nanddev_set_ecc_requirements(base, &requirements); } static int esmt_nand_init(struct nand_chip *chip) diff --git a/drivers/mtd/nand/raw/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index 6d08eb834456..a9f50c9af109 100644 --- a/drivers/mtd/nand/raw/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -495,34 +495,36 @@ static void hynix_nand_extract_oobsize(struct nand_chip *chip, static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip, bool valid_jedecid) { + struct nand_device *base = &chip->base; + struct nand_ecc_props requirements = {}; u8 ecc_level = (chip->id.data[4] >> 4) & 0x7; if (valid_jedecid) { /* Reference: H27UCG8T2E datasheet */ - chip->base.eccreq.step_size = 1024; + requirements.step_size = 1024; switch (ecc_level) { case 0: - chip->base.eccreq.step_size = 0; - chip->base.eccreq.strength = 0; + requirements.step_size = 0; + requirements.strength = 0; break; case 1: - chip->base.eccreq.strength = 4; + requirements.strength = 4; break; case 2: - chip->base.eccreq.strength = 24; + requirements.strength = 24; break; case 3: - chip->base.eccreq.strength = 32; + requirements.strength = 32; break; case 4: - chip->base.eccreq.strength = 40; + requirements.strength = 40; break; case 5: - chip->base.eccreq.strength = 50; + requirements.strength = 50; break; case 6: - chip->base.eccreq.strength = 60; + requirements.strength = 60; break; default: /* @@ -543,14 +545,14 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip, if (nand_tech < 3) { /* > 26nm, reference: H27UBG8T2A datasheet */ if (ecc_level < 5) { - chip->base.eccreq.step_size = 512; - chip->base.eccreq.strength = 1 << ecc_level; + requirements.step_size = 512; + requirements.strength = 1 << ecc_level; } else if (ecc_level < 7) { if (ecc_level == 5) - chip->base.eccreq.step_size = 2048; + requirements.step_size = 2048; else - chip->base.eccreq.step_size = 1024; - chip->base.eccreq.strength = 24; + requirements.step_size = 1024; + requirements.strength = 24; } else { /* * We should never reach this case, but if that @@ -563,18 +565,20 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip, } else { /* <= 26nm, reference: H27UBG8T2B datasheet */ if (!ecc_level) { - chip->base.eccreq.step_size = 0; - chip->base.eccreq.strength = 0; + requirements.step_size = 0; + requirements.strength = 0; } else if (ecc_level < 5) { - chip->base.eccreq.step_size = 512; - chip->base.eccreq.strength = 1 << (ecc_level - 1); + requirements.step_size = 512; + requirements.strength = 1 << (ecc_level - 1); } else { - chip->base.eccreq.step_size = 1024; - chip->base.eccreq.strength = 24 + + requirements.step_size = 1024; + requirements.strength = 24 + (8 * (ecc_level - 5)); } } } + + nanddev_set_ecc_requirements(base, &requirements); } static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip, diff --git a/drivers/mtd/nand/raw/nand_jedec.c b/drivers/mtd/nand/raw/nand_jedec.c index b15c42f48755..85b6d9372d80 100644 --- a/drivers/mtd/nand/raw/nand_jedec.c +++ b/drivers/mtd/nand/raw/nand_jedec.c @@ -23,6 +23,7 @@ */ int nand_jedec_detect(struct nand_chip *chip) { + struct nand_device *base = &chip->base; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_memory_organization *memorg; struct nand_jedec_params *p; @@ -120,8 +121,12 @@ int nand_jedec_detect(struct nand_chip *chip) ecc = &p->ecc_info[0]; if (ecc->codeword_size >= 9) { - chip->base.eccreq.strength = ecc->ecc_bits; - chip->base.eccreq.step_size = 1 << ecc->codeword_size; + struct nand_ecc_props requirements = { + .strength = ecc->ecc_bits, + .step_size = 1 << ecc->codeword_size, + }; + + nanddev_set_ecc_requirements(base, &requirements); } else { pr_warn("Invalid codeword size\n"); } diff --git a/drivers/mtd/nand/raw/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index 4385092a9325..c0192881906b 100644 --- a/drivers/mtd/nand/raw/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -413,6 +413,8 @@ enum { */ static int micron_supports_on_die_ecc(struct nand_chip *chip) { + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); u8 id[5]; int ret; @@ -425,7 +427,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) /* * We only support on-die ECC of 4/512 or 8/512 */ - if (chip->base.eccreq.strength != 4 && chip->base.eccreq.strength != 8) + if (requirements->strength != 4 && requirements->strength != 8) return MICRON_ON_DIE_UNSUPPORTED; /* 0x2 means on-die ECC is available. */ @@ -466,7 +468,7 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) /* * We only support on-die ECC of 4/512 or 8/512 */ - if (chip->base.eccreq.strength != 4 && chip->base.eccreq.strength != 8) + if (requirements->strength != 4 && requirements->strength != 8) return MICRON_ON_DIE_UNSUPPORTED; return MICRON_ON_DIE_SUPPORTED; @@ -474,6 +476,9 @@ static int micron_supports_on_die_ecc(struct nand_chip *chip) static int micron_nand_init(struct nand_chip *chip) { + struct nand_device *base = &chip->base; + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(base); struct mtd_info *mtd = nand_to_mtd(chip); struct micron_nand *micron; int ondie; @@ -497,13 +502,13 @@ static int micron_nand_init(struct nand_chip *chip) ondie = micron_supports_on_die_ecc(chip); if (ondie == MICRON_ON_DIE_MANDATORY && - chip->ecc.mode != NAND_ECC_ON_DIE) { + chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_DIE) { pr_err("On-die ECC forcefully enabled, not supported\n"); ret = -EINVAL; goto err_free_manuf_data; } - if (chip->ecc.mode == NAND_ECC_ON_DIE) { + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE) { if (ondie == MICRON_ON_DIE_UNSUPPORTED) { pr_err("On-die ECC selected but not supported\n"); ret = -EINVAL; @@ -523,7 +528,7 @@ static int micron_nand_init(struct nand_chip *chip) * That's not needed for 8-bit ECC, because the status expose * a better approximation of the number of bitflips in a page. */ - if (chip->base.eccreq.strength == 4) { + if (requirements->strength == 4) { micron->ecc.rawbuf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); @@ -533,17 +538,17 @@ static int micron_nand_init(struct nand_chip *chip) } } - if (chip->base.eccreq.strength == 4) + if (requirements->strength == 4) mtd_set_ooblayout(mtd, µn_nand_on_die_4_ooblayout_ops); else mtd_set_ooblayout(mtd, µn_nand_on_die_8_ooblayout_ops); - chip->ecc.bytes = chip->base.eccreq.strength * 2; + chip->ecc.bytes = requirements->strength * 2; chip->ecc.size = 512; - chip->ecc.strength = chip->base.eccreq.strength; - chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.strength = requirements->strength; + chip->ecc.algo = NAND_ECC_ALGO_BCH; chip->ecc.read_page = micron_nand_read_page_on_die_ecc; chip->ecc.write_page = micron_nand_write_page_on_die_ecc; diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c index be3456627288..45649e03797d 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -34,6 +34,8 @@ u16 onfi_crc16(u16 crc, u8 const *p, size_t len) static int nand_flash_detect_ext_param_page(struct nand_chip *chip, struct nand_onfi_params *p) { + struct nand_device *base = &chip->base; + struct nand_ecc_props requirements; struct onfi_ext_param_page *ep; struct onfi_ext_section *s; struct onfi_ext_ecc_info *ecc; @@ -94,8 +96,10 @@ static int nand_flash_detect_ext_param_page(struct nand_chip *chip, goto ext_out; } - chip->base.eccreq.strength = ecc->ecc_bits; - chip->base.eccreq.step_size = 1 << ecc->codeword_size; + requirements.strength = ecc->ecc_bits; + requirements.step_size = 1 << ecc->codeword_size; + nanddev_set_ecc_requirements(base, &requirements); + ret = 0; ext_out: @@ -139,6 +143,7 @@ static void nand_bit_wise_majority(const void **srcbufs, */ int nand_onfi_detect(struct nand_chip *chip) { + struct nand_device *base = &chip->base; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_memory_organization *memorg; struct nand_onfi_params *p = NULL, *pbuf; @@ -265,8 +270,12 @@ int nand_onfi_detect(struct nand_chip *chip) chip->options |= NAND_BUSWIDTH_16; if (p->ecc_bits != 0xff) { - chip->base.eccreq.strength = p->ecc_bits; - chip->base.eccreq.step_size = 512; + struct nand_ecc_props requirements = { + .strength = p->ecc_bits, + .step_size = 512, + }; + + nanddev_set_ecc_requirements(base, &requirements); } else if (onfi_version >= 21 && (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) { diff --git a/drivers/mtd/nand/raw/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c index 3a4a19e808f6..0be6b7563805 100644 --- a/drivers/mtd/nand/raw/nand_samsung.c +++ b/drivers/mtd/nand/raw/nand_samsung.c @@ -10,6 +10,8 @@ static void samsung_nand_decode_id(struct nand_chip *chip) { + struct nand_device *base = &chip->base; + struct nand_ecc_props requirements = {}; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_memory_organization *memorg; @@ -71,23 +73,23 @@ static void samsung_nand_decode_id(struct nand_chip *chip) /* Extract ECC requirements from 5th id byte*/ extid = (chip->id.data[4] >> 4) & 0x07; if (extid < 5) { - chip->base.eccreq.step_size = 512; - chip->base.eccreq.strength = 1 << extid; + requirements.step_size = 512; + requirements.strength = 1 << extid; } else { - chip->base.eccreq.step_size = 1024; + requirements.step_size = 1024; switch (extid) { case 5: - chip->base.eccreq.strength = 24; + requirements.strength = 24; break; case 6: - chip->base.eccreq.strength = 40; + requirements.strength = 40; break; case 7: - chip->base.eccreq.strength = 60; + requirements.strength = 60; break; default: WARN(1, "Could not decode ECC info"); - chip->base.eccreq.step_size = 0; + requirements.step_size = 0; } } } else { @@ -97,8 +99,8 @@ static void samsung_nand_decode_id(struct nand_chip *chip) switch (chip->id.data[1]) { /* K9F4G08U0D-S[I|C]B0(T00) */ case 0xDC: - chip->base.eccreq.step_size = 512; - chip->base.eccreq.strength = 1; + requirements.step_size = 512; + requirements.strength = 1; break; /* K9F1G08U0E 21nm chips do not support subpage write */ @@ -112,6 +114,8 @@ static void samsung_nand_decode_id(struct nand_chip *chip) } } } + + nanddev_set_ecc_requirements(base, &requirements); } static int samsung_nand_init(struct nand_chip *chip) diff --git a/drivers/mtd/nand/raw/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c index f746c19f3b2c..cf4f37959421 100644 --- a/drivers/mtd/nand/raw/nand_toshiba.c +++ b/drivers/mtd/nand/raw/nand_toshiba.c @@ -140,11 +140,13 @@ static void toshiba_nand_benand_init(struct nand_chip *chip) chip->options |= NAND_SUBPAGE_READ; - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); } static void toshiba_nand_decode_id(struct nand_chip *chip) { + struct nand_device *base = &chip->base; + struct nand_ecc_props requirements = {}; struct mtd_info *mtd = nand_to_mtd(chip); struct nand_memory_organization *memorg; @@ -175,23 +177,25 @@ static void toshiba_nand_decode_id(struct nand_chip *chip) * - 24nm: 8 bit ECC for each 512Byte is required. */ if (chip->id.len >= 6 && nand_is_slc(chip)) { - chip->base.eccreq.step_size = 512; + requirements.step_size = 512; switch (chip->id.data[5] & 0x7) { case 0x4: - chip->base.eccreq.strength = 1; + requirements.strength = 1; break; case 0x5: - chip->base.eccreq.strength = 4; + requirements.strength = 4; break; case 0x6: - chip->base.eccreq.strength = 8; + requirements.strength = 8; break; default: WARN(1, "Could not get ECC info"); - chip->base.eccreq.step_size = 0; + requirements.step_size = 0; break; } } + + nanddev_set_ecc_requirements(base, &requirements); } static int @@ -273,7 +277,8 @@ static int toshiba_nand_init(struct nand_chip *chip) chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE; /* Check that chip is BENAND and ECC mode is on-die */ - if (nand_is_slc(chip) && chip->ecc.mode == NAND_ECC_ON_DIE && + if (nand_is_slc(chip) && + chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE && chip->id.data[4] & TOSHIBA_NAND_ID4_IS_BENAND) toshiba_nand_benand_init(chip); diff --git a/drivers/mtd/nand/raw/nandsim.c b/drivers/mtd/nand/raw/nandsim.c index f5a53aac3c5f..a8048cb8d220 100644 --- a/drivers/mtd/nand/raw/nandsim.c +++ b/drivers/mtd/nand/raw/nandsim.c @@ -2234,8 +2234,8 @@ static int ns_attach_chip(struct nand_chip *chip) return -EINVAL; } - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_BCH; chip->ecc.size = 512; chip->ecc.strength = bch; chip->ecc.bytes = eccbytes; @@ -2274,8 +2274,8 @@ static int __init ns_init_module(void) nsmtd = nand_to_mtd(chip); nand_set_controller_data(chip, (void *)ns); - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ /* and 'badblocks' parameters to work */ chip->options |= NAND_SKIP_BBTSCAN; diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index ed38338c1383..0fb4ba93c41e 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -149,7 +149,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, chip->ecc.correct = nand_correct_data; chip->ecc.hwctl = ndfc_enable_hwecc; chip->ecc.calculate = ndfc_calculate_ecc; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 256; chip->ecc.bytes = 3; chip->ecc.strength = 1; diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index eb7fcfd9276b..512f60780a50 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -884,8 +884,8 @@ static int omap_correct_data(struct nand_chip *chip, u_char *dat, int stat = 0; /* Ex NAND_ECC_HW12_2048 */ - if ((info->nand.ecc.mode == NAND_ECC_HW) && - (info->nand.ecc.size == 2048)) + if (info->nand.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST && + info->nand.ecc.size == 2048) blockCnt = 4; else blockCnt = 1; @@ -2006,12 +2006,12 @@ static int omap_nand_attach_chip(struct nand_chip *chip) return -EINVAL; /* - * Bail out earlier to let NAND_ECC_SOFT code create its own + * Bail out earlier to let NAND_ECC_ENGINE_TYPE_SOFT code create its own * ooblayout instead of using ours. */ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; return 0; } @@ -2019,7 +2019,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) switch (info->ecc_opt) { case OMAP_ECC_HAM1_CODE_HW: dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.bytes = 3; chip->ecc.size = 512; chip->ecc.strength = 1; @@ -2036,7 +2036,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; chip->ecc.bytes = 7; chip->ecc.strength = 4; @@ -2056,7 +2056,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case OMAP_ECC_BCH4_CODE_HW: pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; /* 14th bit is kept reserved for ROM-code compatibility */ chip->ecc.bytes = 7 + 1; @@ -2078,7 +2078,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; chip->ecc.bytes = 13; chip->ecc.strength = 8; @@ -2098,7 +2098,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case OMAP_ECC_BCH8_CODE_HW: pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; /* 14th bit is kept reserved for ROM-code compatibility */ chip->ecc.bytes = 13 + 1; @@ -2121,7 +2121,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip) case OMAP_ECC_BCH16_CODE_HW: pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; chip->ecc.bytes = 26; chip->ecc.strength = 16; diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c index 880b54ca1b41..df9c0f8e4b4e 100644 --- a/drivers/mtd/nand/raw/orion_nand.c +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -139,8 +139,8 @@ 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.mode = NAND_ECC_SOFT; - nc->ecc.algo = NAND_ECC_HAMMING; + 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; diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c index d8eca8c3fdcd..2b8f155cc0c5 100644 --- a/drivers/mtd/nand/raw/pasemi_nand.c +++ b/drivers/mtd/nand/raw/pasemi_nand.c @@ -68,7 +68,7 @@ static void pasemi_hwcontrol(struct nand_chip *chip, int cmd, inl(lpcctl); } -int pasemi_device_ready(struct nand_chip *chip) +static int pasemi_device_ready(struct nand_chip *chip) { return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); } @@ -132,8 +132,8 @@ 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.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + 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; diff --git a/drivers/mtd/nand/raw/plat_nand.c b/drivers/mtd/nand/raw/plat_nand.c index 556182f26057..b98c0d5c413f 100644 --- a/drivers/mtd/nand/raw/plat_nand.c +++ b/drivers/mtd/nand/raw/plat_nand.c @@ -66,8 +66,8 @@ 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.mode = NAND_ECC_SOFT; - data->chip.ecc.algo = NAND_ECC_HAMMING; + data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + data->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; platform_set_drvdata(pdev, data); diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index bd7a7251429b..777fb0de0680 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2550,7 +2550,7 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) ecc->write_page_raw = qcom_nandc_write_page_raw; ecc->write_oob = qcom_nandc_write_oob; - ecc->mode = NAND_ECC_HW; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; mtd_set_ooblayout(mtd, &qcom_nand_ooblayout_ops); @@ -2702,10 +2702,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (IS_ERR(nandc->tx_chan)) { ret = PTR_ERR(nandc->tx_chan); nandc->tx_chan = NULL; - if (ret != -EPROBE_DEFER) - dev_err(nandc->dev, - "tx DMA channel request failed: %d\n", - ret); + dev_err_probe(nandc->dev, ret, + "tx DMA channel request failed\n"); goto unalloc; } @@ -2713,10 +2711,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (IS_ERR(nandc->rx_chan)) { ret = PTR_ERR(nandc->rx_chan); nandc->rx_chan = NULL; - if (ret != -EPROBE_DEFER) - dev_err(nandc->dev, - "rx DMA channel request failed: %d\n", - ret); + dev_err_probe(nandc->dev, ret, + "rx DMA channel request failed\n"); goto unalloc; } @@ -2724,10 +2720,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (IS_ERR(nandc->cmd_chan)) { ret = PTR_ERR(nandc->cmd_chan); nandc->cmd_chan = NULL; - if (ret != -EPROBE_DEFER) - dev_err(nandc->dev, - "cmd DMA channel request failed: %d\n", - ret); + dev_err_probe(nandc->dev, ret, + "cmd DMA channel request failed\n"); goto unalloc; } @@ -2750,10 +2744,8 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) if (IS_ERR(nandc->chan)) { ret = PTR_ERR(nandc->chan); nandc->chan = NULL; - if (ret != -EPROBE_DEFER) - dev_err(nandc->dev, - "rxtx DMA channel request failed: %d\n", - ret); + dev_err_probe(nandc->dev, ret, + "rxtx DMA channel request failed\n"); return ret; } } diff --git a/drivers/mtd/nand/raw/r852.c b/drivers/mtd/nand/raw/r852.c index f865e3a47b01..6b7addd2c420 100644 --- a/drivers/mtd/nand/raw/r852.c +++ b/drivers/mtd/nand/raw/r852.c @@ -859,7 +859,8 @@ static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) chip->legacy.write_buf = r852_write_buf; /* ecc */ - chip->ecc.mode = NAND_ECC_HW_SYNDROME; + 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; diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index 105522205979..fbd0fa48e063 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -904,7 +904,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, nmtd->info = info; nmtd->set = set; - chip->ecc.mode = info->platform->ecc_mode; + chip->ecc.engine_type = info->platform->engine_type; /* * If you use u-boot BBT creation code, specifying this flag will @@ -929,24 +929,24 @@ static int s3c2410_nand_attach_chip(struct nand_chip *chip) struct mtd_info *mtd = nand_to_mtd(chip); struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - switch (chip->ecc.mode) { + switch (chip->ecc.engine_type) { - case NAND_ECC_NONE: + case NAND_ECC_ENGINE_TYPE_NONE: dev_info(info->device, "ECC disabled\n"); break; - case NAND_ECC_SOFT: + case NAND_ECC_ENGINE_TYPE_SOFT: /* - * This driver expects Hamming based ECC when ecc_mode is set - * to NAND_ECC_SOFT. Force ecc.algo to NAND_ECC_HAMMING to - * avoid adding an extra ecc_algo field to - * s3c2410_platform_nand. + * This driver expects Hamming based ECC when engine_type is set + * to NAND_ECC_ENGINE_TYPE_SOFT. Force ecc.algo to + * NAND_ECC_ALGO_HAMMING to avoid adding an extra ecc_algo field + * to s3c2410_platform_nand. */ - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; dev_info(info->device, "soft ECC\n"); break; - case NAND_ECC_HW: + case NAND_ECC_ENGINE_TYPE_ON_HOST: chip->ecc.calculate = s3c2410_nand_calculate_ecc; chip->ecc.correct = s3c2410_nand_correct_data; chip->ecc.strength = 1; diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index a661b8bb2dd5..13df4bdf792a 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -1039,13 +1039,13 @@ static int flctl_chip_attach_chip(struct nand_chip *chip) chip->ecc.strength = 4; chip->ecc.read_page = flctl_read_page_hwecc; chip->ecc.write_page = flctl_write_page_hwecc; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; /* 4 symbols ECC enabled */ flctl->flcmncr_base |= _4ECCEN; } else { - chip->ecc.mode = NAND_ECC_SOFT; - chip->ecc.algo = NAND_ECC_HAMMING; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; } return 0; diff --git a/drivers/mtd/nand/raw/sharpsl.c b/drivers/mtd/nand/raw/sharpsl.c index 51286f7acf54..1327bfb3d5d3 100644 --- a/drivers/mtd/nand/raw/sharpsl.c +++ b/drivers/mtd/nand/raw/sharpsl.c @@ -157,7 +157,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) /* 15 us command delay time */ this->legacy.chip_delay = 15; /* set eccmode using hardware ECC */ - this->ecc.mode = NAND_ECC_HW; + this->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; this->ecc.size = 256; this->ecc.bytes = 3; this->ecc.strength = 1; diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c index 243b34cfbc1b..0f63ff6f7fe7 100644 --- a/drivers/mtd/nand/raw/socrates_nand.c +++ b/drivers/mtd/nand/raw/socrates_nand.c @@ -153,8 +153,9 @@ 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; - nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ - nand_chip->ecc.algo = NAND_ECC_HAMMING; + /* 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 */ diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 7f4546ae9130..b31a5818234d 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1696,14 +1696,15 @@ static int stm32_fmc2_nfc_attach_chip(struct nand_chip *chip) int ret; /* - * Only NAND_ECC_HW mode is actually supported + * Only NAND_ECC_ENGINE_TYPE_ON_HOST mode is actually supported * Hamming => ecc.strength = 1 * BCH4 => ecc.strength = 4 * BCH8 => ecc.strength = 8 * ECC sector size = 512 */ - if (chip->ecc.mode != NAND_ECC_HW) { - dev_err(nfc->dev, "nand_ecc_mode is not well defined in the DT\n"); + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) { + dev_err(nfc->dev, + "nand_ecc_engine_type is not well defined in the DT\n"); return -EINVAL; } @@ -1762,7 +1763,7 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, return ret; } - if (cs > FMC2_MAX_CE) { + if (cs >= FMC2_MAX_CE) { dev_err(nfc->dev, "invalid reg value: %d\n", cs); return -EINVAL; } @@ -1952,7 +1953,7 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) NAND_USES_DMA; /* Default ECC settings */ - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = FMC2_ECC_STEP_SIZE; chip->ecc.strength = FMC2_ECC_BCH8; diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 9c50c2b965e1..2a7ca3072f35 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1575,7 +1575,7 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, * only have 2 bytes available in the first user data * section. */ - if (!section && ecc->mode == NAND_ECC_HW) { + if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { oobregion->offset = 2; oobregion->length = 2; @@ -1609,12 +1609,13 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; 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; - if (ecc->options & NAND_ECC_MAXIMIZE) { + if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { int bytes; ecc->size = 1024; @@ -1720,11 +1721,11 @@ err: static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) { - switch (ecc->mode) { - case NAND_ECC_HW: + switch (ecc->engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: sunxi_nand_hw_ecc_ctrl_cleanup(ecc); break; - case NAND_ECC_NONE: + case NAND_ECC_ENGINE_TYPE_NONE: default: break; } @@ -1732,6 +1733,8 @@ static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) static int sunxi_nand_attach_chip(struct nand_chip *nand) { + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&nand->base); struct nand_ecc_ctrl *ecc = &nand->ecc; struct device_node *np = nand_get_flash_node(nand); int ret; @@ -1745,21 +1748,21 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand) nand->options |= NAND_SUBPAGE_READ; if (!ecc->size) { - ecc->size = nand->base.eccreq.step_size; - ecc->strength = nand->base.eccreq.strength; + ecc->size = requirements->step_size; + ecc->strength = requirements->strength; } if (!ecc->size || !ecc->strength) return -EINVAL; - switch (ecc->mode) { - case NAND_ECC_HW: + switch (ecc->engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: ret = sunxi_nand_hw_ecc_ctrl_init(nand, ecc, np); if (ret) return ret; break; - case NAND_ECC_NONE: - case NAND_ECC_SOFT: + case NAND_ECC_ENGINE_TYPE_NONE: + case NAND_ECC_ENGINE_TYPE_SOFT: break; default: return -EINVAL; @@ -1991,7 +1994,7 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, * Set the ECC mode to the default value in case nothing is specified * in the DT. */ - nand->ecc.mode = NAND_ECC_HW; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; nand_set_flash_node(nand, np); mtd = nand_to_mtd(nand); diff --git a/drivers/mtd/nand/raw/tango_nand.c b/drivers/mtd/nand/raw/tango_nand.c index bdb965ae7a4a..359187b5a4be 100644 --- a/drivers/mtd/nand/raw/tango_nand.c +++ b/drivers/mtd/nand/raw/tango_nand.c @@ -549,8 +549,8 @@ static int tango_attach_chip(struct nand_chip *chip) { struct nand_ecc_ctrl *ecc = &chip->ecc; - ecc->mode = NAND_ECC_HW; - ecc->algo = NAND_ECC_BCH; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + ecc->algo = NAND_ECC_ALGO_BCH; ecc->bytes = DIV_ROUND_UP(ecc->strength * FIELD_ORDER, BITS_PER_BYTE); ecc->read_page_raw = tango_read_page_raw; diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index 6b6212ffa01c..fbf67722a049 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -479,7 +479,7 @@ static void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl, { struct tegra_nand_chip *nand = to_tegra_chip(chip); - if (chip->ecc.algo == NAND_ECC_BCH && enable) + if (chip->ecc.algo == NAND_ECC_ALGO_BCH && enable) writel_relaxed(nand->bch_config, ctrl->regs + BCH_CONFIG); else writel_relaxed(0, ctrl->regs + BCH_CONFIG); @@ -840,7 +840,10 @@ static int tegra_nand_get_strength(struct nand_chip *chip, const int *strength, int strength_len, int bits_per_step, int oobsize) { - bool maximize = chip->ecc.options & NAND_ECC_MAXIMIZE; + struct nand_device *base = mtd_to_nanddev(nand_to_mtd(chip)); + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(base); + bool maximize = base->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH; int i; /* @@ -855,7 +858,7 @@ static int tegra_nand_get_strength(struct nand_chip *chip, const int *strength, } else { strength_sel = strength[i]; - if (strength_sel < chip->base.eccreq.strength) + if (strength_sel < requirements->strength) continue; } @@ -877,7 +880,7 @@ static int tegra_nand_select_strength(struct nand_chip *chip, int oobsize) int strength_len, bits_per_step; switch (chip->ecc.algo) { - case NAND_ECC_RS: + case NAND_ECC_ALGO_RS: bits_per_step = BITS_PER_STEP_RS; if (chip->options & NAND_IS_BOOT_MEDIUM) { strength = rs_strength_bootable; @@ -887,7 +890,7 @@ static int tegra_nand_select_strength(struct nand_chip *chip, int oobsize) strength_len = ARRAY_SIZE(rs_strength); } break; - case NAND_ECC_BCH: + case NAND_ECC_ALGO_BCH: bits_per_step = BITS_PER_STEP_BCH; if (chip->options & NAND_IS_BOOT_MEDIUM) { strength = bch_strength_bootable; @@ -908,6 +911,8 @@ static int tegra_nand_select_strength(struct nand_chip *chip, int oobsize) static int tegra_nand_attach_chip(struct nand_chip *chip) { struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); + const struct nand_ecc_props *requirements = + nanddev_get_ecc_requirements(&chip->base); struct tegra_nand_chip *nand = to_tegra_chip(chip); struct mtd_info *mtd = nand_to_mtd(chip); int bits_per_step; @@ -916,12 +921,12 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) if (chip->bbt_options & NAND_BBT_USE_FLASH) chip->bbt_options |= NAND_BBT_NO_OOB; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.size = 512; chip->ecc.steps = mtd->writesize / chip->ecc.size; - if (chip->base.eccreq.step_size != 512) { + if (requirements->step_size != 512) { dev_err(ctrl->dev, "Unsupported step size %d\n", - chip->base.eccreq.step_size); + requirements->step_size); return -EINVAL; } @@ -935,14 +940,14 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) if (chip->options & NAND_BUSWIDTH_16) nand->config |= CONFIG_BUS_WIDTH_16; - if (chip->ecc.algo == NAND_ECC_UNKNOWN) { + if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) { if (mtd->writesize < 2048) - chip->ecc.algo = NAND_ECC_RS; + chip->ecc.algo = NAND_ECC_ALGO_RS; else - chip->ecc.algo = NAND_ECC_BCH; + chip->ecc.algo = NAND_ECC_ALGO_BCH; } - if (chip->ecc.algo == NAND_ECC_BCH && mtd->writesize < 2048) { + if (chip->ecc.algo == NAND_ECC_ALGO_BCH && mtd->writesize < 2048) { dev_err(ctrl->dev, "BCH supports 2K or 4K page size only\n"); return -EINVAL; } @@ -952,7 +957,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) if (ret < 0) { dev_err(ctrl->dev, "No valid strength found, minimum %d\n", - chip->base.eccreq.strength); + requirements->strength); return ret; } @@ -963,7 +968,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) CONFIG_SKIP_SPARE_SIZE_4; switch (chip->ecc.algo) { - case NAND_ECC_RS: + case NAND_ECC_ALGO_RS: bits_per_step = BITS_PER_STEP_RS * chip->ecc.strength; mtd_set_ooblayout(mtd, &tegra_nand_oob_rs_ops); nand->config_ecc |= CONFIG_HW_ECC | CONFIG_ECC_SEL | @@ -984,7 +989,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) return -EINVAL; } break; - case NAND_ECC_BCH: + case NAND_ECC_ALGO_BCH: bits_per_step = BITS_PER_STEP_BCH * chip->ecc.strength; mtd_set_ooblayout(mtd, &tegra_nand_oob_bch_ops); nand->bch_config = BCH_ENABLE; @@ -1013,7 +1018,7 @@ static int tegra_nand_attach_chip(struct nand_chip *chip) } dev_info(ctrl->dev, "Using %s with strength %d per 512 byte step\n", - chip->ecc.algo == NAND_ECC_BCH ? "BCH" : "RS", + chip->ecc.algo == NAND_ECC_ALGO_BCH ? "BCH" : "RS", chip->ecc.strength); chip->ecc.bytes = DIV_ROUND_UP(bits_per_step, BITS_PER_BYTE); diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c index 843a8683b737..235a2f7b1bad 100644 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ b/drivers/mtd/nand/raw/tmio_nand.c @@ -410,7 +410,7 @@ static int tmio_probe(struct platform_device *dev) nand_chip->legacy.read_buf = tmio_nand_read_buf; /* set eccmode using hardware ECC */ - nand_chip->ecc.mode = NAND_ECC_HW; + 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; diff --git a/drivers/mtd/nand/raw/txx9ndfmc.c b/drivers/mtd/nand/raw/txx9ndfmc.c index 47d966871445..ef81dce6b5c4 100644 --- a/drivers/mtd/nand/raw/txx9ndfmc.c +++ b/drivers/mtd/nand/raw/txx9ndfmc.c @@ -329,7 +329,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) chip->ecc.calculate = txx9ndfmc_calculate_ecc; chip->ecc.correct = txx9ndfmc_correct_data; chip->ecc.hwctl = txx9ndfmc_enable_hwecc; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; chip->ecc.strength = 1; chip->legacy.chip_delay = 100; chip->controller = &drvdata->controller; diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index 7248c5901183..40d70f991d89 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -323,11 +323,6 @@ static inline void vf610_nfc_ecc_mode(struct vf610_nfc *nfc, int ecc_mode) CONFIG_ECC_MODE_SHIFT, ecc_mode); } -static inline void vf610_nfc_transfer_size(struct vf610_nfc *nfc, int size) -{ - vf610_nfc_write(nfc, NFC_SECTOR_SIZE, size); -} - static inline void vf610_nfc_run(struct vf610_nfc *nfc, u32 col, u32 row, u32 cmd1, u32 cmd2, u32 trfr_sz) { @@ -732,7 +727,7 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc) else vf610_nfc_clear(nfc, NFC_FLASH_CONFIG, CONFIG_16BIT); - if (nfc->chip.ecc.mode == NAND_ECC_HW) { + if (nfc->chip.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { /* Set ECC status offset in SRAM */ vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_ADDR_MASK, @@ -761,7 +756,7 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip) return -ENXIO; } - if (chip->ecc.mode != NAND_ECC_HW) + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) return 0; if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { @@ -779,7 +774,7 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip) mtd->oobsize = 64; /* Use default large page ECC layout defined in NAND core */ - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); if (chip->ecc.strength == 32) { nfc->ecc_mode = ECC_60_BYTE; chip->ecc.bytes = 60; @@ -852,8 +847,10 @@ static int vf610_nfc_probe(struct platform_device *pdev) } of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev); - if (!of_id) - return -ENODEV; + if (!of_id) { + err = -ENODEV; + goto err_disable_clk; + } nfc->variant = (enum vf610_nfc_variant)of_id->data; diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c index 29255476afdb..f2dbd63a5c1f 100644 --- a/drivers/mtd/nand/raw/xway_nand.c +++ b/drivers/mtd/nand/raw/xway_nand.c @@ -180,8 +180,8 @@ 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.mode = NAND_ECC_SOFT; - data->chip.ecc.algo = NAND_ECC_HAMMING; + data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + data->chip.ecc.algo = NAND_ECC_ALGO_HAMMING; platform_set_drvdata(pdev, data); nand_set_controller_data(&data->chip, data); diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index e2c382ffc5b6..c35221794645 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -419,7 +419,7 @@ static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status) * fixed, so let's return the maximum possible value so that * wear-leveling layers move the data immediately. */ - return nand->eccreq.strength; + return nanddev_get_ecc_conf(nand)->strength; case STATUS_ECC_UNCOR_ERROR: return -EBADMSG; @@ -497,7 +497,7 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, mutex_lock(&spinand->lock); - nanddev_io_for_each_page(nand, from, ops, &iter) { + nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) { ret = spinand_select_target(spinand, iter.req.pos.target); if (ret) break; @@ -545,7 +545,7 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to, mutex_lock(&spinand->lock); - nanddev_io_for_each_page(nand, to, ops, &iter) { + nanddev_io_for_each_page(nand, NAND_PAGE_WRITE, to, ops, &iter) { ret = spinand_select_target(spinand, iter.req.pos.target); if (ret) break; @@ -902,7 +902,7 @@ int spinand_match_and_init(struct spinand_device *spinand, continue; nand->memorg = table[i].memorg; - nand->eccreq = table[i].eccreq; + nanddev_set_ecc_requirements(nand, &table[i].eccreq); spinand->eccinfo = table[i].eccinfo; spinand->flags = table[i].flags; spinand->id.len = 1 + table[i].devid.len; @@ -1090,8 +1090,8 @@ static int spinand_init(struct spinand_device *spinand) mtd->oobavail = ret; /* Propagate ECC information to mtd_info */ - mtd->ecc_strength = nand->eccreq.strength; - mtd->ecc_step_size = nand->eccreq.step_size; + mtd->ecc_strength = nanddev_get_ecc_conf(nand)->strength; + mtd->ecc_step_size = nanddev_get_ecc_conf(nand)->step_size; return 0; diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index d219c970042a..33c67403c4aa 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -21,7 +21,7 @@ #define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4) static SPINAND_OP_VARIANTS(read_cache_variants, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), @@ -29,7 +29,7 @@ static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); static SPINAND_OP_VARIANTS(read_cache_variants_f, - SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), @@ -132,6 +132,35 @@ static const struct mtd_ooblayout_ops gd5fxgq4_variant2_ooblayout = { .free = gd5fxgq4_variant2_ooblayout_free, }; +static int gd5fxgq4xc_ooblayout_256_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 128; + oobregion->length = 128; + + return 0; +} + +static int gd5fxgq4xc_ooblayout_256_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 1; + oobregion->length = 127; + + return 0; +} + +static const struct mtd_ooblayout_ops gd5fxgq4xc_oob_256_ops = { + .ecc = gd5fxgq4xc_ooblayout_256_ecc, + .free = gd5fxgq4xc_ooblayout_256_free, +}; + static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { @@ -202,7 +231,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - 0, + SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), SPINAND_INFO("GD5F2GQ4xA", @@ -212,7 +241,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - 0, + SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), SPINAND_INFO("GD5F4GQ4xA", @@ -222,9 +251,29 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - 0, + SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4RC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xa4, 0x68), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, + gd5fxgq4ufxxg_ecc_get_status)), + SPINAND_INFO("GD5F4GQ4UC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb4, 0x68), + NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgq4xc_oob_256_ops, + gd5fxgq4ufxxg_ecc_get_status)), SPINAND_INFO("GD5F1GQ4UExxG", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -232,7 +281,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - 0, + SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), SPINAND_INFO("GD5F1GQ4UFxxG", @@ -242,7 +291,7 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, &write_cache_variants, &update_cache_variants), - 0, + SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, gd5fxgq4ufxxg_ecc_get_status)), }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 0f900f3aa21a..8e801e4c3a00 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -84,10 +84,11 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, * data around if it's not necessary. */ if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) - return nand->eccreq.strength; + return nanddev_get_ecc_conf(nand)->strength; - if (WARN_ON(eccsr > nand->eccreq.strength || !eccsr)) - return nand->eccreq.strength; + if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength || + !eccsr)) + return nanddev_get_ecc_conf(nand)->strength; return eccsr; @@ -118,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("MX31LF1GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), + NAND_MEMORG(1, 2048, 64, 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_HAS_QE_BIT*/, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), + SPINAND_INFO("MX31UF1GE4BC", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9e), + NAND_MEMORG(1, 2048, 64, 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_HAS_QE_BIT*/, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), }; static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index bc801d83343e..21fde2875674 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -90,12 +90,12 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, * data around if it's not necessary. */ if (spi_mem_exec_op(spinand->spimem, &op)) - return nand->eccreq.strength; + return nanddev_get_ecc_conf(nand)->strength; mbf >>= 4; - if (WARN_ON(mbf > nand->eccreq.strength || !mbf)) - return nand->eccreq.strength; + if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf)) + return nanddev_get_ecc_conf(nand)->strength; return mbf; diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index f98363c9b363..e72354322f62 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -12,7 +12,7 @@ config MTD_BCM47XX_PARTS boards. config MTD_BCM63XX_PARTS - tristate "BCM63XX CFE partitioning parser" + bool "BCM63XX CFE partitioning parser" depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST select CRC32 select MTD_PARSER_IMAGETAG diff --git a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c index c72aa1ab71ad..555fe55d14ae 100644 --- a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c @@ -73,6 +73,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, + { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index f97f3d127575..9203abaac229 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -50,7 +50,7 @@ static const struct flash_info macronix_parts[] = { { "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) }, { "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) }, { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, - { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, SECT_4K) }, { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, { "mx25r1635f", INFO(0xc22815, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 6dcde15fb1aa..e5dfa786f190 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -63,6 +63,15 @@ static const struct flash_info winbond_parts[] = { { "w25q32jwm", INFO(0xef8016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q64jwm", INFO(0xef8017, 0, 64 * 1024, 128, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q128jwm", INFO(0xef8018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { "w25q256jwm", INFO(0xef8019, 0, 64 * 1024, 512, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, |