diff options
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 168 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 30 | ||||
-rw-r--r-- | drivers/mtd/nand/bbt.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/core.c | 131 | ||||
-rw-r--r-- | drivers/mtd/nand/ecc-sw-bch.c | 406 | ||||
-rw-r--r-- | drivers/mtd/nand/ecc-sw-hamming.c | 660 | ||||
-rw-r--r-- | drivers/mtd/nand/ecc.c | 697 | ||||
-rw-r--r-- | drivers/mtd/nand/nand-bb.c | 10 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bch.c | 219 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_imx.c | 1501 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_s3c24xx.c | 660 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Kconfig | 175 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Makefile | 23 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/Makefile | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/atmel_nand_ecc.h (renamed from drivers/mtd/nand/atmel_nand_ecc.h) | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/legacy.c (renamed from drivers/mtd/nand/atmel_nand.c) | 57 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/nand-controller.c | 2049 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/pmecc.c | 993 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/atmel/pmecc.h | 70 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/denali.h (renamed from drivers/mtd/nand/denali.h) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/fsl_ifc.h (renamed from drivers/mtd/nand/fsl_ifc.h) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/internals.h (renamed from drivers/mtd/nand/internals.h) | 7 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxc_nand.c | 1750 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_amd.c (renamed from drivers/mtd/nand/nand_amd.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_base.c (renamed from drivers/mtd/nand/nand_base.c) | 1649 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_bbt.c (renamed from drivers/mtd/nand/nand_bbt.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_denali.c (renamed from drivers/mtd/nand/nand_denali.c) | 5 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_denali_dt.c (renamed from drivers/mtd/nand/nand_denali_dt.c) | 88 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_ecc.c (renamed from drivers/mtd/nand/nand_ecc.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_esmt.c (renamed from drivers/mtd/nand/nand_esmt.c) | 15 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_fsl_ifc.c (renamed from drivers/mtd/nand/nand_fsl_ifc.c) | 19 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_hynix.c (renamed from drivers/mtd/nand/nand_hynix.c) | 59 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_ids.c (renamed from drivers/mtd/nand/nand_ids.c) | 0 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_jedec.c (renamed from drivers/mtd/nand/nand_jedec.c) | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_legacy.c (renamed from drivers/mtd/nand/nand_legacy.c) | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_macronix.c (renamed from drivers/mtd/nand/nand_macronix.c) | 42 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_micron.c (renamed from drivers/mtd/nand/nand_micron.c) | 24 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_mrvl_nfc.c (renamed from drivers/mtd/nand/nand_mrvl_nfc.c) | 26 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_mxs.c (renamed from drivers/mtd/nand/nand_mxs.c) | 596 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_omap_bch_decoder.c (renamed from drivers/mtd/nand/nand_omap_bch_decoder.c) | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_omap_bch_decoder.h (renamed from drivers/mtd/nand/nand_omap_bch_decoder.h) | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_omap_gpmc.c (renamed from drivers/mtd/nand/nand_omap_gpmc.c) | 22 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_onfi.c (renamed from drivers/mtd/nand/nand_onfi.c) | 30 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_orion.c (renamed from drivers/mtd/nand/nand_orion.c) | 15 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_samsung.c (renamed from drivers/mtd/nand/nand_samsung.c) | 22 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_timings.c (renamed from drivers/mtd/nand/nand_timings.c) | 370 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nand_toshiba.c (renamed from drivers/mtd/nand/nand_toshiba.c) | 27 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/nomadik_nand.c (renamed from drivers/mtd/nand/nomadik_nand.c) | 21 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/omap_elm.c (renamed from drivers/mtd/nand/omap_elm.c) | 7 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/stm32_fmc2_nand.c | 1354 |
50 files changed, 10353 insertions, 3674 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index cf9f1fdc0c..4c19718467 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,154 +1,40 @@ -menuconfig NAND - bool "NAND support" - help - This enables support for accessing all type of NAND flash - devices. For further information see - <http://www.linux-mtd.infradead.org/doc/nand.html>. - -if NAND - -config MTD_NAND_ECC_SOFT - bool - prompt "Support software ecc" +# SPDX-License-Identifier: GPL-2.0-only -config MTD_NAND_ECC_SW_BCH - select BCH - depends on MTD_NAND_ECC_SOFT - bool - prompt "Support software BCH ecc" - -config NAND_ECC_HW_SYNDROME - bool - prompt "Support syndrome hardware ecc controllers" - -config NAND_ALLOW_ERASE_BAD - bool - depends on MTD_WRITE - prompt "Add device parameter to allow erasing bad blocks" - help - This adds a 'erasebad' device parameter to nand devices. When set - to '1' it will be allowed to erase bad blocks. This is a potientially - dangerous operation, so if unsure say no to this option. +menu "NAND" -config NAND_IMX +config MTD_NAND_CORE bool - prompt "i.MX21 to 53 NAND driver aka 'mxc', for NFC" - depends on ARCH_IMX - help - Support for NAND flash on Freescale/NXP i.MX devices. This is for the - "MXC" series: i.MX21/25/27/31/35/51/53. - This is not for the "MXS" series i.MX processors (23 & 28), or i.MX6 - and later, which use the GPMI NAND controller from the MXS series. - See the i.MX 'mxs' driver for those chips. +source "drivers/mtd/nand/raw/Kconfig" -config NAND_FSL_IFC - bool - prompt "FSL IFC NAND driver" - depends on ARCH_LAYERSCAPE - help - Freescale IFC NAND driver for various chips. +menu "ECC engine support" -config NAND_MXS +config MTD_NAND_ECC bool - select STMP_DEVICE - prompt "i.MX23/28 & 6+ NAND driver aka 'mxs', for GPMI" - depends on MXS_APBH_DMA - help - Support for NAND flash on Freescale/NXP i.MX devices. This is for the - "MXS" series: i.MX23/28 and all i.MX6 and later SoCs. - - This is not for the "MXC" series of i.MX processors in the i.MX21 to - i.MX53 range. See the i.MX "mxc" driver for those chips. + select MTD_NAND_CORE -config NAND_OMAP_GPMC - tristate "NAND Flash Support for GPMC based OMAP platforms" - depends on OMAP_GPMC - help - Support for NAND flash using GPMC. GPMC is a common memory - interface found on Texas Instrument's OMAP platforms - -config MTD_NAND_OMAP_ELM - bool "Support for ELM (Error Location Module) on OMAP platforms" - depends on NAND_OMAP_GPMC || COMPILE_TEST - help - This config enables the ELM hardware engine, which can be used to - locate and correct errors when using BCH ECC scheme. This offloads - the cpu from doing ECC error searching and correction. However some - legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine - so this is optional for them. - -config NAND_ORION - bool - prompt "Marvell Orion NAND driver" - depends on ARM && (ARCH_KIRKWOOD || COMPILE_TEST) +config MTD_NAND_ECC_SW_HAMMING + bool "Software Hamming ECC engine" + default y if MTD_RAW_NAND + select MTD_NAND_ECC help - Support for the Orion NAND controller, present in Kirkwood SoCs. + This enables support for software Hamming error + correction. This correction can correct up to 1 bit error + per chunk and detect up to 2 bit errors. While it used to be + widely used with old parts, newer NAND chips usually require + more strength correction and in this case BCH or RS will be + preferred. -config NAND_MRVL_NFC - bool - prompt "Marvell PXA3xx NAND driver" - depends on ARCH_ARMADA_370 || ARCH_ARMADA_XP || ARCH_PXA3XX || COMPILE_TEST - help - Support for the PXA3xx NAND controller, present in Armada 370/XP and - PXA3xx SoCs. - -config NAND_ATMEL - bool - prompt "Atmel (AT91SAM9xxx) NAND driver" - depends on ARCH_AT91 - -config NAND_ATMEL_PMECC - bool - prompt "PMECC support" - depends on NAND_ATMEL || COMPILE_TEST - help - Support for PMECC present on the SoC sam9x5 and sam9n12 - -config NAND_S3C24XX - bool - prompt "Samsung S3C24XX NAND driver" - depends on ARCH_S3C24xx - help - Add support for processor's NAND device controller. - -config MTD_NAND_ECC_SW_HAMMING_SMC - bool "NAND ECC Smart Media byte order" +config MTD_NAND_ECC_SW_BCH + bool "Software BCH ECC engine" + select BCH + select MTD_NAND_ECC default n help - Software ECC according to the Smart Media Specification. - The original Linux implementation had byte 0 and 1 swapped. - -config MTD_NAND_NOMADIK - tristate "ST Nomadik 8815 NAND support" - depends on ARCH_NOMADIK - help - Driver for the NAND flash controller on the Nomadik, with ECC. - -config MTD_NAND_DENALI - tristate "Support Denali NAND controller" - depends on HAS_DMA - help - Enable support for the Denali NAND controller. This should be - combined with either the PCI or platform drivers to provide device - registration. - -config MTD_NAND_DENALI_DT - tristate "Support Denali NAND controller as a DT device" - depends on HAVE_CLK && MTD_NAND_DENALI - help - Enable the driver for NAND flash on platforms using a Denali NAND - controller as a DT device. - -if MTD_NAND_DENALI - -config MTD_NAND_DENALI_TIMING_MODE - int "Overrides default ONFI timing mode." - default -1 - range -1 5 - help - -1 indicates use default timings - -endif + This enables support for software BCH error correction. Binary BCH + codes are more powerful and cpu intensive than traditional Hamming + ECC codes. They are used with NAND devices requiring more than 1 bit + of error correction. -endif +endmenu +endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 4fd14ddd63..617a9c2638 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,25 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only -# Generic NAND options -obj-$(CONFIG_NAND) += nand_ecc.o -obj-$(CONFIG_MTD_NAND_ECC_SW_BCH) += nand_bch.o -obj-$(CONFIG_NAND) += nand_ids.o -obj-$(CONFIG_NAND) += nand_base.o nand-bb.o nand_timings.o -obj-$(CONFIG_NAND) += nand_legacy.o nand_onfi.o nand_amd.o -obj-$(CONFIG_NAND) += nand_esmt.o nand_hynix.o nand_macronix.o -obj-$(CONFIG_NAND) += nand_micron.o nand_samsung.o nand_toshiba.o -obj-$(CONFIG_NAND) += nand_jedec.o core.o bbt.o -obj-$(CONFIG_NAND) += nand_bbt.o +obj-$(CONFIG_MTD_NAND_CORE) += core.o bbt.o nand-bb.o -obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o -obj-$(CONFIG_NAND_IMX) += nand_imx.o -obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o -obj-$(CONFIG_MTD_NAND_OMAP_ELM) += omap_elm.o -obj-$(CONFIG_NAND_ORION) += nand_orion.o -obj-$(CONFIG_NAND_MRVL_NFC) += nand_mrvl_nfc.o -obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o -obj-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o -pbl-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o -obj-$(CONFIG_NAND_MXS) += nand_mxs.o -obj-$(CONFIG_MTD_NAND_DENALI) += nand_denali.o -obj-$(CONFIG_MTD_NAND_DENALI_DT) += nand_denali_dt.o -obj-$(CONFIG_NAND_FSL_IFC) += nand_fsl_ifc.o +obj-y += raw/ + +obj-$(CONFIG_MTD_NAND_ECC) += ecc.o +obj-$(CONFIG_MTD_NAND_ECC_SW_HAMMING) += ecc-sw-hamming.o +obj-$(CONFIG_MTD_NAND_ECC_SW_BCH) += ecc-sw-bch.o diff --git a/drivers/mtd/nand/bbt.c b/drivers/mtd/nand/bbt.c index 8a78f9046f..ad83f2fad3 100644 --- a/drivers/mtd/nand/bbt.c +++ b/drivers/mtd/nand/bbt.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017 Free Electrons * diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c index ba22662e2f..2d165f9474 100644 --- a/drivers/mtd/nand/core.c +++ b/drivers/mtd/nand/core.c @@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(nanddev_isreserved); * * Return: 0 in case of success, a negative error code otherwise. */ -int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) +static int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) { if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { pr_warn("attempt to erase a bad/reserved block @%llx\n", @@ -133,7 +133,6 @@ int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) return nand->ops->erase(nand, pos); } -EXPORT_SYMBOL_GPL(nanddev_erase); /** * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices @@ -208,6 +207,134 @@ int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len) EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks); /** + * nanddev_get_ecc_engine() - Find and get a suitable ECC engine + * @nand: NAND device + */ +static int nanddev_get_ecc_engine(struct nand_device *nand) +{ + int engine_type; + + /* Read the user desires in terms of ECC engine/configuration */ + of_get_nand_ecc_user_config(nand); + + engine_type = nand->ecc.user_conf.engine_type; + if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID) + engine_type = nand->ecc.defaults.engine_type; + + switch (engine_type) { + case NAND_ECC_ENGINE_TYPE_NONE: + return 0; + case NAND_ECC_ENGINE_TYPE_SOFT: + nand->ecc.engine = nand_ecc_get_sw_engine(nand); + break; + case NAND_ECC_ENGINE_TYPE_ON_DIE: + nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand); + break; + case NAND_ECC_ENGINE_TYPE_ON_HOST: + nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand); + if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER) + return -EPROBE_DEFER; + break; + default: + pr_err("Missing ECC engine type\n"); + } + + if (!nand->ecc.engine) + return -EINVAL; + + return 0; +} + +/** + * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine + * @nand: NAND device + */ +static int nanddev_put_ecc_engine(struct nand_device *nand) +{ + switch (nand->ecc.ctx.conf.engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: + nand_ecc_put_on_host_hw_engine(nand); + break; + case NAND_ECC_ENGINE_TYPE_NONE: + case NAND_ECC_ENGINE_TYPE_SOFT: + case NAND_ECC_ENGINE_TYPE_ON_DIE: + default: + break; + } + + return 0; +} + +/** + * nanddev_find_ecc_configuration() - Find a suitable ECC configuration + * @nand: NAND device + */ +static int nanddev_find_ecc_configuration(struct nand_device *nand) +{ + int ret; + + if (!nand->ecc.engine) + return -ENOTSUPP; + + ret = nand_ecc_init_ctx(nand); + if (ret) + return ret; + + if (!nand_ecc_is_strong_enough(nand)) + pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", + nand->mtd.name); + + return 0; +} + +/** + * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip + * @nand: NAND device + */ +int nanddev_ecc_engine_init(struct nand_device *nand) +{ + int ret; + + /* Look for the ECC engine to use */ + ret = nanddev_get_ecc_engine(nand); + if (ret) { + if (ret != -EPROBE_DEFER) + pr_err("No ECC engine found\n"); + + return ret; + } + + /* No ECC engine requested */ + if (!nand->ecc.engine) + return 0; + + /* Configure the engine: balance user input and chip requirements */ + ret = nanddev_find_ecc_configuration(nand); + if (ret) { + pr_err("No suitable ECC configuration\n"); + nanddev_put_ecc_engine(nand); + + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init); + +/** + * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations + * @nand: NAND device + */ +void nanddev_ecc_engine_cleanup(struct nand_device *nand) +{ + if (nand->ecc.engine) + nand_ecc_cleanup_ctx(nand); + + nanddev_put_ecc_engine(nand); +} +EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup); + +/** * nanddev_init() - Initialize a NAND device * @nand: NAND device * @ops: NAND device operations diff --git a/drivers/mtd/nand/ecc-sw-bch.c b/drivers/mtd/nand/ecc-sw-bch.c new file mode 100644 index 0000000000..29cf562aa9 --- /dev/null +++ b/drivers/mtd/nand/ecc-sw-bch.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file provides ECC correction for more than 1 bit per block of data, + * using binary BCH codes. It relies on the generic BCH library lib/bch.c. + * + * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/export.h> +#include <linux/bitops.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand-ecc-sw-bch.h> + +/** + * nand_ecc_sw_bch_calculate - Calculate the ECC corresponding to a data block + * @nand: NAND device + * @buf: Input buffer with raw data + * @code: Output buffer with ECC + */ +int nand_ecc_sw_bch_calculate(struct nand_device *nand, + const unsigned char *buf, unsigned char *code) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int i; + + memset(code, 0, engine_conf->code_size); + bch_encode(engine_conf->bch, buf, nand->ecc.ctx.conf.step_size, code); + + /* apply mask so that an erased page is a valid codeword */ + for (i = 0; i < engine_conf->code_size; i++) + code[i] ^= engine_conf->eccmask[i]; + + return 0; +} +EXPORT_SYMBOL(nand_ecc_sw_bch_calculate); + +/** + * nand_ecc_sw_bch_correct - Detect, correct and report bit error(s) + * @nand: NAND device + * @buf: Raw data read from the chip + * @read_ecc: ECC bytes from the chip + * @calc_ecc: ECC calculated from the raw data + * + * Detect and correct bit errors for a data block. + */ +int nand_ecc_sw_bch_correct(struct nand_device *nand, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int step_size = nand->ecc.ctx.conf.step_size; + unsigned int *errloc = engine_conf->errloc; + int i, count; + + count = bch_decode(engine_conf->bch, NULL, step_size, read_ecc, + calc_ecc, NULL, errloc); + if (count > 0) { + for (i = 0; i < count; i++) { + if (errloc[i] < (step_size * 8)) + /* The error is in the data area: correct it */ + buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); + + /* Otherwise the error is in the ECC area: nothing to do */ + pr_debug("%s: corrected bitflip %u\n", __func__, + errloc[i]); + } + } else if (count < 0) { + pr_err("ECC unrecoverable error\n"); + count = -EBADMSG; + } + + return count; +} +EXPORT_SYMBOL(nand_ecc_sw_bch_correct); + +/** + * nand_ecc_sw_bch_cleanup - Cleanup software BCH ECC resources + * @nand: NAND device + */ +static void nand_ecc_sw_bch_cleanup(struct nand_device *nand) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + + bch_free(engine_conf->bch); + kfree(engine_conf->errloc); + kfree(engine_conf->eccmask); +} + +/** + * nand_ecc_sw_bch_init - Initialize software BCH ECC engine + * @nand: NAND device + * + * Returns: a pointer to a new NAND BCH control structure, or NULL upon failure + * + * Initialize NAND BCH error correction. @nand.ecc parameters 'step_size' and + * 'bytes' are used to compute the following BCH parameters: + * m, the Galois field order + * t, the error correction capability + * 'bytes' should be equal to the number of bytes required to store m * t + * bits, where m is such that 2^m - 1 > step_size * 8. + * + * Example: to configure 4 bit correction per 512 bytes, you should pass + * step_size = 512 (thus, m = 13 is the smallest integer such that 2^m - 1 > 512 * 8) + * bytes = 7 (7 bytes are required to store m * t = 13 * 4 = 52 bits) + */ +static int nand_ecc_sw_bch_init(struct nand_device *nand) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int eccsize = nand->ecc.ctx.conf.step_size; + unsigned int eccbytes = engine_conf->code_size; + unsigned int m, t, i; + unsigned char *erased_page; + int ret; + + m = fls(1 + (8 * eccsize)); + t = (eccbytes * 8) / m; + + engine_conf->bch = bch_init(m, t, 0, false); + if (!engine_conf->bch) + return -EINVAL; + + engine_conf->eccmask = kzalloc(eccbytes, GFP_KERNEL); + engine_conf->errloc = kmalloc_array(t, sizeof(*engine_conf->errloc), + GFP_KERNEL); + if (!engine_conf->eccmask || !engine_conf->errloc) { + ret = -ENOMEM; + goto cleanup; + } + + /* Compute and store the inverted ECC of an erased step */ + erased_page = kmalloc(eccsize, GFP_KERNEL); + if (!erased_page) { + ret = -ENOMEM; + goto cleanup; + } + + memset(erased_page, 0xff, eccsize); + bch_encode(engine_conf->bch, erased_page, eccsize, + engine_conf->eccmask); + kfree(erased_page); + + for (i = 0; i < eccbytes; i++) + engine_conf->eccmask[i] ^= 0xff; + + /* Verify that the number of code bytes has the expected value */ + if (engine_conf->bch->ecc_bytes != eccbytes) { + pr_err("Invalid number of ECC bytes: %u, expected: %u\n", + eccbytes, engine_conf->bch->ecc_bytes); + ret = -EINVAL; + goto cleanup; + } + + /* Sanity checks */ + if (8 * (eccsize + eccbytes) >= (1 << m)) { + pr_err("ECC step size is too large (%u)\n", eccsize); + ret = -EINVAL; + goto cleanup; + } + + return 0; + +cleanup: + nand_ecc_sw_bch_cleanup(nand); + + return ret; +} + +int nand_ecc_sw_bch_init_ctx(struct nand_device *nand) +{ + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); + struct nand_ecc_sw_bch_conf *engine_conf; + unsigned int code_size = 0, nsteps; + int ret; + + /* Only large page NAND chips may use BCH */ + if (mtd->oobsize < 64) { + pr_err("BCH cannot be used with small page NAND chips\n"); + return -EINVAL; + } + + if (!mtd->ooblayout) + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); + + conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + conf->algo = NAND_ECC_ALGO_BCH; + conf->step_size = nand->ecc.user_conf.step_size; + conf->strength = nand->ecc.user_conf.strength; + + /* + * Board driver should supply ECC size and ECC strength + * values to select how many bits are correctable. + * Otherwise, default to 512 bytes for large page devices and 256 for + * small page devices. + */ + if (!conf->step_size) { + if (mtd->oobsize >= 64) + conf->step_size = 512; + else + conf->step_size = 256; + + conf->strength = 4; + } + + nsteps = mtd->writesize / conf->step_size; + + /* Maximize */ + if (nand->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { + conf->step_size = 1024; + nsteps = mtd->writesize / conf->step_size; + /* Reserve 2 bytes for the BBM */ + code_size = (mtd->oobsize - 2) / nsteps; + conf->strength = code_size * 8 / fls(8 * conf->step_size); + } + + if (!code_size) + code_size = DIV_ROUND_UP(conf->strength * + fls(8 * conf->step_size), 8); + + if (!conf->strength) + conf->strength = (code_size * 8) / fls(8 * conf->step_size); + + if (!code_size && !conf->strength) { + pr_err("Missing ECC parameters\n"); + return -EINVAL; + } + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand); + if (ret) + goto free_engine_conf; + + engine_conf->code_size = code_size; + engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!engine_conf->calc_buf || !engine_conf->code_buf) { + ret = -ENOMEM; + goto free_bufs; + } + + nand->ecc.ctx.priv = engine_conf; + nand->ecc.ctx.nsteps = nsteps; + nand->ecc.ctx.total = nsteps * code_size; + + ret = nand_ecc_sw_bch_init(nand); + if (ret) + goto free_bufs; + + /* Verify the layout validity */ + if (mtd_ooblayout_count_eccbytes(mtd) != + nand->ecc.ctx.nsteps * engine_conf->code_size) { + pr_err("Invalid ECC layout\n"); + ret = -EINVAL; + goto cleanup_bch_ctx; + } + + return 0; + +cleanup_bch_ctx: + nand_ecc_sw_bch_cleanup(nand); +free_bufs: + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); +free_engine_conf: + kfree(engine_conf); + + return ret; +} +EXPORT_SYMBOL(nand_ecc_sw_bch_init_ctx); + +void nand_ecc_sw_bch_cleanup_ctx(struct nand_device *nand) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + + if (engine_conf) { + nand_ecc_sw_bch_cleanup(nand); + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); + kfree(engine_conf); + } +} +EXPORT_SYMBOL(nand_ecc_sw_bch_cleanup_ctx); + +static int nand_ecc_sw_bch_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int eccbytes = engine_conf->code_size; + int eccsteps = nand->ecc.ctx.nsteps; + int total = nand->ecc.ctx.total; + u8 *ecccalc = engine_conf->calc_buf; + const u8 *data; + int i; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + nand_ecc_tweak_req(&engine_conf->req_ctx, req); + + /* No more preparation for page read */ + if (req->type == NAND_PAGE_READ) + return 0; + + /* Preparation for page write: derive the ECC bytes and place them */ + for (i = 0, data = req->databuf.out; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]); + + return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out, + 0, total); +} + +static int nand_ecc_sw_bch_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int total = nand->ecc.ctx.total; + int eccbytes = engine_conf->code_size; + int eccsteps = nand->ecc.ctx.nsteps; + u8 *ecccalc = engine_conf->calc_buf; + u8 *ecccode = engine_conf->code_buf; + unsigned int max_bitflips = 0; + u8 *data = req->databuf.in; + int i, ret; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + /* No more preparation for page write */ + if (req->type == NAND_PAGE_WRITE) { + nand_ecc_restore_req(&engine_conf->req_ctx, req); + return 0; + } + + /* Finish a page read: retrieve the (raw) ECC bytes*/ + ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0, + total); + if (ret) + return ret; + + /* Calculate the ECC bytes */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_bch_calculate(nand, data, &ecccalc[i]); + + /* Finish a page read: compare and correct */ + for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) { + int stat = nand_ecc_sw_bch_correct(nand, data, + &ecccode[i], + &ecccalc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + nand_ecc_restore_req(&engine_conf->req_ctx, req); + + return max_bitflips; +} + +static struct nand_ecc_engine_ops nand_ecc_sw_bch_engine_ops = { + .init_ctx = nand_ecc_sw_bch_init_ctx, + .cleanup_ctx = nand_ecc_sw_bch_cleanup_ctx, + .prepare_io_req = nand_ecc_sw_bch_prepare_io_req, + .finish_io_req = nand_ecc_sw_bch_finish_io_req, +}; + +static struct nand_ecc_engine nand_ecc_sw_bch_engine = { + .ops = &nand_ecc_sw_bch_engine_ops, +}; + +struct nand_ecc_engine *nand_ecc_sw_bch_get_engine(void) +{ + return &nand_ecc_sw_bch_engine; +} +EXPORT_SYMBOL(nand_ecc_sw_bch_get_engine); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); +MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/ecc-sw-hamming.c b/drivers/mtd/nand/ecc-sw-hamming.c new file mode 100644 index 0000000000..f773a25a3e --- /dev/null +++ b/drivers/mtd/nand/ecc-sw-hamming.c @@ -0,0 +1,660 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This file contains an ECC algorithm that detects and corrects 1 bit + * errors in a 256 byte block of data. + * + * Copyright © 2008 Koninklijke Philips Electronics NV. + * Author: Frans Meulenbroeks + * + * Completely replaces the previous ECC implementation which was written by: + * Steven J. Hill (sjhill@realitydiluted.com) + * Thomas Gleixner (tglx@linutronix.de) + * + * Information on how this algorithm works and how it was developed + * can be found in Documentation/driver-api/mtd/nand_ecc.rst + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand-ecc-sw-hamming.h> +#include <linux/slab.h> +#include <asm/byteorder.h> + +/* + * invparity is a 256 byte table that contains the odd parity + * for each byte. So if the number of bits in a byte is even, + * the array element is 1, and when the number of bits is odd + * the array eleemnt is 0. + */ +static const char invparity[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +/* + * bitsperbyte contains the number of bits per byte + * this is only used for testing and repairing parity + * (a precalculated value slightly improves performance) + */ +static const char bitsperbyte[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; + +/* + * addressbits is a lookup table to filter out the bits from the xor-ed + * ECC data that identify the faulty location. + * this is only used for repairing parity + * see the comments in nand_ecc_sw_hamming_correct for more details + */ +static const char addressbits[256] = { + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f +}; + +int ecc_sw_hamming_calculate(const unsigned char *buf, unsigned int step_size, + unsigned char *code, bool sm_order) +{ + const u32 *bp = (uint32_t *)buf; + const u32 eccsize_mult = (step_size == 256) ? 1 : 2; + /* current value in buffer */ + u32 cur; + /* rp0..rp17 are the various accumulated parities (per byte) */ + u32 rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9, rp10, rp11, rp12, + rp13, rp14, rp15, rp16, rp17; + /* Cumulative parity for all data */ + u32 par; + /* Cumulative parity at the end of the loop (rp12, rp14, rp16) */ + u32 tmppar; + int i; + + par = 0; + rp4 = 0; + rp6 = 0; + rp8 = 0; + rp10 = 0; + rp12 = 0; + rp14 = 0; + rp16 = 0; + rp17 = 0; + + /* + * The loop is unrolled a number of times; + * This avoids if statements to decide on which rp value to update + * Also we process the data by longwords. + * Note: passing unaligned data might give a performance penalty. + * It is assumed that the buffers are aligned. + * tmppar is the cumulative sum of this iteration. + * needed for calculating rp12, rp14, rp16 and par + * also used as a performance improvement for rp6, rp8 and rp10 + */ + for (i = 0; i < eccsize_mult << 2; i++) { + cur = *bp++; + tmppar = cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= tmppar; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= tmppar; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp10 ^= tmppar; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= cur; + + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + + par ^= tmppar; + if ((i & 0x1) == 0) + rp12 ^= tmppar; + if ((i & 0x2) == 0) + rp14 ^= tmppar; + if (eccsize_mult == 2 && (i & 0x4) == 0) + rp16 ^= tmppar; + } + + /* + * handle the fact that we use longword operations + * we'll bring rp4..rp14..rp16 back to single byte entities by + * shifting and xoring first fold the upper and lower 16 bits, + * then the upper and lower 8 bits. + */ + rp4 ^= (rp4 >> 16); + rp4 ^= (rp4 >> 8); + rp4 &= 0xff; + rp6 ^= (rp6 >> 16); + rp6 ^= (rp6 >> 8); + rp6 &= 0xff; + rp8 ^= (rp8 >> 16); + rp8 ^= (rp8 >> 8); + rp8 &= 0xff; + rp10 ^= (rp10 >> 16); + rp10 ^= (rp10 >> 8); + rp10 &= 0xff; + rp12 ^= (rp12 >> 16); + rp12 ^= (rp12 >> 8); + rp12 &= 0xff; + rp14 ^= (rp14 >> 16); + rp14 ^= (rp14 >> 8); + rp14 &= 0xff; + if (eccsize_mult == 2) { + rp16 ^= (rp16 >> 16); + rp16 ^= (rp16 >> 8); + rp16 &= 0xff; + } + + /* + * we also need to calculate the row parity for rp0..rp3 + * This is present in par, because par is now + * rp3 rp3 rp2 rp2 in little endian and + * rp2 rp2 rp3 rp3 in big endian + * as well as + * rp1 rp0 rp1 rp0 in little endian and + * rp0 rp1 rp0 rp1 in big endian + * First calculate rp2 and rp3 + */ +#ifdef __BIG_ENDIAN + rp2 = (par >> 16); + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; + rp3 = par & 0xffff; + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; +#else + rp3 = (par >> 16); + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; + rp2 = par & 0xffff; + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; +#endif + + /* reduce par to 16 bits then calculate rp1 and rp0 */ + par ^= (par >> 16); +#ifdef __BIG_ENDIAN + rp0 = (par >> 8) & 0xff; + rp1 = (par & 0xff); +#else + rp1 = (par >> 8) & 0xff; + rp0 = (par & 0xff); +#endif + + /* finally reduce par to 8 bits */ + par ^= (par >> 8); + par &= 0xff; + + /* + * and calculate rp5..rp15..rp17 + * note that par = rp4 ^ rp5 and due to the commutative property + * of the ^ operator we can say: + * rp5 = (par ^ rp4); + * The & 0xff seems superfluous, but benchmarking learned that + * leaving it out gives slightly worse results. No idea why, probably + * it has to do with the way the pipeline in pentium is organized. + */ + rp5 = (par ^ rp4) & 0xff; + rp7 = (par ^ rp6) & 0xff; + rp9 = (par ^ rp8) & 0xff; + rp11 = (par ^ rp10) & 0xff; + rp13 = (par ^ rp12) & 0xff; + rp15 = (par ^ rp14) & 0xff; + if (eccsize_mult == 2) + rp17 = (par ^ rp16) & 0xff; + + /* + * Finally calculate the ECC bits. + * Again here it might seem that there are performance optimisations + * possible, but benchmarks showed that on the system this is developed + * the code below is the fastest + */ + if (sm_order) { + code[0] = (invparity[rp7] << 7) | (invparity[rp6] << 6) | + (invparity[rp5] << 5) | (invparity[rp4] << 4) | + (invparity[rp3] << 3) | (invparity[rp2] << 2) | + (invparity[rp1] << 1) | (invparity[rp0]); + code[1] = (invparity[rp15] << 7) | (invparity[rp14] << 6) | + (invparity[rp13] << 5) | (invparity[rp12] << 4) | + (invparity[rp11] << 3) | (invparity[rp10] << 2) | + (invparity[rp9] << 1) | (invparity[rp8]); + } else { + code[1] = (invparity[rp7] << 7) | (invparity[rp6] << 6) | + (invparity[rp5] << 5) | (invparity[rp4] << 4) | + (invparity[rp3] << 3) | (invparity[rp2] << 2) | + (invparity[rp1] << 1) | (invparity[rp0]); + code[0] = (invparity[rp15] << 7) | (invparity[rp14] << 6) | + (invparity[rp13] << 5) | (invparity[rp12] << 4) | + (invparity[rp11] << 3) | (invparity[rp10] << 2) | + (invparity[rp9] << 1) | (invparity[rp8]); + } + + if (eccsize_mult == 1) + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + 3; + else + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + (invparity[rp17] << 1) | + (invparity[rp16] << 0); + + return 0; +} +EXPORT_SYMBOL(ecc_sw_hamming_calculate); + +/** + * nand_ecc_sw_hamming_calculate - Calculate 3-byte ECC for 256/512-byte block + * @nand: NAND device + * @buf: Input buffer with raw data + * @code: Output buffer with ECC + */ +int nand_ecc_sw_hamming_calculate(struct nand_device *nand, + const unsigned char *buf, unsigned char *code) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int step_size = nand->ecc.ctx.conf.step_size; + bool sm_order = engine_conf ? engine_conf->sm_order : false; + + return ecc_sw_hamming_calculate(buf, step_size, code, sm_order); +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_calculate); + +int ecc_sw_hamming_correct(unsigned char *buf, unsigned char *read_ecc, + unsigned char *calc_ecc, unsigned int step_size, + bool sm_order) +{ + const u32 eccsize_mult = step_size >> 8; + unsigned char b0, b1, b2, bit_addr; + unsigned int byte_addr; + + /* + * b0 to b2 indicate which bit is faulty (if any) + * we might need the xor result more than once, + * so keep them in a local var + */ + if (sm_order) { + b0 = read_ecc[0] ^ calc_ecc[0]; + b1 = read_ecc[1] ^ calc_ecc[1]; + } else { + b0 = read_ecc[1] ^ calc_ecc[1]; + b1 = read_ecc[0] ^ calc_ecc[0]; + } + + b2 = read_ecc[2] ^ calc_ecc[2]; + + /* check if there are any bitfaults */ + + /* repeated if statements are slightly more efficient than switch ... */ + /* ordered in order of likelihood */ + + if ((b0 | b1 | b2) == 0) + return 0; /* no error */ + + if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && + (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && + ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) || + (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) { + /* single bit error */ + /* + * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty + * byte, cp 5/3/1 indicate the faulty bit. + * A lookup table (called addressbits) is used to filter + * the bits from the byte they are in. + * A marginal optimisation is possible by having three + * different lookup tables. + * One as we have now (for b0), one for b2 + * (that would avoid the >> 1), and one for b1 (with all values + * << 4). However it was felt that introducing two more tables + * hardly justify the gain. + * + * The b2 shift is there to get rid of the lowest two bits. + * We could also do addressbits[b2] >> 1 but for the + * performance it does not make any difference + */ + if (eccsize_mult == 1) + byte_addr = (addressbits[b1] << 4) + addressbits[b0]; + else + byte_addr = (addressbits[b2 & 0x3] << 8) + + (addressbits[b1] << 4) + addressbits[b0]; + bit_addr = addressbits[b2 >> 2]; + /* flip the bit */ + buf[byte_addr] ^= (1 << bit_addr); + return 1; + + } + /* count nr of bits; use table lookup, faster than calculating it */ + if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) + return 1; /* error in ECC data; no action needed */ + + pr_err("%s: uncorrectable ECC error\n", __func__); + return -EBADMSG; +} +EXPORT_SYMBOL(ecc_sw_hamming_correct); + +/** + * nand_ecc_sw_hamming_correct - Detect and correct bit error(s) + * @nand: NAND device + * @buf: Raw data read from the chip + * @read_ecc: ECC bytes read from the chip + * @calc_ecc: ECC calculated from the raw data + * + * Detect and correct up to 1 bit error per 256/512-byte block. + */ +int nand_ecc_sw_hamming_correct(struct nand_device *nand, unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + unsigned int step_size = nand->ecc.ctx.conf.step_size; + bool sm_order = engine_conf ? engine_conf->sm_order : false; + + return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, step_size, + sm_order); +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_correct); + +int nand_ecc_sw_hamming_init_ctx(struct nand_device *nand) +{ + struct nand_ecc_props *conf = &nand->ecc.ctx.conf; + struct nand_ecc_sw_hamming_conf *engine_conf; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int ret; + + if (!mtd->ooblayout) { + switch (mtd->oobsize) { + case 8: + case 16: + mtd_set_ooblayout(mtd, nand_get_small_page_ooblayout()); + break; + case 64: + case 128: + mtd_set_ooblayout(mtd, + nand_get_large_page_hamming_ooblayout()); + break; + default: + return -ENOTSUPP; + } + } + + conf->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + conf->algo = NAND_ECC_ALGO_HAMMING; + conf->step_size = nand->ecc.user_conf.step_size; + conf->strength = 1; + + /* Use the strongest configuration by default */ + if (conf->step_size != 256 && conf->step_size != 512) + conf->step_size = 256; + + engine_conf = kzalloc(sizeof(*engine_conf), GFP_KERNEL); + if (!engine_conf) + return -ENOMEM; + + ret = nand_ecc_init_req_tweaking(&engine_conf->req_ctx, nand); + if (ret) + goto free_engine_conf; + + engine_conf->code_size = 3; + engine_conf->calc_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + engine_conf->code_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!engine_conf->calc_buf || !engine_conf->code_buf) { + ret = -ENOMEM; + goto free_bufs; + } + + nand->ecc.ctx.priv = engine_conf; + nand->ecc.ctx.nsteps = mtd->writesize / conf->step_size; + nand->ecc.ctx.total = nand->ecc.ctx.nsteps * engine_conf->code_size; + + return 0; + +free_bufs: + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); +free_engine_conf: + kfree(engine_conf); + + return ret; +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_init_ctx); + +void nand_ecc_sw_hamming_cleanup_ctx(struct nand_device *nand) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + + if (engine_conf) { + nand_ecc_cleanup_req_tweaking(&engine_conf->req_ctx); + kfree(engine_conf->calc_buf); + kfree(engine_conf->code_buf); + kfree(engine_conf); + } +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_cleanup_ctx); + +static int nand_ecc_sw_hamming_prepare_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int eccbytes = engine_conf->code_size; + int eccsteps = nand->ecc.ctx.nsteps; + int total = nand->ecc.ctx.total; + u8 *ecccalc = engine_conf->calc_buf; + const u8 *data; + int i; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + nand_ecc_tweak_req(&engine_conf->req_ctx, req); + + /* No more preparation for page read */ + if (req->type == NAND_PAGE_READ) + return 0; + + /* Preparation for page write: derive the ECC bytes and place them */ + for (i = 0, data = req->databuf.out; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]); + + return mtd_ooblayout_set_eccbytes(mtd, ecccalc, (void *)req->oobbuf.out, + 0, total); +} + +static int nand_ecc_sw_hamming_finish_io_req(struct nand_device *nand, + struct nand_page_io_req *req) +{ + struct nand_ecc_sw_hamming_conf *engine_conf = nand->ecc.ctx.priv; + struct mtd_info *mtd = nanddev_to_mtd(nand); + int eccsize = nand->ecc.ctx.conf.step_size; + int total = nand->ecc.ctx.total; + int eccbytes = engine_conf->code_size; + int eccsteps = nand->ecc.ctx.nsteps; + u8 *ecccalc = engine_conf->calc_buf; + u8 *ecccode = engine_conf->code_buf; + unsigned int max_bitflips = 0; + u8 *data = req->databuf.in; + int i, ret; + + /* Nothing to do for a raw operation */ + if (req->mode == MTD_OPS_RAW) + return 0; + + /* This engine does not provide BBM/free OOB bytes protection */ + if (!req->datalen) + return 0; + + /* No more preparation for page write */ + if (req->type == NAND_PAGE_WRITE) { + nand_ecc_restore_req(&engine_conf->req_ctx, req); + return 0; + } + + /* Finish a page read: retrieve the (raw) ECC bytes*/ + ret = mtd_ooblayout_get_eccbytes(mtd, ecccode, req->oobbuf.in, 0, + total); + if (ret) + return ret; + + /* Calculate the ECC bytes */ + for (i = 0; eccsteps; eccsteps--, i += eccbytes, data += eccsize) + nand_ecc_sw_hamming_calculate(nand, data, &ecccalc[i]); + + /* Finish a page read: compare and correct */ + for (eccsteps = nand->ecc.ctx.nsteps, i = 0, data = req->databuf.in; + eccsteps; + eccsteps--, i += eccbytes, data += eccsize) { + int stat = nand_ecc_sw_hamming_correct(nand, data, + &ecccode[i], + &ecccalc[i]); + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + nand_ecc_restore_req(&engine_conf->req_ctx, req); + + return max_bitflips; +} + +static struct nand_ecc_engine_ops nand_ecc_sw_hamming_engine_ops = { + .init_ctx = nand_ecc_sw_hamming_init_ctx, + .cleanup_ctx = nand_ecc_sw_hamming_cleanup_ctx, + .prepare_io_req = nand_ecc_sw_hamming_prepare_io_req, + .finish_io_req = nand_ecc_sw_hamming_finish_io_req, +}; + +static struct nand_ecc_engine nand_ecc_sw_hamming_engine = { + .ops = &nand_ecc_sw_hamming_engine_ops, +}; + +struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void) +{ + return &nand_ecc_sw_hamming_engine; +} +EXPORT_SYMBOL(nand_ecc_sw_hamming_get_engine); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>"); +MODULE_DESCRIPTION("NAND software Hamming ECC support"); diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c new file mode 100644 index 0000000000..60e1fa3cf2 --- /dev/null +++ b/drivers/mtd/nand/ecc.c @@ -0,0 +1,697 @@ +// 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 <common.h> +#include <linux/mtd/nand.h> +#include <linux/slab.h> +#include <linux/mutex.h> + +static LIST_HEAD(on_host_hw_engines); +static DEFINE_MUTEX(on_host_hw_engines_mutex); + +/** + * 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 || !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 && 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 || !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 || !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); + +/* ECC engine driver internal helpers */ +int nand_ecc_init_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_device *nand) +{ + unsigned int total_buffer_size; + + ctx->nand = nand; + + /* Let the user decide the exact length of each buffer */ + if (!ctx->page_buffer_size) + ctx->page_buffer_size = nanddev_page_size(nand); + if (!ctx->oob_buffer_size) + ctx->oob_buffer_size = nanddev_per_page_oobsize(nand); + + total_buffer_size = ctx->page_buffer_size + ctx->oob_buffer_size; + + ctx->spare_databuf = kzalloc(total_buffer_size, GFP_KERNEL); + if (!ctx->spare_databuf) + return -ENOMEM; + + ctx->spare_oobbuf = ctx->spare_databuf + ctx->page_buffer_size; + + return 0; +} +EXPORT_SYMBOL_GPL(nand_ecc_init_req_tweaking); + +void nand_ecc_cleanup_req_tweaking(struct nand_ecc_req_tweak_ctx *ctx) +{ + kfree(ctx->spare_databuf); +} +EXPORT_SYMBOL_GPL(nand_ecc_cleanup_req_tweaking); + +/* + * Ensure data and OOB area is fully read/written otherwise the correction might + * not work as expected. + */ +void nand_ecc_tweak_req(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_page_io_req *req) +{ + struct nand_device *nand = ctx->nand; + struct nand_page_io_req *orig, *tweak; + + /* Save the original request */ + ctx->orig_req = *req; + ctx->bounce_data = false; + ctx->bounce_oob = false; + orig = &ctx->orig_req; + tweak = req; + + /* Ensure the request covers the entire page */ + if (orig->datalen < nanddev_page_size(nand)) { + ctx->bounce_data = true; + tweak->dataoffs = 0; + tweak->datalen = nanddev_page_size(nand); + tweak->databuf.in = ctx->spare_databuf; + memset(tweak->databuf.in, 0xFF, ctx->page_buffer_size); + } + + if (orig->ooblen < nanddev_per_page_oobsize(nand)) { + ctx->bounce_oob = true; + tweak->ooboffs = 0; + tweak->ooblen = nanddev_per_page_oobsize(nand); + tweak->oobbuf.in = ctx->spare_oobbuf; + memset(tweak->oobbuf.in, 0xFF, ctx->oob_buffer_size); + } + + /* Copy the data that must be writen in the bounce buffers, if needed */ + if (orig->type == NAND_PAGE_WRITE) { + if (ctx->bounce_data) + memcpy((void *)tweak->databuf.out + orig->dataoffs, + orig->databuf.out, orig->datalen); + + if (ctx->bounce_oob) + memcpy((void *)tweak->oobbuf.out + orig->ooboffs, + orig->oobbuf.out, orig->ooblen); + } +} +EXPORT_SYMBOL_GPL(nand_ecc_tweak_req); + +void nand_ecc_restore_req(struct nand_ecc_req_tweak_ctx *ctx, + struct nand_page_io_req *req) +{ + struct nand_page_io_req *orig, *tweak; + + orig = &ctx->orig_req; + tweak = req; + + /* Restore the data read from the bounce buffers, if needed */ + if (orig->type == NAND_PAGE_READ) { + if (ctx->bounce_data) + memcpy(orig->databuf.in, + tweak->databuf.in + orig->dataoffs, + orig->datalen); + + if (ctx->bounce_oob) + memcpy(orig->oobbuf.in, + tweak->oobbuf.in + orig->ooboffs, + orig->ooblen); + } + + /* Ensure the original request is restored */ + *req = *orig; +} +EXPORT_SYMBOL_GPL(nand_ecc_restore_req); + +struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand) +{ + unsigned int algo = nand->ecc.user_conf.algo; + + if (algo == NAND_ECC_ALGO_UNKNOWN) + algo = nand->ecc.defaults.algo; + + switch (algo) { + case NAND_ECC_ALGO_HAMMING: + return nand_ecc_sw_hamming_get_engine(); + case NAND_ECC_ALGO_BCH: + return nand_ecc_sw_bch_get_engine(); + default: + break; + } + + return NULL; +} +EXPORT_SYMBOL(nand_ecc_get_sw_engine); + +struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand) +{ + return nand->ecc.ondie_engine; +} +EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine); + +int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine) +{ + struct nand_ecc_engine *item; + + if (!engine) + return -EINVAL; + + /* Prevent multiple registrations of one engine */ + list_for_each_entry(item, &on_host_hw_engines, node) + if (item == engine) + return 0; + + mutex_lock(&on_host_hw_engines_mutex); + list_add_tail(&engine->node, &on_host_hw_engines); + mutex_unlock(&on_host_hw_engines_mutex); + + return 0; +} +EXPORT_SYMBOL(nand_ecc_register_on_host_hw_engine); + +int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine) +{ + if (!engine) + return -EINVAL; + + mutex_lock(&on_host_hw_engines_mutex); + list_del(&engine->node); + mutex_unlock(&on_host_hw_engines_mutex); + + return 0; +} +EXPORT_SYMBOL(nand_ecc_unregister_on_host_hw_engine); + +static struct nand_ecc_engine *nand_ecc_match_on_host_hw_engine(struct device *dev) +{ + struct nand_ecc_engine *item; + + list_for_each_entry(item, &on_host_hw_engines, node) + if (item->dev == dev) + return item; + + return NULL; +} + +struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand) +{ + struct nand_ecc_engine *engine = NULL; + struct device *dev = &nand->mtd.dev; + struct device *pdev; + struct device_node *np; + + if (list_empty(&on_host_hw_engines)) + return NULL; + + /* Check for an explicit nand-ecc-engine property */ + np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0); + if (np) { + pdev = of_find_device_by_node(np); + if (!pdev) + return ERR_PTR(-EPROBE_DEFER); + + engine = nand_ecc_match_on_host_hw_engine(pdev); + + if (!engine) + return ERR_PTR(-EPROBE_DEFER); + } + + return engine; +} +EXPORT_SYMBOL(nand_ecc_get_on_host_hw_engine); + +void nand_ecc_put_on_host_hw_engine(struct nand_device *nand) +{ + put_device(nand->ecc.engine->dev); +} +EXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>"); +MODULE_DESCRIPTION("Generic ECC engine"); diff --git a/drivers/mtd/nand/nand-bb.c b/drivers/mtd/nand/nand-bb.c index c0104c5936..56033022ff 100644 --- a/drivers/mtd/nand/nand-bb.c +++ b/drivers/mtd/nand/nand-bb.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> #include <command.h> diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c deleted file mode 100644 index 0d636d9608..0000000000 --- a/drivers/mtd/nand/nand_bch.c +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * This file provides ECC correction for more than 1 bit per block of data, - * using binary BCH codes. It relies on the generic BCH library lib/bch.c. - * - * Copyright © 2011 Ivan Djelic <ivan.djelic@parrot.com> - */ - -#include <common.h> -#include <malloc.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/bitops.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/rawnand.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/nand_bch.h> -#include <linux/bch.h> - -/** - * struct nand_bch_control - private NAND BCH control structure - * @bch: BCH control structure - * @errloc: error location array - * @eccmask: XOR ecc mask, allows erased pages to be decoded as valid - */ -struct nand_bch_control { - struct bch_control *bch; - unsigned int *errloc; - unsigned char *eccmask; -}; - -/** - * nand_bch_calculate_ecc - [NAND Interface] Calculate ECC for data block - * @chip: NAND chip object - * @buf: input buffer with raw data - * @code: output buffer with ECC - */ -int nand_bch_calculate_ecc(struct nand_chip *chip, const unsigned char *buf, - unsigned char *code) -{ - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int i; - - memset(code, 0, chip->ecc.bytes); - encode_bch(nbc->bch, buf, chip->ecc.size, code); - - /* apply mask so that an erased page is a valid codeword */ - for (i = 0; i < chip->ecc.bytes; i++) - code[i] ^= nbc->eccmask[i]; - - return 0; -} -EXPORT_SYMBOL(nand_bch_calculate_ecc); - -/** - * nand_bch_correct_data - [NAND Interface] Detect and correct bit error(s) - * @chip: NAND chip object - * @buf: raw data read from the chip - * @read_ecc: ECC from the chip - * @calc_ecc: the ECC calculated from raw data - * - * Detect and correct bit errors for a data byte block - */ -int nand_bch_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, unsigned char *calc_ecc) -{ - struct nand_bch_control *nbc = chip->ecc.priv; - unsigned int *errloc = nbc->errloc; - int i, count; - - count = decode_bch(nbc->bch, NULL, chip->ecc.size, read_ecc, calc_ecc, - NULL, errloc); - if (count > 0) { - for (i = 0; i < count; i++) { - if (errloc[i] < (chip->ecc.size*8)) - /* error is located in data, correct it */ - buf[errloc[i] >> 3] ^= (1 << (errloc[i] & 7)); - /* else error in ecc, no action needed */ - - pr_debug("%s: corrected bitflip %u\n", __func__, - errloc[i]); - } - } else if (count < 0) { - pr_err("ecc unrecoverable error\n"); - count = -EBADMSG; - } - return count; -} -EXPORT_SYMBOL(nand_bch_correct_data); - -/** - * nand_bch_init - [NAND Interface] Initialize NAND BCH error correction - * @mtd: MTD block structure - * - * Returns: - * a pointer to a new NAND BCH control structure, or NULL upon failure - * - * Initialize NAND BCH error correction. Parameters @eccsize and @eccbytes - * are used to compute BCH parameters m (Galois field order) and t (error - * correction capability). @eccbytes should be equal to the number of bytes - * required to store m*t bits, where m is such that 2^m-1 > @eccsize*8. - * - * Example: to configure 4 bit correction per 512 bytes, you should pass - * @eccsize = 512 (thus, m=13 is the smallest integer such that 2^m-1 > 512*8) - * @eccbytes = 7 (7 bytes are required to store m*t = 13*4 = 52 bits) - */ -struct nand_bch_control *nand_bch_init(struct mtd_info *mtd) -{ - struct nand_chip *nand = mtd_to_nand(mtd); - unsigned int m, t, eccsteps, i; - struct nand_bch_control *nbc = NULL; - unsigned char *erased_page; - unsigned int eccsize = nand->ecc.size; - unsigned int eccbytes = nand->ecc.bytes; - unsigned int eccstrength = nand->ecc.strength; - - if (!eccbytes && eccstrength) { - eccbytes = DIV_ROUND_UP(eccstrength * fls(8 * eccsize), 8); - nand->ecc.bytes = eccbytes; - } - - if (!eccsize || !eccbytes) { - pr_warn("ecc parameters not supplied\n"); - goto fail; - } - - m = fls(1+8*eccsize); - t = (eccbytes*8)/m; - - nbc = kzalloc(sizeof(*nbc), GFP_KERNEL); - if (!nbc) - goto fail; - - nbc->bch = init_bch(m, t, 0); - if (!nbc->bch) - goto fail; - - /* verify that eccbytes has the expected value */ - if (nbc->bch->ecc_bytes != eccbytes) { - pr_warn("invalid eccbytes %u, should be %u\n", - eccbytes, nbc->bch->ecc_bytes); - goto fail; - } - - eccsteps = mtd->writesize/eccsize; - - /* Check that we have an oob layout description. */ - if (!mtd->ooblayout) { - pr_warn("missing oob scheme"); - goto fail; - } - - /* sanity checks */ - if (8*(eccsize+eccbytes) >= (1 << m)) { - pr_warn("eccsize %u is too large\n", eccsize); - goto fail; - } - - /* - * ecc->steps and ecc->total might be used by mtd->ooblayout->ecc(), - * which is called by mtd_ooblayout_count_eccbytes(). - * Make sure they are properly initialized before calling - * mtd_ooblayout_count_eccbytes(). - * FIXME: we should probably rework the sequencing in nand_scan_tail() - * to avoid setting those fields twice. - */ - nand->ecc.steps = eccsteps; - nand->ecc.total = eccsteps * eccbytes; - if (mtd_ooblayout_count_eccbytes(mtd) != (eccsteps*eccbytes)) { - pr_warn("invalid ecc layout\n"); - goto fail; - } - - nbc->eccmask = kzalloc(eccbytes, GFP_KERNEL); - nbc->errloc = kmalloc_array(t, sizeof(*nbc->errloc), GFP_KERNEL); - if (!nbc->eccmask || !nbc->errloc) - goto fail; - /* - * compute and store the inverted ecc of an erased ecc block - */ - erased_page = kmalloc(eccsize, GFP_KERNEL); - if (!erased_page) - goto fail; - - memset(erased_page, 0xff, eccsize); - encode_bch(nbc->bch, erased_page, eccsize, nbc->eccmask); - kfree(erased_page); - - for (i = 0; i < eccbytes; i++) - nbc->eccmask[i] ^= 0xff; - - if (!eccstrength) - nand->ecc.strength = (eccbytes * 8) / fls(8 * eccsize); - - return nbc; -fail: - nand_bch_free(nbc); - return NULL; -} -EXPORT_SYMBOL(nand_bch_init); - -/** - * nand_bch_free - [NAND Interface] Release NAND BCH ECC resources - * @nbc: NAND BCH control structure - */ -void nand_bch_free(struct nand_bch_control *nbc) -{ - if (nbc) { - free_bch(nbc->bch); - kfree(nbc->errloc); - kfree(nbc->eccmask); - kfree(nbc); - } -} -EXPORT_SYMBOL(nand_bch_free); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ivan Djelic <ivan.djelic@parrot.com>"); -MODULE_DESCRIPTION("NAND software BCH ECC support"); diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c deleted file mode 100644 index 0f20e9d394..0000000000 --- a/drivers/mtd/nand/nand_imx.c +++ /dev/null @@ -1,1501 +0,0 @@ -/* - * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. - * Copyright 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> - */ - -/* - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 or later at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html - */ - -/* - * MX21 Hardware contains a bug which causes HW ECC to fail for two - * consecutive read pages containing 1bit Errors (See MX21 Chip Erata, - * Erratum 16). Use software ECC for this chip. - */ - -#include <common.h> -#include <driver.h> -#include <malloc.h> -#include <init.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/rawnand.h> -#include <linux/clk.h> -#include <mach/generic.h> -#include <mach/imx-nand.h> -#include <io.h> -#include <of_mtd.h> -#include <errno.h> - -struct imx_nand_host { - struct nand_chip nand; - struct mtd_partition *parts; - struct device_d *dev; - - void *spare0; - void *main_area0; - - void __iomem *base; - void __iomem *regs; - void __iomem *regs_axi; - void __iomem *regs_ip; - int status_request; - struct clk *clk; - - int pagesize_2k; - uint8_t *data_buf; - unsigned int buf_start; - int spare_len; - int eccsize; - int eccstatus_v1; - - int hw_ecc; - int data_width; - int flash_bbt; - - void (*preset)(struct nand_chip *); - void (*send_cmd)(struct imx_nand_host *, uint16_t); - void (*send_addr)(struct imx_nand_host *, uint16_t); - void (*send_page)(struct imx_nand_host *, unsigned int); - void (*send_read_id)(struct imx_nand_host *); - void (*send_read_param)(struct imx_nand_host *); - uint16_t (*get_dev_status)(struct imx_nand_host *); - int (*check_int)(struct imx_nand_host *); - int (*correct)(struct nand_chip *); - void (*enable_hwecc)(struct nand_chip *, bool enable); -}; - -/* - * OOB placement block for use with hardware ecc generation - */ -static struct nand_ecclayout nandv1_hw_eccoob_smallpage = { - .eccbytes = 5, - .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 5}, {12, 4}} -}; - -static struct nand_ecclayout nandv1_hw_eccoob_largepage = { - .eccbytes = 20, - .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, - 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, - .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } -}; - -/* OOB description for 512 byte pages with 16 byte OOB */ -static struct nand_ecclayout nandv2_hw_eccoob_smallpage = { - .eccbytes = 1 * 9, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15 - }, - .oobfree = { - {.offset = 0, .length = 5} - } -}; - -/* OOB description for 2048 byte pages with 64 byte OOB */ -static struct nand_ecclayout nandv2_hw_eccoob_largepage = { - .eccbytes = 4 * 9, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15, - 23, 24, 25, 26, 27, 28, 29, 30, 31, - 39, 40, 41, 42, 43, 44, 45, 46, 47, - 55, 56, 57, 58, 59, 60, 61, 62, 63 - }, - .oobfree = { - {.offset = 2, .length = 4}, - {.offset = 16, .length = 7}, - {.offset = 32, .length = 7}, - {.offset = 48, .length = 7} - } -}; - -/* OOB description for 4096 byte pages with 128 byte OOB */ -static struct nand_ecclayout nandv2_hw_eccoob_4k = { - .eccbytes = 8 * 9, - .eccpos = { - 7, 8, 9, 10, 11, 12, 13, 14, 15, - 23, 24, 25, 26, 27, 28, 29, 30, 31, - 39, 40, 41, 42, 43, 44, 45, 46, 47, - 55, 56, 57, 58, 59, 60, 61, 62, 63, - 71, 72, 73, 74, 75, 76, 77, 78, 79, - 87, 88, 89, 90, 91, 92, 93, 94, 95, - 103, 104, 105, 106, 107, 108, 109, 110, 111, - 119, 120, 121, 122, 123, 124, 125, 126, 127, - }, - .oobfree = { - {.offset = 2, .length = 4}, - {.offset = 16, .length = 7}, - {.offset = 32, .length = 7}, - {.offset = 48, .length = 7}, - {.offset = 64, .length = 7}, - {.offset = 80, .length = 7}, - {.offset = 96, .length = 7}, - {.offset = 112, .length = 7}, - } -}; - -static void memcpy32(void *trg, const void *src, int size) -{ - int i; - unsigned int *t = trg; - unsigned const int *s = src; - -#ifdef CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS - if (!((unsigned long)trg & 0x3) && !((unsigned long)src & 0x3)) - memcpy(trg, src, size); - else -#endif - for (i = 0; i < (size >> 2); i++) - *t++ = *s++; -} - -static int check_int_v3(struct imx_nand_host *host) -{ - uint32_t tmp; - - tmp = readl(NFC_V3_IPC); - if (!(tmp & NFC_V3_IPC_INT)) - return 0; - - tmp &= ~NFC_V3_IPC_INT; - writel(tmp, NFC_V3_IPC); - - return 1; -} - -static int check_int_v1_v2(struct imx_nand_host *host) -{ - uint32_t tmp; - - tmp = readw(host->regs + NFC_V1_V2_CONFIG2); - if (!(tmp & NFC_V1_V2_CONFIG2_INT)) - return 0; - - writew(tmp & ~NFC_V1_V2_CONFIG2_INT, host->regs + NFC_V1_V2_CONFIG2); - - return 1; -} - -static void wait_op_done(struct imx_nand_host *host) -{ - int i; - - /* This is a timeout of roughly 15ms on my system. We - * need about 2us, but be generous. Don't use udelay - * here as we might be here from nand booting. - */ - for (i = 0; i < 100000; i++) { - if (host->check_int(host)) - return; - } -} - -/* - * This function issues the specified command to the NAND device and - * waits for completion. - * - * @param cmd command for NAND Flash - */ -static void send_cmd_v3(struct imx_nand_host *host, uint16_t cmd) -{ - /* fill command */ - writel(cmd, NFC_V3_FLASH_CMD); - - /* send out command */ - writel(NFC_CMD, NFC_V3_LAUNCH); - - /* Wait for operation to complete */ - wait_op_done(host); -} - -static void send_cmd_v1_v2(struct imx_nand_host *host, u16 cmd) -{ - writew(cmd, host->regs + NFC_V1_V2_FLASH_CMD); - writew(NFC_CMD, host->regs + NFC_V1_V2_CONFIG2); - - if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { - /* Reset completion is indicated by NFC_CONFIG2 */ - /* being set to 0 */ - int i; - for (i = 0; i < 100000; i++) { - if (readw(host->regs + NFC_V1_V2_CONFIG2) == 0) { - break; - } - } - } else - /* Wait for operation to complete */ - wait_op_done(host); -} - -/* - * This function sends an address (or partial address) to the - * NAND device. The address is used to select the source/destination for - * a NAND command. - * - * @param addr address to be written to NFC. - * @param islast True if this is the last address cycle for command - */ -static void send_addr_v3(struct imx_nand_host *host, uint16_t addr) -{ - /* fill address */ - writel(addr, NFC_V3_FLASH_ADDR0); - - /* send out address */ - writel(NFC_ADDR, NFC_V3_LAUNCH); - - wait_op_done(host); -} - -static void send_addr_v1_v2(struct imx_nand_host *host, u16 addr) -{ - writew(addr, host->regs + NFC_V1_V2_FLASH_ADDR); - writew(NFC_ADDR, host->regs + NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host); -} - -/* - * This function requests the NANDFC to initate the transfer - * of data currently in the NANDFC RAM buffer to the NAND device. - * - * @param buf_id Specify Internal RAM Buffer number (0-3) - * @param spare_only set true if only the spare area is transferred - */ -static void send_page_v3(struct imx_nand_host *host, unsigned int ops) -{ - uint32_t tmp; - - tmp = readl(NFC_V3_CONFIG1); - tmp &= ~(7 << 4); - writel(tmp, NFC_V3_CONFIG1); - - /* transfer data from NFC ram to nand */ - writel(ops, NFC_V3_LAUNCH); - - wait_op_done(host); -} - -static void send_page_v1_v2(struct imx_nand_host *host, - unsigned int ops) -{ - int bufs, i; - - host->eccstatus_v1 = 0; - - if (nfc_is_v1() && host->pagesize_2k) - bufs = 4; - else - bufs = 1; - - for (i = 0; i < bufs; i++) { - u16 status; - int errors; - - /* NANDFC buffer 0 is used for page read/write */ - writew(i, host->regs + NFC_V1_V2_BUF_ADDR); - - writew(ops, host->regs + NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host); - - status = readw(host->regs + NFC_V1_ECC_STATUS_RESULT); - errors = max(status & 0x3, status >> 2); - - if (errors == 1 && host->eccstatus_v1 >= 0) - host->eccstatus_v1++; - if (errors == 2) - host->eccstatus_v1 = -EBADMSG; - } -} - -/* - * This function requests the NANDFC to perform a read of the - * NAND device ID. - */ -static void send_read_id_v3(struct imx_nand_host *host) -{ - /* Read ID into main buffer */ - writel(NFC_ID, NFC_V3_LAUNCH); - - wait_op_done(host); - - /* - * NFC_ID results in reading 6 bytes or words (depending on data width), - * so copying 3 32-bit values is just fine. - */ - memcpy(host->data_buf, host->main_area0, 12); -} - -static void send_read_param_v3(struct imx_nand_host *host) -{ - /* Read ID into main buffer */ - writel(NFC_OUTPUT, NFC_V3_LAUNCH); - - wait_op_done(host); - - memcpy(host->data_buf, host->main_area0, 1024); -} - -static void send_read_id_v1_v2(struct imx_nand_host *host) -{ - /* NANDFC buffer 0 is used for device ID output */ - writew(0x0, host->regs + NFC_V1_V2_BUF_ADDR); - - writew(NFC_ID, host->regs + NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host); - - /* - * NFC_ID results in reading 6 bytes or words (depending on data width), - * so copying 3 32-bit values is just fine. - */ - memcpy32(host->data_buf, host->main_area0, 12); -} - -static void send_read_param_v1_v2(struct imx_nand_host *host) -{ - u32 backup = readw(host->regs + NFC_V1_V2_CONFIG1); - - /* Temporary disable ECC to be able to read param page */ - writew(backup & ~NFC_V1_V2_CONFIG1_ECC_EN, host->regs + NFC_V1_V2_CONFIG1); - - /* NANDFC buffer 0 is used for param output */ - writew(0x0, host->regs + NFC_V1_V2_BUF_ADDR); - - writew(NFC_OUTPUT, host->regs + NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host); - - memcpy32(host->data_buf, host->main_area0, 1024); - - /* Restore original CONFIG1 value */ - writew(backup, host->regs + NFC_V1_V2_CONFIG1); -} -/* - * This function requests the NANDFC to perform a read of the - * NAND device status and returns the current status. - * - * @return device status - */ -static uint16_t get_dev_status_v3(struct imx_nand_host *host) -{ - writew(NFC_STATUS, NFC_V3_LAUNCH); - wait_op_done(host); - - return readl(NFC_V3_CONFIG1) >> 16; -} - -static u16 get_dev_status_v1_v2(struct imx_nand_host *host) -{ - void *main_buf = host->main_area0; - u32 store; - u16 ret; - - writew(0x0, host->regs + NFC_V1_V2_BUF_ADDR); - - /* - * The device status is stored in main_area0. To - * prevent corruption of the buffer save the value - * and restore it afterwards. - */ - store = readl(main_buf); - - writew(NFC_STATUS, host->regs + NFC_V1_V2_CONFIG2); - - /* Wait for operation to complete */ - wait_op_done(host); - - /* Status is placed in first word of main buffer */ - /* get status, then recovery area 1 data */ - ret = readw(main_buf); - - writel(store, main_buf); - - return ret; -} - -/* - * This function is used by upper layer to checks if device is ready - * - * @param mtd MTD structure for the NAND Flash - * - * @return 0 if device is busy else 1 - */ -static int imx_nand_dev_ready(struct nand_chip *chip) -{ - /* - * NFC handles R/B internally.Therefore,this function - * always returns status as ready. - */ - return 1; -} - -static void imx_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) -{ - struct imx_nand_host *host = chip->priv; - uint16_t config1; - - if (chip->ecc.mode != NAND_ECC_HW) - return; - - config1 = readw(host->regs + NFC_V1_V2_CONFIG1); - - if (enable) - config1 |= NFC_V1_V2_CONFIG1_ECC_EN; - else - config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; - - writew(config1, host->regs + NFC_V1_V2_CONFIG1); - -} - -static void imx_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) -{ - struct imx_nand_host *host = chip->priv; - uint32_t config2; - - if (chip->ecc.mode != NAND_ECC_HW) - return; - - config2 = readl(NFC_V3_CONFIG2); - - if (enable) - config2 |= NFC_V3_CONFIG2_ECC_EN; - else - config2 &= ~NFC_V3_CONFIG2_ECC_EN; - - writel(config2, NFC_V3_CONFIG2); -} - -static int imx_nand_correct_data_v1(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - - if (host->eccstatus_v1 < 0) - return host->eccstatus_v1; - - mtd->ecc_stats.corrected += host->eccstatus_v1; - - if (host->eccstatus_v1 > 0) - return 1; - else - return 0; -} - -static int imx_nand_correct_data_v2_v3(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - u32 ecc_stat, err; - int no_subpages; - u8 ecc_bit_mask, err_limit, max_bitflips = 0; - - ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; - err_limit = (host->eccsize == 4) ? 0x4 : 0x8; - - no_subpages = mtd->writesize >> 9; - - if (nfc_is_v21()) - ecc_stat = readl(host->regs + NFC_V2_ECC_STATUS_RESULT1); - else - ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); - - do { - err = ecc_stat & ecc_bit_mask; - if (err > err_limit) - return -EBADMSG; - ecc_stat >>= 4; - max_bitflips = max_t(unsigned int, max_bitflips, err); - mtd->ecc_stats.corrected += err; - } while (--no_subpages); - - return max_bitflips; -} - -static int imx_nand_calculate_ecc(struct nand_chip *chip, const u_char * dat, - u_char * ecc_code) -{ - return 0; -} - -/* - * This function reads byte from the NAND Flash - * - * @param mtd MTD structure for the NAND Flash - * - * @return data read from the NAND Flash - */ -static u_char imx_nand_read_byte(struct nand_chip *chip) -{ - struct imx_nand_host *host = chip->priv; - u_char ret; - - /* Check for status request */ - if (host->status_request) - return host->get_dev_status(host) & 0xFF; - - if (chip->options & NAND_BUSWIDTH_16) { - /* only take the lower byte of each word */ - BUG_ON(host->buf_start & 1); - ret = *(uint16_t *)(host->data_buf + host->buf_start); - - host->buf_start += 2; - } else { - ret = *(uint8_t *)(host->data_buf + host->buf_start); - host->buf_start++; - } - - return ret; -} - -/* - * This function reads word from the NAND Flash - * - * @param mtd MTD structure for the NAND Flash - * - * @return data read from the NAND Flash - */ -static u16 imx_nand_read_word(struct nand_chip *chip) -{ - struct imx_nand_host *host = chip->priv; - uint16_t ret; - - ret = *(uint16_t *)(host->data_buf + host->buf_start); - host->buf_start += 2; - - return ret; -} - -/* - * This function writes data of length \b len to buffer \b buf. The data to be - * written on NAND Flash is first copied to RAMbuffer. After the Data Input - * Operation by the NFC, the data is written to NAND Flash - * - * @param mtd MTD structure for the NAND Flash - * @param buf data to be written to NAND Flash - * @param len number of bytes to be written - */ -static void imx_nand_write_buf(struct nand_chip *chip, - const u_char *buf, int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - u16 col = host->buf_start; - int n = mtd->oobsize + mtd->writesize - col; - - n = min(n, len); - memcpy(host->data_buf + col, buf, n); - - host->buf_start += n; -} - -/* - * This function is used to read the data buffer from the NAND Flash. To - * read the data from NAND Flash first the data output cycle is initiated by - * the NFC, which copies the data to RAMbuffer. This data of length \b len is - * then copied to buffer \b buf. - * - * @param mtd MTD structure for the NAND Flash - * @param buf data to be read from NAND Flash - * @param len number of bytes to be read - */ -static void imx_nand_read_buf(struct nand_chip *chip, u_char * buf, int len) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - u16 col = host->buf_start; - int n = mtd->oobsize + mtd->writesize - col; - - n = min(n, len); - - /* handle the read param special case */ - if ((mtd->writesize == 0) && (len != 0)) - n = len; - - memcpy(buf, host->data_buf + col, n); - - host->buf_start += n; -} - -/* - * Function to transfer data to/from spare area. - */ -static void copy_spare(struct nand_chip *chip, int bfrom, void *buf) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - u16 i, j; - u16 n = mtd->writesize >> 9; - u8 *d = buf; - u8 *s = host->spare0; - u16 t = host->spare_len; - - j = (mtd->oobsize / n >> 1) << 1; - - if (bfrom) { - for (i = 0; i < n - 1; i++) - memcpy32(d + i * j, s + i * t, j); - - /* the last section */ - memcpy32(d + i * j, s + i * t, mtd->oobsize - i * j); - } else { - for (i = 0; i < n - 1; i++) - memcpy32(&s[i * t], &d[i * j], j); - - /* the last section */ - memcpy32(&s[i * t], &d[i * j], mtd->oobsize - i * j); - } -} - -/* - * This function is used by upper layer for select and deselect of the NAND - * chip - * - * @param mtd MTD structure for the NAND Flash - * @param chip val indicating select or deselect - */ -static void imx_nand_select_chip(struct nand_chip *_chip, int chip) -{ -} - -static void mxc_do_addr_cycle(struct nand_chip *chip, int column, int page_addr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - - /* - * Write out column address, if necessary - */ - if (column != -1) { - /* - * MXC NANDFC can only perform full page+spare or - * spare-only read/write. When the upper layers - * layers perform a read/write buf operation, - * we will used the saved column adress to index into - * the full page. - * - * The colum address must be sent to the flash in - * order to get the ONFI header (0x20) - */ - host->send_addr(host, column); - if (host->pagesize_2k) - /* another col addr cycle for 2k page */ - host->send_addr(host, 0); - } - - /* - * Write out page address, if necessary - */ - if (page_addr != -1) { - host->send_addr(host, (page_addr & 0xff)); /* paddr_0 - p_addr_7 */ - - if (host->pagesize_2k) { - host->send_addr(host, (page_addr >> 8) & 0xFF); - if (mtd->size >= 0x10000000) { - host->send_addr(host, (page_addr >> 16) & 0xff); - } - } else { - /* One more address cycle for higher density devices */ - if (mtd->size >= 0x4000000) { - /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff); - host->send_addr(host, (page_addr >> 16) & 0xff); - } else - /* paddr_8 - paddr_15 */ - host->send_addr(host, (page_addr >> 8) & 0xff); - } - } -} - -/* - * v2 and v3 type controllers can do 4bit or 8bit ecc depending - * on how much oob the nand chip has. For 8bit ecc we need at least - * 26 bytes of oob data per 512 byte block. - */ -static int get_eccsize(struct mtd_info *mtd) -{ - int oobbytes_per_512 = 0; - - oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; - - if (oobbytes_per_512 < 26) - return 4; - else - return 8; -} - -static void preset_v1(struct nand_chip *chip) -{ - struct imx_nand_host *host = chip->priv; - uint16_t config1 = 0; - - host->eccsize = 1; - - writew(config1, host->regs + NFC_V1_V2_CONFIG1); - /* preset operation */ - - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_V1_V2_CONFIG); - - /* Blocks to be unlocked */ - writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); - - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_V1_V2_WRPROT); -} - -static void preset_v2(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - uint16_t config1 = 0; - int mode; - - mode = onfi_get_async_timing_mode(chip); - if (mode != ONFI_TIMING_MODE_UNKNOWN && !IS_ERR(host->clk)) { - const struct nand_sdr_timings *timings; - - mode = fls(mode) - 1; - if (mode < 0) - mode = 0; - - timings = onfi_async_timing_mode_to_sdr_timings(mode); - if (!IS_ERR(timings)) { - unsigned long rate; - int tRC_min_ns = timings->tRC_min / 1000; - - rate = 1000000000 / tRC_min_ns; - if (tRC_min_ns < 30) - /* If tRC is smaller than 30ns we have to use EDO timing */ - config1 |= NFC_V1_V2_CONFIG1_ONE_CYCLE; - else - /* Otherwise we have two clock cycles per access */ - rate *= 2; - - clk_set_rate(host->clk, rate); - } - } - - config1 |= NFC_V2_CONFIG1_FP_INT; - - if (mtd->writesize) { - uint16_t pages_per_block = mtd->erasesize / mtd->writesize; - - host->eccsize = get_eccsize(mtd); - if (host->eccsize == 4) - config1 |= NFC_V2_CONFIG1_ECC_MODE_4; - - config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); - } else { - host->eccsize = 1; - } - - writew(config1, host->regs + NFC_V1_V2_CONFIG1); - /* preset operation */ - - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_V1_V2_CONFIG); - - /* Blocks to be unlocked */ - writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); - - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_V1_V2_WRPROT); -} - -static void preset_v3(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - uint32_t config2, config3; - int i, addr_phases; - - writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); - writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); - - /* Unlock the internal RAM Buffer */ - writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, - NFC_V3_WRPROT); - - /* Blocks to be unlocked */ - for (i = 0; i < NAND_MAX_CHIPS; i++) - writel(0x0 | (0xffff << 16), - NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); - - writel(0, NFC_V3_IPC); - - /* if the flash has a 224 oob, the NFC must be configured to 218 */ - config2 = NFC_V3_CONFIG2_ONE_CYCLE | - NFC_V3_CONFIG2_2CMD_PHASES | - NFC_V3_CONFIG2_SPAS(((mtd->oobsize > 218) ? - 218 : mtd->oobsize) >> 1) | - NFC_V3_CONFIG2_ST_CMD(0x70) | - NFC_V3_CONFIG2_NUM_ADDR_PHASE0; - - addr_phases = fls(chip->pagemask) >> 3; - - if (mtd->writesize == 2048) { - config2 |= NFC_V3_CONFIG2_PS_2048; - config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); - } else if (mtd->writesize == 4096) { - config2 |= NFC_V3_CONFIG2_PS_4096; - config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); - } else { - config2 |= NFC_V3_CONFIG2_PS_512; - config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); - } - - if (mtd->writesize) { - if (cpu_is_mx51()) - config2 |= NFC_V3_MX51_CONFIG2_PPB( - ffs(mtd->erasesize / mtd->writesize) - 6); - else - config2 |= NFC_V3_MX53_CONFIG2_PPB( - ffs(mtd->erasesize / mtd->writesize) - 6); - host->eccsize = get_eccsize(mtd); - if (host->eccsize == 8) - config2 |= NFC_V3_CONFIG2_ECC_MODE_8; - } - - writel(config2, NFC_V3_CONFIG2); - - config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | - NFC_V3_CONFIG3_NO_SDMA | - NFC_V3_CONFIG3_RBB_MODE | - NFC_V3_CONFIG3_SBB(6) | /* Reset default */ - NFC_V3_CONFIG3_ADD_OP(0); - - if (!(chip->options & NAND_BUSWIDTH_16)) - config3 |= NFC_V3_CONFIG3_FW8; - - writel(config3, NFC_V3_CONFIG3); - - writel(0, NFC_V3_DELAY_LINE); -} - -static int imx_nand_write_page(struct nand_chip *chip, - const uint8_t *buf, bool ecc, int page) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - int status; - - host->enable_hwecc(chip, ecc); - - chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, 0x00, page); - - memcpy32(host->main_area0, buf, mtd->writesize); - copy_spare(chip, 0, chip->oob_poi); - - host->send_page(host, NFC_INPUT); - chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1); - status = chip->legacy.waitfunc(chip); - - if (status & NAND_STATUS_FAIL) - return -EIO; - - return 0; -} - -static int imx_nand_write_page_ecc(struct nand_chip *chip, const uint8_t *buf, - int oob_required, int page) -{ - return imx_nand_write_page(chip, buf, true, page); -} - -static int imx_nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, - int oob_required, int page) -{ - return imx_nand_write_page(chip, buf, false, page); -} - -static void imx_nand_do_read_page(struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - - nand_read_page_op(chip, page, 0, NULL, 0); - - host->send_page(host, NFC_OUTPUT); - - memcpy32(buf, host->main_area0, mtd->writesize); - - if (oob_required) - copy_spare(chip, 1, chip->oob_poi); -} - -static int imx_nand_read_page(struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct imx_nand_host *host = chip->priv; - - host->enable_hwecc(chip, true); - - imx_nand_do_read_page(chip, buf, oob_required, page); - - return host->correct(chip); -} - -static int imx_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf, - int oob_required, int page) -{ - struct imx_nand_host *host = chip->priv; - - host->enable_hwecc(chip, false); - - imx_nand_do_read_page(chip, buf, oob_required, page); - - return 0; -} - -/* - * This function is used by the upper layer to write command to NAND Flash for - * different operations to be carried out on NAND Flash - * - * @param mtd MTD structure for the NAND Flash - * @param command command for NAND Flash - * @param column column offset for the page read - * @param page_addr page to be read from NAND Flash - */ -static void imx_nand_command(struct nand_chip *chip, unsigned command, - int column, int page_addr) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - struct imx_nand_host *host = chip->priv; - - dev_dbg(host->dev, - "imx_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", - command, column, page_addr); - - /* - * Reset command state information - */ - host->status_request = 0; - - /* - * Command pre-processing step - */ - switch (command) { - case NAND_CMD_RESET: - host->preset(chip); - host->send_cmd(host, command); - break; - - case NAND_CMD_STATUS: - host->buf_start = 0; - host->status_request = 1; - host->send_cmd(host, command); - mxc_do_addr_cycle(chip, column, page_addr); - break; - - case NAND_CMD_READ0: - case NAND_CMD_READOOB: - if (command == NAND_CMD_READ0) - host->buf_start = column; - else - host->buf_start = column + mtd->writesize; - - host->send_cmd(host, NAND_CMD_READ0); - mxc_do_addr_cycle(chip, column, page_addr); - - if (host->pagesize_2k) - /* send read confirm command */ - host->send_cmd(host, NAND_CMD_READSTART); - - /* - * After the core issued READOOB the result is read using - * .read_buf, so we have to make sure the data is actually - * there. - */ - if (command == NAND_CMD_READOOB) { - host->send_page(host, NFC_OUTPUT); - copy_spare(chip, 1, host->data_buf + mtd->writesize); - } - - break; - - case NAND_CMD_SEQIN: - if (column >= mtd->writesize) { - if (host->pagesize_2k) { - /** - * FIXME: before send SEQIN command for write - * OOB, we must read one page out. For K9F1GXX - * has no READ1 command to set current HW - * pointer to spare area, we must write the - * whole page including OOB together. - */ - /* call ourself to read a page */ - imx_nand_command(chip, NAND_CMD_READ0, 0, - page_addr); - } - host->buf_start = column; - - /* Set program pointer to spare region */ - if (!host->pagesize_2k) - host->send_cmd(host, NAND_CMD_READOOB); - } else { - host->buf_start = column; - - /* Set program pointer to page start */ - if (!host->pagesize_2k) - host->send_cmd(host, NAND_CMD_READ0); - } - host->send_cmd(host, command); - mxc_do_addr_cycle(chip, column, page_addr); - - break; - - case NAND_CMD_PAGEPROG: - host->send_cmd(host, command); - mxc_do_addr_cycle(chip, column, page_addr); - break; - - case NAND_CMD_READID: - host->send_cmd(host, command); - mxc_do_addr_cycle(chip, column, page_addr); - host->send_read_id(host); - host->buf_start = 0; - break; - - case NAND_CMD_PARAM: - host->send_cmd(host, command); - mxc_do_addr_cycle(chip, column, page_addr); - host->send_read_param(host); - host->buf_start = 0; - break; - - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - host->send_cmd(host, command); - mxc_do_addr_cycle(chip, column, page_addr); - break; - } -} - -/* - * The generic flash bbt decriptors overlap with our ecc - * hardware, so define some i.MX specific ones. - */ -static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; -static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; - -static struct nand_bbt_descr bbt_main_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 4, - .maxblocks = 4, - .pattern = bbt_pattern, -}; - -static struct nand_bbt_descr bbt_mirror_descr = { - .options = NAND_BBT_LASTBLOCK | NAND_BBT_WRITE - | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, - .offs = 0, - .len = 4, - .veroffs = 4, - .maxblocks = 4, - .pattern = mirror_pattern, -}; - -static int __init mxcnd_probe_dt(struct imx_nand_host *host) -{ - struct device_node *np = host->dev->device_node; - int buswidth; - - if (!IS_ENABLED(CONFIG_OFDEVICE)) - return 1; - - if (!np) - return 1; - - if (of_get_nand_ecc_mode(np) == NAND_ECC_HW) - host->hw_ecc = 1; - - host->flash_bbt = of_get_nand_on_flash_bbt(np); - - buswidth = of_get_nand_bus_width(np); - if (buswidth < 0) - return buswidth; - - host->data_width = buswidth / 8; - - return 0; -} - -/* - * The i.MX NAND controller has the problem that it handles the - * data in chunks of 512 bytes. It doesn't treat 2k NAND chips as - * 2048 byte data + 64 OOB, but instead: - * - * 512b data + 16b OOB + - * 512b data + 16b OOB + - * 512b data + 16b OOB + - * 512b data + 16b OOB - * - * This means that the factory provided bad block marker ends up - * in the page data at offset 2000 instead of in the OOB data. - * - * To preserve the factory bad block information we take the following - * strategy: - * - * - If the NAND driver detects that no flash BBT is present on 2k NAND - * chips it will not create one because it would do so based on the wrong - * BBM position - * - This command is used to create a flash BBT then. - * - * From this point on we can forget about the BBMs and rely completely - * on the flash BBT. - * - */ -static int checkbad(struct nand_chip *chip, loff_t ofs) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - uint8_t buf[mtd->writesize + mtd->oobsize]; - struct mtd_oob_ops ops; - - ops.mode = MTD_OPS_RAW; - ops.ooboffs = 0; - ops.datbuf = buf; - ops.len = mtd->writesize; - ops.oobbuf = buf + mtd->writesize; - ops.ooblen = mtd->oobsize; - - ret = mtd_read_oob(mtd, ofs, &ops); - if (ret < 0) - return ret; - - if (buf[2000] != 0xff) - return 1; - - return 0; -} - -static int imxnd_create_bbt(struct nand_chip *chip) -{ - struct mtd_info *mtd = nand_to_mtd(chip); - int len, i, numblocks, ret; - loff_t from = 0; - uint8_t *bbt; - - len = mtd->size >> (chip->bbt_erase_shift + 2); - - /* Allocate memory (2bit per block) and clear the memory bad block table */ - bbt = kzalloc(len, GFP_KERNEL); - if (!bbt) - return -ENOMEM; - - numblocks = mtd->size >> (chip->bbt_erase_shift - 1); - - for (i = 0; i < numblocks;) { - ret = checkbad(chip, from); - if (ret < 0) - goto out; - - if (ret) { - bbt[i >> 3] |= 0x03 << (i & 0x6); - dev_info(mtd->dev.parent, "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int)from); - } - - i += 2; - from += (1 << chip->bbt_erase_shift); - } - - chip->bbt_td->options |= NAND_BBT_CREATE; - chip->bbt_md->options |= NAND_BBT_CREATE; - - free(chip->bbt); - chip->bbt = bbt; - - ret = nand_update_bbt(chip, 0); - if (ret) - return ret; - - ret = nand_create_bbt(chip); - if (ret) - return ret; - - ret = 0; -out: - free(bbt); - - return ret; -} - -/* - * This function is called during the driver binding process. - * - * @param pdev the device structure used to store device specific - * information that is used by the suspend, resume and - * remove functions - * - * @return The function always returns 0. - */ - -static int __init imxnd_probe(struct device_d *dev) -{ - struct resource *iores; - struct nand_chip *this; - struct mtd_info *mtd; - struct imx_nand_host *host; - struct nand_ecclayout *oob_smallpage, *oob_largepage, *oob_4kpage; - int err = 0; - - /* Allocate memory for MTD device structure and private data */ - host = kzalloc(sizeof(struct imx_nand_host) + NAND_MAX_PAGESIZE + - NAND_MAX_OOBSIZE, GFP_KERNEL); - if (!host) - return -ENOMEM; - - host->dev = dev; - - err = mxcnd_probe_dt(host); - if (err < 0) - goto escan; - - if (err > 0) { - struct imx_nand_platform_data *pdata; - - pdata = dev->platform_data; - host->flash_bbt = pdata->flash_bbt; - host->data_width = pdata->width; - host->hw_ecc = pdata->hw_ecc; - } - - host->data_buf = (uint8_t *)(host + 1); - - /* No error check, not all SoCs provide a clk yet */ - host->clk = clk_get(dev, NULL); - - if (nfc_is_v1() || nfc_is_v21()) { - host->send_cmd = send_cmd_v1_v2; - host->send_addr = send_addr_v1_v2; - host->send_page = send_page_v1_v2; - host->send_read_id = send_read_id_v1_v2; - host->send_read_param = send_read_param_v1_v2; - host->get_dev_status = get_dev_status_v1_v2; - host->check_int = check_int_v1_v2; - } - - if (nfc_is_v21()) { - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - host->base = IOMEM(iores->start); - host->main_area0 = host->base; - host->regs = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - oob_4kpage = &nandv2_hw_eccoob_4k; /* FIXME : to check */ - host->preset = preset_v2; - } else if (nfc_is_v1()) { - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - host->base = IOMEM(iores->start); - host->main_area0 = host->base; - host->regs = host->base + 0xe00; - host->spare0 = host->base + 0x800; - host->spare_len = 16; - oob_smallpage = &nandv1_hw_eccoob_smallpage; - oob_largepage = &nandv1_hw_eccoob_largepage; - oob_4kpage = &nandv1_hw_eccoob_smallpage; /* FIXME : to check */ - host->preset = preset_v1; - } else if (nfc_is_v3_2()) { - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - host->regs_ip = IOMEM(iores->start); - - iores = dev_request_mem_resource(dev, 1); - if (IS_ERR(iores)) - return PTR_ERR(iores); - host->base = IOMEM(iores->start); - host->main_area0 = host->base; - - if (IS_ERR(host->regs_ip)) { - dev_err(dev, "no second mem region\n"); - err = PTR_ERR(host->regs_ip); - goto escan; - } - - host->regs_axi = host->base + 0x1e00; - host->spare0 = host->base + 0x1000; - host->spare_len = 64; - host->preset = preset_v3; - host->send_cmd = send_cmd_v3; - host->send_addr = send_addr_v3; - host->send_page = send_page_v3; - host->send_read_id = send_read_id_v3; - host->send_read_param = send_read_param_v3; - host->get_dev_status = get_dev_status_v3; - host->check_int = check_int_v3; - oob_smallpage = &nandv2_hw_eccoob_smallpage; - oob_largepage = &nandv2_hw_eccoob_largepage; - oob_4kpage = &nandv2_hw_eccoob_4k; - } else { - err = -EINVAL; - goto escan; - } - - /* structures must be linked */ - this = &host->nand; - mtd = nand_to_mtd(this); - mtd->dev.parent = dev; - mtd->name = "imx_nand"; - - /* 50 us command delay time */ - this->legacy.chip_delay = 5; - - this->priv = host; - this->legacy.dev_ready = imx_nand_dev_ready; - this->legacy.cmdfunc = imx_nand_command; - this->legacy.select_chip = imx_nand_select_chip; - this->legacy.read_byte = imx_nand_read_byte; - this->legacy.read_word = imx_nand_read_word; - this->legacy.write_buf = imx_nand_write_buf; - this->legacy.read_buf = imx_nand_read_buf; - this->ecc.write_page = imx_nand_write_page_ecc; - this->ecc.write_page_raw = imx_nand_write_page_raw; - - if (host->hw_ecc) { - this->ecc.calculate = imx_nand_calculate_ecc; - if (nfc_is_v3()) - host->enable_hwecc = imx_nand_enable_hwecc_v3; - else - host->enable_hwecc = imx_nand_enable_hwecc_v1_v2; - if (nfc_is_v1()) - host->correct = imx_nand_correct_data_v1; - else - host->correct = imx_nand_correct_data_v2_v3; - this->ecc.mode = NAND_ECC_HW; - this->ecc.size = 512; - this->ecc.read_page_raw = imx_nand_read_page_raw; - this->ecc.read_page = imx_nand_read_page; - } else { - this->ecc.size = 512; - this->ecc.mode = NAND_ECC_SOFT; - } - - mtd_set_ecclayout(mtd, oob_smallpage); - - /* NAND bus width determines access functions used by upper layer */ - if (host->data_width == 2) { - this->options |= NAND_BUSWIDTH_16; - mtd_set_ecclayout(mtd, &nandv1_hw_eccoob_smallpage); - imx_nand_set_layout(0, 16); - } - - if (host->flash_bbt) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; - /* update flash based bbt */ - this->bbt_options |= NAND_BBT_USE_FLASH; - } - - /* first scan to find the device and get the page size */ - if (nand_scan_ident(this, 1, NULL)) { - err = -ENXIO; - goto escan; - } - - /* Call preset again, with correct writesize this time */ - host->preset(this); - - imx_nand_set_layout(mtd->writesize, host->data_width == 2 ? 16 : 8); - - if (mtd->writesize >= 2048) { - if (!host->flash_bbt) - dev_warn(dev, "2k or 4k flash detected without flash_bbt. " - "You will loose factory bad block markers!\n"); - - if (mtd->writesize == 2048) - mtd_set_ecclayout(mtd, oob_largepage); - else - mtd_set_ecclayout(mtd, oob_4kpage); - host->pagesize_2k = 1; - if (nfc_is_v21()) - writew(NFC_V2_SPAS_SPARESIZE(64), host->regs + NFC_V2_SPAS); - } else { - bbt_main_descr.options |= NAND_BBT_CREATE; - bbt_mirror_descr.options |= NAND_BBT_CREATE; - - if (nfc_is_v21()) - writew(NFC_V2_SPAS_SPARESIZE(16), host->regs + NFC_V2_SPAS); - } - - if (this->ecc.mode == NAND_ECC_HW) - this->ecc.strength = host->eccsize; - - /* second phase scan */ - if (nand_scan_tail(this)) { - err = -ENXIO; - goto escan; - } - - if (host->flash_bbt && this->bbt_td->pages[0] == -1 && this->bbt_md->pages[0] == -1) { - dev_info(dev, "no BBT found. creating one\n"); - err = imxnd_create_bbt(this); - if (err) - dev_warn(dev, "Failed to create bbt: %s\n", - strerror(-err)); - err = 0; - } - - add_mtd_nand_device(mtd, "nand"); - - dev->priv = host; - - return 0; - -escan: - kfree(host); - - return err; - -} - -static __maybe_unused struct of_device_id imx_nand_compatible[] = { - { - .compatible = "fsl,imx21-nand", - }, { - .compatible = "fsl,imx25-nand", - }, { - .compatible = "fsl,imx27-nand", - }, { - .compatible = "fsl,imx51-nand", - }, { - .compatible = "fsl,imx53-nand", - }, { - /* sentinel */ - } -}; - -static struct driver_d imx_nand_driver = { - .name = "imx_nand", - .probe = imxnd_probe, - .of_compatible = DRV_OF_COMPAT(imx_nand_compatible), -}; -device_platform_driver(imx_nand_driver); - -MODULE_AUTHOR("Freescale Semiconductor, Inc."); -MODULE_DESCRIPTION("MXC NAND MTD driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/nand_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c deleted file mode 100644 index f1d1441f50..0000000000 --- a/drivers/mtd/nand/nand_s3c24xx.c +++ /dev/null @@ -1,660 +0,0 @@ -/* linux/drivers/mtd/nand/s3c2410.c - * - * Copyright (C) 2009 Juergen Beisert, Pengutronix - * - * Copyright © 2004-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks <ben@simtec.co.uk> - * - * Samsung S3C2410 NAND driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * -*/ - -#include <config.h> -#include <common.h> -#include <driver.h> -#include <malloc.h> -#include <init.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/rawnand.h> -#include <linux/mtd/nand.h> -#include <mach/s3c-generic.h> -#include <mach/s3c-iomap.h> -#include <mach/s3c24xx-nand.h> -#include <io.h> -#include <errno.h> -#include <asm/sections.h> - -#ifdef CONFIG_S3C_NAND_BOOT -# define __nand_boot_init __bare_init -# ifndef BOARD_DEFAULT_NAND_TIMING -# define BOARD_DEFAULT_NAND_TIMING 0x0737 -# endif -#else -# define __nand_boot_init -#endif - -/** - * Define this symbol for testing purpose. It will add a command to read an - * image from the NAND like it the boot strap code will do. - */ -#define CONFIG_NAND_S3C_BOOT_DEBUG - -/* NAND controller's register */ - -#define NFCONF 0x00 - -#ifdef CONFIG_CPU_S3C2410 - -#define NFCMD 0x04 -#define NFADDR 0x08 -#define NFDATA 0x0c -#define NFSTAT 0x10 -#define NFECC 0x14 - -/* S3C2410 specific bits */ -#define NFSTAT_BUSY (1) -#define NFCONF_nFCE (1 << 11) -#define NFCONF_INITECC (1 << 12) -#define NFCONF_EN (1 << 15) - -#endif /* CONFIG_CPU_S3C2410 */ - -#ifdef CONFIG_CPU_S3C2440 - -#define NFCONT 0x04 -#define NFCMD 0x08 -#define NFADDR 0x0C -#define NFDATA 0x10 -#define NFSTAT 0x20 -#define NFECC 0x2C - -/* S3C2440 specific bits */ -#define NFSTAT_BUSY (1) -#define NFCONT_nFCE (1 << 1) -#define NFCONT_INITECC (1 << 4) -#define NFCONT_EN (1) - -#endif /* CONFIG_CPU_S3C2440 */ - - -struct s3c24x0_nand_host { - struct nand_chip nand; - struct mtd_partition *parts; - struct device_d *dev; - - void __iomem *base; -}; - -/** - * oob placement block for use with hardware ecc generation on small page - */ -static struct nand_ecclayout nand_hw_eccoob = { - .eccbytes = 3, - .eccpos = { 0, 1, 2}, - .oobfree = { - { - .offset = 8, - .length = 8 - } - } -}; - -/* - Functions shared between the boot strap code and the regular driver - */ - -/** - * Issue the specified command to the NAND device - * @param[in] host Base address of the NAND controller - * @param[in] cmd Command for NAND flash - */ -static void __nand_boot_init send_cmd(void __iomem *host, uint8_t cmd) -{ - writeb(cmd, host + NFCMD); -} - -/** - * Issue the specified address to the NAND device - * @param[in] host Base address of the NAND controller - * @param[in] addr Address for the NAND flash - */ -static void __nand_boot_init send_addr(void __iomem *host, uint8_t addr) -{ - writeb(addr, host + NFADDR); -} - -/** - * Enable the NAND flash access - * @param[in] host Base address of the NAND controller - */ -static void __nand_boot_init enable_cs(void __iomem *host) -{ -#ifdef CONFIG_CPU_S3C2410 - writew(readw(host + NFCONF) & ~NFCONF_nFCE, host + NFCONF); -#endif -#ifdef CONFIG_CPU_S3C2440 - writew(readw(host + NFCONT) & ~NFCONT_nFCE, host + NFCONT); -#endif -} - -/** - * Disable the NAND flash access - * @param[in] host Base address of the NAND controller - */ -static void __nand_boot_init disable_cs(void __iomem *host) -{ -#ifdef CONFIG_CPU_S3C2410 - writew(readw(host + NFCONF) | NFCONF_nFCE, host + NFCONF); -#endif -#ifdef CONFIG_CPU_S3C2440 - writew(readw(host + NFCONT) | NFCONT_nFCE, host + NFCONT); -#endif -} - -/** - * Enable the NAND flash controller - * @param[in] host Base address of the NAND controller - * @param[in] timing Timing to access the NAND memory - */ -static void __nand_boot_init enable_nand_controller(void __iomem *host, uint32_t timing) -{ -#ifdef CONFIG_CPU_S3C2410 - writew(timing + NFCONF_EN + NFCONF_nFCE, host + NFCONF); -#endif -#ifdef CONFIG_CPU_S3C2440 - writew(NFCONT_EN + NFCONT_nFCE, host + NFCONT); - writew(timing, host + NFCONF); -#endif -} - -/** - * Diable the NAND flash controller - * @param[in] host Base address of the NAND controller - */ -static void __nand_boot_init disable_nand_controller(void __iomem *host) -{ -#ifdef CONFIG_CPU_S3C2410 - writew(NFCONF_nFCE, host + NFCONF); -#endif -#ifdef CONFIG_CPU_S3C2440 - writew(NFCONT_nFCE, host + NFCONT); -#endif -} - -/* ----------------------------------------------------------------------- */ - -#ifdef CONFIG_CPU_S3C2440 -/** - * Read one block of data from the NAND port - * @param[in] mtd Instance data - * @param[out] buf buffer to write data to - * @param[in] len byte count - * - * This is a special block read variant for the S3C2440 CPU. - */ -static void s3c2440_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len) -{ - struct s3c24x0_nand_host *host = chip->priv; - - readsl(host->base + NFDATA, buf, len >> 2); - - /* cleanup any fractional read */ - if (len & 3) { - buf += len & ~3; - - for (; len & 3; len--) - *buf++ = readb(host->base + NFDATA); - } -} - -/** - * Write one block of data to the NAND port - * @param[in] mtd Instance data - * @param[out] buf buffer to read data from - * @param[in] len byte count - * - * This is a special block write variant for the S3C2440 CPU. - */ -static void s3c2440_nand_write_buf(struct nand_chip *chip, const uint8_t *buf, - int len) -{ - struct s3c24x0_nand_host *host = chip->priv; - - writesl(host->base + NFDATA, buf, len >> 2); - - /* cleanup any fractional write */ - if (len & 3) { - buf += len & ~3; - - for (; len & 3; len--, buf++) - writeb(*buf, host->base + NFDATA); - } -} -#endif - -/** - * Check the ECC and try to repair the data if possible - * @param[in] mtd_info Not used - * @param[inout] dat Pointer to the data buffer that might contain a bit error - * @param[in] read_ecc ECC data from the OOB space - * @param[in] calc_ecc ECC data calculated from the data - * @return 0 no error, 1 repaired error, -1 no way... - * - * @note: This routine works always on a 24 bit ECC - */ -static int s3c2410_nand_correct_data(struct nand_chip *chip, uint8_t *dat, - uint8_t *read_ecc, uint8_t *calc_ecc) -{ - unsigned int diff0, diff1, diff2; - unsigned int bit, byte; - - diff0 = read_ecc[0] ^ calc_ecc[0]; - diff1 = read_ecc[1] ^ calc_ecc[1]; - diff2 = read_ecc[2] ^ calc_ecc[2]; - - if (diff0 == 0 && diff1 == 0 && diff2 == 0) - return 0; /* ECC is ok */ - - /* sometimes people do not think about using the ECC, so check - * to see if we have an 0xff,0xff,0xff read ECC and then ignore - * the error, on the assumption that this is an un-eccd page. - */ - if (read_ecc[0] == 0xff && read_ecc[1] == 0xff && read_ecc[2] == 0xff) - return 0; - - /* Can we correct this ECC (ie, one row and column change). - * Note, this is similar to the 256 error code on smartmedia */ - - if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && - ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && - ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { - /* calculate the bit position of the error */ - - bit = ((diff2 >> 3) & 1) | - ((diff2 >> 4) & 2) | - ((diff2 >> 5) & 4); - - /* calculate the byte position of the error */ - - byte = ((diff2 << 7) & 0x100) | - ((diff1 << 0) & 0x80) | - ((diff1 << 1) & 0x40) | - ((diff1 << 2) & 0x20) | - ((diff1 << 3) & 0x10) | - ((diff0 >> 4) & 0x08) | - ((diff0 >> 3) & 0x04) | - ((diff0 >> 2) & 0x02) | - ((diff0 >> 1) & 0x01); - - dat[byte] ^= (1 << bit); - return 1; - } - - /* if there is only one bit difference in the ECC, then - * one of only a row or column parity has changed, which - * means the error is most probably in the ECC itself */ - - diff0 |= (diff1 << 8); - diff0 |= (diff2 << 16); - - if ((diff0 & ~(1<<fls(diff0))) == 0) - return 1; - - return -1; -} - -static void s3c2410_nand_enable_hwecc(struct nand_chip *chip, int mode) -{ - struct s3c24x0_nand_host *host = chip->priv; - -#ifdef CONFIG_CPU_S3C2410 - writel(readl(host->base + NFCONF) | NFCONF_INITECC , host->base + NFCONF); -#endif -#ifdef CONFIG_CPU_S3C2440 - writel(readl(host->base + NFCONT) | NFCONT_INITECC , host->base + NFCONT); -#endif -} - -static int s3c2410_nand_calculate_ecc(struct nand_chip *chip, const uint8_t *dat, uint8_t *ecc_code) -{ - struct s3c24x0_nand_host *host = chip->priv; - -#ifdef CONFIG_CPU_S3C2410 - ecc_code[0] = readb(host->base + NFECC); - ecc_code[1] = readb(host->base + NFECC + 1); - ecc_code[2] = readb(host->base + NFECC + 2); -#endif -#ifdef CONFIG_CPU_S3C2440 - unsigned long ecc = readl(host->base + NFECC); - - ecc_code[0] = ecc; - ecc_code[1] = ecc >> 8; - ecc_code[2] = ecc >> 16; -#endif - return 0; -} - -static void s3c24x0_nand_select_chip(struct nand_chip *chip, int num) -{ - struct s3c24x0_nand_host *host = chip->priv; - - if (num == -1) - disable_cs(host->base); - else - enable_cs(host->base); -} - -static int s3c24x0_nand_devready(struct nand_chip *chip) -{ - struct s3c24x0_nand_host *host = chip->priv; - - return readw(host->base + NFSTAT) & NFSTAT_BUSY; -} - -static void s3c24x0_nand_hwcontrol(struct nand_chip *chip, int cmd, - unsigned int ctrl) -{ - struct s3c24x0_nand_host *host = chip->priv; - - if (cmd == NAND_CMD_NONE) - return; - /* - * If the CLE should be active, this call is a NAND command - */ - if (ctrl & NAND_CLE) - send_cmd(host->base, cmd); - /* - * If the ALE should be active, this call is a NAND address - */ - if (ctrl & NAND_ALE) - send_addr(host->base, cmd); -} - -static int s3c24x0_nand_inithw(struct s3c24x0_nand_host *host) -{ - struct s3c24x0_nand_platform_data *pdata = host->dev->platform_data; - uint32_t tmp; - - /* reset the NAND controller */ - disable_nand_controller(host->base); - - if (pdata != NULL) - tmp = pdata->nand_timing; - else - /* else slowest possible timing */ - tmp = CALC_NFCONF_TIMING(4, 8, 8); - - /* reenable the NAND controller */ - enable_nand_controller(host->base, tmp); - - return 0; -} - -static int s3c24x0_nand_probe(struct device_d *dev) -{ - struct resource *iores; - struct nand_chip *chip; - struct s3c24x0_nand_platform_data *pdata = dev->platform_data; - struct mtd_info *mtd; - struct s3c24x0_nand_host *host; - int ret; - - /* Allocate memory for MTD device structure and private data */ - host = kzalloc(sizeof(struct s3c24x0_nand_host), GFP_KERNEL); - if (!host) - return -ENOMEM; - - host->dev = dev; - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - host->base = IOMEM(iores->start); - - /* structures must be linked */ - chip = &host->nand; - mtd = nand_to_mtd(chip); - mtd->dev.parent = dev; - - /* init the default settings */ - - /* 50 us command delay time */ - chip->legacy.chip_delay = 50; - chip->priv = host; - - chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W = host->base + NFDATA; - -#ifdef CONFIG_CPU_S3C2440 - chip->legacy.read_buf = s3c2440_nand_read_buf; - chip->legacy.write_buf = s3c2440_nand_write_buf; -#endif - chip->legacy.cmd_ctrl = s3c24x0_nand_hwcontrol; - chip->legacy.dev_ready = s3c24x0_nand_devready; - chip->legacy.select_chip = s3c24x0_nand_select_chip; - - /* we are using the hardware ECC feature of this device */ - chip->ecc.calculate = s3c2410_nand_calculate_ecc; - chip->ecc.correct = s3c2410_nand_correct_data; - chip->ecc.hwctl = s3c2410_nand_enable_hwecc; - - /* - * Setup ECC handling in accordance to the kernel - * - 1 times 512 bytes with 24 bit ECC for small page - * - 8 times 256 bytes with 24 bit ECC each for large page - */ - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.bytes = 3; /* always 24 bit ECC per turn */ - chip->ecc.strength = 1; - -#ifdef CONFIG_CPU_S3C2440 - if (readl(host->base) & 0x8) { - /* large page (2048 bytes per page) */ - chip->ecc.size = 256; - } else -#endif - { - /* small page (512 bytes per page) */ - chip->ecc.size = 512; - mtd_set_ecclayout(mtd, &nand_hw_eccoob); - } - - if (pdata->flash_bbt) { - /* use a flash based bbt */ - chip->bbt_options |= NAND_BBT_USE_FLASH; - } - - ret = s3c24x0_nand_inithw(host); - if (ret != 0) - goto on_error; - - /* Scan to find existence of the device */ - ret = nand_scan(chip, 1); - if (ret != 0) { - ret = -ENXIO; - goto on_error; - } - - return add_mtd_nand_device(mtd, "nand"); - -on_error: - free(host); - return ret; -} - -static struct driver_d s3c24x0_nand_driver = { - .name = "s3c24x0_nand", - .probe = s3c24x0_nand_probe, -}; -device_platform_driver(s3c24x0_nand_driver); - -#ifdef CONFIG_S3C_NAND_BOOT - -static void __nand_boot_init wait_for_completion(void __iomem *host) -{ - while (!(readw(host + NFSTAT) & NFSTAT_BUSY)) - ; -} - -/** - * Convert a page offset into a page address for the NAND - * @param host Where to write the address to - * @param offs Page's offset in the NAND - * @param ps Page size (512 or 2048) - * @param c Address cycle count (3, 4 or 5) - * - * Uses the offset of the page to generate an page address into the NAND. This - * differs when using a 512 byte or 2048 bytes per page NAND. - * The column part of the page address to be generated is always forced to '0'. - */ -static void __nand_boot_init nfc_addr(void __iomem *host, uint32_t offs, - int ps, int c) -{ - send_addr(host, 0); /* column part 1 */ - - if (ps == 512) { - send_addr(host, offs >> 9); - send_addr(host, offs >> 17); - if (c > 3) - send_addr(host, offs >> 25); - } else { - send_addr(host, 0); /* column part 2 */ - send_addr(host, offs >> 11); - send_addr(host, offs >> 19); - if (c > 4) - send_addr(host, offs >> 27); - send_cmd(host, NAND_CMD_READSTART); - } -} - -/** - * Load a sequential count of pages from the NAND into memory - * @param[out] dest Pointer to target area (in SDRAM) - * @param[in] size Bytes to read from NAND device - * @param[in] page Start page to read from - * - * This function must be located in the first 4kiB of the barebox image - * (guess why). - */ -void __nand_boot_init s3c24x0_nand_load_image(void *dest, int size, int page) -{ - void __iomem *host = (void __iomem *)S3C24X0_NAND_BASE; - unsigned pagesize; - int i, cycle; - - /* - * Reenable the NFC and use the default (but slow) access - * timing or the board specific setting if provided. - */ - enable_nand_controller(host, BOARD_DEFAULT_NAND_TIMING); - - /* use the current NAND hardware configuration */ - switch (readl(S3C24X0_NAND_BASE) & 0xf) { - case 0x6: /* 8 bit, 4 addr cycles, 512 bpp, normal NAND */ - pagesize = 512; - cycle = 4; - break; - case 0xc: /* 8 bit, 4 addr cycles, 2048 bpp, advanced NAND */ - pagesize = 2048; - cycle = 4; - break; - case 0xe: /* 8 bit, 5 addr cycles, 2048 bpp, advanced NAND */ - pagesize = 2048; - cycle = 5; - break; - default: - /* we cannot output an error message here :-( */ - disable_nand_controller(host); - return; - } - - enable_cs(host); - - /* Reset the NAND device */ - send_cmd(host, NAND_CMD_RESET); - wait_for_completion(host); - disable_cs(host); - - do { - enable_cs(host); - send_cmd(host, NAND_CMD_READ0); - nfc_addr(host, page * pagesize, pagesize, cycle); - wait_for_completion(host); - /* copy one page (do *not* use readsb() here!)*/ - for (i = 0; i < pagesize; i++) - writeb(readb(host + NFDATA), (void __iomem *)(dest + i)); - disable_cs(host); - - page++; - dest += pagesize; - size -= pagesize; - } while (size >= 0); - - /* disable the controller again */ - disable_nand_controller(host); -} - -#include <asm/sections.h> - -void __nand_boot_init nand_boot(void) -{ - void *dest = _text; - int size = barebox_image_size; - int page = 0; - - s3c24x0_nand_load_image(dest, size, page); -} -#ifdef CONFIG_NAND_S3C_BOOT_DEBUG -#include <command.h> - -static int do_nand_boot_test(int argc, char *argv[]) -{ - void *dest; - int size; - - if (argc < 3) - return COMMAND_ERROR_USAGE; - - dest = (void *)strtoul_suffix(argv[1], NULL, 0); - size = strtoul_suffix(argv[2], NULL, 0); - - s3c24x0_nand_load_image(dest, size, 0); - - /* re-enable the controller again, as this was a test only */ - enable_nand_controller((void *)S3C24X0_NAND_BASE, - BOARD_DEFAULT_NAND_TIMING); - - return 0; -} - -BAREBOX_CMD_START(nand_boot_test) - .cmd = do_nand_boot_test, - BAREBOX_CMD_DESC("load an image from NAND") - BAREBOX_CMD_OPTS("DEST SIZE") - BAREBOX_CMD_GROUP(CMD_GRP_BOOT) -BAREBOX_CMD_END -#endif - -#endif /* CONFIG_S3C_NAND_BOOT */ - -/** - * @file - * @brief Support for various kinds of NAND devices - * - * ECC handling in this driver (in accordance to the current 2.6.38 kernel): - * - for small page NANDs it generates 3 ECC bytes out of 512 data bytes - * - for large page NANDs it generates 24 ECC bytes out of 2048 data bytes - * - * As small page NANDs are using 48 bits ECC per default, this driver uses a - * local OOB layout description, to shrink it down to 24 bits. This is a bad - * idea, but we cannot change it here, as the kernel is using this layout. - * - * For large page NANDs this driver uses the default layout, as the kernel does. - */ diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig new file mode 100644 index 0000000000..cf65a90db1 --- /dev/null +++ b/drivers/mtd/nand/raw/Kconfig @@ -0,0 +1,175 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config MTD_RAW_NAND + bool + +menuconfig NAND + bool "Raw/Parallel NAND Device Support" + select MTD_NAND_CORE + select MTD_NAND_ECC + select MTD_RAW_NAND + help + This enables support for accessing all type of raw/parallel + NAND flash devices. For further information see + <http://www.linux-mtd.infradead.org/doc/nand.html>. + +if MTD_RAW_NAND + +config MTD_NAND_ECC_SOFT + bool + prompt "Support software ecc" + +config NAND_ECC_HW_SYNDROME + bool + prompt "Support syndrome hardware ecc controllers" + +config NAND_ALLOW_ERASE_BAD + bool + depends on MTD_WRITE + prompt "Add device parameter to allow erasing bad blocks" + help + This adds a 'erasebad' device parameter to nand devices. When set + to '1' it will be allowed to erase bad blocks. This is a potientially + dangerous operation, so if unsure say no to this option. + +comment "Raw/parallel NAND flash controllers" + +config NAND_IMX + bool + prompt "i.MX21 to 53 NAND driver aka 'mxc', for NFC" + depends on ARCH_IMX + help + Support for NAND flash on Freescale/NXP i.MX devices. This is for the + "MXC" series: i.MX21/25/27/31/35/51/53. + + This is not for the "MXS" series i.MX processors (23 & 28), or i.MX6 + and later, which use the GPMI NAND controller from the MXS series. + See the i.MX 'mxs' driver for those chips. + +config NAND_FSL_IFC + bool + prompt "FSL IFC NAND driver" + depends on ARCH_LAYERSCAPE + help + Freescale IFC NAND driver for various chips. + +config NAND_MXS + bool + select STMP_DEVICE + prompt "i.MX23/28 & 6+ NAND driver aka 'mxs', for GPMI" + depends on MXS_APBH_DMA + help + Support for NAND flash on Freescale/NXP i.MX devices. This is for the + "MXS" series: i.MX23/28 and all i.MX6 and later SoCs. + + This is not for the "MXC" series of i.MX processors in the i.MX21 to + i.MX53 range. See the i.MX "mxc" driver for those chips. + +config NAND_OMAP_GPMC + tristate "NAND Flash Support for GPMC based OMAP platforms" + depends on OMAP_GPMC + depends on BUS_OMAP_GPMC + help + Support for NAND flash using GPMC. GPMC is a common memory + interface found on Texas Instrument's OMAP platforms + +config MTD_NAND_OMAP_ELM + bool "Support for ELM (Error Location Module) on OMAP platforms" + depends on NAND_OMAP_GPMC || COMPILE_TEST + help + This config enables the ELM hardware engine, which can be used to + locate and correct errors when using BCH ECC scheme. This offloads + the cpu from doing ECC error searching and correction. However some + legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine + so this is optional for them. + +config NAND_ORION + bool + prompt "Marvell Orion NAND driver" + depends on (ARM && !CPU_32v4T) && (ARCH_KIRKWOOD || COMPILE_TEST) + help + Support for the Orion NAND controller, present in Kirkwood SoCs. + +config NAND_MRVL_NFC + bool + prompt "Marvell PXA3xx NAND driver" + depends on ARCH_ARMADA_370 || ARCH_ARMADA_XP || ARCH_PXA3XX || COMPILE_TEST + help + Support for the PXA3xx NAND controller, present in Armada 370/XP and + PXA3xx SoCs. + +config NAND_STM32 + bool "Support for NAND controller on STM32MP SoCs" + depends on ARCH_STM32MP || COMPILE_TEST + select STM32_FMC2_EBI if ARCH_STM32MP + select RESET_CONTROLLER if ARCH_STM32MP + select RESET_SIMPLE if ARCH_STM32MP + help + Enables support for NAND Flash chips on SoCs containing the FMC2 + NAND controller. This controller is found on STM32MP SoCs. + The controller supports a maximum 8k page size and supports + a maximum 8-bit correction error per sector of 512 bytes. + +config NAND_ATMEL + bool + prompt "Atmel (AT91SAM9xxx) NAND driver" + select GENERIC_ALLOCATOR if OFDEVICE + depends on ARCH_AT91 || (OFDEVICE && COMPILE_TEST) + +config NAND_ATMEL_LEGACY + def_bool !AT91_MULTI_BOARDS || SOC_AT91SAM9 + depends on NAND_ATMEL + help + Select legacy driver for non-DT-enabled platforms + and for the deprecated non-EBI binding. + + The deprecated binding is currently the only one + support for AT91SAM9. + +config NAND_ATMEL_PMECC + bool + prompt "PMECC support" + depends on NAND_ATMEL_LEGACY + help + Support for PMECC present on the SoC sam9x5 and sam9n12 + +config MTD_NAND_ECC_SW_HAMMING_SMC + bool "NAND ECC Smart Media byte order" + default n + help + Software ECC according to the Smart Media Specification. + The original Linux implementation had byte 0 and 1 swapped. + +config MTD_NAND_NOMADIK + tristate "ST Nomadik 8815 NAND support" + depends on ARCH_NOMADIK + help + Driver for the NAND flash controller on the Nomadik, with ECC. + +config MTD_NAND_DENALI + tristate "Support Denali NAND controller" + depends on HAS_DMA + help + Enable support for the Denali NAND controller. This should be + combined with either the PCI or platform drivers to provide device + registration. + +config MTD_NAND_DENALI_DT + tristate "Support Denali NAND controller as a DT device" + depends on HAVE_CLK && MTD_NAND_DENALI + help + Enable the driver for NAND flash on platforms using a Denali NAND + controller as a DT device. + +if MTD_NAND_DENALI + +config MTD_NAND_DENALI_TIMING_MODE + int "Overrides default ONFI timing mode." + default -1 + range -1 5 + help + -1 indicates use default timings + +endif + +endif diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile new file mode 100644 index 0000000000..38c7cc809d --- /dev/null +++ b/drivers/mtd/nand/raw/Makefile @@ -0,0 +1,23 @@ +# Generic NAND options + +obj-$(CONFIG_MTD_RAW_NAND) += nand_ecc.o +obj-$(CONFIG_MTD_RAW_NAND) += nand_ids.o +obj-$(CONFIG_MTD_RAW_NAND) += nand_base.o nand_timings.o +obj-$(CONFIG_MTD_RAW_NAND) += nand_legacy.o nand_onfi.o nand_amd.o +obj-$(CONFIG_MTD_RAW_NAND) += nand_esmt.o nand_hynix.o nand_macronix.o +obj-$(CONFIG_MTD_RAW_NAND) += nand_micron.o nand_samsung.o nand_toshiba.o +obj-$(CONFIG_MTD_RAW_NAND) += nand_jedec.o +obj-$(CONFIG_MTD_RAW_NAND) += nand_bbt.o + +obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o +obj-$(CONFIG_NAND_IMX) += mxc_nand.o +obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o +obj-$(CONFIG_MTD_NAND_OMAP_ELM) += omap_elm.o +obj-$(CONFIG_NAND_ORION) += nand_orion.o +obj-$(CONFIG_NAND_STM32) += stm32_fmc2_nand.o +obj-$(CONFIG_NAND_MRVL_NFC) += nand_mrvl_nfc.o +obj-$(CONFIG_NAND_ATMEL) += atmel/ +obj-$(CONFIG_NAND_MXS) += nand_mxs.o +obj-$(CONFIG_MTD_NAND_DENALI) += nand_denali.o +obj-$(CONFIG_MTD_NAND_DENALI_DT) += nand_denali_dt.o +obj-$(CONFIG_NAND_FSL_IFC) += nand_fsl_ifc.o diff --git a/drivers/mtd/nand/raw/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile new file mode 100644 index 0000000000..0f739c3f31 --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_OFDEVICE) += nand-controller.o pmecc.o +obj-$(CONFIG_NAND_ATMEL_LEGACY) += legacy.o diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/raw/atmel/atmel_nand_ecc.h index e39aada36b..c7864d96dd 100644 --- a/drivers/mtd/nand/atmel_nand_ecc.h +++ b/drivers/mtd/nand/raw/atmel/atmel_nand_ecc.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * Error Corrected Code Controller (ECC) - System peripherals regsters. * Based on AT91SAM9260 datasheet revision B. * * Copyright (C) 2007 Andrew Victor * Copyright (C) 2007 - 2012 Atmel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #ifndef ATMEL_NAND_ECC_H diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/raw/atmel/legacy.c index 58d53b7a78..5e2fd540ea 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/raw/atmel/legacy.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2003 Rick Bronson * @@ -14,12 +15,6 @@ * Derived from Das U-Boot source code * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <common.h> @@ -38,7 +33,7 @@ #include <linux/err.h> #include <io.h> -#include <mach/board.h> +#include <mach/at91/board.h> #include <errno.h> @@ -80,7 +75,7 @@ struct atmel_nand_host { struct nand_chip nand_chip; void __iomem *io_base; struct atmel_nand_data *board; - struct device_d *dev; + struct device *dev; void __iomem *ecc; int pmecc_bytes_per_sector; @@ -840,8 +835,8 @@ static int pmecc_build_galois_table(unsigned int mm, int16_t *index_of, return 0; } -static int __init atmel_pmecc_nand_init_params(struct device_d *dev, - struct atmel_nand_host *host) +static int __init atmel_pmecc_nand_init_params(struct device *dev, + struct atmel_nand_host *host) { struct resource *iores; struct nand_chip *nand_chip = &host->nand_chip; @@ -858,10 +853,6 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev, if (IS_ERR(iores)) return PTR_ERR(iores); host->ecc = IOMEM(iores->start); - if (IS_ERR(host->ecc)) { - dev_err(host->dev, "ioremap failed\n"); - return -EIO; - } iores = dev_request_mem_resource(dev, 2); if (IS_ERR(iores)) { @@ -918,7 +909,7 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev, dev_err(host->dev, "No room for ECC bytes\n"); return -EINVAL; } - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); + mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); break; case 512: case 1024: @@ -928,7 +919,8 @@ static int __init atmel_pmecc_nand_init_params(struct device_d *dev, default: /* page size not handled by HW ECC */ /* switching back to soft ECC */ - nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + nand_chip->ecc.algo = NAND_ECC_ALGO_HAMMING; return 0; } @@ -1204,7 +1196,7 @@ static int atmel_nand_of_init(struct atmel_nand_host *host, struct device_node * return 0; } -static int atmel_hw_nand_init_params(struct device_d *dev, +static int atmel_hw_nand_init_params(struct device *dev, struct atmel_nand_host *host) { struct resource *iores; @@ -1215,10 +1207,6 @@ static int atmel_hw_nand_init_params(struct device_d *dev, if (IS_ERR(iores)) return PTR_ERR(iores); host->ecc = IOMEM(iores->start); - if (IS_ERR(host->ecc)) { - dev_err(host->dev, "ioremap failed\n"); - return -EIO; - } /* ECC is calculated for the whole page (1 step) */ nand_chip->ecc.size = mtd->writesize; @@ -1248,7 +1236,7 @@ static int atmel_hw_nand_init_params(struct device_d *dev, default: /* page size not handled by HW ECC */ /* switching back to soft ECC */ - nand_chip->ecc.mode = NAND_ECC_SOFT; + nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; return 0; } @@ -1266,7 +1254,7 @@ static int atmel_hw_nand_init_params(struct device_d *dev, /* * Probe for the NAND device. */ -static int __init atmel_nand_probe(struct device_d *dev) +static int __init atmel_nand_probe(struct device *dev) { struct resource *iores; struct atmel_nand_data *pdata = NULL; @@ -1294,8 +1282,8 @@ static int __init atmel_nand_probe(struct device_d *dev) host->board = pdata; host->dev = dev; - if (dev->device_node) { - res = atmel_nand_of_init(host, dev->device_node); + if (dev->of_node) { + res = atmel_nand_of_init(host, dev->of_node); if (res) goto err_no_card; } else { @@ -1347,21 +1335,18 @@ static int __init atmel_nand_probe(struct device_d *dev) } } - nand_chip->ecc.mode = pdata->ecc_mode; nand_chip->ecc.strength = pdata->ecc_strength ? : 1; - nand_chip->ecc.size = 1 << pdata->ecc_size_shift ? : 512; + nand_chip->ecc.size = 1 << (pdata->ecc_size_shift ? : 9); - if (pdata->ecc_mode == NAND_ECC_HW) { - nand_chip->ecc.mode = NAND_ECC_HW; + if (pdata->ecc_mode == NAND_ECC_SOFT) { + nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + nand_chip->ecc.algo = NAND_ECC_ALGO_HAMMING; + } else { + nand_chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; } nand_chip->legacy.chip_delay = 40; /* 40us command delay time */ - if (IS_ENABLED(CONFIG_NAND_ECC_BCH) && - pdata->ecc_mode == NAND_ECC_SOFT_BCH) { - nand_chip->ecc.mode = NAND_ECC_SOFT_BCH; - } - if (host->board->bus_width_16) { /* 16-bit bus width */ nand_chip->options |= NAND_BUSWIDTH_16; nand_chip->legacy.read_buf = atmel_read_buf16; @@ -1410,7 +1395,7 @@ static int __init atmel_nand_probe(struct device_d *dev) host->ecc_code = xmalloc(mtd->oobsize); - if (nand_chip->ecc.mode == NAND_ECC_HW) { + if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { if (IS_ENABLED(CONFIG_NAND_ATMEL_PMECC) && pdata->has_pmecc) res = atmel_pmecc_nand_init_params(dev, host); else @@ -1446,7 +1431,7 @@ static struct of_device_id atmel_nand_dt_ids[] = { { /* sentinel */ } }; -static struct driver_d atmel_nand_driver = { +static struct driver atmel_nand_driver = { .name = "atmel_nand", .probe = atmel_nand_probe, .of_compatible = DRV_OF_COMPAT(atmel_nand_dt_ids), diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c new file mode 100644 index 0000000000..5188a11cbe --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -0,0 +1,2049 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017 ATMEL + * Copyright 2017 Free Electrons + * + * Author: Boris Brezillon <boris.brezillon@free-electrons.com> + * + * Derived from the atmel_nand.c driver which contained the following + * copyrights: + * + * Copyright 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) + * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c (removed in v3.8) + * Copyright 2000 Steven J. Hill (sjhill@cotw.com) + * + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * Copyright 2012 ATMEL, Hong Xu + * + * Add Nand Flash Controller support for SAMA5 SoC + * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) + * + * A few words about the naming convention in this file. This convention + * applies to structure and function names. + * + * Prefixes: + * + * - atmel_nand_: all generic structures/functions + * - atmel_smc_nand_: all structures/functions specific to the SMC interface + * (at91sam9 and avr32 SoCs) + * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface + * (sama5 SoCs and later) + * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block + * that is available in the HSMC block + * - <soc>_nand_: all SoC specific structures/functions + */ + +#include <linux/clk.h> +#include <linux/genalloc.h> +#include <linux/gpio/consumer.h> +#include <mfd/syscon.h> +#include <linux/mfd/syscon/atmel-matrix.h> +#include <linux/mfd/syscon/atmel-smc.h> +#include <module.h> +#include <linux/mtd/rawnand.h> +#include <of_address.h> +#include <of.h> +#include <of_device.h> +#include <linux/iopoll.h> +#include <linux/regmap.h> +#include <soc/at91/atmel-sfr.h> + +#include "pmecc.h" + +#define ATMEL_HSMC_NFC_CFG 0x0 +#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24) +#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24) +#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20)) +#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16) +#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13) +#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12) +#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9) +#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8) +#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0) +#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1) + +#define ATMEL_HSMC_NFC_CTRL 0x4 +#define ATMEL_HSMC_NFC_CTRL_EN BIT(0) +#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1) + +#define ATMEL_HSMC_NFC_SR 0x8 +#define ATMEL_HSMC_NFC_IER 0xc +#define ATMEL_HSMC_NFC_IDR 0x10 +#define ATMEL_HSMC_NFC_IMR 0x14 +#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1) +#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4) +#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5) +#define ATMEL_HSMC_NFC_SR_BUSY BIT(8) +#define ATMEL_HSMC_NFC_SR_WR BIT(11) +#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12) +#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16) +#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17) +#define ATMEL_HSMC_NFC_SR_DTOE BIT(20) +#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21) +#define ATMEL_HSMC_NFC_SR_AWB BIT(22) +#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23) +#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \ + ATMEL_HSMC_NFC_SR_UNDEF | \ + ATMEL_HSMC_NFC_SR_AWB | \ + ATMEL_HSMC_NFC_SR_NFCASE) +#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24) + +#define ATMEL_HSMC_NFC_ADDR 0x18 +#define ATMEL_HSMC_NFC_BANK 0x1c + +#define ATMEL_NFC_MAX_RB_ID 7 + +#define ATMEL_NFC_SRAM_SIZE 0x2400 + +#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2)) +#define ATMEL_NFC_VCMD2 BIT(18) +#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19) +#define ATMEL_NFC_CSID(cs) ((cs) << 22) +#define ATMEL_NFC_DATAEN BIT(25) +#define ATMEL_NFC_NFCWR BIT(26) + +#define ATMEL_NFC_MAX_ADDR_CYCLES 5 + +#define ATMEL_NAND_ALE_OFFSET BIT(21) +#define ATMEL_NAND_CLE_OFFSET BIT(22) + +#define DEFAULT_TIMEOUT_MS 1000 + +enum atmel_nand_rb_type { + ATMEL_NAND_NO_RB, + ATMEL_NAND_NATIVE_RB, + ATMEL_NAND_GPIO_RB, +}; + +struct atmel_nand_rb { + enum atmel_nand_rb_type type; + union { + struct gpio_desc *gpio; + int id; + }; +}; + +struct atmel_nand_cs { + int id; + struct atmel_nand_rb rb; + struct gpio_desc *csgpio; + struct { + void __iomem *virt; + } io; + + struct atmel_smc_cs_conf smcconf; +}; + +struct atmel_nand { + struct list_head node; + struct device *dev; + struct nand_chip base; + struct atmel_nand_cs *activecs; + struct atmel_pmecc_user *pmecc; + struct gpio_desc *cdgpio; + int numcs; + struct atmel_nand_cs cs[]; +}; + +static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip) +{ + return container_of(chip, struct atmel_nand, base); +} + +enum atmel_nfc_data_xfer { + ATMEL_NFC_NO_DATA, + ATMEL_NFC_READ_DATA, + ATMEL_NFC_WRITE_DATA, +}; + +struct atmel_nfc_op { + u8 cs; + u8 ncmds; + u8 cmds[2]; + u8 naddrs; + u8 addrs[5]; + enum atmel_nfc_data_xfer data; + u32 wait; + u32 errors; +}; + +struct atmel_nand_controller; +struct atmel_nand_controller_caps; + +struct atmel_nand_controller_ops { + int (*probe)(struct device *dev, + const struct atmel_nand_controller_caps *caps); + void (*nand_init)(struct atmel_nand_controller *nc, + struct atmel_nand *nand); + 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 { + u32 ale_offs; + u32 cle_offs; + const char *ebi_csa_regmap_name; + const struct atmel_nand_controller_ops *ops; +}; + +struct atmel_nand_controller { + struct nand_controller base; + const struct atmel_nand_controller_caps *caps; + struct device *dev; + struct regmap *smc; + struct atmel_pmecc *pmecc; + struct list_head chips; + struct clk *mck; +}; + +static inline struct atmel_nand_controller * +to_nand_controller(struct nand_controller *ctl) +{ + return container_of(ctl, struct atmel_nand_controller, base); +} + +struct atmel_smc_nand_ebi_csa_cfg { + u32 offs; + u32 nfd0_on_d16; +}; + +struct atmel_smc_nand_controller { + struct atmel_nand_controller base; + struct regmap *ebi_csa_regmap; + struct atmel_smc_nand_ebi_csa_cfg *ebi_csa; +}; + +static inline struct atmel_smc_nand_controller * +to_smc_nand_controller(struct nand_controller *ctl) +{ + return container_of(to_nand_controller(ctl), + struct atmel_smc_nand_controller, base); +} + +struct atmel_hsmc_nand_controller { + struct atmel_nand_controller base; + struct { + struct gen_pool *pool; + void __iomem *virt; + } sram; + const struct atmel_hsmc_reg_layout *hsmc_layout; + struct regmap *io; + struct atmel_nfc_op op; + u32 cfg; +}; + +static inline struct atmel_hsmc_nand_controller * +to_hsmc_nand_controller(struct nand_controller *ctl) +{ + return container_of(to_nand_controller(ctl), + struct atmel_hsmc_nand_controller, base); +} + +static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status) +{ + op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS; + op->wait ^= status & op->wait; + + return !op->wait || op->errors; +} + +static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, + unsigned int timeout_ms) +{ + u32 status; + int ret; + + if (!timeout_ms) + timeout_ms = DEFAULT_TIMEOUT_MS; + + + ret = regmap_read_poll_timeout(nc->base.smc, + ATMEL_HSMC_NFC_SR, status, + atmel_nfc_op_done(&nc->op, + status), + timeout_ms * 1000); + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) { + dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n"); + ret = -ETIMEDOUT; + } + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) { + dev_err(nc->base.dev, "Access to an undefined area\n"); + ret = -EIO; + } + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) { + dev_err(nc->base.dev, "Access while busy\n"); + ret = -EIO; + } + + if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) { + dev_err(nc->base.dev, "Wrong access size\n"); + ret = -EIO; + } + + return ret; +} + +static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc) +{ + 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; + + 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, 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_data_in(struct atmel_nand *nand, void *buf, + unsigned int len, bool force_8bit) +{ + struct atmel_nand_controller *nc; + + nc = to_nand_controller(nand->base.controller); + + 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_data_out(struct atmel_nand *nand, const void *buf, + unsigned int len, bool force_8bit) +{ + struct atmel_nand_controller *nc; + + nc = to_nand_controller(nand->base.controller); + + 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_waitrdy(struct atmel_nand *nand, unsigned int timeout_ms) +{ + if (nand->activecs->rb.type == ATMEL_NAND_NO_RB) + return nand_soft_waitrdy(&nand->base, timeout_ms); + + return nand_gpio_waitrdy(&nand->base, nand->activecs->rb.gpio, + timeout_ms); +} + +static int atmel_hsmc_nand_waitrdy(struct atmel_nand *nand, + unsigned int timeout_ms) +{ + struct atmel_hsmc_nand_controller *nc; + u32 status, mask; + + if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) + return atmel_nand_waitrdy(nand, timeout_ms); + + nc = to_hsmc_nand_controller(nand->base.controller); + mask = ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); + return regmap_read_poll_timeout(nc->base.smc, ATMEL_HSMC_NFC_SR, + status, status & mask, + timeout_ms * 1000); +} + +static void atmel_nand_select_target(struct atmel_nand *nand, + unsigned int cs) +{ + nand->activecs = &nand->cs[cs]; +} + +static void atmel_hsmc_nand_select_target(struct atmel_nand *nand, + unsigned int cs) +{ + 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; + + nand->activecs = &nand->cs[cs]; + nc = to_hsmc_nand_controller(nand->base.controller); + if (nc->cfg == cfg) + return; + + 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, + cfg); + nc->cfg = cfg; +} + +static int atmel_smc_nand_exec_instr(struct atmel_nand *nand, + const struct nand_op_instr *instr) +{ + struct atmel_nand_controller *nc; + unsigned int i; + + 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; + } + + return -EINVAL; +} + +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; + + if (check_only) + return 0; + + 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 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); + + nc->op.cs = nand->activecs->id; + for (i = 0; i < subop->ninstrs; i++) { + const struct nand_op_instr *instr = &subop->instrs[i]; + + if (instr->type == NAND_OP_CMD_INSTR) { + nc->op.cmds[nc->op.ncmds++] = instr->ctx.cmd.opcode; + continue; + } + + 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++; + } + } + + return atmel_nfc_exec_op(nc); +} + +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); + + 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); + + 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); + + 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, + bool oob_required) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_hsmc_nand_controller *nc; + + nc = to_hsmc_nand_controller(chip->controller); + + /* Falling back to CPU copy. */ + memcpy_toio(nc->sram.virt, buf, mtd->writesize); + + if (oob_required) + memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi, + mtd->oobsize); +} + +static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf, + bool oob_required) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_hsmc_nand_controller *nc; + + nc = to_hsmc_nand_controller(chip->controller); + + memcpy_fromio(buf, nc->sram.virt, mtd->writesize); + + if (oob_required) + memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize, + mtd->oobsize); +} + +static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_hsmc_nand_controller *nc; + + nc = to_hsmc_nand_controller(chip->controller); + + if (column >= 0) { + nc->op.addrs[nc->op.naddrs++] = column; + + /* + * 2 address cycles for the column offset on large page NANDs. + */ + if (mtd->writesize > 512) + nc->op.addrs[nc->op.naddrs++] = column >> 8; + } + + if (page >= 0) { + nc->op.addrs[nc->op.naddrs++] = page; + nc->op.addrs[nc->op.naddrs++] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) + nc->op.addrs[nc->op.naddrs++] = page >> 16; + } +} + +static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(chip->controller); + + if (raw) + return 0; + + ret = atmel_pmecc_enable(nand->pmecc, op); + if (ret) + dev_err(nc->dev, + "Failed to enable ECC engine (err = %d)\n", ret); + + return ret; +} + +static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + + if (!raw) + atmel_pmecc_disable(nand->pmecc); +} + +static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand_controller *nc; + struct mtd_oob_region oobregion; + void *eccbuf; + int ret, i; + + nc = to_nand_controller(chip->controller); + + if (raw) + return 0; + + ret = atmel_pmecc_wait_rdy(nand->pmecc); + if (ret) { + dev_err(nc->dev, + "Failed to transfer NAND page data (err = %d)\n", + ret); + return ret; + } + + mtd_ooblayout_ecc(mtd, 0, &oobregion); + eccbuf = chip->oob_poi + oobregion.offset; + + for (i = 0; i < chip->ecc.steps; i++) { + atmel_pmecc_get_generated_eccbytes(nand->pmecc, i, + eccbuf); + eccbuf += chip->ecc.bytes; + } + + return 0; +} + +static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf, + bool raw) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand_controller *nc; + struct mtd_oob_region oobregion; + int ret, i, max_bitflips = 0; + void *databuf, *eccbuf; + + nc = to_nand_controller(chip->controller); + + if (raw) + return 0; + + ret = atmel_pmecc_wait_rdy(nand->pmecc); + if (ret) { + dev_err(nc->dev, + "Failed to read NAND page data (err = %d)\n", + ret); + return ret; + } + + mtd_ooblayout_ecc(mtd, 0, &oobregion); + eccbuf = chip->oob_poi + oobregion.offset; + databuf = buf; + + for (i = 0; i < chip->ecc.steps; i++) { + ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf, + eccbuf); + if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc)) + ret = nand_check_erased_ecc_chunk(databuf, + chip->ecc.size, + eccbuf, + chip->ecc.bytes, + NULL, 0, + chip->ecc.strength); + + if (ret >= 0) { + mtd->ecc_stats.corrected += ret; + max_bitflips = max(ret, max_bitflips); + } else { + mtd->ecc_stats.failed++; + } + + databuf += chip->ecc.size; + eccbuf += chip->ecc.bytes; + } + + return max_bitflips; +} + +static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, + bool oob_required, int page, bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand *nand = to_atmel_nand(chip); + int ret; + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); + if (ret) + return ret; + + nand_write_data_op(chip, buf, mtd->writesize, false); + + ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); + if (ret) { + atmel_pmecc_disable(nand->pmecc); + return ret; + } + + atmel_nand_pmecc_disable(chip, raw); + + nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + + return nand_prog_page_end_op(chip); +} + +static int atmel_nand_pmecc_write_page(struct nand_chip *chip, const u8 *buf, + int oob_required, int page) +{ + return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false); +} + +static int atmel_nand_pmecc_write_page_raw(struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true); +} + +static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, + bool oob_required, int page, bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + nand_read_page_op(chip, page, 0, NULL, 0); + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); + if (ret) + return ret; + + 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; +} + +static int atmel_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false); +} + +static int atmel_nand_pmecc_read_page_raw(struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true); +} + +static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, + const u8 *buf, bool oob_required, + int page, bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_hsmc_nand_controller *nc; + 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); + + nc->op.cmds[0] = NAND_CMD_SEQIN; + nc->op.ncmds = 1; + atmel_nfc_set_op_addr(chip, page, 0x0); + nc->op.cs = nand->activecs->id; + nc->op.data = ATMEL_NFC_WRITE_DATA; + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); + if (ret) + return ret; + + ret = atmel_nfc_exec_op(nc); + if (ret) { + atmel_nand_pmecc_disable(chip, raw); + dev_err(nc->base.dev, + "Failed to transfer NAND page data (err = %d)\n", + ret); + return ret; + } + + ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); + + atmel_nand_pmecc_disable(chip, raw); + + if (ret) + return ret; + + nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); + + return nand_prog_page_end_op(chip); +} + +static int atmel_hsmc_nand_pmecc_write_page(struct nand_chip *chip, + const u8 *buf, int oob_required, + int page) +{ + return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, + false); +} + +static int atmel_hsmc_nand_pmecc_write_page_raw(struct nand_chip *chip, + const u8 *buf, + int oob_required, int page) +{ + return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, + true); +} + +static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, + bool oob_required, int page, + bool raw) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct atmel_nand *nand = to_atmel_nand(chip); + struct atmel_hsmc_nand_controller *nc; + int ret; + + atmel_hsmc_nand_select_target(nand, chip->cur_cs); + nc = to_hsmc_nand_controller(chip->controller); + + /* + * Optimized read page accessors only work when the NAND R/B pin is + * 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) + return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, + raw); + + nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0; + + if (mtd->writesize > 512) + nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART; + + atmel_nfc_set_op_addr(chip, page, 0x0); + nc->op.cs = nand->activecs->id; + nc->op.data = ATMEL_NFC_READ_DATA; + + ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); + if (ret) + return ret; + + ret = atmel_nfc_exec_op(nc); + if (ret) { + atmel_nand_pmecc_disable(chip, raw); + dev_err(nc->base.dev, + "Failed to load NAND page data (err = %d)\n", + ret); + return ret; + } + + atmel_nfc_copy_from_sram(chip, buf, true); + + ret = atmel_nand_pmecc_correct_data(chip, buf, raw); + + atmel_nand_pmecc_disable(chip, raw); + + return ret; +} + +static int atmel_hsmc_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, + false); +} + +static int atmel_hsmc_nand_pmecc_read_page_raw(struct nand_chip *chip, + u8 *buf, int oob_required, + int page) +{ + return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, + true); +} + +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 atmel_nand *nand = to_atmel_nand(chip); + struct atmel_nand_controller *nc; + struct atmel_pmecc_user_req req; + struct device_node *dn; + + nc = to_nand_controller(chip->controller); + + if (!nc->pmecc) { + dev_err(nc->dev, "HW ECC not supported\n"); + return -ENOTSUPP; + } + + dn = nand_get_flash_node(chip); + + if (of_property_read_bool(dn, "nand-ecc-maximize")) + req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; + else if (chip->ecc.strength) + req.ecc.strength = chip->ecc.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 (requirements->step_size) + req.ecc.sectorsize = requirements->step_size; + else + req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO; + + req.pagesize = mtd->writesize; + req.oobsize = mtd->oobsize; + + if (mtd->writesize <= 512) { + req.ecc.bytes = 4; + req.ecc.ooboffset = 0; + } else { + req.ecc.bytes = mtd->oobsize - 2; + req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO; + } + + nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req); + if (IS_ERR(nand->pmecc)) + return PTR_ERR(nand->pmecc); + + 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_get_large_page_ooblayout()); + + return 0; +} + +static int atmel_nand_ecc_init(struct nand_chip *chip) +{ + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(chip->controller); + + 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_ENGINE_TYPE_ON_HOST: + ret = atmel_nand_pmecc_init(chip); + if (ret) + return ret; + + chip->ecc.read_page = atmel_nand_pmecc_read_page; + chip->ecc.write_page = atmel_nand_pmecc_write_page; + chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw; + chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw; + break; + + default: + /* Other modes are not supported. */ + dev_err(nc->dev, "Unsupported ECC mode: %d\n", + chip->ecc.engine_type); + return -ENOTSUPP; + } + + return 0; +} + +static int atmel_hsmc_nand_ecc_init(struct nand_chip *chip) +{ + int ret; + + ret = atmel_nand_ecc_init(chip); + if (ret) + return ret; + + if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return 0; + + /* Adjust the ECC operations for the HSMC IP. */ + chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page; + chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; + chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; + chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; + + return 0; +} + +static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, + const struct nand_interface_config *conf, + struct atmel_smc_cs_conf *smcconf) +{ + u32 ncycles, totalcycles, timeps, mckperiodps; + struct atmel_nand_controller *nc; + int ret; + + nc = to_nand_controller(nand->base.controller); + + /* DDR interface not supported. */ + if (!nand_interface_is_sdr(conf)) + return -ENOTSUPP; + + /* + * tRC < 30ns implies EDO mode. This controller does not support this + * mode. + */ + if (conf->timings.sdr.tRC_min < 30000) + return -ENOTSUPP; + + atmel_smc_cs_conf_init(smcconf); + + mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck); + mckperiodps *= 1000; + + /* + * Set write pulse timing. This one is easy to extract: + * + * NWE_PULSE = tWP + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps); + totalcycles = ncycles; + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * The write setup timing depends on the operation done on the NAND. + * All operations goes through the same data bus, but the operation + * type depends on the address we are writing to (ALE/CLE address + * lines). + * Since we have no way to differentiate the different operations at + * the SMC level, we must consider the worst case (the biggest setup + * time among all operation types): + * + * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE + */ + timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min, + conf->timings.sdr.tALS_min); + timeps = max(timeps, conf->timings.sdr.tDS_min); + ncycles = DIV_ROUND_UP(timeps, mckperiodps); + ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0; + totalcycles += ncycles; + ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * As for the write setup timing, the write hold timing depends on the + * operation done on the NAND: + * + * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH) + */ + timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min, + conf->timings.sdr.tALH_min); + timeps = max3(timeps, conf->timings.sdr.tDH_min, + conf->timings.sdr.tWH_min); + ncycles = DIV_ROUND_UP(timeps, mckperiodps); + totalcycles += ncycles; + + /* + * The write cycle timing is directly matching tWC, but is also + * dependent on the other timings on the setup and hold timings we + * calculated earlier, which gives: + * + * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD) + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps); + ncycles = max(totalcycles, ncycles); + ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * We don't want the CS line to be toggled between each byte/word + * transfer to the NAND. The only way to guarantee that is to have the + * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: + * + * NCS_WR_PULSE = NWE_CYCLE + */ + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * As for the write setup timing, the read hold timing depends on the + * operation done on the NAND: + * + * NRD_HOLD = max(tREH, tRHOH) + */ + timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min); + ncycles = DIV_ROUND_UP(timeps, mckperiodps); + totalcycles = ncycles; + + /* + * TDF = tRHZ - NRD_HOLD + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps); + ncycles -= totalcycles; + + /* + * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and + * we might end up with a config that does not fit in the TDF field. + * Just take the max value in this case and hope that the NAND is more + * tolerant than advertised. + */ + if (ncycles > ATMEL_SMC_MODE_TDF_MAX) + ncycles = ATMEL_SMC_MODE_TDF_MAX; + else if (ncycles < ATMEL_SMC_MODE_TDF_MIN) + ncycles = ATMEL_SMC_MODE_TDF_MIN; + + smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) | + ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; + + /* + * Read pulse timing directly matches tRP: + * + * NRD_PULSE = tRP + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); + totalcycles += ncycles; + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * The write cycle timing is directly matching tWC, but is also + * dependent on the setup and hold timings we calculated earlier, + * which gives: + * + * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD) + * + * NRD_SETUP is always 0. + */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps); + ncycles = max(totalcycles, ncycles); + ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT, + ncycles); + if (ret) + return ret; + + /* + * We don't want the CS line to be toggled between each byte/word + * transfer from the NAND. The only way to guarantee that is to have + * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: + * + * NCS_RD_PULSE = NRD_CYCLE + */ + ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT, + ncycles); + if (ret) + return ret; + + /* Txxx timings are directly matching tXXX ones. */ + ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TCLR_SHIFT, + ncycles); + if (ret) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TADL_SHIFT, + ncycles); + /* + * Version 4 of the ONFI spec mandates that tADL be at least 400 + * nanoseconds, but, depending on the master clock rate, 400 ns may not + * fit in the tADL field of the SMC reg. We need to relax the check and + * accept the -ERANGE return code. + * + * Note that previous versions of the ONFI spec had a lower tADL_min + * (100 or 200 ns). It's not clear why this timing constraint got + * increased but it seems most NANDs are fine with values lower than + * 400ns, so we should be safe. + */ + if (ret && ret != -ERANGE) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TAR_SHIFT, + ncycles); + if (ret) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TRR_SHIFT, + ncycles); + if (ret) + return ret; + + ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps); + ret = atmel_smc_cs_conf_set_timing(smcconf, + ATMEL_HSMC_TIMINGS_TWB_SHIFT, + ncycles); + if (ret) + return ret; + + /* Attach the CS line to the NFC logic. */ + smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL; + + /* Set the appropriate data bus width. */ + if (nand->base.options & NAND_BUSWIDTH_16) + smcconf->mode |= ATMEL_SMC_MODE_DBW_16; + + /* Operate in NRD/NWE READ/WRITEMODE. */ + smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD | + ATMEL_SMC_MODE_WRITEMODE_NWE; + + return 0; +} + +static int atmel_smc_nand_setup_interface(struct atmel_nand *nand, + int csline, + const struct nand_interface_config *conf) +{ + struct atmel_nand_controller *nc; + struct atmel_smc_cs_conf smcconf; + struct atmel_nand_cs *cs; + int ret; + + nc = to_nand_controller(nand->base.controller); + + ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); + if (ret) + return ret; + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + cs = &nand->cs[csline]; + cs->smcconf = smcconf; + atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf); + + return 0; +} + +static int atmel_hsmc_nand_setup_interface(struct atmel_nand *nand, + int csline, + const struct nand_interface_config *conf) +{ + struct atmel_hsmc_nand_controller *nc; + struct atmel_smc_cs_conf smcconf; + struct atmel_nand_cs *cs; + int ret; + + nc = to_hsmc_nand_controller(nand->base.controller); + + ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); + if (ret) + return ret; + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + cs = &nand->cs[csline]; + cs->smcconf = smcconf; + + if (cs->rb.type == ATMEL_NAND_NATIVE_RB) + cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id); + + atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id, + &cs->smcconf); + + return 0; +} + +static int atmel_nand_setup_interface(struct nand_chip *chip, int csline, + const struct nand_interface_config *conf) +{ + struct atmel_nand *nand = to_atmel_nand(chip); + const struct nand_sdr_timings *sdr; + struct atmel_nand_controller *nc; + + sdr = nand_get_sdr_timings(conf); + if (IS_ERR(sdr)) + return PTR_ERR(sdr); + + nc = to_nand_controller(nand->base.controller); + + if (csline >= nand->numcs || + (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY)) + return -EINVAL; + + 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) +{ + struct nand_chip *chip = &nand->base; + struct mtd_info *mtd = nand_to_mtd(chip); + + mtd->dev.parent = nc->dev; + nand->base.controller = &nc->base; + + if (!nc->mck || !nc->caps->ops->setup_interface) + chip->options |= NAND_KEEP_TIMINGS; + + /* Default to HW ECC if pmecc is available. */ + if (nc->pmecc) + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; +} + +static void atmel_smc_nand_init(struct atmel_nand_controller *nc, + struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct atmel_smc_nand_controller *smc_nc; + int i; + + atmel_nand_init(nc, nand); + + smc_nc = to_smc_nand_controller(chip->controller); + if (!smc_nc->ebi_csa_regmap) + return; + + /* Attach the CS to the NAND Flash logic. */ + for (i = 0; i < nand->numcs; i++) + regmap_update_bits(smc_nc->ebi_csa_regmap, + smc_nc->ebi_csa->offs, + BIT(nand->cs[i].id), BIT(nand->cs[i].id)); + + if (smc_nc->ebi_csa->nfd0_on_d16) + regmap_update_bits(smc_nc->ebi_csa_regmap, + smc_nc->ebi_csa->offs, + smc_nc->ebi_csa->nfd0_on_d16, + smc_nc->ebi_csa->nfd0_on_d16); +} + +static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc, + struct device_node *np, + int reg_cells) +{ + struct atmel_nand *nand; + struct gpio_desc *gpio; + int numcs, ret, i; + + numcs = of_property_count_elems_of_size(np, "reg", + reg_cells * sizeof(u32)); + if (numcs < 1) { + dev_err(nc->dev, "Missing or invalid reg property\n"); + return ERR_PTR(-EINVAL); + } + + nand = kzalloc(struct_size(nand, cs, numcs), GFP_KERNEL); + if (!nand) + return ERR_PTR(-ENOMEM); + + nand->numcs = numcs; + + gpio = dev_gpiod_get(nc->dev, np, "det", GPIOD_IN, "nand-det"); + if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { + dev_err(nc->dev, + "Failed to get detect gpio (err = %ld)\n", + PTR_ERR(gpio)); + return ERR_CAST(gpio); + } + + if (!IS_ERR(gpio)) + nand->cdgpio = gpio; + + for (i = 0; i < numcs; i++) { + struct resource res; + u32 val; + + ret = of_address_to_resource(np, 0, &res); + if (ret) { + dev_err(nc->dev, "Invalid reg property (err = %d)\n", + ret); + return ERR_PTR(ret); + } + + ret = of_property_read_u32_index(np, "reg", i * reg_cells, + &val); + if (ret) { + dev_err(nc->dev, "Invalid reg property (err = %d)\n", + ret); + return ERR_PTR(ret); + } + + nand->cs[i].id = val; + + nand->cs[i].io.virt = IOMEM(res.start); + ret = dev_request_resource(nc->dev, &res); + if (ret < 0) + return ERR_PTR(ret); + + if (!of_property_read_u32(np, "atmel,rb", &val)) { + if (val > ATMEL_NFC_MAX_RB_ID) + return ERR_PTR(-EINVAL); + + nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB; + nand->cs[i].rb.id = val; + } else { + gpio = dev_gpiod_get_index(nc->dev, np, "rb", i, GPIOD_IN, "nand-rb"); + if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { + dev_errp_probe(nc->dev, gpio, "Failed to get detect gpio\n"); + return ERR_CAST(gpio); + } + + if (!IS_ERR(gpio)) { + nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB; + nand->cs[i].rb.gpio = gpio; + } + } + + gpio = dev_gpiod_get_index(nc->dev, np, "cs", i, GPIOD_OUT_HIGH, "nand-cs"); + if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { + dev_errp_probe(nc->dev, gpio, "Failed to get CS gpio\n"); + return ERR_CAST(gpio); + } + + if (!IS_ERR(gpio)) + nand->cs[i].csgpio = gpio; + } + + nand_set_flash_node(&nand->base, np); + + return nand; +} + +static int +atmel_nand_controller_add_nand(struct atmel_nand_controller *nc, + struct atmel_nand *nand) +{ + struct nand_chip *chip = &nand->base; + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + /* No card inserted, skip this NAND. */ + if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) { + dev_info(nc->dev, "No SmartMedia card inserted.\n"); + return 0; + } + + nc->caps->ops->nand_init(nc, nand); + + ret = nand_scan(chip, nand->numcs); + if (ret) { + dev_err(nc->dev, "NAND scan failed: %d\n", ret); + return ret; + } + + ret = add_mtd_nand_device(mtd, "nand"); + if (ret) { + dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); + nand_cleanup(chip); + return ret; + } + + list_add_tail(&nand->node, &nc->chips); + + return 0; +} + +static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc) +{ + struct device_node *np, *nand_np; + struct device *dev = nc->dev; + int ret, reg_cells; + u32 val; + + np = dev->of_node; + + ret = of_property_read_u32(np, "#address-cells", &val); + if (ret) { + dev_err(dev, "missing #address-cells property\n"); + return ret; + } + + reg_cells = val; + + ret = of_property_read_u32(np, "#size-cells", &val); + if (ret) { + dev_err(dev, "missing #size-cells property\n"); + return ret; + } + + reg_cells += val; + + for_each_child_of_node(np, nand_np) { + struct atmel_nand *nand; + + nand = atmel_nand_create(nc, nand_np, reg_cells); + if (IS_ERR(nand)) + return PTR_ERR(nand); + + ret = atmel_nand_controller_add_nand(nc, nand); + if (ret) + return ret; + } + + return 0; +} + +static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc) +{ + clk_put(nc->mck); +} + +static const struct atmel_smc_nand_ebi_csa_cfg at91sam9260_ebi_csa = { + .offs = AT91SAM9260_MATRIX_EBICSA, +}; + +static const struct atmel_smc_nand_ebi_csa_cfg at91sam9261_ebi_csa = { + .offs = AT91SAM9261_MATRIX_EBICSA, +}; + +static const struct atmel_smc_nand_ebi_csa_cfg at91sam9263_ebi_csa = { + .offs = AT91SAM9263_MATRIX_EBI0CSA, +}; + +static const struct atmel_smc_nand_ebi_csa_cfg at91sam9rl_ebi_csa = { + .offs = AT91SAM9RL_MATRIX_EBICSA, +}; + +static const struct atmel_smc_nand_ebi_csa_cfg at91sam9g45_ebi_csa = { + .offs = AT91SAM9G45_MATRIX_EBICSA, +}; + +static const struct atmel_smc_nand_ebi_csa_cfg at91sam9n12_ebi_csa = { + .offs = AT91SAM9N12_MATRIX_EBICSA, +}; + +static const struct atmel_smc_nand_ebi_csa_cfg at91sam9x5_ebi_csa = { + .offs = AT91SAM9X5_MATRIX_EBICSA, +}; + +static const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = { + .offs = AT91_SFR_CCFG_EBICSA, + .nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16, +}; + +static const struct of_device_id __maybe_unused atmel_ebi_csa_regmap_of_ids[] = { + { + .compatible = "atmel,at91sam9260-matrix", + .data = &at91sam9260_ebi_csa, + }, + { + .compatible = "atmel,at91sam9261-matrix", + .data = &at91sam9261_ebi_csa, + }, + { + .compatible = "atmel,at91sam9263-matrix", + .data = &at91sam9263_ebi_csa, + }, + { + .compatible = "atmel,at91sam9rl-matrix", + .data = &at91sam9rl_ebi_csa, + }, + { + .compatible = "atmel,at91sam9g45-matrix", + .data = &at91sam9g45_ebi_csa, + }, + { + .compatible = "atmel,at91sam9n12-matrix", + .data = &at91sam9n12_ebi_csa, + }, + { + .compatible = "atmel,at91sam9x5-matrix", + .data = &at91sam9x5_ebi_csa, + }, + { + .compatible = "microchip,sam9x60-sfr", + .data = &sam9x60_ebi_csa, + }, + { /* sentinel */ }, +}; + +static int atmel_nand_attach_chip(struct nand_chip *chip) +{ + struct atmel_nand_controller *nc = to_nand_controller(chip->controller); + struct atmel_nand *nand = to_atmel_nand(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + ret = nc->caps->ops->ecc_init(chip); + if (ret) + return ret; + + if (!mtd->name) { + /* + * If the new bindings are used and the bootloader has not been + * updated to pass a new mtdparts parameter on the cmdline, you + * should define the following property in your nand node: + * + * label = "atmel_nand"; + * + * This way, mtd->name will be set by the core when + * nand_set_flash_node() is called. + */ + mtd->name = basprintf("%s:nand.%d", dev_name(nc->dev), + nand->cs[0].id); + if (!mtd->name) { + dev_err(nc->dev, "Failed to allocate mtd->name\n"); + return -ENOMEM; + } + } + + return 0; +} + +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, + struct device *dev, + const struct atmel_nand_controller_caps *caps) +{ + struct device_node *np = dev->of_node; + int ret; + + nand_controller_init(&nc->base); + nc->base.ops = &atmel_nand_controller_ops; + INIT_LIST_HEAD(&nc->chips); + nc->dev = dev; + nc->caps = caps; + + dev->priv = nc; + + nc->pmecc = dev_atmel_pmecc_get(dev); + if (IS_ERR(nc->pmecc)) + return dev_err_probe(dev, PTR_ERR(nc->pmecc), + "Could not get PMECC object\n"); + + nc->mck = of_clk_get(dev->parent->of_node, 0); + if (IS_ERR(nc->mck)) { + dev_err(dev, "Failed to retrieve MCK clk\n"); + ret = PTR_ERR(nc->mck); + goto out_release_dma; + } + + np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); + if (!np) { + dev_err(dev, "Missing or invalid atmel,smc property\n"); + ret = -EINVAL; + goto out_release_dma; + } + + nc->smc = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(nc->smc)) { + ret = PTR_ERR(nc->smc); + dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); + goto out_release_dma; + } + + return 0; + +out_release_dma: + return ret; +} + +static int +atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc) +{ + struct device *dev = nc->base.dev; + const struct of_device_id *match; + struct device_node *np; + int ret; + + np = of_parse_phandle(dev->parent->of_node, + nc->base.caps->ebi_csa_regmap_name, 0); + if (!np) + return 0; + + match = of_match_node(atmel_ebi_csa_regmap_of_ids, np); + if (!match) { + of_node_put(np); + return 0; + } + + nc->ebi_csa_regmap = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(nc->ebi_csa_regmap)) { + ret = PTR_ERR(nc->ebi_csa_regmap); + dev_err(dev, "Could not get EBICSA regmap (err = %d)\n", ret); + return ret; + } + + nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data; + + /* + * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1 + * add 4 to ->ebi_csa->offs. + */ + if (of_device_is_compatible(dev->parent->of_node, + "atmel,at91sam9263-ebi1")) + nc->ebi_csa->offs += 4; + + return 0; +} + +static int +atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) +{ + struct device *dev = nc->base.dev; + struct device_node *np; + int ret; + + np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); + if (!np) { + dev_err(dev, "Missing or invalid atmel,smc property\n"); + return -EINVAL; + } + + nc->hsmc_layout = atmel_hsmc_get_reg_layout(np); + + np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0); + if (!np) { + dev_err(dev, "Missing or invalid atmel,nfc-io property\n"); + return -EINVAL; + } + + nc->io = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(nc->io)) { + ret = PTR_ERR(nc->io); + dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret); + return ret; + } + + nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node, + "atmel,nfc-sram", 0); + if (!nc->sram.pool) { + dev_err(nc->base.dev, "Missing SRAM\n"); + return -ENOMEM; + } + + nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool, + ATMEL_NFC_SRAM_SIZE, + NULL); + if (!nc->sram.virt) { + dev_err(nc->base.dev, + "Could not allocate memory from the NFC SRAM pool\n"); + return -ENOMEM; + } + + return 0; +} + +static void +atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc) +{ + struct atmel_hsmc_nand_controller *hsmc_nc; + + 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); + + atmel_nand_controller_cleanup(nc); +} + +static int atmel_hsmc_nand_controller_probe(struct device *dev, + const struct atmel_nand_controller_caps *caps) +{ + struct atmel_hsmc_nand_controller *nc; + int ret; + + nc = kzalloc(sizeof(*nc), GFP_KERNEL); + if (!nc) + return -ENOMEM; + + ret = atmel_nand_controller_init(&nc->base, dev, caps); + if (ret) + return ret; + + ret = atmel_hsmc_nand_controller_init(nc); + if (ret) + return ret; + + /* Make sure all irqs are masked . */ + regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); + + /* 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) + goto err; + + return 0; + +err: + atmel_hsmc_nand_controller_remove(&nc->base); + + return ret; +} + +static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = { + .probe = atmel_hsmc_nand_controller_probe, + .ecc_init = atmel_hsmc_nand_ecc_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 = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ops = &atmel_hsmc_nc_ops, +}; + +static int atmel_smc_nand_controller_probe(struct device *dev, + const struct atmel_nand_controller_caps *caps) +{ + struct atmel_smc_nand_controller *nc; + int ret; + + nc = kzalloc(sizeof(*nc), GFP_KERNEL); + if (!nc) + return -ENOMEM; + + ret = atmel_nand_controller_init(&nc->base, dev, caps); + if (ret) + return ret; + + ret = atmel_smc_nand_controller_init(nc); + if (ret) + return ret; + + return atmel_nand_controller_add_nands(&nc->base); +} + +/* + * The SMC reg layout of at91rm9200 is completely different which prevents us + * from re-using atmel_smc_nand_setup_interface() for the + * ->setup_interface() hook. + * At this point, there's no support for the at91rm9200 SMC IP, so we leave + * ->setup_interface() unassigned. + */ +static const struct atmel_nand_controller_ops at91rm9200_nc_ops = { + .probe = atmel_smc_nand_controller_probe, + .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 = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ebi_csa_regmap_name = "atmel,matrix", + .ops = &at91rm9200_nc_ops, +}; + +static const struct atmel_nand_controller_ops atmel_smc_nc_ops = { + .probe = atmel_smc_nand_controller_probe, + .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 = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ebi_csa_regmap_name = "atmel,matrix", + .ops = &atmel_smc_nc_ops, +}; + +static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = { + .ale_offs = BIT(22), + .cle_offs = BIT(21), + .ebi_csa_regmap_name = "atmel,matrix", + .ops = &atmel_smc_nc_ops, +}; + +static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ebi_csa_regmap_name = "atmel,matrix", + .ops = &atmel_smc_nc_ops, +}; + +static const struct atmel_nand_controller_caps microchip_sam9x60_nc_caps = { + .ale_offs = BIT(21), + .cle_offs = BIT(22), + .ebi_csa_regmap_name = "microchip,sfr", + .ops = &atmel_smc_nc_ops, +}; + +static const struct of_device_id atmel_nand_controller_of_ids[] = { + { + .compatible = "atmel,at91rm9200-nand-controller", + .data = &atmel_rm9200_nc_caps, + }, + { + .compatible = "atmel,at91sam9260-nand-controller", + .data = &atmel_sam9260_nc_caps, + }, + { + .compatible = "atmel,at91sam9261-nand-controller", + .data = &atmel_sam9261_nc_caps, + }, + { + .compatible = "atmel,at91sam9g45-nand-controller", + .data = &atmel_sam9g45_nc_caps, + }, + { + .compatible = "atmel,sama5d3-nand-controller", + .data = &atmel_sama5_nc_caps, + }, + { + .compatible = "microchip,sam9x60-nand-controller", + .data = µchip_sam9x60_nc_caps, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids); + +static int atmel_nand_controller_probe(struct device *dev) +{ + const struct atmel_nand_controller_caps *caps; + + if (dev->id_entry) + caps = (void *)dev->id_entry->driver_data; + else + caps = of_device_get_match_data(dev); + + if (!caps) { + dev_err(dev, "Could not retrieve NFC caps\n"); + return -EINVAL; + } + + return caps->ops->probe(dev, caps); +} + +static struct driver atmel_nand_controller_driver = { + .name = "atmel-nand-controller", + .of_match_table = atmel_nand_controller_of_ids, + .probe = atmel_nand_controller_probe, +}; +device_platform_driver(atmel_nand_controller_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); +MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs"); +MODULE_ALIAS("platform:atmel-nand-controller"); diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c new file mode 100644 index 0000000000..1b89607a33 --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/pmecc.c @@ -0,0 +1,993 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017 ATMEL + * Copyright 2017 Free Electrons + * + * Author: Boris Brezillon <boris.brezillon@free-electrons.com> + * + * Derived from the atmel_nand.c driver which contained the following + * copyrights: + * + * Copyright 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) + * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c (removed in v3.8) + * Copyright 2000 Steven J. Hill (sjhill@cotw.com) + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * Copyright 2012 ATMEL, Hong Xu + * + * Add Nand Flash Controller support for SAMA5 SoC + * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) + * + * The PMECC is an hardware assisted BCH engine, which means part of the + * ECC algorithm is left to the software. The hardware/software repartition + * is explained in the "PMECC Controller Functional Description" chapter in + * Atmel datasheets, and some of the functions in this file are directly + * implementing the algorithms described in the "Software Implementation" + * sub-section. + * + * TODO: it seems that the software BCH implementation in lib/bch.c is already + * providing some of the logic we are implementing here. It would be smart + * to expose the needed lib/bch.c helpers/functions and re-use them here. + */ + +#include <linux/iopoll.h> +#include <module.h> +#include <linux/mtd/rawnand.h> +#include <of.h> +#include <of_device.h> +#include <linux/slab.h> + +#include "pmecc.h" + +/* Galois field dimension */ +#define PMECC_GF_DIMENSION_13 13 +#define PMECC_GF_DIMENSION_14 14 + +/* Primitive Polynomial used by PMECC */ +#define PMECC_GF_13_PRIMITIVE_POLY 0x201b +#define PMECC_GF_14_PRIMITIVE_POLY 0x4443 + +#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000 +#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000 + +/* Time out value for reading PMECC status register */ +#define PMECC_MAX_TIMEOUT_MS 100 + +/* PMECC Register Definitions */ +#define ATMEL_PMECC_CFG 0x0 +#define PMECC_CFG_BCH_STRENGTH(x) (x) +#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0) +#define PMECC_CFG_SECTOR512 (0 << 4) +#define PMECC_CFG_SECTOR1024 (1 << 4) +#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8) +#define PMECC_CFG_READ_OP (0 << 12) +#define PMECC_CFG_WRITE_OP (1 << 12) +#define PMECC_CFG_SPARE_ENABLE BIT(16) +#define PMECC_CFG_AUTO_ENABLE BIT(20) + +#define ATMEL_PMECC_SAREA 0x4 +#define ATMEL_PMECC_SADDR 0x8 +#define ATMEL_PMECC_EADDR 0xc + +#define ATMEL_PMECC_CLK 0x10 +#define PMECC_CLK_133MHZ (2 << 0) + +#define ATMEL_PMECC_CTRL 0x14 +#define PMECC_CTRL_RST BIT(0) +#define PMECC_CTRL_DATA BIT(1) +#define PMECC_CTRL_USER BIT(2) +#define PMECC_CTRL_ENABLE BIT(4) +#define PMECC_CTRL_DISABLE BIT(5) + +#define ATMEL_PMECC_SR 0x18 +#define PMECC_SR_BUSY BIT(0) +#define PMECC_SR_ENABLE BIT(4) + +#define ATMEL_PMECC_IER 0x1c +#define ATMEL_PMECC_IDR 0x20 +#define ATMEL_PMECC_IMR 0x24 +#define ATMEL_PMECC_ISR 0x28 +#define PMECC_ERROR_INT BIT(0) + +#define ATMEL_PMECC_ECC(sector, n) \ + ((((sector) + 1) * 0x40) + (n)) + +#define ATMEL_PMECC_REM(sector, n) \ + ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200) + +/* PMERRLOC Register Definitions */ +#define ATMEL_PMERRLOC_ELCFG 0x0 +#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0) +#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0) +#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16) + +#define ATMEL_PMERRLOC_ELPRIM 0x4 +#define ATMEL_PMERRLOC_ELEN 0x8 +#define ATMEL_PMERRLOC_ELDIS 0xc +#define PMERRLOC_DISABLE BIT(0) + +#define ATMEL_PMERRLOC_ELSR 0x10 +#define PMERRLOC_ELSR_BUSY BIT(0) + +#define ATMEL_PMERRLOC_ELIER 0x14 +#define ATMEL_PMERRLOC_ELIDR 0x18 +#define ATMEL_PMERRLOC_ELIMR 0x1c +#define ATMEL_PMERRLOC_ELISR 0x20 +#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8) +#define PMERRLOC_CALC_DONE BIT(0) + +#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28) + +#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs)) + +struct atmel_pmecc_gf_tables { + u16 *alpha_to; + u16 *index_of; +}; + +struct atmel_pmecc_caps { + const int *strengths; + int nstrengths; + int el_offset; + bool correct_erased_chunks; +}; + +struct atmel_pmecc { + struct device *dev; + const struct atmel_pmecc_caps *caps; + + struct { + void __iomem *base; + void __iomem *errloc; + } regs; + + struct mutex lock; +}; + +struct atmel_pmecc_user_conf_cache { + u32 cfg; + u32 sarea; + u32 saddr; + u32 eaddr; +}; + +struct atmel_pmecc_user { + struct atmel_pmecc_user_conf_cache cache; + struct atmel_pmecc *pmecc; + const struct atmel_pmecc_gf_tables *gf_tables; + int eccbytes; + s16 *partial_syn; + s16 *si; + s16 *lmu; + s16 *smu; + s32 *mu; + s32 *dmu; + s32 *delta; + u32 isr; +}; + +static DEFINE_MUTEX(pmecc_gf_tables_lock); +static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512; +static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024; + +static inline int deg(unsigned int poly) +{ + /* polynomial degree is the most-significant bit index */ + return fls(poly) - 1; +} + +static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly, + struct atmel_pmecc_gf_tables *gf_tables) +{ + unsigned int i, x = 1; + const unsigned int k = BIT(deg(poly)); + unsigned int nn = BIT(mm) - 1; + + /* primitive polynomial must be of degree m */ + if (k != (1u << mm)) + return -EINVAL; + + for (i = 0; i < nn; i++) { + gf_tables->alpha_to[i] = x; + gf_tables->index_of[x] = i; + if (i && (x == 1)) + /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ + return -EINVAL; + x <<= 1; + if (x & k) + x ^= poly; + } + gf_tables->alpha_to[nn] = 1; + gf_tables->index_of[0] = 0; + + return 0; +} + +static const struct atmel_pmecc_gf_tables * +atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req) +{ + struct atmel_pmecc_gf_tables *gf_tables; + unsigned int poly, degree, table_size; + int ret; + + if (req->ecc.sectorsize == 512) { + degree = PMECC_GF_DIMENSION_13; + poly = PMECC_GF_13_PRIMITIVE_POLY; + table_size = PMECC_LOOKUP_TABLE_SIZE_512; + } else { + degree = PMECC_GF_DIMENSION_14; + poly = PMECC_GF_14_PRIMITIVE_POLY; + table_size = PMECC_LOOKUP_TABLE_SIZE_1024; + } + + gf_tables = kzalloc(sizeof(*gf_tables) + + (2 * table_size * sizeof(u16)), + GFP_KERNEL); + if (!gf_tables) + return ERR_PTR(-ENOMEM); + + gf_tables->alpha_to = (void *)(gf_tables + 1); + gf_tables->index_of = gf_tables->alpha_to + table_size; + + ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables); + if (ret) { + kfree(gf_tables); + return ERR_PTR(ret); + } + + return gf_tables; +} + +static const struct atmel_pmecc_gf_tables * +atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req) +{ + const struct atmel_pmecc_gf_tables **gf_tables, *ret; + + mutex_lock(&pmecc_gf_tables_lock); + if (req->ecc.sectorsize == 512) + gf_tables = &pmecc_gf_tables_512; + else + gf_tables = &pmecc_gf_tables_1024; + + ret = *gf_tables; + + if (!ret) { + ret = atmel_pmecc_create_gf_tables(req); + if (!IS_ERR(ret)) + *gf_tables = ret; + } + mutex_unlock(&pmecc_gf_tables_lock); + + return ret; +} + +static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc, + struct atmel_pmecc_user_req *req) +{ + int i, max_eccbytes, eccbytes = 0, eccstrength = 0; + + if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0) + return -EINVAL; + + if (req->ecc.ooboffset >= 0 && + req->ecc.ooboffset + req->ecc.bytes > req->oobsize) + return -EINVAL; + + if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) { + if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) + return -EINVAL; + + if (req->pagesize > 512) + req->ecc.sectorsize = 1024; + else + req->ecc.sectorsize = 512; + } + + if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024) + return -EINVAL; + + if (req->pagesize % req->ecc.sectorsize) + return -EINVAL; + + req->ecc.nsectors = req->pagesize / req->ecc.sectorsize; + + max_eccbytes = req->ecc.bytes; + + for (i = 0; i < pmecc->caps->nstrengths; i++) { + int nbytes, strength = pmecc->caps->strengths[i]; + + if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH && + strength < req->ecc.strength) + continue; + + nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize), + 8); + nbytes *= req->ecc.nsectors; + + if (nbytes > max_eccbytes) + break; + + eccstrength = strength; + eccbytes = nbytes; + + if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH) + break; + } + + if (!eccstrength) + return -EINVAL; + + req->ecc.bytes = eccbytes; + req->ecc.strength = eccstrength; + + if (req->ecc.ooboffset < 0) + req->ecc.ooboffset = req->oobsize - eccbytes; + + return 0; +} + +struct atmel_pmecc_user * +atmel_pmecc_create_user(struct atmel_pmecc *pmecc, + struct atmel_pmecc_user_req *req) +{ + struct atmel_pmecc_user *user; + const struct atmel_pmecc_gf_tables *gf_tables; + int strength, size, ret; + + ret = atmel_pmecc_prepare_user_req(pmecc, req); + if (ret) + return ERR_PTR(ret); + + size = sizeof(*user); + size = ALIGN(size, sizeof(u16)); + /* Reserve space for partial_syn, si and smu */ + size += ((2 * req->ecc.strength) + 1) * sizeof(u16) * + (2 + req->ecc.strength + 2); + /* Reserve space for lmu. */ + size += (req->ecc.strength + 1) * sizeof(u16); + /* Reserve space for mu, dmu and delta. */ + size = ALIGN(size, sizeof(s32)); + size += (req->ecc.strength + 1) * sizeof(s32) * 3; + + user = kzalloc(size, GFP_KERNEL); + if (!user) + return ERR_PTR(-ENOMEM); + + user->pmecc = pmecc; + + user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16)); + user->si = user->partial_syn + ((2 * req->ecc.strength) + 1); + user->lmu = user->si + ((2 * req->ecc.strength) + 1); + user->smu = user->lmu + (req->ecc.strength + 1); + user->mu = (s32 *)PTR_ALIGN(user->smu + + (((2 * req->ecc.strength) + 1) * + (req->ecc.strength + 2)), + sizeof(s32)); + user->dmu = user->mu + req->ecc.strength + 1; + user->delta = user->dmu + req->ecc.strength + 1; + + gf_tables = atmel_pmecc_get_gf_tables(req); + if (IS_ERR(gf_tables)) { + kfree(user); + return ERR_CAST(gf_tables); + } + + user->gf_tables = gf_tables; + + user->eccbytes = req->ecc.bytes / req->ecc.nsectors; + + for (strength = 0; strength < pmecc->caps->nstrengths; strength++) { + if (pmecc->caps->strengths[strength] == req->ecc.strength) + break; + } + + user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) | + PMECC_CFG_NSECTORS(req->ecc.nsectors); + + if (req->ecc.sectorsize == 1024) + user->cache.cfg |= PMECC_CFG_SECTOR1024; + + user->cache.sarea = req->oobsize - 1; + user->cache.saddr = req->ecc.ooboffset; + user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1; + + return user; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_create_user); + +void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user) +{ + kfree(user); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user); + +static int get_strength(struct atmel_pmecc_user *user) +{ + const int *strengths = user->pmecc->caps->strengths; + + return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK]; +} + +static int get_sectorsize(struct atmel_pmecc_user *user) +{ + return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512; +} + +static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector) +{ + int strength = get_strength(user); + u32 value; + int i; + + /* Fill odd syndromes */ + for (i = 0; i < strength; i++) { + value = readl_relaxed(user->pmecc->regs.base + + ATMEL_PMECC_REM(sector, i / 2)); + if (i & 1) + value >>= 16; + + user->partial_syn[(2 * i) + 1] = value; + } +} + +static void atmel_pmecc_substitute(struct atmel_pmecc_user *user) +{ + int degree = get_sectorsize(user) == 512 ? 13 : 14; + int cw_len = BIT(degree) - 1; + int strength = get_strength(user); + s16 *alpha_to = user->gf_tables->alpha_to; + s16 *index_of = user->gf_tables->index_of; + s16 *partial_syn = user->partial_syn; + s16 *si; + int i, j; + + /* + * si[] is a table that holds the current syndrome value, + * an element of that table belongs to the field + */ + si = user->si; + + memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1)); + + /* Computation 2t syndromes based on S(x) */ + /* Odd syndromes */ + for (i = 1; i < 2 * strength; i += 2) { + for (j = 0; j < degree; j++) { + if (partial_syn[i] & BIT(j)) + si[i] = alpha_to[i * j] ^ si[i]; + } + } + /* Even syndrome = (Odd syndrome) ** 2 */ + for (i = 2, j = 1; j <= strength; i = ++j << 1) { + if (si[j] == 0) { + si[i] = 0; + } else { + s16 tmp; + + tmp = index_of[si[j]]; + tmp = (tmp * 2) % cw_len; + si[i] = alpha_to[tmp]; + } + } +} + +static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user) +{ + s16 *lmu = user->lmu; + s16 *si = user->si; + s32 *mu = user->mu; + s32 *dmu = user->dmu; + s32 *delta = user->delta; + int degree = get_sectorsize(user) == 512 ? 13 : 14; + int cw_len = BIT(degree) - 1; + int strength = get_strength(user); + int num = 2 * strength + 1; + s16 *index_of = user->gf_tables->index_of; + s16 *alpha_to = user->gf_tables->alpha_to; + int i, j, k; + u32 dmu_0_count, tmp; + s16 *smu = user->smu; + + /* index of largest delta */ + int ro; + int largest; + int diff; + + dmu_0_count = 0; + + /* First Row */ + + /* Mu */ + mu[0] = -1; + + memset(smu, 0, sizeof(s16) * num); + smu[0] = 1; + + /* discrepancy set to 1 */ + dmu[0] = 1; + /* polynom order set to 0 */ + lmu[0] = 0; + delta[0] = (mu[0] * 2 - lmu[0]) >> 1; + + /* Second Row */ + + /* Mu */ + mu[1] = 0; + /* Sigma(x) set to 1 */ + memset(&smu[num], 0, sizeof(s16) * num); + smu[num] = 1; + + /* discrepancy set to S1 */ + dmu[1] = si[1]; + + /* polynom order set to 0 */ + lmu[1] = 0; + + delta[1] = (mu[1] * 2 - lmu[1]) >> 1; + + /* Init the Sigma(x) last row */ + memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num); + + for (i = 1; i <= strength; i++) { + mu[i + 1] = i << 1; + /* Begin Computing Sigma (Mu+1) and L(mu) */ + /* check if discrepancy is set to 0 */ + if (dmu[i] == 0) { + dmu_0_count++; + + tmp = ((strength - (lmu[i] >> 1) - 1) / 2); + if ((strength - (lmu[i] >> 1) - 1) & 0x1) + tmp += 2; + else + tmp += 1; + + if (dmu_0_count == tmp) { + for (j = 0; j <= (lmu[i] >> 1) + 1; j++) + smu[(strength + 1) * num + j] = + smu[i * num + j]; + + lmu[strength + 1] = lmu[i]; + return; + } + + /* copy polynom */ + for (j = 0; j <= lmu[i] >> 1; j++) + smu[(i + 1) * num + j] = smu[i * num + j]; + + /* copy previous polynom order to the next */ + lmu[i + 1] = lmu[i]; + } else { + ro = 0; + largest = -1; + /* find largest delta with dmu != 0 */ + for (j = 0; j < i; j++) { + if ((dmu[j]) && (delta[j] > largest)) { + largest = delta[j]; + ro = j; + } + } + + /* compute difference */ + diff = (mu[i] - mu[ro]); + + /* Compute degree of the new smu polynomial */ + if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) + lmu[i + 1] = lmu[i]; + else + lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; + + /* Init smu[i+1] with 0 */ + for (k = 0; k < num; k++) + smu[(i + 1) * num + k] = 0; + + /* Compute smu[i+1] */ + for (k = 0; k <= lmu[ro] >> 1; k++) { + s16 a, b, c; + + if (!(smu[ro * num + k] && dmu[i])) + continue; + + a = index_of[dmu[i]]; + b = index_of[dmu[ro]]; + c = index_of[smu[ro * num + k]]; + tmp = a + (cw_len - b) + c; + a = alpha_to[tmp % cw_len]; + smu[(i + 1) * num + (k + diff)] = a; + } + + for (k = 0; k <= lmu[i] >> 1; k++) + smu[(i + 1) * num + k] ^= smu[i * num + k]; + } + + /* End Computing Sigma (Mu+1) and L(mu) */ + /* In either case compute delta */ + delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; + + /* Do not compute discrepancy for the last iteration */ + if (i >= strength) + continue; + + for (k = 0; k <= (lmu[i + 1] >> 1); k++) { + tmp = 2 * (i - 1); + if (k == 0) { + dmu[i + 1] = si[tmp + 3]; + } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { + s16 a, b, c; + + a = index_of[smu[(i + 1) * num + k]]; + b = si[2 * (i - 1) + 3 - k]; + c = index_of[b]; + tmp = a + c; + tmp %= cw_len; + dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1]; + } + } + } +} + +static int atmel_pmecc_err_location(struct atmel_pmecc_user *user) +{ + int sector_size = get_sectorsize(user); + int degree = sector_size == 512 ? 13 : 14; + struct atmel_pmecc *pmecc = user->pmecc; + int strength = get_strength(user); + int ret, roots_nbr, i, err_nbr = 0; + int num = (2 * strength) + 1; + s16 *smu = user->smu; + u32 val; + + writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS); + + for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) { + writel_relaxed(smu[(strength + 1) * num + i], + pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i)); + err_nbr++; + } + + val = (err_nbr - 1) << 16; + if (sector_size == 1024) + val |= 1; + + writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG); + writel((sector_size * 8) + (degree * strength), + pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN); + + ret = readl_relaxed_poll_timeout(pmecc->regs.errloc + + ATMEL_PMERRLOC_ELISR, + val, val & PMERRLOC_CALC_DONE, + PMECC_MAX_TIMEOUT_MS * 1000); + if (ret) { + dev_err(pmecc->dev, + "PMECC: Timeout to calculate error location.\n"); + return ret; + } + + roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8; + /* Number of roots == degree of smu hence <= cap */ + if (roots_nbr == user->lmu[strength + 1] >> 1) + return err_nbr - 1; + + /* + * Number of roots does not match the degree of smu + * unable to correct error. + */ + return -EBADMSG; +} + +int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, + void *data, void *ecc) +{ + struct atmel_pmecc *pmecc = user->pmecc; + int sectorsize = get_sectorsize(user); + int eccbytes = user->eccbytes; + int i, nerrors; + + if (!(user->isr & BIT(sector))) + return 0; + + atmel_pmecc_gen_syndrome(user, sector); + atmel_pmecc_substitute(user); + atmel_pmecc_get_sigma(user); + + nerrors = atmel_pmecc_err_location(user); + if (nerrors < 0) + return nerrors; + + for (i = 0; i < nerrors; i++) { + const char *area; + int byte, bit; + u32 errpos; + u8 *ptr; + + errpos = readl_relaxed(pmecc->regs.errloc + + ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i)); + errpos--; + + byte = errpos / 8; + bit = errpos % 8; + + if (byte < sectorsize) { + ptr = data + byte; + area = "data"; + } else if (byte < sectorsize + eccbytes) { + ptr = ecc + byte - sectorsize; + area = "ECC"; + } else { + dev_dbg(pmecc->dev, + "Invalid errpos value (%d, max is %d)\n", + errpos, (sectorsize + eccbytes) * 8); + return -EINVAL; + } + + dev_dbg(pmecc->dev, + "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n", + area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit))); + + *ptr ^= BIT(bit); + } + + return nerrors; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector); + +bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user) +{ + return user->pmecc->caps->correct_erased_chunks; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks); + +void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, + int sector, void *ecc) +{ + struct atmel_pmecc *pmecc = user->pmecc; + u8 *ptr = ecc; + int i; + + for (i = 0; i < user->eccbytes; i++) + ptr[i] = readb_relaxed(pmecc->regs.base + + ATMEL_PMECC_ECC(sector, i)); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes); + +void atmel_pmecc_reset(struct atmel_pmecc *pmecc) +{ + writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL); + writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_reset); + +int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op) +{ + struct atmel_pmecc *pmecc = user->pmecc; + u32 cfg; + + if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) { + dev_err(pmecc->dev, "Bad ECC operation!"); + return -EINVAL; + } + + mutex_lock(&user->pmecc->lock); + + cfg = user->cache.cfg; + if (op == NAND_ECC_WRITE) + cfg |= PMECC_CFG_WRITE_OP; + else + cfg |= PMECC_CFG_AUTO_ENABLE; + + writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG); + writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA); + writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR); + writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR); + + writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL); + writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL); + + return 0; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_enable); + +void atmel_pmecc_disable(struct atmel_pmecc_user *user) +{ + atmel_pmecc_reset(user->pmecc); + mutex_unlock(&user->pmecc->lock); +} +EXPORT_SYMBOL_GPL(atmel_pmecc_disable); + +int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user) +{ + struct atmel_pmecc *pmecc = user->pmecc; + u32 status; + int ret; + + ret = readl_relaxed_poll_timeout(pmecc->regs.base + + ATMEL_PMECC_SR, + status, !(status & PMECC_SR_BUSY), + PMECC_MAX_TIMEOUT_MS * 1000); + if (ret) { + dev_err(pmecc->dev, + "Timeout while waiting for PMECC ready.\n"); + return ret; + } + + user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR); + + return 0; +} +EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy); + +static struct atmel_pmecc *atmel_pmecc_create(struct device *dev, + const struct atmel_pmecc_caps *caps, + int pmecc_res_idx, int errloc_res_idx) +{ + struct atmel_pmecc *pmecc; + + pmecc = kzalloc(sizeof(*pmecc), GFP_KERNEL); + if (!pmecc) + return ERR_PTR(-ENOMEM); + + pmecc->caps = caps; + pmecc->dev = dev; + mutex_init(&pmecc->lock); + + pmecc->regs.base = dev_request_mem_region_err_null(dev, pmecc_res_idx); + if (!pmecc->regs.base) + return ERR_PTR(-EINVAL); + + pmecc->regs.errloc = dev_request_mem_region_err_null(dev, errloc_res_idx); + if (!pmecc->regs.errloc) + return ERR_PTR(-EINVAL); + + /* Disable all interrupts before registering the PMECC handler. */ + writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); + atmel_pmecc_reset(pmecc); + + return pmecc; +} + +static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev, + struct device_node *np) +{ + struct device *dev; + struct atmel_pmecc *pmecc; + int ret; + + dev = of_find_device_by_node(np); + if (!dev) + return ERR_PTR(-EPROBE_DEFER); + pmecc = dev->priv; + if (!pmecc) { + ret = -EPROBE_DEFER; + goto err_put_device; + } + + return pmecc; + +err_put_device: + put_device(dev); + return ERR_PTR(ret); +} + +static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 }; + +static struct atmel_pmecc_caps at91sam9g45_caps = { + .strengths = atmel_pmecc_strengths, + .nstrengths = 5, + .el_offset = 0x8c, +}; + +static struct atmel_pmecc_caps sama5d4_caps = { + .strengths = atmel_pmecc_strengths, + .nstrengths = 5, + .el_offset = 0x8c, + .correct_erased_chunks = true, +}; + +static struct atmel_pmecc_caps sama5d2_caps = { + .strengths = atmel_pmecc_strengths, + .nstrengths = 6, + .el_offset = 0xac, + .correct_erased_chunks = true, +}; + +static const struct of_device_id __maybe_unused atmel_pmecc_legacy_match[] = { + { .compatible = "atmel,sama5d4-nand", &sama5d4_caps }, + { .compatible = "atmel,sama5d2-nand", &sama5d2_caps }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_pmecc_legacy_match); + +struct atmel_pmecc *dev_atmel_pmecc_get(struct device *userdev) +{ + struct atmel_pmecc *pmecc; + struct device_node *np; + + if (!userdev) + return ERR_PTR(-EINVAL); + + if (!userdev->of_node) + return NULL; + + np = of_parse_phandle(userdev->of_node, "ecc-engine", 0); + if (np) { + pmecc = atmel_pmecc_get_by_node(userdev, np); + of_node_put(np); + } else { + /* + * Support old DT bindings: in this case the PMECC iomem + * resources are directly defined in the user dev at position + * 1 and 2. Extract all relevant information from there. + */ + struct device *dev = userdev; + const struct atmel_pmecc_caps *caps; + const struct of_device_id *match; + + /* No PMECC engine available. */ + if (!of_property_read_bool(userdev->of_node, + "atmel,has-pmecc")) + return NULL; + + caps = &at91sam9g45_caps; + + /* Find the caps associated to the NAND dev node. */ + match = of_match_node(atmel_pmecc_legacy_match, + userdev->of_node); + if (match && match->data) + caps = match->data; + + pmecc = atmel_pmecc_create(dev, caps, 1, 2); + } + + return pmecc; +} +EXPORT_SYMBOL(dev_atmel_pmecc_get); + +static const struct of_device_id atmel_pmecc_match[] = { + { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps }, + { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps }, + { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_pmecc_match); + +static int atmel_pmecc_probe(struct device *dev) +{ + const struct atmel_pmecc_caps *caps; + struct atmel_pmecc *pmecc; + + caps = of_device_get_match_data(dev); + if (!caps) { + dev_err(dev, "Invalid caps\n"); + return -EINVAL; + } + + pmecc = atmel_pmecc_create(dev, caps, 0, 1); + if (IS_ERR(pmecc)) + return PTR_ERR(pmecc); + + dev->priv = pmecc; + + return 0; +} + +static struct driver atmel_pmecc_driver = { + .name = "atmel-pmecc", + .of_match_table = atmel_pmecc_match, + .probe = atmel_pmecc_probe, +}; +device_platform_driver(atmel_pmecc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); +MODULE_DESCRIPTION("PMECC engine driver"); +MODULE_ALIAS("platform:atmel_pmecc"); diff --git a/drivers/mtd/nand/raw/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h new file mode 100644 index 0000000000..6178a35e9d --- /dev/null +++ b/drivers/mtd/nand/raw/atmel/pmecc.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * © Copyright 2016 ATMEL + * © Copyright 2016 Free Electrons + * + * Author: Boris Brezillon <boris.brezillon@free-electrons.com> + * + * Derived from the atmel_nand.c driver which contained the following + * copyrights: + * + * Copyright © 2003 Rick Bronson + * + * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) + * Copyright © 2001 Thomas Gleixner (gleixner@autronix.de) + * + * Derived from drivers/mtd/spia.c (removed in v3.8) + * Copyright © 2000 Steven J. Hill (sjhill@cotw.com) + * + * + * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 + * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007 + * + * Derived from Das U-Boot source code + * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) + * © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas + * + * Add Programmable Multibit ECC support for various AT91 SoC + * © Copyright 2012 ATMEL, Hong Xu + * + * Add Nand Flash Controller support for SAMA5 SoC + * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) + */ + +#ifndef ATMEL_PMECC_H +#define ATMEL_PMECC_H + +#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0 +#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0 +#define ATMEL_PMECC_OOBOFFSET_AUTO -1 + +struct atmel_pmecc_user_req { + int pagesize; + int oobsize; + struct { + int strength; + int bytes; + int sectorsize; + int nsectors; + int ooboffset; + } ecc; +}; + +struct atmel_pmecc *dev_atmel_pmecc_get(struct device *dev); + +struct atmel_pmecc_user * +atmel_pmecc_create_user(struct atmel_pmecc *pmecc, + struct atmel_pmecc_user_req *req); +void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user); + +void atmel_pmecc_reset(struct atmel_pmecc *pmecc); +int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op); +void atmel_pmecc_disable(struct atmel_pmecc_user *user); +int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user); +int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector, + void *data, void *ecc); +bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user); +void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user, + int sector, void *ecc); + +#endif /* ATMEL_PMECC_H */ diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/raw/denali.h index f9c209d58d..ed489d010b 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/raw/denali.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NAND Flash Controller Device Driver * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers. @@ -356,7 +356,7 @@ struct denali_chip { */ struct denali_controller { struct nand_controller controller; - struct device_d *dev; + struct device *dev; struct list_head chips; unsigned long clk_rate; unsigned long clk_x_rate; diff --git a/drivers/mtd/nand/fsl_ifc.h b/drivers/mtd/nand/raw/fsl_ifc.h index 4c89f569f5..4c89f569f5 100644 --- a/drivers/mtd/nand/fsl_ifc.h +++ b/drivers/mtd/nand/raw/fsl_ifc.h diff --git a/drivers/mtd/nand/internals.h b/drivers/mtd/nand/raw/internals.h index 7716470a56..6dab25ecab 100644 --- a/drivers/mtd/nand/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2018 - Bootlin * @@ -90,9 +90,14 @@ void onfi_fill_interface_config(struct nand_chip *chip, unsigned int timing_mode); unsigned int onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings); +unsigned int +onfi_find_closest_nvddr_mode(const struct nand_nvddr_timings *spec_timings); int nand_choose_best_sdr_timings(struct nand_chip *chip, struct nand_interface_config *iface, struct nand_sdr_timings *spec_timings); +int nand_choose_best_nvddr_timings(struct nand_chip *chip, + struct nand_interface_config *iface, + struct nand_nvddr_timings *spec_timings); const struct nand_interface_config *nand_get_reset_interface_config(void); int nand_get_features(struct nand_chip *chip, int addr, u8 *subfeature_param); int nand_set_features(struct nand_chip *chip, int addr, u8 *subfeature_param); diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c new file mode 100644 index 0000000000..2774b6bb4f --- /dev/null +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -0,0 +1,1750 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Sascha Hauer, kernel@pengutronix.de + */ + +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/rawnand.h> +#include <linux/mtd/partitions.h> +#include <linux/device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/bitfield.h> +#include <linux/completion.h> + +#define DRIVER_NAME "mxc_nand" + +/* Addresses for NFC registers */ +#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) +#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) +#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06) +#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08) +#define NFC_V1_V2_CONFIG (host->regs + 0x0a) +#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) +#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) +#define NFC_V21_RSLTSPARE_AREA (host->regs + 0x10) +#define NFC_V1_V2_WRPROT (host->regs + 0x12) +#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) +#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) +#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) +#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) +#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) +#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) +#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) +#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) +#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) +#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) +#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) +#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) +#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) + +#define NFC_V1_V2_ECC_STATUS_RESULT_ERM GENMASK(3, 2) + +#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) +#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) +#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) +#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) +#define NFC_V1_V2_CONFIG1_BIG (1 << 5) +#define NFC_V1_V2_CONFIG1_RST (1 << 6) +#define NFC_V1_V2_CONFIG1_CE (1 << 7) +#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) +#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9) +#define NFC_V2_CONFIG1_FP_INT (1 << 11) + +#define NFC_V1_V2_CONFIG2_INT (1 << 15) + +/* + * Operation modes for the NFC. Valid for v1, v2 and v3 + * type controllers. + */ +#define NFC_CMD (1 << 0) +#define NFC_ADDR (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) + +#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00) +#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04) + +#define NFC_V3_CONFIG1 (host->regs_axi + 0x34) +#define NFC_V3_CONFIG1_SP_EN (1 << 0) +#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4) + +#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38) + +#define NFC_V3_LAUNCH (host->regs_axi + 0x40) + +#define NFC_V3_WRPROT (host->regs_ip + 0x0) +#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0) +#define NFC_V3_WRPROT_LOCK (1 << 1) +#define NFC_V3_WRPROT_UNLOCK (1 << 2) +#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) + +#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04) + +#define NFC_V3_CONFIG2 (host->regs_ip + 0x24) +#define NFC_V3_CONFIG2_PS_512 (0 << 0) +#define NFC_V3_CONFIG2_PS_2048 (1 << 0) +#define NFC_V3_CONFIG2_PS_4096 (2 << 0) +#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) +#define NFC_V3_CONFIG2_ECC_EN (1 << 3) +#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) +#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) +#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift) +#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) +#define NFC_V3_CONFIG2_INT_MSK (1 << 15) +#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) +#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) + +#define NFC_V3_CONFIG3 (host->regs_ip + 0x28) +#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) +#define NFC_V3_CONFIG3_FW8 (1 << 3) +#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) +#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12) +#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) +#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) + +#define NFC_V3_IPC (host->regs_ip + 0x2C) +#define NFC_V3_IPC_CREQ (1 << 0) +#define NFC_V3_IPC_INT (1 << 31) + +#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) + +struct mxc_nand_host; + +struct mxc_nand_devtype_data { + void (*preset)(struct mtd_info *); + int (*read_page)(struct nand_chip *chip); + void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); + void (*send_addr)(struct mxc_nand_host *, uint16_t, int); + void (*send_page)(struct mtd_info *, unsigned int); + void (*send_read_id)(struct mxc_nand_host *); + uint16_t (*get_dev_status)(struct mxc_nand_host *); + int (*check_int)(struct mxc_nand_host *); + void (*irq_control)(struct mxc_nand_host *, int); + u32 (*get_ecc_status)(struct nand_chip *); + const struct mtd_ooblayout_ops *ooblayout; + void (*select_chip)(struct nand_chip *chip, int cs); + int (*setup_interface)(struct nand_chip *chip, int csline, + const struct nand_interface_config *conf); + void (*enable_hwecc)(struct nand_chip *chip, bool enable); + + /* + * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked + * (CONFIG1:INT_MSK is set). To handle this the driver uses + * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK + */ + int irqpending_quirk; + int needs_ip; + + size_t regs_offset; + size_t spare0_offset; + size_t axi_offset; + + int spare_len; + int eccbytes; + int eccsize; + int ppb_shift; +}; + +struct mxc_nand_host { + struct nand_chip nand; + struct device *dev; + + void __iomem *spare0; + void __iomem *main_area0; + + void __iomem *base; + void __iomem *regs; + void __iomem *regs_axi; + void __iomem *regs_ip; + int status_request; + struct clk *clk; + int clk_act; + int irq; + int eccsize; + int used_oobsize; + int active_cs; + unsigned int ecc_stats_v1; + + struct completion op_completion; + + void *data_buf; + + const struct mxc_nand_devtype_data *devtype_data; +}; + +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u32 *t = trg; + const __iomem u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + *t++ = __raw_readl(s++); +} + +static void memcpy16_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u16 *t = trg; + const __iomem u16 *s = src; + + /* We assume that src (IO) is always 32bit aligned */ + if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) { + memcpy32_fromio(trg, src, size); + return; + } + + for (i = 0; i < (size >> 1); i++) + *t++ = __raw_readw(s++); +} + +static inline void memcpy32_toio(void __iomem *trg, const void *src, int size) +{ + int i; + u32 __iomem *t = trg; + const u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + __raw_writel(*s++, t++); +} + +static void memcpy16_toio(void __iomem *trg, const void *src, int size) +{ + int i; + __iomem u16 *t = trg; + const u16 *s = src; + + /* We assume that trg (IO) is always 32bit aligned */ + if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) { + memcpy32_toio(trg, src, size); + return; + } + + for (i = 0; i < (size >> 1); i++) + __raw_writew(*s++, t++); +} + +/* + * The controller splits a page into data chunks of 512 bytes + partial oob. + * There are writesize / 512 such chunks, the size of the partial oob parts is + * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then + * contains additionally the byte lost by rounding (if any). + * This function handles the needed shuffling between host->data_buf (which + * holds a page in natural order, i.e. writesize bytes data + oobsize bytes + * spare) and the NFC buffer. + */ +static void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf) +{ + struct nand_chip *this = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(this); + u16 i, oob_chunk_size; + u16 num_chunks = mtd->writesize / 512; + + u8 *d = buf; + u8 __iomem *s = host->spare0; + u16 sparebuf_size = host->devtype_data->spare_len; + + /* size of oob chunk for all but possibly the last one */ + oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; + + if (bfrom) { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + oob_chunk_size); + + /* the last chunk */ + memcpy16_fromio(d + i * oob_chunk_size, + s + i * sparebuf_size, + host->used_oobsize - i * oob_chunk_size); + } else { + for (i = 0; i < num_chunks - 1; i++) + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + oob_chunk_size); + + /* the last chunk */ + memcpy16_toio(&s[i * sparebuf_size], + &d[i * oob_chunk_size], + host->used_oobsize - i * oob_chunk_size); + } +} + +static int check_int_v3(struct mxc_nand_host *host) +{ + uint32_t tmp; + + tmp = readl(NFC_V3_IPC); + if (!(tmp & NFC_V3_IPC_INT)) + return 0; + + tmp &= ~NFC_V3_IPC_INT; + writel(tmp, NFC_V3_IPC); + + return 1; +} + +static int check_int_v1_v2(struct mxc_nand_host *host) +{ + uint32_t tmp; + + tmp = readw(NFC_V1_V2_CONFIG2); + if (!(tmp & NFC_V1_V2_CONFIG2_INT)) + return 0; + + if (!host->devtype_data->irqpending_quirk) + writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); + + return 1; +} + +static void irq_control_v1_v2(struct mxc_nand_host *host, int activate) +{ + uint16_t tmp; + + tmp = readw(NFC_V1_V2_CONFIG1); + + if (activate) + tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; + else + tmp |= NFC_V1_V2_CONFIG1_INT_MSK; + + writew(tmp, NFC_V1_V2_CONFIG1); +} + +static void irq_control_v3(struct mxc_nand_host *host, int activate) +{ + uint32_t tmp; + + tmp = readl(NFC_V3_CONFIG2); + + if (activate) + tmp &= ~NFC_V3_CONFIG2_INT_MSK; + else + tmp |= NFC_V3_CONFIG2_INT_MSK; + + writel(tmp, NFC_V3_CONFIG2); +} + +static u32 get_ecc_status_v1(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + unsigned int ecc_stats, max_bitflips = 0; + int no_subpages, i; + + no_subpages = mtd->writesize >> 9; + + ecc_stats = host->ecc_stats_v1; + + for (i = 0; i < no_subpages; i++) { + switch (ecc_stats & 0x3) { + case 0: + default: + break; + case 1: + mtd->ecc_stats.corrected++; + max_bitflips = 1; + break; + case 2: + mtd->ecc_stats.failed++; + break; + } + + ecc_stats >>= 2; + } + + return max_bitflips; +} + +static u32 get_ecc_status_v2_v3(struct nand_chip *chip, unsigned int ecc_stat) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + u8 ecc_bit_mask, err_limit; + unsigned int max_bitflips = 0; + int no_subpages, err; + + ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; + err_limit = (host->eccsize == 4) ? 0x4 : 0x8; + + no_subpages = mtd->writesize >> 9; + + do { + err = ecc_stat & ecc_bit_mask; + if (err > err_limit) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += err; + max_bitflips = max_t(unsigned int, max_bitflips, err); + } + + ecc_stat >>= 4; + } while (--no_subpages); + + return max_bitflips; +} + +static u32 get_ecc_status_v2(struct nand_chip *chip) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + u32 ecc_stat = readl(NFC_V1_V2_ECC_STATUS_RESULT); + + return get_ecc_status_v2_v3(chip, ecc_stat); +} + +static u32 get_ecc_status_v3(struct nand_chip *chip) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + u32 ecc_stat = readl(NFC_V3_ECC_STATUS_RESULT); + + return get_ecc_status_v2_v3(chip, ecc_stat); +} + +/* This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit of config2 register. + */ +static int wait_op_done(struct mxc_nand_host *host, int useirq) +{ + int ret = 0; + int max_retries = 8000; + int done; + + /* + * If operation is already complete, don't bother to setup an irq or a + * loop. + */ + if (host->devtype_data->check_int(host)) + return 0; + + do { + udelay(1); + + done = host->devtype_data->check_int(host); + if (done) + break; + + } while (--max_retries); + + if (!done) { + dev_dbg(host->dev, "timeout polling for completion\n"); + ret = -ETIMEDOUT; + } + + WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq); + + return ret; +} + +static void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) +{ + /* fill command */ + writel(cmd, NFC_V3_FLASH_CMD); + + /* send out command */ + writel(NFC_CMD, NFC_V3_LAUNCH); + + /* Wait for operation to complete */ + wait_op_done(host, useirq); +} + +/* This function issues the specified command to the NAND device and + * waits for completion. */ +static void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) +{ + dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); + + writew(cmd, NFC_V1_V2_FLASH_CMD); + writew(NFC_CMD, NFC_V1_V2_CONFIG2); + + if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { + int max_retries = 100; + /* Reset completion is indicated by NFC_CONFIG2 */ + /* being set to 0 */ + while (max_retries-- > 0) { + if (readw(NFC_V1_V2_CONFIG2) == 0) { + break; + } + udelay(1); + } + if (max_retries < 0) + dev_dbg(host->dev, "%s: RESET failed\n", __func__); + } else { + /* Wait for operation to complete */ + wait_op_done(host, useirq); + } +} + +static void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) +{ + /* fill address */ + writel(addr, NFC_V3_FLASH_ADDR0); + + /* send out address */ + writel(NFC_ADDR, NFC_V3_LAUNCH); + + wait_op_done(host, 0); +} + +/* This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. */ +static void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) +{ + dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast); + + writew(addr, NFC_V1_V2_FLASH_ADDR); + writew(NFC_ADDR, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, islast); +} + +static void send_page_v3(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint32_t tmp; + + tmp = readl(NFC_V3_CONFIG1); + tmp &= ~(7 << 4); + writel(tmp, NFC_V3_CONFIG1); + + /* transfer data from NFC ram to nand */ + writel(ops, NFC_V3_LAUNCH); + + wait_op_done(host, false); +} + +static void send_page_v2(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + /* NANDFC buffer 0 is used for page read/write */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); +} + +static void send_page_v1(struct mtd_info *mtd, unsigned int ops) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + int bufs, i; + + if (mtd->writesize > 512) + bufs = 4; + else + bufs = 1; + + for (i = 0; i < bufs; i++) { + + /* NANDFC buffer 0 is used for page read/write */ + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); + + writew(ops, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + } +} + +static void send_read_id_v3(struct mxc_nand_host *host) +{ + /* Read ID into main buffer */ + writel(NFC_ID, NFC_V3_LAUNCH); + + wait_op_done(host, true); + + memcpy32_fromio(host->data_buf, host->main_area0, 16); +} + +/* Request the NANDFC to perform a read of the NAND device ID. */ +static void send_read_id_v1_v2(struct mxc_nand_host *host) +{ + /* NANDFC buffer 0 is used for device ID output */ + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + writew(NFC_ID, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + + memcpy32_fromio(host->data_buf, host->main_area0, 16); +} + +static uint16_t get_dev_status_v3(struct mxc_nand_host *host) +{ + writew(NFC_STATUS, NFC_V3_LAUNCH); + wait_op_done(host, true); + + return readl(NFC_V3_CONFIG1) >> 16; +} + +/* This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. */ +static uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) +{ + void __iomem *main_buf = host->main_area0; + uint32_t store; + uint16_t ret; + + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); + + /* + * The device status is stored in main_area0. To + * prevent corruption of the buffer save the value + * and restore it afterwards. + */ + store = readl(main_buf); + + writew(NFC_STATUS, NFC_V1_V2_CONFIG2); + wait_op_done(host, true); + + ret = readw(main_buf); + + writel(store, main_buf); + + return ret; +} + +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.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return; + + config1 = readw(NFC_V1_V2_CONFIG1); + + if (enable) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + else + config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; + + writew(config1, NFC_V1_V2_CONFIG1); +} + +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.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) + return; + + config2 = readl(NFC_V3_CONFIG2); + + if (enable) + config2 |= NFC_V3_CONFIG2_ECC_EN; + else + config2 &= ~NFC_V3_CONFIG2_ECC_EN; + + writel(config2, NFC_V3_CONFIG2); +} + +static int mxc_nand_read_page_v1(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + int no_subpages; + int i; + unsigned int ecc_stats = 0; + + no_subpages = mtd->writesize >> 9; + + for (i = 0; i < no_subpages; i++) { + /* NANDFC buffer 0 is used for page read/write */ + writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); + + writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, true); + + ecc_stats |= FIELD_GET(NFC_V1_V2_ECC_STATUS_RESULT_ERM, + readw(NFC_V1_V2_ECC_STATUS_RESULT)) << i * 2; + } + + host->ecc_stats_v1 = ecc_stats; + + return 0; +} + +static int mxc_nand_read_page_v2_v3(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + + host->devtype_data->send_page(mtd, NFC_OUTPUT); + + return 0; +} + +static int mxc_nand_read_page(struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + int ret; + + host->devtype_data->enable_hwecc(chip, true); + + ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize); + if (ret) + return ret; + + if (oob_required) + copy_spare(mtd, true, chip->oob_poi); + + return host->devtype_data->get_ecc_status(chip); +} + +static int mxc_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + int ret; + + host->devtype_data->enable_hwecc(chip, false); + + ret = nand_read_page_op(chip, page, 0, buf, mtd->writesize); + if (ret) + return ret; + + if (oob_required) + copy_spare(mtd, true, chip->oob_poi); + + return 0; +} + +static int mxc_nand_read_oob(struct nand_chip *chip, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + int ret; + + host->devtype_data->enable_hwecc(chip, false); + + ret = nand_read_page_op(chip, page, 0, host->data_buf, mtd->writesize); + if (ret) + return ret; + + copy_spare(mtd, true, chip->oob_poi); + + return 0; +} + +static int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf, + bool ecc, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + + host->devtype_data->enable_hwecc(chip, ecc); + + return nand_prog_page_op(chip, page, 0, buf, mtd->writesize); +} + +static int mxc_nand_write_page_ecc(struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + return mxc_nand_write_page(chip, buf, true, page); +} + +static int mxc_nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, + int oob_required, int page) +{ + return mxc_nand_write_page(chip, buf, false, page); +} + +static int mxc_nand_write_oob(struct nand_chip *chip, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + + memset(host->data_buf, 0xff, mtd->writesize); + + return mxc_nand_write_page(chip, host->data_buf, false, page); +} + +/* This function is used by upper layer for select and + * deselect of the NAND chip */ +static void mxc_nand_select_chip_v1_v3(struct nand_chip *nand_chip, int chip) +{ + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable_unprepare(host->clk); + host->clk_act = 0; + } + return; + } + + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_prepare_enable(host->clk); + host->clk_act = 1; + } +} + +static void mxc_nand_select_chip_v2(struct nand_chip *nand_chip, int chip) +{ + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + + if (chip == -1) { + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable_unprepare(host->clk); + host->clk_act = 0; + } + return; + } + + if (!host->clk_act) { + /* Enable the NFC clock */ + clk_prepare_enable(host->clk); + host->clk_act = 1; + } + + host->active_cs = chip; + writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); +} + +#define MXC_V1_ECCBYTES 5 + +static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = MXC_V1_ECCBYTES; + + return 0; +} + +static int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + + if (section > nand_chip->ecc.steps) + return -ERANGE; + + if (!section) { + if (mtd->writesize <= 512) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 2; + oobregion->length = 4; + } + } else { + oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6; + if (section < nand_chip->ecc.steps) + oobregion->length = (section * 16) + 6 - + oobregion->offset; + else + oobregion->length = mtd->oobsize - oobregion->offset; + } + + return 0; +} + +static const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = { + .ecc = mxc_v1_ooblayout_ecc, + .free = mxc_v1_ooblayout_free, +}; + +static int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * stepsize) + 7; + oobregion->length = nand_chip->ecc.bytes; + + return 0; +} + +static int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; + + if (section >= nand_chip->ecc.steps) + return -ERANGE; + + if (!section) { + if (mtd->writesize <= 512) { + oobregion->offset = 0; + oobregion->length = 5; + } else { + oobregion->offset = 2; + oobregion->length = 4; + } + } else { + oobregion->offset = section * stepsize; + oobregion->length = 7; + } + + return 0; +} + +static const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = { + .ecc = mxc_v2_ooblayout_ecc, + .free = mxc_v2_ooblayout_free, +}; + +/* + * v2 and v3 type controllers can do 4bit or 8bit ecc depending + * on how much oob the nand chip has. For 8bit ecc we need at least + * 26 bytes of oob data per 512 byte block. + */ +static int get_eccsize(struct mtd_info *mtd) +{ + int oobbytes_per_512 = 0; + + oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; + + if (oobbytes_per_512 < 26) + return 4; + else + return 8; +} + +static void preset_v1(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint16_t config1 = 0; + + 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) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + host->eccsize = 1; + + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); + writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static int mxc_nand_v2_setup_interface(struct nand_chip *chip, int csline, + const struct nand_interface_config *conf) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + int tRC_min_ns, tRC_ps, ret; + unsigned long rate, rate_round; + const struct nand_sdr_timings *timings; + u16 config1; + + timings = nand_get_sdr_timings(conf); + if (IS_ERR(timings)) + return -ENOTSUPP; + + config1 = readw(NFC_V1_V2_CONFIG1); + + tRC_min_ns = timings->tRC_min / 1000; + rate = 1000000000 / tRC_min_ns; + + /* + * For tRC < 30ns we have to use EDO mode. In this case the controller + * does one access per clock cycle. Otherwise the controller does one + * access in two clock cycles, thus we have to double the rate to the + * controller. + */ + if (tRC_min_ns < 30) { + rate_round = clk_round_rate(host->clk, rate); + config1 |= NFC_V2_CONFIG1_ONE_CYCLE; + tRC_ps = 1000000000 / (rate_round / 1000); + } else { + rate *= 2; + rate_round = clk_round_rate(host->clk, rate); + config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE; + tRC_ps = 1000000000 / (rate_round / 1000 / 2); + } + + /* + * The timing values compared against are from the i.MX25 Automotive + * datasheet, Table 50. NFC Timing Parameters + */ + if (timings->tCLS_min > tRC_ps - 1000 || + timings->tCLH_min > tRC_ps - 2000 || + timings->tCS_min > tRC_ps - 1000 || + timings->tCH_min > tRC_ps - 2000 || + timings->tWP_min > tRC_ps - 1500 || + timings->tALS_min > tRC_ps || + timings->tALH_min > tRC_ps - 3000 || + timings->tDS_min > tRC_ps || + timings->tDH_min > tRC_ps - 5000 || + timings->tWC_min > 2 * tRC_ps || + timings->tWH_min > tRC_ps - 2500 || + timings->tRR_min > 6 * tRC_ps || + timings->tRP_min > 3 * tRC_ps / 2 || + timings->tRC_min > 2 * tRC_ps || + timings->tREH_min > (tRC_ps / 2) - 2500) { + dev_dbg(host->dev, "Timing out of bounds\n"); + return -EINVAL; + } + + if (csline == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + ret = clk_set_rate(host->clk, rate); + if (ret) + return ret; + + writew(config1, NFC_V1_V2_CONFIG1); + + dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round, + config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" : + "normal"); + + return 0; +} + +static void preset_v2(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(nand_chip); + uint16_t config1 = 0; + + config1 |= NFC_V2_CONFIG1_FP_INT; + + if (!host->devtype_data->irqpending_quirk) + config1 |= NFC_V1_V2_CONFIG1_INT_MSK; + + if (mtd->writesize) { + uint16_t pages_per_block = mtd->erasesize / mtd->writesize; + + if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) + config1 |= NFC_V1_V2_CONFIG1_ECC_EN; + + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 4) + config1 |= NFC_V2_CONFIG1_ECC_MODE_4; + + config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); + } else { + host->eccsize = 1; + } + + writew(config1, NFC_V1_V2_CONFIG1); + /* preset operation */ + + /* spare area size in 16-bit half-words */ + writew(mtd->oobsize / 2, NFC_V21_RSLTSPARE_AREA); + + /* Unlock the internal RAM Buffer */ + writew(0x2, NFC_V1_V2_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); + writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); + writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); + + /* Unlock Block Command for given address range */ + writew(0x4, NFC_V1_V2_WRPROT); +} + +static void preset_v3(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxc_nand_host *host = nand_get_controller_data(chip); + uint32_t config2, config3; + int i, addr_phases; + + writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); + writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); + + /* Unlock the internal RAM Buffer */ + writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, + NFC_V3_WRPROT); + + /* Blocks to be unlocked */ + for (i = 0; i < NAND_MAX_CHIPS; i++) + writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); + + writel(0, NFC_V3_IPC); + + config2 = NFC_V3_CONFIG2_ONE_CYCLE | + NFC_V3_CONFIG2_2CMD_PHASES | + NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | + NFC_V3_CONFIG2_ST_CMD(0x70) | + NFC_V3_CONFIG2_INT_MSK | + NFC_V3_CONFIG2_NUM_ADDR_PHASE0; + + addr_phases = fls(chip->pagemask) >> 3; + + if (mtd->writesize == 2048) { + config2 |= NFC_V3_CONFIG2_PS_2048; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else if (mtd->writesize == 4096) { + config2 |= NFC_V3_CONFIG2_PS_4096; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); + } else { + config2 |= NFC_V3_CONFIG2_PS_512; + config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); + } + + if (mtd->writesize) { + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) + config2 |= NFC_V3_CONFIG2_ECC_EN; + + config2 |= NFC_V3_CONFIG2_PPB( + ffs(mtd->erasesize / mtd->writesize) - 6, + host->devtype_data->ppb_shift); + host->eccsize = get_eccsize(mtd); + if (host->eccsize == 8) + config2 |= NFC_V3_CONFIG2_ECC_MODE_8; + } + + writel(config2, NFC_V3_CONFIG2); + + config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | + NFC_V3_CONFIG3_NO_SDMA | + NFC_V3_CONFIG3_RBB_MODE | + NFC_V3_CONFIG3_SBB(6) | /* Reset default */ + NFC_V3_CONFIG3_ADD_OP(0); + + if (!(chip->options & NAND_BUSWIDTH_16)) + config3 |= NFC_V3_CONFIG3_FW8; + + writel(config3, NFC_V3_CONFIG3); + + writel(0, NFC_V3_DELAY_LINE); +} + +/* + * The generic flash bbt descriptors overlap with our ecc + * hardware, so define some i.MX specific ones. + */ +static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 0, + .len = 4, + .veroffs = 4, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* v1 + irqpending_quirk: i.MX21 */ +static const struct mxc_nand_devtype_data imx21_nand_devtype_data = { + .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ooblayout = &mxc_v1_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, + .irqpending_quirk = 1, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ +static const struct mxc_nand_devtype_data imx27_nand_devtype_data = { + .preset = preset_v1, + .read_page = mxc_nand_read_page_v1, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v1, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v1, + .ooblayout = &mxc_v1_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0xe00, + .spare0_offset = 0x800, + .axi_offset = 0, + .spare_len = 16, + .eccbytes = 3, + .eccsize = 1, +}; + +/* v21: i.MX25, i.MX35 */ +static const struct mxc_nand_devtype_data imx25_nand_devtype_data = { + .preset = preset_v2, + .read_page = mxc_nand_read_page_v2_v3, + .send_cmd = send_cmd_v1_v2, + .send_addr = send_addr_v1_v2, + .send_page = send_page_v2, + .send_read_id = send_read_id_v1_v2, + .get_dev_status = get_dev_status_v1_v2, + .check_int = check_int_v1_v2, + .irq_control = irq_control_v1_v2, + .get_ecc_status = get_ecc_status_v2, + .ooblayout = &mxc_v2_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v2, + .setup_interface = mxc_nand_v2_setup_interface, + .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, + .irqpending_quirk = 0, + .needs_ip = 0, + .regs_offset = 0x1e00, + .spare0_offset = 0x1000, + .axi_offset = 0, + .spare_len = 64, + .eccbytes = 9, + .eccsize = 0, +}; + +/* v3.2a: i.MX51 */ +static const struct mxc_nand_devtype_data imx51_nand_devtype_data = { + .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ooblayout = &mxc_v2_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, + .ppb_shift = 7, +}; + +/* v3.2b: i.MX53 */ +static const struct mxc_nand_devtype_data imx53_nand_devtype_data = { + .preset = preset_v3, + .read_page = mxc_nand_read_page_v2_v3, + .send_cmd = send_cmd_v3, + .send_addr = send_addr_v3, + .send_page = send_page_v3, + .send_read_id = send_read_id_v3, + .get_dev_status = get_dev_status_v3, + .check_int = check_int_v3, + .irq_control = irq_control_v3, + .get_ecc_status = get_ecc_status_v3, + .ooblayout = &mxc_v2_ooblayout_ops, + .select_chip = mxc_nand_select_chip_v1_v3, + .enable_hwecc = mxc_nand_enable_hwecc_v3, + .irqpending_quirk = 0, + .needs_ip = 1, + .regs_offset = 0, + .spare0_offset = 0x1000, + .axi_offset = 0x1e00, + .spare_len = 64, + .eccbytes = 0, + .eccsize = 0, + .ppb_shift = 8, +}; + +static inline int is_imx21_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx21_nand_devtype_data; +} + +static inline int is_imx27_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx27_nand_devtype_data; +} + +static inline int is_imx25_nfc(struct mxc_nand_host *host) +{ + return host->devtype_data == &imx25_nand_devtype_data; +} + +static const struct of_device_id mxcnd_dt_ids[] = { + { .compatible = "fsl,imx21-nand", .data = &imx21_nand_devtype_data, }, + { .compatible = "fsl,imx27-nand", .data = &imx27_nand_devtype_data, }, + { .compatible = "fsl,imx25-nand", .data = &imx25_nand_devtype_data, }, + { .compatible = "fsl,imx51-nand", .data = &imx51_nand_devtype_data, }, + { .compatible = "fsl,imx53-nand", .data = &imx53_nand_devtype_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxcnd_dt_ids); + +static int mxcnd_attach_chip(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxc_nand_host *host = nand_get_controller_data(chip); + struct device *dev = mtd->dev.parent; + + chip->ecc.bytes = host->devtype_data->eccbytes; + host->eccsize = host->devtype_data->eccsize; + chip->ecc.size = 512; + + chip->ecc.read_oob = mxc_nand_read_oob; + chip->ecc.read_page_raw = mxc_nand_read_page_raw; + chip->ecc.write_page_raw = mxc_nand_write_page_raw; + + switch (chip->ecc.engine_type) { + case NAND_ECC_ENGINE_TYPE_ON_HOST: + mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); + chip->ecc.read_page = mxc_nand_read_page; + chip->ecc.write_page = mxc_nand_write_page_ecc; + chip->ecc.write_oob = mxc_nand_write_oob; + break; + + case NAND_ECC_ENGINE_TYPE_SOFT: + break; + + default: + return -EINVAL; + } + + if (chip->bbt_options & NAND_BBT_USE_FLASH) { + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + } + + /* Allocate the right size buffer now */ + devm_kfree(dev, (void *)host->data_buf); + host->data_buf = devm_kzalloc(dev, mtd->writesize + mtd->oobsize, + GFP_KERNEL); + if (!host->data_buf) + return -ENOMEM; + + /* Call preset again, with correct writesize chip time */ + host->devtype_data->preset(mtd); + + if (!chip->ecc.bytes) { + if (host->eccsize == 8) + chip->ecc.bytes = 18; + else if (host->eccsize == 4) + chip->ecc.bytes = 9; + } + + /* + * Experimentation shows that i.MX NFC can only handle up to 218 oob + * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare() + * into copying invalid data to/from the spare IO buffer, as this + * might cause ECC data corruption when doing sub-page write to a + * partially written page. + */ + host->used_oobsize = min(mtd->oobsize, 218U); + + 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 + chip->ecc.strength = (host->eccsize == 4) ? 4 : 8; + } + + return 0; +} + +static int mxcnd_setup_interface(struct nand_chip *chip, int chipnr, + const struct nand_interface_config *conf) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + + return host->devtype_data->setup_interface(chip, chipnr, conf); +} + +static int mxcnd_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + struct mxc_nand_host *host = nand_get_controller_data(chip); + struct mtd_info *mtd = nand_to_mtd(chip); + int i, j, buf_len; + void *buf_read = NULL; + const void *buf_write = NULL; + const struct nand_op_instr *instr; + bool readid = false; + bool statusreq = false; + + dev_dbg(host->dev, "%s: %d instructions\n", __func__, op->ninstrs); + + for (i = 0; i < op->ninstrs; i++) { + instr = &op->instrs[i]; + + nand_op_trace(" ", instr); + + switch (instr->type) { + case NAND_OP_WAITRDY_INSTR: + /* + * NFC handles R/B internally. Therefore, this function + * always returns status as ready. + */ + break; + case NAND_OP_CMD_INSTR: + host->devtype_data->send_cmd(host, instr->ctx.cmd.opcode, true); + + if (instr->ctx.cmd.opcode == NAND_CMD_READID) + readid = true; + if (instr->ctx.cmd.opcode == NAND_CMD_STATUS) + statusreq = true; + + break; + case NAND_OP_ADDR_INSTR: + for (j = 0; j < instr->ctx.addr.naddrs; j++) { + bool islast = j == instr->ctx.addr.naddrs - 1; + host->devtype_data->send_addr(host, instr->ctx.addr.addrs[j], islast); + } + break; + case NAND_OP_DATA_OUT_INSTR: + buf_write = instr->ctx.data.buf.out; + buf_len = instr->ctx.data.len; + + memcpy32_toio(host->main_area0, buf_write, buf_len); + copy_spare(mtd, false, chip->oob_poi); + + host->devtype_data->send_page(mtd, NFC_INPUT); + + break; + case NAND_OP_DATA_IN_INSTR: + + buf_read = instr->ctx.data.buf.in; + buf_len = instr->ctx.data.len; + + if (readid) { + host->devtype_data->send_read_id(host); + readid = false; + + memcpy32_fromio(host->data_buf, host->main_area0, buf_len * 2); + + if (chip->options & NAND_BUSWIDTH_16) { + u8 *bufr = buf_read; + u16 *bufw = host->data_buf; + for (j = 0; j < buf_len; j++) + bufr[j] = bufw[j]; + } else { + memcpy(buf_read, host->data_buf, buf_len); + } + break; + } + + if (statusreq) { + *(u8*)buf_read = host->devtype_data->get_dev_status(host); + statusreq = false; + break; + } + + host->devtype_data->read_page(chip); + + if (IS_ALIGNED(buf_len, 4)) { + memcpy32_fromio(buf_read, host->main_area0, buf_len); + } else { + memcpy32_fromio(host->data_buf, host->main_area0, mtd->writesize); + memcpy(buf_read, host->data_buf, buf_len); + } + + break; + } + } + + return 0; +} + +static const struct nand_controller_ops mxcnd_controller_ops = { + .attach_chip = mxcnd_attach_chip, + .setup_interface = mxcnd_setup_interface, + .exec_op = mxcnd_exec_op, +}; + +/* + * The i.MX NAND controller has the problem that it handles the + * data in chunks of 512 bytes. It doesn't treat 2k NAND chips as + * 2048 byte data + 64 OOB, but instead: + * + * 512b data + 16b OOB + + * 512b data + 16b OOB + + * 512b data + 16b OOB + + * 512b data + 16b OOB + * + * This means that the factory provided bad block marker ends up + * in the page data at offset 2000 instead of in the OOB data. + * + * To preserve the factory bad block information we take the following + * strategy: + * + * - If the NAND driver detects that no flash BBT is present on 2k NAND + * chips it will not create one because it would do so based on the wrong + * BBM position + * - This command is used to create a flash BBT then. + * + * From this point on we can forget about the BBMs and rely completely + * on the flash BBT. + * + */ +static int checkbad(struct nand_chip *chip, loff_t ofs) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + uint8_t buf[mtd->writesize + mtd->oobsize]; + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_RAW; + ops.ooboffs = 0; + ops.datbuf = buf; + ops.len = mtd->writesize; + ops.oobbuf = buf + mtd->writesize; + ops.ooblen = mtd->oobsize; + + ret = mtd_read_oob(mtd, ofs, &ops); + if (ret < 0) + return ret; + + if (buf[2000] != 0xff) + return 1; + + return 0; +} + +static int imxnd_create_bbt(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int len, i, numblocks, ret; + loff_t from = 0; + uint8_t *bbt; + + len = mtd->size >> (chip->bbt_erase_shift + 2); + + /* Allocate memory (2bit per block) and clear the memory bad block table */ + bbt = kzalloc(len, GFP_KERNEL); + if (!bbt) + return -ENOMEM; + + numblocks = mtd->size >> (chip->bbt_erase_shift - 1); + + for (i = 0; i < numblocks;) { + ret = checkbad(chip, from); + if (ret < 0) + goto out; + + if (ret) { + bbt[i >> 3] |= 0x03 << (i & 0x6); + dev_info(mtd->dev.parent, "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int)from); + } + + i += 2; + from += (1 << chip->bbt_erase_shift); + } + + chip->bbt_td->options |= NAND_BBT_CREATE; + chip->bbt_md->options |= NAND_BBT_CREATE; + + free(chip->bbt); + chip->bbt = bbt; + + ret = nand_update_bbt(chip, 0); + if (ret) + return ret; + + ret = nand_create_bbt(chip); + if (ret) + return ret; + + ret = 0; +out: + free(bbt); + + return ret; +} + +static int mxcnd_probe(struct device *dev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct mxc_nand_host *host; + struct resource *iores; + struct mxc_nand_devtype_data* devtype; + int err = 0; + + err = dev_get_drvdata(dev, (const void **)&devtype); + if (err) + return err; + + /* Allocate memory for MTD device structure and private data */ + host = devm_kzalloc(dev, sizeof(struct mxc_nand_host), + GFP_KERNEL); + if (!host) + return -ENOMEM; + + /* allocate a temporary buffer for the nand_scan_ident() */ + host->data_buf = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL); + if (!host->data_buf) + return -ENOMEM; + + host->dev = dev; + /* structures must be linked */ + this = &host->nand; + mtd = nand_to_mtd(this); + mtd->dev.parent = dev; + mtd->name = DRIVER_NAME; + + /* 50 us command delay time */ + this->legacy.chip_delay = 5; + + nand_set_controller_data(this, host); + nand_set_flash_node(this, dev->of_node); + + host->clk = clk_get(dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + host->devtype_data = devtype; + + if (!host->devtype_data->setup_interface) + this->options |= NAND_KEEP_TIMINGS; + + if (host->devtype_data->needs_ip) { + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + host->regs_ip = IOMEM(iores->start); + + iores = dev_request_mem_resource(dev, 1); + if (IS_ERR(iores)) + return PTR_ERR(iores); + host->base = IOMEM(iores->start); + } else { + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + host->base = IOMEM(iores->start); + } + + if (IS_ERR(host->base)) + return PTR_ERR(host->base); + + host->main_area0 = host->base; + + if (host->devtype_data->regs_offset) + host->regs = host->base + host->devtype_data->regs_offset; + host->spare0 = host->base + host->devtype_data->spare0_offset; + if (host->devtype_data->axi_offset) + host->regs_axi = host->base + host->devtype_data->axi_offset; + + this->legacy.select_chip = host->devtype_data->select_chip; + + init_completion(&host->op_completion); + + err = clk_prepare_enable(host->clk); + if (err) + return err; + host->clk_act = 1; + + /* Scan the NAND device */ + this->legacy.dummy_controller.ops = &mxcnd_controller_ops; + err = nand_scan(this, is_imx25_nfc(host) ? 4 : 1); + if (err) + goto escan; + + this->options &= ~NAND_SUBPAGE_READ; + + if ((this->bbt_options & NAND_BBT_USE_FLASH) && + this->bbt_td->pages[0] == -1 && this->bbt_md->pages[0] == -1) { + dev_info(dev, "no BBT found. creating one\n"); + err = imxnd_create_bbt(this); + if (err) + dev_warn(dev, "Failed to create bbt: %s\n", + strerror(-err)); + err = 0; + } + + /* Register the partitions */ + err = add_mtd_nand_device(mtd, "nand"); + if (err) + goto cleanup_nand; + + dev->priv = host; + + return 0; + +cleanup_nand: + nand_cleanup(this); +escan: + if (host->clk_act) + clk_disable_unprepare(host->clk); + + return err; +} + +static struct driver mxcnd_driver = { + .name = DRIVER_NAME, + .probe = mxcnd_probe, + .of_compatible = DRV_OF_COMPAT(mxcnd_dt_ids), +}; +device_platform_driver(mxcnd_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/raw/nand_amd.c index c3d4dae3cd..c3d4dae3cd 100644 --- a/drivers/mtd/nand/nand_amd.c +++ b/drivers/mtd/nand/raw/nand_amd.c diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 4c90ad9757..810b58a0c0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -36,171 +36,15 @@ #include <asm/byteorder.h> #include <io.h> #include <malloc.h> +#include <linux/gpio/consumer.h> #include <module.h> #include <of_mtd.h> +#include <linux/mtd/nand-ecc-sw-bch.h> +#include <linux/mtd/nand-ecc-sw-hamming.h> +#include <linux/sizes.h> #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 check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len) { int ret = 0; @@ -399,19 +243,9 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs) * @chip: NAND chip structure * * Lock the device and its controller for exclusive access - * - * Return: -EBUSY if the chip has been suspended, 0 otherwise */ -static int nand_get_device(struct nand_chip *chip) +static void nand_get_device(struct nand_chip *chip) { - mutex_lock(&chip->lock); - if (chip->suspended) { - mutex_unlock(&chip->lock); - return -EBUSY; - } - mutex_lock(&chip->controller->lock); - - return 0; } /** @@ -430,6 +264,10 @@ static int nand_check_wp(struct nand_chip *chip) if (chip->options & NAND_BROKEN_XD) return 0; + /* controller responsible for NAND write protect */ + if (chip->controller->controller_wp) + return 0; + /* Check the WP bit */ ret = nand_status_op(chip, &status); if (ret) @@ -636,9 +474,7 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs) nand_erase_nand(chip, &einfo, 0); /* Write bad block marker to OOB */ - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); ret = nand_markbad_bbm(chip, ofs); nand_release_device(chip); @@ -658,46 +494,20 @@ static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs) } /** - * nand_block_markgood_lowlevel - mark a block good + * nand_block_isreserved - [GENERIC] Check if a block is marked reserved. * @mtd: MTD device structure * @ofs: offset from device start * - * We try operations in the following order: - * (1) erase the affected block - * (2) check bad block marker - * (3) update the BBT + * Check if the block is marked as reserved. */ -static int nand_block_markgood_lowlevel(struct nand_chip *chip, loff_t ofs) +static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs) { - struct mtd_info *mtd = nand_to_mtd(chip); - bool allow_erasebad; - int ret; - - if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { - struct erase_info einfo; - - /* Attempt erase possibly bad block */ - allow_erasebad = mtd->allow_erasebad; - mtd->allow_erasebad = true; - memset(&einfo, 0, sizeof(einfo)); - einfo.mtd = mtd; - einfo.addr = ofs; - einfo.len = 1 << chip->phys_erase_shift; - nand_erase_nand(chip, &einfo, 0); - mtd->allow_erasebad = allow_erasebad; - } - - /* Mark block good in BBT */ - if (chip->bbt) { - ret = nand_markgood_bbt(chip, ofs); - if (ret) - return ret; - } - - if (mtd->ecc_stats.badblocks > 0) - mtd->ecc_stats.badblocks--; + struct nand_chip *chip = mtd_to_nand(mtd); - return 0; + if (!chip->bbt) + return 0; + /* Return info from the table */ + return nand_isreserved_bbt(chip, ofs); } /** @@ -737,7 +547,7 @@ static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt) */ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) { - const struct nand_sdr_timings *timings; + const struct nand_interface_config *conf; u8 status = 0; int ret; uint64_t start; @@ -746,13 +556,18 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) return -ENOTSUPP; /* Wait tWB before polling the STATUS reg. */ - timings = nand_get_sdr_timings(nand_get_interface_config(chip)); - ndelay(PSEC_TO_NSEC(timings->tWB_max)); + conf = nand_get_interface_config(chip); + ndelay(NAND_COMMON_TIMING_NS(conf, tWB_max)); ret = nand_status_op(chip, NULL); if (ret) return ret; + /* + * +1 below is necessary because if we are now in the last fraction + * of jiffy and msecs_to_jiffies is 1 then we will wait only that + * small jiffy fraction - possibly leading to false timeout + */ start = get_time_ns(); do { ret = nand_read_data_op(chip, &status, sizeof(status), true, @@ -769,7 +584,7 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) * deriving a delay from the timeout value, timeout_ms/ratio). */ udelay(10); - } while (!is_timeout(start, timeout_ms * MSECOND)); + } while (!is_timeout(start, timeout_ms * MSECOND)); /* * We have to exit READ_STATUS mode in order to read real data on the @@ -785,6 +600,59 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms) }; EXPORT_SYMBOL_GPL(nand_soft_waitrdy); +/** + * nand_gpio_waitrdy - Poll R/B GPIO pin until ready + * @chip: NAND chip structure + * @gpiod: GPIO descriptor of R/B pin + * @timeout_ms: Timeout in ms + * + * Poll the R/B GPIO pin until it becomes ready. If that does not happen + * whitin the specified timeout, -ETIMEDOUT is returned. + * + * This helper is intended to be used when the controller has access to the + * NAND R/B pin over GPIO. + * + * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise. + */ +int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod, + unsigned long timeout_ms) +{ + return gpiod_poll_timeout_us(gpiod, true, timeout_ms * USEC_PER_MSEC); +}; +EXPORT_SYMBOL_GPL(nand_gpio_waitrdy); + +/** + * panic_nand_wait - [GENERIC] wait until the command is done + * @chip: NAND chip structure + * @timeo: timeout + * + * Wait for command done. This is a helper function for nand_wait used when + * we are in interrupt context. May happen when in panic and trying to write + * an oops through mtdoops. + */ +void panic_nand_wait(struct nand_chip *chip, unsigned long timeo) +{ + int i; + for (i = 0; i < timeo; i++) { + if (chip->legacy.dev_ready) { + if (chip->legacy.dev_ready(chip)) + break; + } else { + int ret; + u8 status; + + ret = nand_read_data_op(chip, &status, sizeof(status), + true, false); + if (ret) + return; + + if (status & NAND_STATUS_READY) + break; + } + mdelay(1); + } +} + static bool nand_supports_get_features(struct nand_chip *chip, int addr) { return (chip->parameters.supports_set_get_features && @@ -850,7 +718,7 @@ static int nand_reset_interface(struct nand_chip *chip, int chipnr) static int nand_setup_interface(struct nand_chip *chip, int chipnr) { const struct nand_controller_ops *ops = chip->controller->ops; - u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { }; + u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { }, request; int ret; if (!nand_controller_can_setup_interface(chip)) @@ -866,7 +734,12 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr) if (!chip->best_interface_config) return 0; - tmode_param[0] = chip->best_interface_config->timings.mode; + request = chip->best_interface_config->timings.mode; + if (nand_interface_is_sdr(chip->best_interface_config)) + request |= ONFI_DATA_INTERFACE_SDR; + else + request |= ONFI_DATA_INTERFACE_NVDDR; + tmode_param[0] = request; /* Change the mode on the chip side (if supported by the NAND chip) */ if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) { @@ -895,9 +768,13 @@ static int nand_setup_interface(struct nand_chip *chip, int chipnr) if (ret) goto err_reset_chip; - if (tmode_param[0] != chip->best_interface_config->timings.mode) { - pr_warn("timing mode %d not acknowledged by the NAND chip\n", + if (request != tmode_param[0]) { + pr_warn("%s timing mode %d not acknowledged by the NAND chip\n", + nand_interface_is_nvddr(chip->best_interface_config) ? "NV-DDR" : "SDR", chip->best_interface_config->timings.mode); + pr_debug("NAND chip would work in %s timing mode %d\n", + tmode_param[0] & ONFI_DATA_INTERFACE_NVDDR ? "NV-DDR" : "SDR", + (unsigned int)ONFI_TIMING_MODE_PARAM(tmode_param[0])); goto err_reset_chip; } @@ -934,7 +811,7 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip, struct nand_sdr_timings *spec_timings) { const struct nand_controller_ops *ops = chip->controller->ops; - int best_mode = 0, mode, ret; + int best_mode = 0, mode, ret = -EOPNOTSUPP; iface->type = NAND_SDR_IFACE; @@ -953,7 +830,7 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip, /* Fallback to slower modes */ best_mode = iface->timings.mode; } else if (chip->parameters.onfi) { - best_mode = fls(chip->parameters.onfi->async_timing_mode) - 1; + best_mode = fls(chip->parameters.onfi->sdr_timing_modes) - 1; } for (mode = best_mode; mode >= 0; mode--) { @@ -961,13 +838,87 @@ int nand_choose_best_sdr_timings(struct nand_chip *chip, ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY, iface); - if (!ret) + if (!ret) { + chip->best_interface_config = iface; break; + } } - chip->best_interface_config = iface; + return ret; +} - return 0; +/** + * nand_choose_best_nvddr_timings - Pick up the best NVDDR timings that both the + * NAND controller and the NAND chip support + * @chip: the NAND chip + * @iface: the interface configuration (can eventually be updated) + * @spec_timings: specific timings, when not fitting the ONFI specification + * + * If specific timings are provided, use them. Otherwise, retrieve supported + * timing modes from ONFI information. + */ +int nand_choose_best_nvddr_timings(struct nand_chip *chip, + struct nand_interface_config *iface, + struct nand_nvddr_timings *spec_timings) +{ + const struct nand_controller_ops *ops = chip->controller->ops; + int best_mode = 0, mode, ret = -EOPNOTSUPP; + + iface->type = NAND_NVDDR_IFACE; + + if (spec_timings) { + iface->timings.nvddr = *spec_timings; + iface->timings.mode = onfi_find_closest_nvddr_mode(spec_timings); + + /* Verify the controller supports the requested interface */ + ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY, + iface); + if (!ret) { + chip->best_interface_config = iface; + return ret; + } + + /* Fallback to slower modes */ + best_mode = iface->timings.mode; + } else if (chip->parameters.onfi) { + best_mode = fls(chip->parameters.onfi->nvddr_timing_modes) - 1; + } + + for (mode = best_mode; mode >= 0; mode--) { + onfi_fill_interface_config(chip, iface, NAND_NVDDR_IFACE, mode); + + ret = ops->setup_interface(chip, NAND_DATA_IFACE_CHECK_ONLY, + iface); + if (!ret) { + chip->best_interface_config = iface; + break; + } + } + + return ret; +} + +/** + * nand_choose_best_timings - Pick up the best NVDDR or SDR timings that both + * NAND controller and the NAND chip support + * @chip: the NAND chip + * @iface: the interface configuration (can eventually be updated) + * + * If specific timings are provided, use them. Otherwise, retrieve supported + * timing modes from ONFI information. + */ +static int nand_choose_best_timings(struct nand_chip *chip, + struct nand_interface_config *iface) +{ + int ret; + + /* Try the fastest timings: NV-DDR */ + ret = nand_choose_best_nvddr_timings(chip, iface, NULL); + if (!ret) + return 0; + + /* Fallback to SDR timings otherwise */ + return nand_choose_best_sdr_timings(chip, iface, NULL); } /** @@ -998,7 +949,7 @@ static int nand_choose_interface_config(struct nand_chip *chip) if (chip->ops.choose_interface_config) ret = chip->ops.choose_interface_config(chip, iface); else - ret = nand_choose_best_sdr_timings(chip, iface, NULL); + ret = nand_choose_best_timings(chip, iface); if (ret) kfree(iface); @@ -1064,15 +1015,15 @@ static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_page, void *buf, unsigned int len) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct mtd_info *mtd = nand_to_mtd(chip); u8 addrs[4]; struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_READ0, 0), - NAND_OP_ADDR(3, addrs, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), - PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_ADDR(3, addrs, NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), NAND_OP_DATA_IN(len, buf, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1107,15 +1058,15 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_page, void *buf, unsigned int len) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); u8 addrs[5]; struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_READ0, 0), NAND_OP_ADDR(4, addrs, 0), - NAND_OP_CMD(NAND_CMD_READSTART, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), - PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), NAND_OP_DATA_IN(len, buf, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1140,6 +1091,117 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, return nand_exec_op(chip, &op); } +static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun) +{ + /* lun is expected to be very small */ + return (lun * pages_per_lun) + pages_per_lun - 1; +} + +static void rawnand_cap_cont_reads(struct nand_chip *chip) +{ + struct nand_memory_organization *memorg; + unsigned int ppl, first_lun, last_lun; + + memorg = nanddev_get_memorg(&chip->base); + ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun; + first_lun = chip->cont_read.first_page / ppl; + last_lun = chip->cont_read.last_page / ppl; + + /* Prevent sequential cache reads across LUN boundaries */ + if (first_lun != last_lun) + chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun); + else + chip->cont_read.pause_page = chip->cont_read.last_page; + + if (chip->cont_read.first_page == chip->cont_read.pause_page) { + chip->cont_read.first_page++; + chip->cont_read.pause_page = min(chip->cont_read.last_page, + rawnand_last_page_of_lun(ppl, first_lun + 1)); + } + + if (chip->cont_read.first_page >= chip->cont_read.last_page) + chip->cont_read.ongoing = false; +} + +static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, + unsigned int len, bool check_only) +{ + const struct nand_interface_config *conf = + nand_get_interface_config(chip); + u8 addrs[5]; + struct nand_op_instr start_instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_ADDR(4, addrs, 0), + NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), 0), + NAND_OP_CMD(NAND_CMD_READCACHESEQ, NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_op_instr cont_instrs[] = { + NAND_OP_CMD(page == chip->cont_read.pause_page ? + NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ, + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation start_op = NAND_OPERATION(chip->cur_cs, start_instrs); + struct nand_operation cont_op = NAND_OPERATION(chip->cur_cs, cont_instrs); + int ret; + + if (!len) { + start_op.ninstrs--; + cont_op.ninstrs--; + } + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + addrs[2] = page; + addrs[3] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) { + addrs[4] = page >> 16; + start_instrs[1].ctx.addr.naddrs++; + } + + /* Check if cache reads are supported */ + if (check_only) { + if (nand_check_op(chip, &start_op) || nand_check_op(chip, &cont_op)) + return -EOPNOTSUPP; + + return 0; + } + + if (page == chip->cont_read.first_page) + ret = nand_exec_op(chip, &start_op); + else + ret = nand_exec_op(chip, &cont_op); + if (ret) + return ret; + + if (!chip->cont_read.ongoing) + return 0; + + if (page == chip->cont_read.last_page) { + chip->cont_read.ongoing = false; + } else if (page == chip->cont_read.pause_page) { + chip->cont_read.first_page++; + rawnand_cap_cont_reads(chip); + } + + return 0; +} + +static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page) +{ + return chip->cont_read.ongoing && page >= chip->cont_read.first_page; +} + /** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip @@ -1165,10 +1227,16 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page, return -EINVAL; if (nand_has_exec_op(chip)) { - if (mtd->writesize > 512) - return nand_lp_exec_read_page_op(chip, page, - offset_in_page, buf, - len); + if (mtd->writesize > 512) { + if (rawnand_cont_read_ongoing(chip, page)) + return nand_lp_exec_cont_read_page_op(chip, page, + offset_in_page, + buf, len, false); + else + return nand_lp_exec_read_page_op(chip, page, + offset_in_page, buf, + len); + } return nand_sp_exec_read_page_op(chip, page, offset_in_page, buf, len); @@ -1204,13 +1272,14 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, return -EINVAL; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_PARAM, 0), - NAND_OP_ADDR(1, &page, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tR_max), - PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_ADDR(1, &page, + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), NAND_OP_8BIT_DATA_IN(len, buf, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1259,14 +1328,14 @@ int nand_change_read_column_op(struct nand_chip *chip, return -ENOTSUPP; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); u8 addrs[2] = {}; struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_RNDOUT, 0), NAND_OP_ADDR(2, addrs, 0), NAND_OP_CMD(NAND_CMD_RNDOUTSTART, - PSEC_TO_NSEC(sdr->tCCS_min)), + NAND_COMMON_TIMING_NS(conf, tCCS_min)), NAND_OP_DATA_IN(len, buf, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1334,8 +1403,8 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_page, const void *buf, unsigned int len, bool prog) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct mtd_info *mtd = nand_to_mtd(chip); u8 addrs[5] = {}; struct nand_op_instr instrs[] = { @@ -1346,15 +1415,15 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, */ NAND_OP_CMD(NAND_CMD_READ0, 0), NAND_OP_CMD(NAND_CMD_SEQIN, 0), - NAND_OP_ADDR(0, addrs, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_ADDR(0, addrs, NAND_COMMON_TIMING_NS(conf, tADL_min)), NAND_OP_DATA_OUT(len, buf, 0), - NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), + NAND_OP_CMD(NAND_CMD_PAGEPROG, + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), 0), }; - struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs, + instrs); int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); - int ret; - u8 status; if (naddrs < 0) return naddrs; @@ -1394,15 +1463,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, op.ninstrs--; } - ret = nand_exec_op(chip, &op); - if (!prog || ret) - return ret; - - ret = nand_status_op(chip, &status); - if (ret) - return ret; - - return status; + return nand_exec_op(chip, &op); } /** @@ -1458,12 +1519,13 @@ int nand_prog_page_end_op(struct nand_chip *chip) u8 status; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_PAGEPROG, - PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0), + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), + 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1508,7 +1570,8 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, unsigned int len) { struct mtd_info *mtd = nand_to_mtd(chip); - int status; + u8 status; + int ret; if (!len || !buf) return -EINVAL; @@ -1517,14 +1580,24 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page, return -EINVAL; if (nand_has_exec_op(chip)) { - status = nand_exec_prog_page_op(chip, page, offset_in_page, buf, + ret = nand_exec_prog_page_op(chip, page, offset_in_page, buf, len, true); + if (ret) + return ret; + + ret = nand_status_op(chip, &status); + if (ret) + return ret; } else { chip->legacy.cmdfunc(chip, NAND_CMD_SEQIN, offset_in_page, page); chip->legacy.write_buf(chip, buf, len); chip->legacy.cmdfunc(chip, NAND_CMD_PAGEPROG, -1, -1); - status = chip->legacy.waitfunc(chip); + ret = chip->legacy.waitfunc(chip); + if (ret < 0) + return ret; + + status = ret; } if (status & NAND_STATUS_FAIL) @@ -1565,12 +1638,12 @@ int nand_change_write_column_op(struct nand_chip *chip, return -ENOTSUPP; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); u8 addrs[2]; struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_RNDIN, 0), - NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)), + NAND_OP_ADDR(2, addrs, NAND_COMMON_TIMING_NS(conf, tCCS_min)), NAND_OP_DATA_OUT(len, buf, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1614,26 +1687,46 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, unsigned int len) { unsigned int i; - u8 *id = buf; + u8 *id = buf, *ddrbuf = NULL; if (len && !buf) return -EINVAL; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_READID, 0), - NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_ADDR(1, &addr, + NAND_COMMON_TIMING_NS(conf, tADL_min)), NAND_OP_8BIT_DATA_IN(len, buf, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + int ret; + + /* READ_ID data bytes are received twice in NV-DDR mode */ + if (len && nand_interface_is_nvddr(conf)) { + ddrbuf = kzalloc(len * 2, GFP_KERNEL); + if (!ddrbuf) + return -ENOMEM; + + instrs[2].ctx.data.len *= 2; + instrs[2].ctx.data.buf.in = ddrbuf; + } /* Drop the DATA_IN instruction if len is set to 0. */ if (!len) op.ninstrs--; - return nand_exec_op(chip, &op); + ret = nand_exec_op(chip, &op); + if (!ret && len && nand_interface_is_nvddr(conf)) { + for (i = 0; i < len; i++) + id[i] = ddrbuf[i * 2]; + } + + kfree(ddrbuf); + + return ret; } chip->legacy.cmdfunc(chip, NAND_CMD_READID, addr, -1); @@ -1659,19 +1752,31 @@ EXPORT_SYMBOL_GPL(nand_readid_op); int nand_status_op(struct nand_chip *chip, u8 *status) { if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); + u8 ddrstatus[2]; struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_STATUS, - PSEC_TO_NSEC(sdr->tADL_min)), + NAND_COMMON_TIMING_NS(conf, tADL_min)), NAND_OP_8BIT_DATA_IN(1, status, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + int ret; + + /* The status data byte will be received twice in NV-DDR mode */ + if (status && nand_interface_is_nvddr(conf)) { + instrs[1].ctx.data.len *= 2; + instrs[1].ctx.data.buf.in = ddrstatus; + } if (!status) op.ninstrs--; - return nand_exec_op(chip, &op); + ret = nand_exec_op(chip, &op); + if (!ret && status && nand_interface_is_nvddr(conf)) + *status = ddrstatus[0]; + + return ret; } chip->legacy.cmdfunc(chip, NAND_CMD_STATUS, -1, -1); @@ -1708,6 +1813,7 @@ int nand_exit_status_op(struct nand_chip *chip) return 0; } +EXPORT_SYMBOL_GPL(nand_exit_status_op); /** * nand_erase_op - Do an erase operation @@ -1728,17 +1834,19 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) u8 status; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); u8 addrs[3] = { page, page >> 8, page >> 16 }; struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_ERASE1, 0), NAND_OP_ADDR(2, addrs, 0), NAND_OP_CMD(NAND_CMD_ERASE2, - PSEC_TO_MSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0), + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tBERS_max), + 0), }; - struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs, + instrs); if (chip->options & NAND_ROW_ADDR_3) instrs[1].ctx.addr.naddrs++; @@ -1787,14 +1895,17 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, int i, ret; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_SET_FEATURES, 0), - NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tADL_min)), + NAND_OP_ADDR(1, &feature, NAND_COMMON_TIMING_NS(conf, + tADL_min)), NAND_OP_8BIT_DATA_OUT(ONFI_SUBFEATURE_PARAM_LEN, data, - PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0), + NAND_COMMON_TIMING_NS(conf, + tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max), + 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1830,23 +1941,37 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature, static int nand_get_features_op(struct nand_chip *chip, u8 feature, void *data) { - u8 *params = data; + u8 *params = data, ddrbuf[ONFI_SUBFEATURE_PARAM_LEN * 2]; int i; if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct nand_op_instr instrs[] = { NAND_OP_CMD(NAND_CMD_GET_FEATURES, 0), - NAND_OP_ADDR(1, &feature, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), - PSEC_TO_NSEC(sdr->tRR_min)), + NAND_OP_ADDR(1, &feature, + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tFEAT_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN, data, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + int ret; - return nand_exec_op(chip, &op); + /* GET_FEATURE data bytes are received twice in NV-DDR mode */ + if (nand_interface_is_nvddr(conf)) { + instrs[3].ctx.data.len *= 2; + instrs[3].ctx.data.buf.in = ddrbuf; + } + + ret = nand_exec_op(chip, &op); + if (nand_interface_is_nvddr(conf)) { + for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; i++) + params[i] = ddrbuf[i * 2]; + } + + return ret; } chip->legacy.cmdfunc(chip, NAND_CMD_GET_FEATURES, feature, -1); @@ -1891,11 +2016,13 @@ static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms, int nand_reset_op(struct nand_chip *chip) { if (nand_has_exec_op(chip)) { - const struct nand_sdr_timings *sdr = - nand_get_sdr_timings(nand_get_interface_config(chip)); + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct nand_op_instr instrs[] = { - NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)), - NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0), + NAND_OP_CMD(NAND_CMD_RESET, + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tRST_max), + 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); @@ -1930,17 +2057,50 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len, return -EINVAL; if (nand_has_exec_op(chip)) { + const struct nand_interface_config *conf = + nand_get_interface_config(chip); struct nand_op_instr instrs[] = { NAND_OP_DATA_IN(len, buf, 0), }; struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); + u8 *ddrbuf = NULL; + int ret, i; instrs[0].ctx.data.force_8bit = force_8bit; - if (check_only) - return nand_check_op(chip, &op); + /* + * Parameter payloads (ID, status, features, etc) do not go + * through the same pipeline as regular data, hence the + * force_8bit flag must be set and this also indicates that in + * case NV-DDR timings are being used the data will be received + * twice. + */ + if (force_8bit && nand_interface_is_nvddr(conf)) { + ddrbuf = kzalloc(len * 2, GFP_KERNEL); + if (!ddrbuf) + return -ENOMEM; + + instrs[0].ctx.data.len *= 2; + instrs[0].ctx.data.buf.in = ddrbuf; + } - return nand_exec_op(chip, &op); + if (check_only) { + ret = nand_check_op(chip, &op); + kfree(ddrbuf); + return ret; + } + + ret = nand_exec_op(chip, &op); + if (!ret && force_8bit && nand_interface_is_nvddr(conf)) { + u8 *dst = buf; + + for (i = 0; i < len; i++) + dst[i] = ddrbuf[i * 2]; + } + + kfree(ddrbuf); + + return ret; } if (check_only) @@ -3006,6 +3166,73 @@ static int nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf, } /** + * nand_read_page_hwecc_oob_first - Hardware ECC page read with ECC + * data read from OOB area + * @chip: nand chip info structure + * @buf: buffer to store read data + * @oob_required: caller requires OOB data read to chip->oob_poi + * @page: page number to read + * + * Hardware ECC for large page chips, which requires the ECC data to be + * extracted from the OOB before the actual data is read. + */ +int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int i, eccsize = chip->ecc.size, ret; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_code = chip->ecc.code_buf; + unsigned int max_bitflips = 0; + + /* Read the OOB area first */ + ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); + if (ret) + return ret; + + /* Move read cursor to start of page */ + ret = nand_change_read_column_op(chip, 0, NULL, 0, false); + if (ret) + return ret; + + ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, + chip->ecc.total); + if (ret) + return ret; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + chip->ecc.hwctl(chip, NAND_ECC_READ); + + ret = nand_read_data_op(chip, p, eccsize, false, false); + if (ret) + return ret; + + stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + &ecc_code[i], + eccbytes, NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + return max_bitflips; +} +EXPORT_SYMBOL_GPL(nand_read_page_hwecc_oob_first); + +/** * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read * @chip: nand chip info structure * @buf: buffer to store read data @@ -3129,6 +3356,51 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, return NULL; } +static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page, + u32 readlen, int col) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + unsigned int first_page, last_page; + + chip->cont_read.ongoing = false; + + if (!chip->controller->supported_op.cont_read) + return; + + /* + * Don't bother making any calculations if the length is too small. + * Side effect: avoids possible integer underflows below. + */ + if (readlen < (2 * mtd->writesize)) + return; + + /* Derive the page where continuous read should start (the first full page read) */ + first_page = page; + if (col) + first_page++; + + /* Derive the page where continuous read should stop (the last full page read) */ + last_page = page + ((col + readlen) / mtd->writesize) - 1; + + /* Configure and enable continuous read when suitable */ + if (first_page < last_page) { + chip->cont_read.first_page = first_page; + chip->cont_read.last_page = last_page; + chip->cont_read.ongoing = true; + /* May reset the ongoing flag */ + rawnand_cap_cont_reads(chip); + } +} + +static void rawnand_cont_read_skip_first_page(struct nand_chip *chip, unsigned int page) +{ + if (!chip->cont_read.ongoing || page != chip->cont_read.first_page) + return; + + chip->cont_read.first_page++; + rawnand_cap_cont_reads(chip); +} + /** * nand_setup_read_retry - [INTERN] Set the READ RETRY mode * @chip: NAND chip object @@ -3153,13 +3425,13 @@ static int nand_setup_read_retry(struct nand_chip *chip, int retry_mode) static void nand_wait_readrdy(struct nand_chip *chip) { - const struct nand_sdr_timings *sdr; + const struct nand_interface_config *conf; if (!(chip->options & NAND_NEED_READRDY)) return; - sdr = nand_get_sdr_timings(nand_get_interface_config(chip)); - WARN_ON(nand_wait_rdy_op(chip, PSEC_TO_MSEC(sdr->tR_max), 0)); + conf = nand_get_interface_config(chip); + WARN_ON(nand_wait_rdy_op(chip, NAND_COMMON_TIMING_MS(conf, tR_max), 0)); } /** @@ -3198,6 +3470,9 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from, oob = ops->oobbuf; oob_required = oob ? 1 : 0; + if (likely(ops->mode != MTD_OPS_RAW)) + rawnand_enable_cont_reads(chip, page, readlen, col); + while (1) { struct mtd_ecc_stats ecc_stats = mtd->ecc_stats; @@ -3296,6 +3571,8 @@ read_retry: buf += bytes; max_bitflips = max_t(unsigned int, max_bitflips, chip->pagecache.bitflips); + + rawnand_cont_read_skip_first_page(chip, page); } readlen -= bytes; @@ -3326,6 +3603,9 @@ read_retry: } nand_deselect_target(chip); + if (WARN_ON_ONCE(chip->cont_read.ongoing)) + chip->cont_read.ongoing = false; + ops->retlen = ops->len - (size_t) readlen; if (oob) ops->oobretlen = ops->ooblen - oobreadlen; @@ -3415,9 +3695,6 @@ int nand_write_oob_std(struct nand_chip *chip, int page) { struct mtd_info *mtd = nand_to_mtd(chip); - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, mtd->oobsize); } @@ -3437,9 +3714,6 @@ static int nand_write_oob_syndrome(struct nand_chip *chip, int page) int ret, i, len, pos, sndcmd = 0, steps = chip->ecc.steps; const uint8_t *bufpoi = chip->oob_poi; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - /* * data-ecc-data-ecc ... ecc-oob * or @@ -3589,6 +3863,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd_to_nand(mtd); + struct mtd_ecc_stats old_stats; int ret; ops->retlen = 0; @@ -3598,9 +3873,9 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, ops->mode != MTD_OPS_RAW) return -ENOTSUPP; - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); + + old_stats = mtd->ecc_stats; if (!ops->datbuf) ret = nand_do_read_oob(chip, from, ops); @@ -3641,9 +3916,6 @@ int nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, struct mtd_info *mtd = nand_to_mtd(chip); int ret; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - ret = nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); if (ret) return ret; @@ -3682,9 +3954,6 @@ int nand_monolithic_write_page_raw(struct nand_chip *chip, const u8 *buf, unsigned int size = mtd->writesize; u8 *write_buf = (u8 *)buf; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - if (oob_required) { size += mtd->oobsize; @@ -3717,9 +3986,6 @@ static int nand_write_page_raw_syndrome(struct nand_chip *chip, uint8_t *oob = chip->oob_poi; int steps, size, ret; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); if (ret) return ret; @@ -3782,9 +4048,6 @@ static int nand_write_page_swecc(struct nand_chip *chip, const uint8_t *buf, uint8_t *ecc_calc = chip->ecc.calc_buf; const uint8_t *p = buf; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - /* Software ECC calculation */ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(chip, p, &ecc_calc[i]); @@ -3814,9 +4077,6 @@ static int nand_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf, uint8_t *ecc_calc = chip->ecc.calc_buf; const uint8_t *p = buf; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); if (ret) return ret; @@ -3868,9 +4128,6 @@ static int nand_write_subpage_hwecc(struct nand_chip *chip, uint32_t offset, int oob_bytes = mtd->oobsize / ecc_steps; int step, ret; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); if (ret) return ret; @@ -3938,9 +4195,6 @@ static int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf, uint8_t *oob = chip->oob_poi; int ret; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); if (ret) return ret; @@ -4007,9 +4261,6 @@ static int nand_write_page(struct nand_chip *chip, uint32_t offset, struct mtd_info *mtd = nand_to_mtd(chip); int status, subpage; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && chip->ecc.write_subpage) subpage = offset || (data_len < mtd->writesize); @@ -4056,15 +4307,12 @@ static int nand_do_write_ops(struct nand_chip *chip, loff_t to, int ret; int oob_required = oob ? 1 : 0; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - ops->retlen = 0; if (!writelen) return 0; /* Reject writes, which are not page aligned */ - if (NOTALIGNED(to)) { + if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { pr_notice("%s: attempt to write non page aligned data\n", __func__); return -EINVAL; @@ -4174,16 +4422,11 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { struct nand_chip *chip = mtd_to_nand(mtd); - int ret; - - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; + int ret = 0; ops->retlen = 0; - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); switch (ops->mode) { case MTD_OPS_PLACE_OOB: @@ -4214,9 +4457,6 @@ out: */ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr) { - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - return nand_erase_nand(mtd_to_nand(mtd), instr, 0); } @@ -4232,13 +4472,9 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, int allowbbt) { struct mtd_info *mtd = nand_to_mtd(chip); - int page, pages_per_block, ret, chipnr; loff_t len; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)instr->addr, (unsigned long long)instr->len); @@ -4247,9 +4483,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, return -EINVAL; /* Grab the lock and see if the device is available */ - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); /* Shift to get first page */ page = (int)(instr->addr >> chip->page_shift); @@ -4273,12 +4507,14 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, len = instr->len; while (len) { + loff_t ofs = (loff_t)page << chip->page_shift; + /* Check if we have a bad block, we do not erase bad blocks! */ if (!mtd->allow_erasebad && nand_block_checkbad(chip, ((loff_t) page) << chip->page_shift, allowbbt)) { - pr_warn("%s: attempt to erase a bad block at page 0x%08x\n", - __func__, page); + pr_warn("%s: attempt to erase a bad block at 0x%08llx\n", + __func__, (unsigned long long)ofs); ret = -EIO; goto erase_exit; } @@ -4296,8 +4532,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr, if (ret) { pr_debug("%s: failed erase, page 0x%08x\n", __func__, page); - instr->fail_addr = - ((loff_t)page << chip->page_shift); + instr->fail_addr = ofs; goto erase_exit; } @@ -4337,7 +4572,7 @@ static void nand_sync(struct mtd_info *mtd) pr_debug("%s: called\n", __func__); /* Grab the lock and see if the device is available */ - WARN_ON(nand_get_device(chip)); + nand_get_device(chip); /* Release it and go back */ nand_release_device(chip); } @@ -4354,9 +4589,7 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) int ret; /* Select the NAND device */ - ret = nand_get_device(chip); - if (ret) - return ret; + nand_get_device(chip); nand_select_target(chip, chipnr); @@ -4377,9 +4610,6 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { int ret; - if (!IS_ENABLED(CONFIG_MTD_WRITE)) - return -ENOTSUPP; - ret = nand_block_isbad(mtd, ofs); if (ret) { /* If it was bad already, return success and do nothing */ @@ -4392,32 +4622,12 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) } /** - * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad - * @mtd: MTD device structure - * @ofs: offset relative to mtd start - */ -static int nand_block_markgood(struct mtd_info *mtd, loff_t ofs) -{ - int ret; - - ret = nand_block_isbad(mtd, ofs); - if (ret < 0) - return ret; - - if (!ret) - /* If it was good already, return success and do nothing */ - return 0; - - return nand_block_markgood_lowlevel(mtd_to_nand(mtd), ofs); -} - -/** * nand_lock - [MTD Interface] Lock the NAND flash * @mtd: MTD device structure * @ofs: offset byte address * @len: number of bytes to lock (must be a multiple of block/page size) */ -static int nand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct nand_chip *chip = mtd_to_nand(mtd); @@ -4433,7 +4643,7 @@ static int nand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) * @ofs: offset byte address * @len: number of bytes to unlock (must be a multiple of block/page size) */ -static int nand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { struct nand_chip *chip = mtd_to_nand(mtd); @@ -4630,6 +4840,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; @@ -4651,10 +4863,11 @@ 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 = strdup(type->name); + chip->parameters.model = kstrdup(type->name, GFP_KERNEL); if (!chip->parameters.model) return false; @@ -4723,6 +4936,67 @@ nand_manufacturer_name(const struct nand_manufacturer_desc *manufacturer_desc) return manufacturer_desc ? manufacturer_desc->name : "Unknown"; } +static void rawnand_check_data_only_read_support(struct nand_chip *chip) +{ + /* Use an arbitrary size for the check */ + if (!nand_read_data_op(chip, NULL, SZ_512, true, true)) + chip->controller->supported_op.data_only_read = 1; +} + +static void rawnand_early_check_supported_ops(struct nand_chip *chip) +{ + /* The supported_op fields should not be set by individual drivers */ + WARN_ON_ONCE(chip->controller->supported_op.data_only_read); + + if (!nand_has_exec_op(chip)) + return; + + rawnand_check_data_only_read_support(chip); +} + +static void rawnand_check_cont_read_support(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!chip->parameters.supports_read_cache) + return; + + if (chip->read_retries) + return; + + if (!nand_lp_exec_cont_read_page_op(chip, 0, 0, NULL, + mtd->writesize, true)) + chip->controller->supported_op.cont_read = 1; +} + +static void rawnand_late_check_supported_ops(struct nand_chip *chip) +{ + /* The supported_op fields should not be set by individual drivers */ + WARN_ON_ONCE(chip->controller->supported_op.cont_read); + + /* + * Too many devices do not support sequential cached reads with on-die + * ECC correction enabled, so in this case refuse to perform the + * automation. + */ + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE) + return; + + if (!nand_has_exec_op(chip)) + return; + + /* + * For now, continuous reads can only be used with the core page helpers. + * This can be extended later. + */ + if (!(chip->ecc.read_page == nand_read_page_hwecc || + chip->ecc.read_page == nand_read_page_syndrome || + chip->ecc.read_page == nand_read_page_swecc)) + return; + + rawnand_check_cont_read_support(chip); +} + /* * Get the flash and manufacturer id and lookup if the type is supported. */ @@ -4755,6 +5029,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) /* Select the device */ nand_select_target(chip, 0); + rawnand_early_check_supported_ops(chip); + /* Send the command for reading device ID */ ret = nand_readid_op(chip, 0, id_data, 2); if (ret) @@ -4834,7 +5110,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) if (!type->name) return -ENODEV; - chip->parameters.model = strdup(type->name); + chip->parameters.model = kstrdup(type->name, GFP_KERNEL); if (!chip->parameters.model) return -ENOMEM; @@ -4913,80 +5189,139 @@ free_detect_allocation: return ret; } -static const char * const nand_ecc_algos[] = { - [NAND_ECC_HAMMING] = "hamming", - [NAND_ECC_BCH] = "bch", - [NAND_ECC_RS] = "rs", -}; +static enum nand_ecc_engine_type +of_get_rawnand_ecc_engine_type_legacy(struct device_node *np) +{ + 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; + + err = of_property_read_string(np, "nand-ecc-mode", &pm); + 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; + } + } + } -static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np) + return NAND_ECC_ENGINE_TYPE_INVALID; +} + +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 void of_get_nand_ecc_legacy_user_config(struct nand_chip *chip) +{ + struct device_node *dn = nand_get_flash_node(chip); + struct nand_ecc_props *user_conf = &chip->base.ecc.user_conf; + + if (user_conf->engine_type == NAND_ECC_ENGINE_TYPE_INVALID) + user_conf->engine_type = of_get_rawnand_ecc_engine_type_legacy(dn); + + if (user_conf->algo == NAND_ECC_ALGO_UNKNOWN) + user_conf->algo = of_get_rawnand_ecc_algo_legacy(dn); + + if (user_conf->placement == NAND_ECC_PLACEMENT_UNKNOWN) + user_conf->placement = of_get_rawnand_ecc_placement_legacy(dn); } -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; + int ret; if (!dn) return 0; - if (of_get_nand_bus_width(dn) == 16) + ret = of_get_nand_bus_width(dn); + if (ret < 0) + return ret; + + if (ret == 16) chip->options |= NAND_BUSWIDTH_16; if (of_property_read_bool(dn, "nand-is-boot-medium")) chip->options |= NAND_IS_BOOT_MEDIUM; - if (of_get_nand_on_flash_bbt(dn)) + if (of_property_read_bool(dn, "nand-on-flash-bbt")) 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); + of_get_nand_ecc_user_config(nand); + of_get_nand_ecc_legacy_user_config(chip); - if (ecc_mode >= 0) - chip->ecc.mode = ecc_mode; - - 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; } @@ -5024,11 +5359,9 @@ 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(); - if (IS_ENABLED(CONFIG_OFTREE)) { - ret = nand_dt_init(chip); - if (ret) - return ret; - } + ret = rawnand_dt_init(chip); + if (ret) + return ret; if (!mtd->name && mtd->dev.parent) mtd->name = strdup(dev_name(mtd->dev.parent)); @@ -5093,21 +5426,189 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip) kfree(chip->parameters.onfi); } +int rawnand_sw_hamming_init(struct nand_chip *chip) +{ + struct nand_ecc_sw_hamming_conf *engine_conf; + struct nand_device *base = &chip->base; + int ret; + + base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + base->ecc.user_conf.algo = NAND_ECC_ALGO_HAMMING; + base->ecc.user_conf.strength = chip->ecc.strength; + base->ecc.user_conf.step_size = chip->ecc.size; + + ret = nand_ecc_sw_hamming_init_ctx(base); + if (ret) + return ret; + + engine_conf = base->ecc.ctx.priv; + + if (chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER) + engine_conf->sm_order = true; + + chip->ecc.size = base->ecc.ctx.conf.step_size; + chip->ecc.strength = base->ecc.ctx.conf.strength; + chip->ecc.total = base->ecc.ctx.total; + chip->ecc.steps = nanddev_get_ecc_nsteps(base); + chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base); + + return 0; +} +EXPORT_SYMBOL(rawnand_sw_hamming_init); + +int rawnand_sw_hamming_calculate(struct nand_chip *chip, + const unsigned char *buf, + unsigned char *code) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_hamming_calculate(base, buf, code); +} +EXPORT_SYMBOL(rawnand_sw_hamming_calculate); + +int rawnand_sw_hamming_correct(struct nand_chip *chip, + unsigned char *buf, + unsigned char *read_ecc, + unsigned char *calc_ecc) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_hamming_correct(base, buf, read_ecc, calc_ecc); +} +EXPORT_SYMBOL(rawnand_sw_hamming_correct); + +void rawnand_sw_hamming_cleanup(struct nand_chip *chip) +{ + struct nand_device *base = &chip->base; + + nand_ecc_sw_hamming_cleanup_ctx(base); +} +EXPORT_SYMBOL(rawnand_sw_hamming_cleanup); + +int rawnand_sw_bch_init(struct nand_chip *chip) +{ + struct nand_device *base = &chip->base; + const struct nand_ecc_props *ecc_conf = nanddev_get_ecc_conf(base); + int ret; + + base->ecc.user_conf.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + base->ecc.user_conf.algo = NAND_ECC_ALGO_BCH; + base->ecc.user_conf.step_size = chip->ecc.size; + base->ecc.user_conf.strength = chip->ecc.strength; + + ret = nand_ecc_sw_bch_init_ctx(base); + if (ret) + return ret; + + chip->ecc.size = ecc_conf->step_size; + chip->ecc.strength = ecc_conf->strength; + chip->ecc.total = base->ecc.ctx.total; + chip->ecc.steps = nanddev_get_ecc_nsteps(base); + chip->ecc.bytes = base->ecc.ctx.total / nanddev_get_ecc_nsteps(base); + + return 0; +} +EXPORT_SYMBOL(rawnand_sw_bch_init); + +static int rawnand_sw_bch_calculate(struct nand_chip *chip, + const unsigned char *buf, + unsigned char *code) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_bch_calculate(base, buf, code); +} + +int rawnand_sw_bch_correct(struct nand_chip *chip, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) +{ + struct nand_device *base = &chip->base; + + return nand_ecc_sw_bch_correct(base, buf, read_ecc, calc_ecc); +} +EXPORT_SYMBOL(rawnand_sw_bch_correct); + +void rawnand_sw_bch_cleanup(struct nand_chip *chip) +{ + struct nand_device *base = &chip->base; + + nand_ecc_sw_bch_cleanup_ctx(base); +} +EXPORT_SYMBOL(rawnand_sw_bch_cleanup); + +static int nand_set_ecc_on_host_ops(struct nand_chip *chip) +{ + struct nand_ecc_ctrl *ecc = &chip->ecc; + + 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; + int ret; - if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SOFT)) - return -ENOSYS; - - 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: - ecc->calculate = nand_calculate_ecc; - ecc->correct = nand_correct_data; + case NAND_ECC_ALGO_HAMMING: + ecc->calculate = rawnand_sw_hamming_calculate; + ecc->correct = rawnand_sw_hamming_correct; ecc->read_page = nand_read_page_swecc; ecc->read_subpage = nand_read_subpage; ecc->write_page = nand_write_page_swecc; @@ -5125,14 +5626,20 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) if (IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING_SMC)) ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER; + ret = rawnand_sw_hamming_init(chip); + if (ret) { + WARN(1, "Hamming ECC initialization failed!\n"); + return ret; + } + return 0; - case NAND_ECC_BCH: - if (!mtd_nand_has_bch()) { + case NAND_ECC_ALGO_BCH: + if (!IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) { WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n"); return -EINVAL; } - ecc->calculate = nand_bch_calculate_ecc; - ecc->correct = nand_bch_correct_data; + ecc->calculate = rawnand_sw_bch_calculate; + ecc->correct = rawnand_sw_bch_correct; ecc->read_page = nand_read_page_swecc; ecc->read_subpage = nand_read_subpage; ecc->write_page = nand_write_page_swecc; @@ -5144,55 +5651,20 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip) ecc->write_oob = nand_write_oob_std; /* - * Board driver should supply ecc.size and ecc.strength - * values to select how many bits are correctable. - * Otherwise, default to 4 bits for large page devices. - */ - if (!ecc->size && (mtd->oobsize >= 64)) { - ecc->size = 512; - ecc->strength = 4; - } - - /* - * if no ecc placement scheme was provided pickup the default - * large page one. - */ - if (!mtd->ooblayout) { - /* handle large page devices only */ - if (mtd->oobsize < 64) { - WARN(1, "OOB layout is required when using software BCH on small pages\n"); - return -EINVAL; - } - - mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops); - - } - - /* * We can only maximize ECC config when the default layout is * used, otherwise we don't know how many bytes can really be * used. */ - if (mtd->ooblayout == &nand_ooblayout_lp_ops && - ecc->options & NAND_ECC_MAXIMIZE) { - int steps, bytes; - - /* Always prefer 1k blocks over 512bytes ones */ - ecc->size = 1024; - steps = mtd->writesize / ecc->size; + if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH && + mtd->ooblayout != nand_get_large_page_ooblayout()) + nanddev->ecc.user_conf.flags &= ~NAND_ECC_MAXIMIZE_STRENGTH; - /* Reserve 2 bytes for the BBM */ - bytes = (mtd->oobsize - 2) / steps; - ecc->strength = bytes * 8 / fls(8 * ecc->size); - } - - /* See nand_bch_init() for details. */ - ecc->bytes = 0; - ecc->priv = nand_bch_init(mtd); - if (!ecc->priv) { + ret = rawnand_sw_bch_init(chip); + if (ret) { WARN(1, "BCH ECC initialization failed!\n"); - return -EINVAL; + return ret; } + return 0; default: WARN(1, "Unsupported ECC algorithm!\n"); @@ -5233,7 +5705,7 @@ nand_check_ecc_caps(struct nand_chip *chip, ecc_bytes = caps->calc_ecc_bytes(preset_step, preset_strength); - if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__)) + if (WARN_ON_ONCE(ecc_bytes < 0)) return ecc_bytes; if (ecc_bytes * nsteps > oobavail) { @@ -5268,12 +5740,14 @@ 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_step = 0, best_strength = 0, best_ecc_bytes = 0; int best_ecc_bytes_total = INT_MAX; int i, j; @@ -5305,7 +5779,7 @@ nand_match_ecc_req(struct nand_chip *chip, nsteps = mtd->writesize / step_size; ecc_bytes = caps->calc_ecc_bytes(step_size, strength); - if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__)) + if (WARN_ON_ONCE(ecc_bytes < 0)) continue; ecc_bytes_total = ecc_bytes * nsteps; @@ -5354,7 +5828,7 @@ nand_maximize_ecc(struct nand_chip *chip, int step_size, strength, nsteps, ecc_bytes, corr; int best_corr = 0; int best_step = 0; - int best_strength, best_ecc_bytes; + int best_strength = 0, best_ecc_bytes = 0; int i, j; for (i = 0; i < caps->nstepinfos; i++) { @@ -5374,7 +5848,7 @@ nand_maximize_ecc(struct nand_chip *chip, nsteps = mtd->writesize / step_size; ecc_bytes = caps->calc_ecc_bytes(step_size, strength); - if (WARN_ONCE(ecc_bytes < 0, "%s: eccbytes < 0\n", __func__)) + if (WARN_ON_ONCE(ecc_bytes < 0)) continue; if (ecc_bytes * nsteps > oobavail) @@ -5412,11 +5886,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. @@ -5427,6 +5902,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; @@ -5434,7 +5910,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)) @@ -5444,41 +5920,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, @@ -5566,15 +6007,19 @@ 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) && + !(ecc->engine_type == NAND_ECC_ENGINE_TYPE_SOFT && + ecc->algo == NAND_ECC_ALGO_HAMMING)) { 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: /* @@ -5584,9 +6029,9 @@ 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; } @@ -5602,54 +6047,11 @@ 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; - 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; - goto err_nand_manuf_cleanup; - } - if (IS_ENABLED(CONFIG_NAND_ECC_HW_SYNDROME)) { - /* 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; - } else if (ecc->mode == NAND_ECC_HW_SYNDROME) { - WARN(1, "CONFIG_NAND_ECC_HW_SYNDROME not enabled\n"); - ret = -ENOSYS; + 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; - } if (mtd->writesize >= ecc->size) { if (!ecc->strength) { @@ -5661,17 +6063,17 @@ 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; - case NAND_ECC_SOFT: + ecc->engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + ecc->algo = NAND_ECC_ALGO_HAMMING; + fallthrough; + + 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; @@ -5683,8 +6085,8 @@ 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; @@ -5697,7 +6099,7 @@ 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; } @@ -5725,13 +6127,19 @@ int nand_scan_tail(struct nand_chip *chip) * Set the number of read / write steps for one page depending on ECC * mode. */ - ecc->steps = mtd->writesize / ecc->size; + if (!ecc->steps) + ecc->steps = mtd->writesize / ecc->size; if (ecc->steps * ecc->size != mtd->writesize) { WARN(1, "Invalid ECC parameters\n"); ret = -EINVAL; goto err_nand_manuf_cleanup; } - ecc->total = ecc->steps * ecc->bytes; + + if (!ecc->total) { + ecc->total = ecc->steps * ecc->bytes; + chip->base.ecc.ctx.total = ecc->total; + } + if (ecc->total > mtd->oobsize) { WARN(1, "Total number of ECC bytes exceeded oobsize\n"); ret = -EINVAL; @@ -5749,11 +6157,11 @@ 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)) { @@ -5774,8 +6182,8 @@ 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; @@ -5799,9 +6207,10 @@ int nand_scan_tail(struct nand_chip *chip) mtd->_sync = nand_sync; mtd->_lock = nand_lock; mtd->_unlock = nand_unlock; + mtd->_block_isreserved = nand_block_isreserved; mtd->_block_isbad = nand_block_isbad; mtd->_block_markbad = nand_block_markbad; - mtd->_block_markgood = nand_block_markgood; + mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks; /* * Initialize bitflip_threshold to its default prior scan_bbt() call. @@ -5823,6 +6232,8 @@ int nand_scan_tail(struct nand_chip *chip) goto err_free_interface_config; } + rawnand_late_check_supported_ops(chip); + /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) return 0; @@ -5830,10 +6241,11 @@ int nand_scan_tail(struct nand_chip *chip) /* Build bad block table */ ret = nand_create_bbt(chip); if (ret) - goto err_free_interface_config; + goto err_free_secure_regions; return 0; +err_free_secure_regions: err_free_interface_config: kfree(chip->best_interface_config); @@ -5912,9 +6324,12 @@ 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) - nand_bch_free((struct nand_bch_control *)chip->ecc.priv); + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT) { + if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING) + rawnand_sw_hamming_cleanup(chip); + else if (chip->ecc.algo == NAND_ECC_ALGO_BCH) + rawnand_sw_bch_cleanup(chip); + } nanddev_cleanup(&chip->base); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/raw/nand_bbt.c index a86b5b2da3..a86b5b2da3 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/raw/nand_bbt.c diff --git a/drivers/mtd/nand/nand_denali.c b/drivers/mtd/nand/raw/nand_denali.c index 1d7d1b62a8..8fef992ef8 100644 --- a/drivers/mtd/nand/nand_denali.c +++ b/drivers/mtd/nand/raw/nand_denali.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * NAND Flash Controller Device Driver * Copyright © 2009-2010, Intel Corporation and its suppliers. @@ -1231,7 +1231,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/nand_denali_dt.c b/drivers/mtd/nand/raw/nand_denali_dt.c index 8deea0292e..d21cdc9756 100644 --- a/drivers/mtd/nand/nand_denali_dt.c +++ b/drivers/mtd/nand/raw/nand_denali_dt.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NAND Flash Controller Device Driver for DT * * Copyright © 2011, Picochip. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #include <common.h> @@ -22,6 +14,7 @@ #include <io.h> #include <of_mtd.h> #include <errno.h> +#include <globalvar.h> #include <linux/clk.h> #include <linux/spinlock.h> @@ -51,6 +44,18 @@ static const struct denali_dt_data denali_socfpga_data = { .ecc_caps = &denali_socfpga_ecc_caps, }; +enum of_binding_name { + DENALI_OF_BINDING_CHIP, + DENALI_OF_BINDING_CONTROLLER, + DENALI_OF_BINDING_AUTO, +}; + +static const char *denali_of_binding_names[] = { + "chip", "controller", "auto" +}; + +static int denali_of_binding; + /* * Older versions of the kernel driver require the partition nodes * to be direct subnodes of the controller node. Starting with Kernel @@ -78,28 +83,50 @@ static int denali_partition_fixup(struct mtd_info *mtd, struct device_node *root struct denali_controller, controller); struct device_node *np, *mtdnp = mtd_get_of_node(mtd); + struct device_node *chip_np, *controller_np; char *name; name = of_get_reproducible_name(mtdnp); - np = of_find_node_by_reproducible_name(root, name); + chip_np = of_find_node_by_reproducible_name(root, name); free(name); - if (np) { - dev_info(denali->dev, "Fixing up chip node %s\n", - np->full_name); - } else { - name = of_get_reproducible_name(mtdnp->parent); - np = of_find_node_by_reproducible_name(root, name); - free(name); - - if (np) - dev_info(denali->dev, "Fixing up controller node %s\n", - np->full_name); - } + name = of_get_reproducible_name(mtdnp->parent); + controller_np = of_find_node_by_reproducible_name(root, name); + free(name); + + if (!controller_np) + return -EINVAL; + + switch (denali_of_binding) { + case DENALI_OF_BINDING_CHIP: + if (chip_np) { + np = chip_np; + } else { + np = of_new_node(controller_np, mtdnp->name); + of_property_write_u32(np, "reg", 0); + chip_np = np; + } + break; + case DENALI_OF_BINDING_CONTROLLER: + np = controller_np; + break; + case DENALI_OF_BINDING_AUTO: + default: + np = chip_np ? chip_np : controller_np; + break; + }; if (!np) return -EINVAL; + dev_info(denali->dev, "Fixing up %s node %pOF\n", + chip_np ? "chip" : "controller", np); + + if (!chip_np) { + of_property_write_bool(np, "#size-cells", false); + of_property_write_bool(np, "#address-cells", false); + } + return of_fixup_partitions(np, &mtd->cdev); } @@ -133,10 +160,18 @@ static int denali_dt_chip_init(struct denali_controller *denali, nand_set_flash_node(&dchip->chip, chip_np); } - return denali_chip_init(denali, dchip); + ret = denali_chip_init(denali, dchip); + if (ret) + return ret; + + dev_add_param_enum(&dchip->chip.base.mtd.dev, "denali_partition_binding", + NULL, NULL, &denali_of_binding, denali_of_binding_names, + ARRAY_SIZE(denali_of_binding_names), NULL); + + return 0; } -static int denali_dt_probe(struct device_d *ofdev) +static int denali_dt_probe(struct device *ofdev) { struct resource *iores; struct denali_dt *dt; @@ -197,7 +232,7 @@ static int denali_dt_probe(struct device_d *ofdev) if (ret) goto out_disable_clk; - for_each_child_of_node(ofdev->device_node, np) { + for_each_child_of_node(ofdev->of_node, np) { ret = denali_dt_chip_init(denali, np); if (ret) goto out_disable_clk; @@ -219,8 +254,9 @@ static __maybe_unused struct of_device_id denali_nand_compatible[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, denali_nand_compatible); -static struct driver_d denali_dt_driver = { +static struct driver denali_dt_driver = { .name = "denali-nand-dt", .probe = denali_dt_probe, .of_compatible = DRV_OF_COMPAT(denali_nand_compatible) diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/raw/nand_ecc.c index 58fb335bb4..58fb335bb4 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/raw/nand_ecc.c diff --git a/drivers/mtd/nand/nand_esmt.c b/drivers/mtd/nand/raw/nand_esmt.c index 3338c68aaa..4412c407ae 100644 --- a/drivers/mtd/nand/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/nand_fsl_ifc.c b/drivers/mtd/nand/raw/nand_fsl_ifc.c index 64dc9c225f..1905e7b508 100644 --- a/drivers/mtd/nand/nand_fsl_ifc.c +++ b/drivers/mtd/nand/raw/nand_fsl_ifc.c @@ -46,7 +46,7 @@ struct fsl_ifc_ctrl { /* mtd information per set */ struct fsl_ifc_mtd { - struct device_d *dev; + struct device *dev; struct nand_chip chip; struct fsl_ifc_ctrl *ctrl; uint32_t cs; /* On which chipsel NAND is connected */ @@ -300,7 +300,7 @@ static void fsl_ifc_cmdfunc(struct nand_chip *chip, uint32_t command, ctrl->read_bytes = mtd->writesize + mtd->oobsize; ctrl->index += column; - if (chip->ecc.mode == NAND_ECC_HW) + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) ctrl->eccread = 1; fsl_ifc_do_read(chip, 0, mtd); @@ -322,7 +322,7 @@ static void fsl_ifc_cmdfunc(struct nand_chip *chip, uint32_t command, return; case NAND_CMD_RNDOUT: - if (chip->ecc.mode == NAND_ECC_HW) + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) break; ifc_out32(ctrl->rregs + FSL_IFC_NAND_BC, 0); set_addr(mtd, column, -1, 0); @@ -851,7 +851,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) } ctrl = priv->ctrl = ifc_ctrl; - if (priv->dev->device_node) { + if (priv->dev->of_node) { int bank, banks; /* find which chip select it is connected to */ @@ -960,11 +960,11 @@ 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) { - nand->ecc.mode = NAND_ECC_HW; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); } else { - nand->ecc.mode = NAND_ECC_SOFT; - nand->ecc.algo = NAND_ECC_HAMMING; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + nand->ecc.algo = NAND_ECC_ALGO_HAMMING; } if (ctrl->version >= FSL_IFC_V1_1_0) { @@ -979,7 +979,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) return 0; } -static int fsl_ifc_nand_probe(struct device_d *dev) +static int fsl_ifc_nand_probe(struct device *dev) { struct fsl_ifc_mtd *priv; struct resource *iores; @@ -1025,8 +1025,9 @@ static __maybe_unused struct of_device_id fsl_nand_compatible[] = { }, { } }; +MODULE_DEVICE_TABLE(of, fsl_nand_compatible); -static struct driver_d fsl_ifc_driver = { +static struct driver fsl_ifc_driver = { .name = "fsl_nand", .probe = fsl_ifc_nand_probe, .of_compatible = DRV_OF_COMPAT(fsl_nand_compatible), diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/raw/nand_hynix.c index 0422ed53aa..4c0ea1ffa3 100644 --- a/drivers/mtd/nand/nand_hynix.c +++ b/drivers/mtd/nand/raw/nand_hynix.c @@ -7,6 +7,7 @@ */ #include <linux/sizes.h> +#include <linux/slab.h> #include "internals.h" @@ -30,7 +31,6 @@ struct hynix_read_retry { /** * struct hynix_nand - private Hynix NAND struct - * @nand_technology: manufacturing process expressed in picometer * @read_retry: read-retry information */ struct hynix_nand { @@ -494,34 +494,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: /* @@ -542,14 +544,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 @@ -562,18 +564,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, @@ -709,8 +713,21 @@ static int hynix_nand_init(struct nand_chip *chip) return ret; } +static void hynix_fixup_onfi_param_page(struct nand_chip *chip, + struct nand_onfi_params *p) +{ + /* + * Certain chips might report a 0 on sdr_timing_mode field + * (bytes 129-130). This has been seen on H27U4G8F2GDA-BI. + * According to ONFI specification, bit 0 of this field "shall be 1". + * Forcibly set this bit. + */ + p->sdr_timing_modes |= cpu_to_le16(BIT(0)); +} + const struct nand_manufacturer_ops hynix_nand_manuf_ops = { .detect = hynix_nand_decode_id, .init = hynix_nand_init, .cleanup = hynix_nand_cleanup, + .fixup_onfi_param_page = hynix_fixup_onfi_param_page, }; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/raw/nand_ids.c index b9945791a9..b9945791a9 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/raw/nand_ids.c diff --git a/drivers/mtd/nand/nand_jedec.c b/drivers/mtd/nand/raw/nand_jedec.c index 5632d2c73f..2b21e2d5b5 100644 --- a/drivers/mtd/nand/nand_jedec.c +++ b/drivers/mtd/nand/raw/nand_jedec.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * 2002-2006 Thomas Gleixner (tglx@linutronix.de) @@ -121,8 +121,8 @@ 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; + chip->base.ecc.requirements.strength = ecc->ecc_bits; + chip->base.ecc.requirements.step_size = 1 << ecc->codeword_size; } else { pr_warn("Invalid codeword size\n"); } diff --git a/drivers/mtd/nand/nand_legacy.c b/drivers/mtd/nand/raw/nand_legacy.c index 0fcafe38f9..074a34e7f8 100644 --- a/drivers/mtd/nand/nand_legacy.c +++ b/drivers/mtd/nand/raw/nand_legacy.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * 2002-2006 Thomas Gleixner (tglx@linutronix.de) diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/raw/nand_macronix.c index bfedc789fb..7c0b2f40e3 100644 --- a/drivers/mtd/nand/nand_macronix.c +++ b/drivers/mtd/nand/raw/nand_macronix.c @@ -6,6 +6,7 @@ * Author: Boris Brezillon <boris.brezillon@free-electrons.com> */ +#include <linux/slab.h> #include <linux/bitmap.h> #include "internals.h" @@ -31,6 +32,16 @@ #define MXIC_CMD_POWER_DOWN 0xB9 +#define ONFI_FEATURE_ADDR_30LFXG18AC_OTP 0x90 +#define MACRONIX_30LFXG18AC_OTP_START_PAGE 2 +#define MACRONIX_30LFXG18AC_OTP_PAGES 30 +#define MACRONIX_30LFXG18AC_OTP_PAGE_SIZE 2112 +#define MACRONIX_30LFXG18AC_OTP_SIZE_BYTES \ + (MACRONIX_30LFXG18AC_OTP_PAGES * \ + MACRONIX_30LFXG18AC_OTP_PAGE_SIZE) + +#define MACRONIX_30LFXG18AC_OTP_EN BIT(0) + struct nand_onfi_vendor_macronix { u8 reserved; u8 reliability_func; @@ -93,14 +104,13 @@ static void macronix_nand_onfi_init(struct nand_chip *chip) struct nand_parameters *p = &chip->parameters; struct nand_onfi_vendor_macronix *mxic; struct device_node *dn = nand_get_flash_node(chip); - int rand_otp = 0; + int rand_otp; int ret; if (!p->onfi) return; - if (of_find_property(dn, "mxic,enable-randomizer-otp", NULL)) - rand_otp = 1; + rand_otp = of_property_read_bool(dn, "mxic,enable-randomizer-otp"); mxic = (struct nand_onfi_vendor_macronix *)p->onfi->vendor; /* Subpage write is prohibited in randomizer operatoin */ @@ -316,6 +326,31 @@ static void macronix_nand_deep_power_down_support(struct nand_chip *chip) chip->ops.resume = mxic_nand_resume; } +static void macronix_nand_setup_otp(struct nand_chip *chip) +{ + static const char * const supported_otp_models[] = { + "MX30LF1G18AC", + "MX30LF2G18AC", + "MX30LF4G18AC", + }; + struct mtd_info *mtd; + + if (match_string(supported_otp_models, + ARRAY_SIZE(supported_otp_models), + chip->parameters.model) < 0) + return; + + if (!chip->parameters.supports_set_get_features) + return; + + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_30LFXG18AC_OTP, 1); + + mtd = nand_to_mtd(chip); +} + static int macronix_nand_init(struct nand_chip *chip) { if (nand_is_slc(chip)) @@ -325,6 +360,7 @@ static int macronix_nand_init(struct nand_chip *chip) macronix_nand_onfi_init(chip); macronix_nand_block_protection_support(chip); macronix_nand_deep_power_down_support(chip); + macronix_nand_setup_otp(chip); return 0; } diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/raw/nand_micron.c index d59be7ca7b..c019288190 100644 --- a/drivers/mtd/nand/nand_micron.c +++ b/drivers/mtd/nand/raw/nand_micron.c @@ -6,7 +6,6 @@ * Author: Boris Brezillon <boris.brezillon@free-electrons.com> */ -#include <common.h> #include <linux/slab.h> #include "internals.h" @@ -414,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; @@ -426,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. */ @@ -467,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; @@ -475,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; @@ -498,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; @@ -524,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); @@ -534,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/nand_mrvl_nfc.c b/drivers/mtd/nand/raw/nand_mrvl_nfc.c index 1f3e152375..0e2a2b639a 100644 --- a/drivers/mtd/nand/nand_mrvl_nfc.c +++ b/drivers/mtd/nand/raw/nand_mrvl_nfc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/mtd/nand/mrvl_nand.c * @@ -5,10 +6,6 @@ * Copyright © 2006 Marvell International Ltd. * Copyright (C) 2014 Robert Jarzmik * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * See Documentation/mtd/nand/pxa3xx-nand.txt for more details. */ #include <common.h> @@ -140,7 +137,7 @@ struct mrvl_nand_variant { struct mrvl_nand_host { struct nand_chip chip; struct mtd_partition *parts; - struct device_d *dev; + struct device *dev; struct clk *core_clk; /* calculated from mrvl_nand_flash data */ @@ -302,6 +299,7 @@ static struct of_device_id mrvl_nand_dt_ids[] = { }, {} }; +MODULE_DEVICE_TABLE(of, mrvl_nand_dt_ids); /* convert nano-seconds to nand flash controller clock cycles */ static int ns2cycle(int ns, unsigned long clk_rate) @@ -934,7 +932,7 @@ static int pxa_ecc_strength1(struct mrvl_nand_host *host, host->spare_size = 40; host->ecc_size = 24; host->ecc_bch = 0; - ecc->mode = NAND_ECC_HW; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; ecc->size = 512; ecc->strength = 1; mtd_set_ecclayout(mtd, &ecc_layout_2KB_hwecc); @@ -946,7 +944,7 @@ static int pxa_ecc_strength1(struct mrvl_nand_host *host, host->spare_size = 8; host->ecc_size = 8; host->ecc_bch = 0; - ecc->mode = NAND_ECC_HW; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; ecc->size = 512; mtd_set_ecclayout(mtd, &ecc_layout_512B_hwecc); ecc->strength = 1; @@ -974,7 +972,7 @@ static int pxa_ecc_strength4(struct mrvl_nand_host *host, host->spare_size = 32; host->ecc_size = 32; host->ecc_bch = 1; - ecc->mode = NAND_ECC_HW; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; ecc->size = 2048; mtd_set_ecclayout(mtd, &ecc_layout_2KB_bch4bit); ecc->strength = 16; @@ -986,7 +984,7 @@ static int pxa_ecc_strength4(struct mrvl_nand_host *host, host->spare_size = 32; host->ecc_size = 32; host->ecc_bch = 1; - ecc->mode = NAND_ECC_HW; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; ecc->size = 2048; mtd_set_ecclayout(mtd, &ecc_layout_4KB_bch4bit); ecc->strength = 16; @@ -1014,7 +1012,7 @@ static int pxa_ecc_strength8(struct mrvl_nand_host *host, host->spare_size = 0; host->ecc_size = 32; host->ecc_bch = 1; - ecc->mode = NAND_ECC_HW; + ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; ecc->size = 1024; mtd_set_ecclayout(mtd, &ecc_layout_4KB_bch8bit); ecc->strength = 16; @@ -1126,7 +1124,7 @@ static int mrvl_nand_scan(struct nand_chip *chip) return nand_scan_tail(chip); } -static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev) +static struct mrvl_nand_host *alloc_nand_resource(struct device *dev) { struct resource *iores; struct mrvl_nand_platform_data *pdata; @@ -1191,7 +1189,7 @@ static struct mrvl_nand_host *alloc_nand_resource(struct device_d *dev) static int mrvl_nand_probe_dt(struct mrvl_nand_host *host) { - struct device_node *np = host->dev->device_node; + struct device_node *np = host->dev->of_node; const struct of_device_id *match; const struct mrvl_nand_variant *variant; @@ -1222,7 +1220,7 @@ static int mrvl_nand_probe_dt(struct mrvl_nand_host *host) return 0; } -static int mrvl_nand_probe(struct device_d *dev) +static int mrvl_nand_probe(struct device *dev) { struct mrvl_nand_host *host; struct nand_chip *chip; @@ -1253,7 +1251,7 @@ static int mrvl_nand_probe(struct device_d *dev) return ret; } -static struct driver_d mrvl_nand_driver = { +static struct driver mrvl_nand_driver = { .name = "mrvl_nand", .probe = mrvl_nand_probe, .of_compatible = DRV_OF_COMPAT(mrvl_nand_dt_ids), diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/raw/nand_mxs.c index 0540ba0216..ca3471a226 100644 --- a/drivers/mtd/nand/nand_mxs.c +++ b/drivers/mtd/nand/raw/nand_mxs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Freescale i.MX28 NAND flash driver * @@ -11,11 +12,6 @@ * * Copyright (C) 2010 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/mtd/mtd.h> @@ -25,6 +21,7 @@ #include <linux/types.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/bitfield.h> #include <of_mtd.h> #include <common.h> #include <dma.h> @@ -35,130 +32,11 @@ #include <io.h> #include <dma/apbh-dma.h> #include <stmp-device.h> -#include <mach/generic.h> +#include <mach/imx/generic.h> +#include <soc/imx/gpmi-nand.h> #include "internals.h" -#define MX28_BLOCK_SFTRST (1 << 31) -#define MX28_BLOCK_CLKGATE (1 << 30) - -#define GPMI_CTRL0 0x00000000 -#define GPMI_CTRL0_RUN (1 << 29) -#define GPMI_CTRL0_DEV_IRQ_EN (1 << 28) -/* Disable for now since we don't need it and it is different on MX23. -#define GPMI_CTRL0_LOCK_CS (1 << 27) -*/ -#define GPMI_CTRL0_UDMA (1 << 26) -#define GPMI_CTRL0_COMMAND_MODE_MASK (0x3 << 24) -#define GPMI_CTRL0_COMMAND_MODE_OFFSET 24 -#define GPMI_CTRL0_COMMAND_MODE_WRITE (0x0 << 24) -#define GPMI_CTRL0_COMMAND_MODE_READ (0x1 << 24) -#define GPMI_CTRL0_COMMAND_MODE_READ_AND_COMPARE (0x2 << 24) -#define GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY (0x3 << 24) -#define GPMI_CTRL0_WORD_LENGTH (1 << 23) -/* Careful: Is 0x3 on MX23 -#define GPMI_CTRL0_CS_MASK (0x7 << 20) -*/ -#define GPMI_CTRL0_CS_OFFSET 20 -#define GPMI_CTRL0_ADDRESS_MASK (0x7 << 17) -#define GPMI_CTRL0_ADDRESS_OFFSET 17 -#define GPMI_CTRL0_ADDRESS_NAND_DATA (0x0 << 17) -#define GPMI_CTRL0_ADDRESS_NAND_CLE (0x1 << 17) -#define GPMI_CTRL0_ADDRESS_NAND_ALE (0x2 << 17) -#define GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) -#define GPMI_CTRL0_XFER_COUNT_MASK 0xffff -#define GPMI_CTRL0_XFER_COUNT_OFFSET 0 - -#define GPMI_CTRL1 0x00000060 -#define GPMI_CTRL1_SET 0x00000064 -#define GPMI_CTRL1_CLR 0x00000068 -#define GPMI_CTRL1_DECOUPLE_CS (1 << 24) -#define GPMI_CTRL1_WRN_DLY(d) (((d) & 0x3) << 22) -#define GPMI_CTRL1_TIMEOUT_IRQ_EN (1 << 20) -#define GPMI_CTRL1_GANGED_RDYBUSY (1 << 19) -#define GPMI_CTRL1_BCH_MODE (1 << 18) -#define GPMI_CTRL1_DLL_ENABLE (1 << 17) -#define GPMI_CTRL1_HALF_PERIOD (1 << 16) -#define GPMI_CTRL1_RDN_DELAY(d) (((d) & 0xf) << 12) -#define GPMI_CTRL1_DMA2ECC_MODE (1 << 11) -#define GPMI_CTRL1_DEV_IRQ (1 << 10) -#define GPMI_CTRL1_TIMEOUT_IRQ (1 << 9) -#define GPMI_CTRL1_BURST_EN (1 << 8) -#define GPMI_CTRL1_ABORT_WAIT_REQUEST (1 << 7) -#define GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL_MASK (0x7 << 4) -#define GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL_OFFSET 4 -#define GPMI_CTRL1_DEV_RESET (1 << 3) -#define GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) -#define GPMI_CTRL1_CAMERA_MODE (1 << 1) -#define GPMI_CTRL1_GPMI_MODE (1 << 0) - -#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0 -#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1 -#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2 -#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3 - -#define GPMI_TIMING0 0x00000070 - -#define GPMI_TIMING0_ADDRESS_SETUP(d) (((d) & 0xff) << 16) -#define GPMI_TIMING0_DATA_HOLD(d) (((d) & 0xff) << 8) -#define GPMI_TIMING0_DATA_SETUP(d) (((d) & 0xff) << 0) - -#define GPMI_TIMING1 0x00000080 -#define GPMI_TIMING1_BUSY_TIMEOUT(d) (((d) & 0xffff) << 16) - -#define GPMI_ECCCTRL_HANDLE_MASK (0xffff << 16) -#define GPMI_ECCCTRL_HANDLE_OFFSET 16 -#define GPMI_ECCCTRL_ECC_CMD_MASK (0x3 << 13) -#define GPMI_ECCCTRL_ECC_CMD_OFFSET 13 -#define GPMI_ECCCTRL_ECC_CMD_DECODE (0x0 << 13) -#define GPMI_ECCCTRL_ECC_CMD_ENCODE (0x1 << 13) -#define GPMI_ECCCTRL_ENABLE_ECC (1 << 12) -#define GPMI_ECCCTRL_BUFFER_MASK_MASK 0x1ff -#define GPMI_ECCCTRL_BUFFER_MASK_OFFSET 0 -#define GPMI_ECCCTRL_BUFFER_MASK_BCH_AUXONLY 0x100 -#define GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE 0x1ff - -#define GPMI_STAT 0x000000b0 -#define GPMI_STAT_READY_BUSY_OFFSET 24 - -#define GPMI_DEBUG 0x000000c0 -#define GPMI_DEBUG_READY0_OFFSET 28 - -#define GPMI_VERSION 0x000000d0 -#define GPMI_VERSION_MINOR_OFFSET 16 -#define GPMI_VERSION_TYPE_MX23 0x0300 - -#define BCH_CTRL 0x00000000 -#define BCH_CTRL_COMPLETE_IRQ (1 << 0) -#define BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) - -#define BCH_LAYOUTSELECT 0x00000070 - -#define BCH_FLASH0LAYOUT0 0x00000080 -#define BCH_FLASHLAYOUT0_NBLOCKS_MASK (0xff << 24) -#define BCH_FLASHLAYOUT0_NBLOCKS_OFFSET 24 -#define BCH_FLASHLAYOUT0_META_SIZE_MASK (0xff << 16) -#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16 -#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12) -#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12 -#define IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET 11 - -#define BCH_FLASH0LAYOUT1 0x00000090 -#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16) -#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16 -#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12) -#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12 -#define IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET 11 - -#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 - -#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 -#define MXS_NAND_METADATA_SIZE 10 - -#define MXS_NAND_COMMAND_BUFFER_SIZE 32 - -#define MXS_NAND_BCH_TIMEOUT 10000 - enum gpmi_type { GPMI_MXS, GPMI_IMX6, @@ -198,7 +76,7 @@ struct nand_timing { }; struct mxs_nand_info { - struct device_d *dev; + struct device *dev; struct nand_chip nand_chip; void __iomem *io_base; void __iomem *bch_base; @@ -224,7 +102,7 @@ struct mxs_nand_info { loff_t to, struct mtd_oob_ops *ops); /* DMA descriptors */ - struct mxs_dma_desc **desc; + struct mxs_dma_cmd *desc; uint32_t desc_index; #define GPMI_ASYNC_EDO_ENABLED (1 << 0) @@ -240,16 +118,16 @@ static inline int mxs_nand_is_imx6(struct mxs_nand_info *info) return info->type == GPMI_IMX6; } -static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info) +static struct mxs_dma_cmd *mxs_nand_get_dma_desc(struct mxs_nand_info *info) { - struct mxs_dma_desc *desc; + struct mxs_dma_cmd *desc; if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) { printf("MXS NAND: Too many DMA descriptors requested\n"); return NULL; } - desc = info->desc[info->desc_index]; + desc = &info->desc[info->desc_index]; info->desc_index++; return desc; @@ -258,12 +136,11 @@ static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info) static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) { int i; - struct mxs_dma_desc *desc; + struct mxs_dma_cmd *desc; for (i = 0; i < info->desc_index; i++) { - desc = info->desc[i]; - memset(desc, 0, sizeof(struct mxs_dma_desc)); - desc->address = (dma_addr_t)desc; + desc = &info->desc[i]; + memset(desc, 0, sizeof(struct mxs_dma_cmd)); } info->desc_index = 0; @@ -446,7 +323,7 @@ static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info) static void mxs_nand_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctrl) { struct mxs_nand_info *nand_info = chip->priv; - struct mxs_dma_desc *d; + struct mxs_dma_cmd *d; uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip; int ret; @@ -486,26 +363,24 @@ static void mxs_nand_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctr /* Compile the DMA descriptor -- a descriptor that sends command. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM | - MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET); + MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(3) | + MXS_DMA_DESC_XFER_COUNT(nand_info->cmd_queue_len); - d->cmd.address = (dma_addr_t)nand_info->cmd_buf; + d->address = (dma_addr_t)nand_info->cmd_buf; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_CLE | GPMI_CTRL0_ADDRESS_INCREMENT | nand_info->cmd_queue_len; - mxs_dma_desc_append(channel, d); - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); + ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index); if (ret) printf("MXS NAND: Error sending command (%d)\n", ret); @@ -601,7 +476,7 @@ static void mxs_nand_swap_block_mark(struct nand_chip *chip, static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length) { struct mxs_nand_info *nand_info = chip->priv; - struct mxs_dma_desc *d; + struct mxs_dma_cmd *d; uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip; int ret; @@ -617,23 +492,21 @@ static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length) /* Compile the DMA descriptor - a descriptor that reads data. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - (length << MXS_DMA_DESC_BYTES_OFFSET); + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(length); - d->cmd.address = (dma_addr_t)nand_info->data_buf; + d->address = (dma_addr_t)nand_info->data_buf; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_DATA | length; - mxs_dma_desc_append(channel, d); - /* * A DMA descriptor that waits for the command to end and the chip to * become ready. @@ -643,23 +516,21 @@ static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length) * did that and no one has re-thought it yet. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM | - MXS_DMA_DESC_WAIT4END | (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(4); - d->cmd.address = 0; + d->address = 0; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_DATA; - mxs_dma_desc_append(channel, d); - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); + ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index); if (ret) { printf("MXS NAND: DMA read error\n"); goto rtn; @@ -678,7 +549,7 @@ static void mxs_nand_write_buf(struct nand_chip *chip, const uint8_t *buf, int length) { struct mxs_nand_info *nand_info = chip->priv; - struct mxs_dma_desc *d; + struct mxs_dma_cmd *d; uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip; int ret; @@ -696,25 +567,23 @@ static void mxs_nand_write_buf(struct nand_chip *chip, const uint8_t *buf, /* Compile the DMA descriptor - a descriptor that writes data. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | - (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | - (length << MXS_DMA_DESC_BYTES_OFFSET); + MXS_DMA_DESC_PIO_WORDS(4) | + MXS_DMA_DESC_XFER_COUNT(length); - d->cmd.address = (dma_addr_t)nand_info->data_buf; + d->address = (dma_addr_t)nand_info->data_buf; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_DATA | length; - mxs_dma_desc_append(channel, d); - /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); + ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index); if (ret) printf("MXS NAND: DMA write error\n"); @@ -736,138 +605,155 @@ static void mxs_nand_config_bch(struct nand_chip *chip, int readlen) struct mxs_nand_info *nand_info = chip->priv; int chunk_size; void __iomem *bch_regs = nand_info->bch_base; + u32 fl0, fl1; if (mxs_nand_is_imx6(nand_info)) chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2; else chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE; - writel((mxs_nand_ecc_chunk_cnt(readlen) - 1) - << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET | - MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET | - (chip->ecc.strength >> 1) - << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET | - chunk_size, - bch_regs + BCH_FLASH0LAYOUT0); - - writel(readlen << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET | - (chip->ecc.strength >> 1) - << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET | - chunk_size, - bch_regs + BCH_FLASH0LAYOUT1); + fl0 = FIELD_PREP(BCH_FLASHLAYOUT0_NBLOCKS, mxs_nand_ecc_chunk_cnt(readlen) - 1); + fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_META_SIZE, MXS_NAND_METADATA_SIZE); + if (mxs_nand_is_imx6(nand_info)) + fl0 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT0_ECC0, chip->ecc.strength >> 1); + else + fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_ECC0, chip->ecc.strength >> 1); + fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_DATA0_SIZE, chunk_size); + writel(fl0, bch_regs + BCH_FLASH0LAYOUT0); + + fl1 = FIELD_PREP(BCH_FLASHLAYOUT1_PAGE_SIZE, readlen); + if (mxs_nand_is_imx6(nand_info)) + fl1 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT1_ECCN, chip->ecc.strength >> 1); + else + fl1 |= FIELD_PREP(BCH_FLASHLAYOUT1_ECCN, chip->ecc.strength >> 1); + + fl1 |= FIELD_PREP(BCH_FLASHLAYOUT1_DATAN_SIZE, chunk_size); + writel(fl1, bch_regs + BCH_FLASH0LAYOUT1); } -/* - * Read a page from NAND. - */ -static int __mxs_nand_ecc_read_page(struct nand_chip *chip, - uint8_t *buf, int oob_required, int page, - int readlen) +static int mxs_nand_do_bch_read(struct nand_chip *chip, int channel, int readtotal, + bool randomizer, int page) { - struct mtd_info *mtd = nand_to_mtd(chip); struct mxs_nand_info *nand_info = chip->priv; - struct mxs_dma_desc *d; - uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip; - uint32_t corrected = 0, failed = 0; - uint8_t *status; - unsigned int max_bitflips = 0; - int i, ret, readtotal, nchunks; - - nand_read_page_op(chip, page, 0, NULL, 0); - - readlen = roundup(readlen, MXS_NAND_CHUNK_DATA_CHUNK_SIZE); - nchunks = mxs_nand_ecc_chunk_cnt(readlen); - readtotal = MXS_NAND_METADATA_SIZE; - readtotal += MXS_NAND_CHUNK_DATA_CHUNK_SIZE * nchunks; - readtotal += DIV_ROUND_UP(13 * chip->ecc.strength * nchunks, 8); - - mxs_nand_config_bch(chip, readtotal); + struct mxs_dma_cmd *d; + int ret; /* Compile the DMA descriptor - wait for ready. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + MXS_DMA_DESC_PIO_WORDS(1); - d->cmd.address = 0; + d->address = 0; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_DATA; - mxs_dma_desc_append(channel, d); - /* Compile the DMA descriptor - enable the BCH block and read. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | - MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(6); - d->cmd.address = 0; + d->address = 0; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_DATA | readtotal; - d->cmd.pio_words[1] = 0; - d->cmd.pio_words[2] = + d->pio_words[1] = 0; + d->pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC | GPMI_ECCCTRL_ECC_CMD_DECODE | GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; - d->cmd.pio_words[3] = readtotal; - d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; - d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; - - mxs_dma_desc_append(channel, d); + d->pio_words[3] = readtotal; + d->pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + if (randomizer) { + d->pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE | + GPMI_ECCCTRL_RANDOMIZER_TYPE2; + d->pio_words[3] |= (page % 256) << 16; + } /* Compile the DMA descriptor - disable the BCH block. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | - (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + MXS_DMA_DESC_PIO_WORDS(3); - d->cmd.address = 0; + d->address = 0; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_DATA | readtotal; - d->cmd.pio_words[1] = 0; - d->cmd.pio_words[2] = 0; - - mxs_dma_desc_append(channel, d); + d->pio_words[1] = 0; + d->pio_words[2] = 0; /* Compile the DMA descriptor - deassert the NAND lock and interrupt. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; - d->cmd.address = 0; - - mxs_dma_desc_append(channel, d); + d->address = 0; /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); + ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index); if (ret) { - printf("MXS NAND: DMA read error (ecc)\n"); - goto rtn; + dev_err(nand_info->dev, "MXS NAND: DMA read error (ecc)\n"); + goto out; } ret = mxs_nand_wait_for_bch_complete(nand_info); if (ret) { - printf("MXS NAND: BCH read timeout\n"); - goto rtn; + dev_err(nand_info->dev, "MXS NAND: BCH read timeout\n"); + goto out; } +out: + mxs_nand_return_dma_descs(nand_info); + + return ret; +} + +/* + * Read a page from NAND. + */ +static int __mxs_nand_ecc_read_page(struct nand_chip *chip, + uint8_t *buf, int oob_required, int page, + int readlen) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + struct mxs_nand_info *nand_info = chip->priv; + uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip; + uint32_t corrected = 0, failed = 0; + uint8_t *status; + unsigned int max_bitflips = 0; + int i, ret, readtotal, nchunks; + + nand_read_page_op(chip, page, 0, NULL, 0); + + readlen = roundup(readlen, MXS_NAND_CHUNK_DATA_CHUNK_SIZE); + nchunks = mxs_nand_ecc_chunk_cnt(readlen); + readtotal = MXS_NAND_METADATA_SIZE; + readtotal += MXS_NAND_CHUNK_DATA_CHUNK_SIZE * nchunks; + readtotal += DIV_ROUND_UP(13 * chip->ecc.strength * nchunks, 8); + + mxs_nand_config_bch(chip, readtotal); + + mxs_nand_do_bch_read(chip, channel, readtotal, false, page); + /* Read DMA completed, now do the mark swapping. */ mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf); @@ -947,7 +833,7 @@ static int __mxs_nand_ecc_read_page(struct nand_chip *chip, chip->oob_poi[0] = nand_info->oob_buf[0]; ret = 0; -rtn: + mxs_nand_return_dma_descs(nand_info); mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize); @@ -988,7 +874,7 @@ static int mxs_nand_ecc_write_page(struct nand_chip *chip, const uint8_t *buf, { struct mtd_info *mtd = nand_to_mtd(chip); struct mxs_nand_info *nand_info = chip->priv; - struct mxs_dma_desc *d; + struct mxs_dma_cmd *d; uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip; int ret = 0; @@ -1002,31 +888,29 @@ static int mxs_nand_ecc_write_page(struct nand_chip *chip, const uint8_t *buf, /* Compile the DMA descriptor - write data. */ d = mxs_nand_get_dma_desc(nand_info); - d->cmd.data = + d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | - (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + MXS_DMA_DESC_PIO_WORDS(6); - d->cmd.address = 0; + d->address = 0; - d->cmd.pio_words[0] = + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) | GPMI_CTRL0_ADDRESS_NAND_DATA; - d->cmd.pio_words[1] = 0; - d->cmd.pio_words[2] = + d->pio_words[1] = 0; + d->pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC | GPMI_ECCCTRL_ECC_CMD_ENCODE | GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; - d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize); - d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; - d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; - - mxs_dma_desc_append(channel, d); + d->pio_words[3] = (mtd->writesize + mtd->oobsize); + d->pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->pio_words[5] = (dma_addr_t)nand_info->oob_buf; /* Execute the DMA chain. */ - ret = mxs_dma_go(channel); + ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index); if (ret) { printf("MXS NAND: DMA write error\n"); goto rtn; @@ -1239,6 +1123,160 @@ static int mxs_nand_block_markbad(struct nand_chip *chip , loff_t ofs) return 0; } +int mxs_nand_read_fcb_bch62(unsigned int block, void *buf, size_t size) +{ + struct nand_chip *chip; + struct mxs_nand_info *nand_info; + struct mtd_info *mtd = mxs_nand_mtd; + int ret; + int page; + int flips = 0; + uint8_t *status; + int i; + + if (!mtd) + return -ENODEV; + + chip = mtd_to_nand(mtd); + nand_info = chip->priv; + + nand_select_target(chip, 0); + + page = block * (mtd->erasesize / mtd->writesize); + + mxs_nand_mode_fcb_62bit(nand_info->bch_base); + + nand_read_page_op(chip, page, 0, NULL, 0); + + ret = mxs_nand_do_bch_read(chip, 0, BCH62_PAGESIZE, true, page); + if (ret) + goto out; + + /* Read DMA completed, now do the mark swapping. */ + mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf); + + /* Loop over status bytes, accumulating ECC status. */ + status = nand_info->oob_buf + 32; + + for (i = 0; i < 8; i++) { + switch (status[i]) { + case 0x0: + break; + case 0xff: + /* + * A status of 0xff means the chunk is erased, but due to + * the randomizer we see this as random data. Explicitly + * memset it. + */ + memset(nand_info->data_buf + 0x80 * i, 0xff, 0x80); + break; + case 0xfe: + ret = -EBADMSG; + goto out; + default: + flips += status[0]; + break; + } + } + + memcpy(buf, nand_info->data_buf, size); + +out: + mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize); + nand_deselect_target(chip); + + return ret; +} + +int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size) +{ + struct nand_chip *chip; + struct mtd_info *mtd = mxs_nand_mtd; + struct mxs_nand_info *nand_info; + struct mxs_dma_cmd *d; + uint32_t channel; + int ret = 0; + int page; + + if (!mtd) + return -ENODEV; + + if (size > BCH62_WRITESIZE) + return -EINVAL; + + chip = mtd_to_nand(mtd); + nand_info = chip->priv; + channel = nand_info->dma_channel_base; + + mxs_nand_mode_fcb_62bit(nand_info->bch_base); + + nand_select_target(chip, 0); + + page = block * (mtd->erasesize / mtd->writesize); + + nand_prog_page_begin_op(chip, page, 0, NULL, 0); + + memset(nand_info->data_buf, 0x0, BCH62_WRITESIZE); + memcpy(nand_info->data_buf, buf, size); + + /* Handle block mark swapping. */ + mxs_nand_swap_block_mark(chip, nand_info->data_buf, nand_info->oob_buf); + + /* Compile the DMA descriptor - write data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(6); + + d->address = 0; + + d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + GPMI_CTRL0_ADDRESS_NAND_DATA; + d->pio_words[1] = 0; + d->pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_ENCODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->pio_words[3] = BCH62_PAGESIZE; + d->pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + d->pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE | + GPMI_ECCCTRL_RANDOMIZER_TYPE2; + /* + * Write NAND page number needed to be randomized + * to GPMI_ECCCOUNT register. + * + * The value is between 0-255. For additional details + * check 9.6.6.4 of i.MX7D Applications Processor reference + */ + d->pio_words[3] |= (page % 256) << 16; + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index); + if (ret) { + dev_err(nand_info->dev, "MXS NAND: DMA write error: %d\n", ret); + goto out; + } + + ret = mxs_nand_wait_for_bch_complete(nand_info); + if (ret) { + dev_err(nand_info->dev, "MXS NAND: BCH write timeout\n"); + goto out; + } + +out: + mxs_nand_return_dma_descs(nand_info); + + if (!ret) + ret = nand_prog_page_end_op(chip); + + mxs_nand_config_bch(chip, mtd->writesize + mtd->oobsize); + nand_deselect_target(chip); + + return ret; +} + /* * Nominally, the purpose of this function is to look for or create the bad * block table. In fact, since the we call this function at the very end of @@ -1327,20 +1365,13 @@ static int mxs_nand_hw_init(struct mxs_nand_info *info) { void __iomem *gpmi_regs = info->io_base; void __iomem *bch_regs = info->bch_base; - int i = 0, ret; + int ret; u32 val; - info->desc = malloc(sizeof(struct mxs_dma_desc *) * - MXS_NAND_DMA_DESCRIPTOR_COUNT); + info->desc = dma_alloc_coherent(sizeof(struct mxs_dma_cmd) * MXS_NAND_DMA_DESCRIPTOR_COUNT, + DMA_ADDRESS_BROKEN); if (!info->desc) - goto err1; - - /* Allocate the DMA descriptors. */ - for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) { - info->desc[i] = mxs_dma_desc_alloc(); - if (!info->desc[i]) - goto err2; - } + return -ENOMEM; /* Reset the GPMI block. */ ret = stmp_reset_block(gpmi_regs + GPMI_CTRL0, 0); @@ -1367,24 +1398,17 @@ static int mxs_nand_hw_init(struct mxs_nand_info *info) writel(val, gpmi_regs + GPMI_CTRL1); return 0; - -err2: - free(info->desc); -err1: - for (--i; i >= 0; i--) - mxs_dma_desc_free(info->desc[i]); - printf("MXS NAND: Unable to allocate DMA descriptors\n"); - return -ENOMEM; } -static void mxs_nand_probe_dt(struct device_d *dev, struct mxs_nand_info *nand_info) +static void mxs_nand_probe_dt(struct device *dev, + struct mxs_nand_info *nand_info) { struct nand_chip *chip = &nand_info->nand_chip; if (!IS_ENABLED(CONFIG_OFTREE)) return; - if (of_get_nand_on_flash_bbt(dev->device_node)) + if (of_get_nand_on_flash_bbt(dev->of_node)) chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; } @@ -2134,7 +2158,7 @@ static void mxs_nand_setup_timing(struct mxs_nand_info *info) } } -static int mxs_nand_probe(struct device_d *dev) +static int mxs_nand_probe(struct device *dev) { struct resource *iores; struct mxs_nand_info *nand_info; @@ -2218,7 +2242,7 @@ static int mxs_nand_probe(struct device_d *dev) chip->ecc.read_oob = mxs_nand_ecc_read_oob; chip->ecc.write_oob = mxs_nand_ecc_write_oob; - chip->ecc.mode = NAND_ECC_HW; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; /* first scan to find the device and get the page size */ err = nand_scan_ident(chip, 4, NULL); @@ -2276,11 +2300,15 @@ static __maybe_unused struct of_device_id gpmi_dt_ids[] = { .compatible = "fsl,imx6q-gpmi-nand", .data = (void *)GPMI_IMX6, }, { + .compatible = "fsl,imx7d-gpmi-nand", + .data = (void *)GPMI_IMX6, + }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, gpmi_dt_ids); -static struct driver_d mxs_nand_driver = { +static struct driver mxs_nand_driver = { .name = "mxs_nand", .probe = mxs_nand_probe, .of_compatible = DRV_OF_COMPAT(gpmi_dt_ids), diff --git a/drivers/mtd/nand/nand_omap_bch_decoder.c b/drivers/mtd/nand/raw/nand_omap_bch_decoder.c index 4dd28a7704..eb51e608e4 100644 --- a/drivers/mtd/nand/nand_omap_bch_decoder.c +++ b/drivers/mtd/nand/raw/nand_omap_bch_decoder.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/mtd/nand/omap_omap_bch_decoder.c * @@ -8,9 +9,6 @@ * Author: Sukumar Ghorai <s-ghorai@xxxxxx * Michael Fillinger <m-fillinger@xxxxxx> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <common.h> diff --git a/drivers/mtd/nand/nand_omap_bch_decoder.h b/drivers/mtd/nand/raw/nand_omap_bch_decoder.h index 74d24be028..a8c71f77f8 100644 --- a/drivers/mtd/nand/nand_omap_bch_decoder.h +++ b/drivers/mtd/nand/raw/nand_omap_bch_decoder.h @@ -1,6 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef MTD_OMAP_GPMC_DECODE_BCH_H #define MTD_OMAP_GPMC_DECODE_BCH_H int omap_gpmc_decode_bch(int select_4_8, unsigned char *ecc, unsigned int *err_loc); -#endif /* MTD_OMAP_GPMC_DECODE_BCH_H */
\ No newline at end of file +#endif /* MTD_OMAP_GPMC_DECODE_BCH_H */ diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/raw/nand_omap_gpmc.c index 0f3ffa1c0e..2b1fb07f93 100644 --- a/drivers/mtd/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/raw/nand_omap_gpmc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /** * @file * @brief Provide Generic GPMC NAND implementation for OMAP platforms @@ -11,7 +12,7 @@ * A typical device registration is as follows: * * @code - * static struct device_d my_nand_device = { + * static struct device my_nand_device = { * .name = "gpmc_nand", * .id = some identifier you need to show.. e.g. "gpmc_nand0" * .resource[0].start = GPMC base address @@ -53,9 +54,7 @@ * Copyright (c) 2004 Micron Technology Inc. * Copyright (c) 2004 David Brownell * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * */ #include <common.h> @@ -69,8 +68,8 @@ #include <linux/mtd/rawnand.h> #include <linux/mtd/nand_ecc.h> #include <io.h> -#include <mach/gpmc.h> -#include <mach/gpmc_nand.h> +#include <mach/omap/gpmc.h> +#include <mach/omap/gpmc_nand.h> #include <platform_data/elm.h> #include "nand_omap_bch_decoder.h" @@ -110,7 +109,7 @@ static const char *ecc_mode_strings[] = { /** internal structure maintained for nand information */ struct gpmc_nand_info { - struct device_d *pdev; + struct device *pdev; struct gpmc_nand_platform_data *pdata; struct nand_chip nand; int gpmc_cs; @@ -1055,7 +1054,7 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo, nand->ecc.write_page = NULL; nand->ecc.read_oob = NULL; nand->ecc.write_oob = NULL; - nand->ecc.mode = NAND_ECC_HW; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; nand->options &= ~NAND_SUBPAGE_READ; } @@ -1139,7 +1138,8 @@ static int omap_gpmc_eccmode(struct gpmc_nand_info *oinfo, break; case OMAP_ECC_SOFT: minfo->ecclayout = NULL; - nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + nand->ecc.algo = NAND_ECC_ALGO_HAMMING; oinfo->nand.ecc.strength = 1; break; default: @@ -1185,7 +1185,7 @@ static int gpmc_set_buswidth(struct nand_chip *chip, int buswidth) * * @return -failure reason or give 0 */ -static int gpmc_nand_probe(struct device_d *pdev) +static int gpmc_nand_probe(struct device *pdev) { struct resource *iores; struct gpmc_nand_info *oinfo; @@ -1344,7 +1344,7 @@ out_release_mem: } /** GMPC nand driver -> device registered by platforms */ -static struct driver_d gpmc_nand_driver = { +static struct driver gpmc_nand_driver = { .name = "gpmc_nand", .probe = gpmc_nand_probe, }; diff --git a/drivers/mtd/nand/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c index 5f4c5c3437..9dc2ee5fcf 100644 --- a/drivers/mtd/nand/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -35,6 +35,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; @@ -95,8 +97,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: @@ -140,6 +144,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; @@ -162,8 +167,7 @@ int nand_onfi_detect(struct nand_chip *chip) if (!pbuf) return -ENOMEM; - if (!nand_has_exec_op(chip) || - !nand_read_data_op(chip, &pbuf[0], sizeof(*pbuf), true, true)) + if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read) use_datain = true; for (i = 0; i < ONFI_PARAM_PAGES; i++) { @@ -232,7 +236,7 @@ int nand_onfi_detect(struct nand_chip *chip) sanitize_string(p->manufacturer, sizeof(p->manufacturer)); sanitize_string(p->model, sizeof(p->model)); - chip->parameters.model = strdup(p->model); + chip->parameters.model = kstrdup(p->model, GFP_KERNEL); if (!chip->parameters.model) { ret = -ENOMEM; goto free_onfi_param_page; @@ -266,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)) { @@ -296,6 +304,9 @@ int nand_onfi_detect(struct nand_chip *chip) ONFI_FEATURE_ADDR_TIMING_MODE, 1); } + if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_READ_CACHE) + chip->parameters.supports_read_cache = true; + onfi = kzalloc(sizeof(*onfi), GFP_KERNEL); if (!onfi) { ret = -ENOMEM; @@ -307,7 +318,10 @@ int nand_onfi_detect(struct nand_chip *chip) onfi->tBERS = le16_to_cpu(p->t_bers); onfi->tR = le16_to_cpu(p->t_r); onfi->tCCS = le16_to_cpu(p->t_ccs); - onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode); + onfi->fast_tCAD = le16_to_cpu(p->nvddr_nvddr2_features) & BIT(0); + onfi->sdr_timing_modes = le16_to_cpu(p->sdr_timing_modes); + if (le16_to_cpu(p->features) & ONFI_FEATURE_NV_DDR) + onfi->nvddr_timing_modes = le16_to_cpu(p->nvddr_timing_modes); onfi->vendor_revision = le16_to_cpu(p->vendor_revision); memcpy(onfi->vendor, p->vendor, sizeof(p->vendor)); chip->parameters.onfi = onfi; diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/raw/nand_orion.c index 3899a67b56..c7fa4a08e9 100644 --- a/drivers/mtd/nand/nand_orion.c +++ b/drivers/mtd/nand/raw/nand_orion.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * (C) Copyright 2014, Ezequiel Garcia <ezequiel.garcia@free-electrons.com> * * Based on Orion NAND driver from Linux (drivers/mtd/nand/orion_nand.c): * Author: Tzachi Perelstein <tzachi@marvell.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <common.h> @@ -76,10 +73,10 @@ static void orion_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int len) buf[i++] = readb(io_base); } -static int orion_nand_probe(struct device_d *dev) +static int orion_nand_probe(struct device *dev) { struct resource *iores; - struct device_node *dev_node = dev->device_node; + struct device_node *dev_node = dev->of_node; struct orion_nand *priv; struct mtd_info *mtd; struct nand_chip *chip; @@ -120,7 +117,8 @@ static int orion_nand_probe(struct device_d *dev) chip->legacy.IO_ADDR_R = chip->legacy.IO_ADDR_W = io_base; chip->legacy.cmd_ctrl = orion_nand_cmd_ctrl; chip->legacy.read_buf = orion_nand_read_buf; - chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; + chip->ecc.algo = NAND_ECC_ALGO_HAMMING; WARN(width > 16, "%d bit bus width out of range", width); if (width == 16) @@ -150,8 +148,9 @@ static __maybe_unused struct of_device_id orion_nand_compatible[] = { { .compatible = "marvell,orion-nand", }, {}, }; +MODULE_DEVICE_TABLE(of, orion_nand_compatible); -static struct driver_d orion_nand_driver = { +static struct driver orion_nand_driver = { .name = "orion_nand", .probe = orion_nand_probe, .of_compatible = DRV_OF_COMPAT(orion_nand_compatible), diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/raw/nand_samsung.c index 3a4a19e808..0be6b75638 100644 --- a/drivers/mtd/nand/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/nand_timings.c b/drivers/mtd/nand/raw/nand_timings.c index 1338133e81..a10dad927e 100644 --- a/drivers/mtd/nand/nand_timings.c +++ b/drivers/mtd/nand/raw/nand_timings.c @@ -292,6 +292,261 @@ static const struct nand_interface_config onfi_sdr_timings[] = { }, }; +static const struct nand_interface_config onfi_nvddr_timings[] = { + /* Mode 0 */ + { + .type = NAND_NVDDR_IFACE, + .timings.mode = 0, + .timings.nvddr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tAC_min = 3000, + .tAC_max = 25000, + .tADL_min = 400000, + .tCAD_min = 45000, + .tCAH_min = 10000, + .tCALH_min = 10000, + .tCALS_min = 10000, + .tCAS_min = 10000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCK_min = 50000, + .tCS_min = 35000, + .tDH_min = 5000, + .tDQSCK_min = 3000, + .tDQSCK_max = 25000, + .tDQSD_min = 0, + .tDQSD_max = 18000, + .tDQSHZ_max = 20000, + .tDQSQ_max = 5000, + .tDS_min = 5000, + .tDSC_min = 50000, + .tFEAT_max = 1000000, + .tITC_max = 1000000, + .tQHS_max = 6000, + .tRHW_min = 100000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWHR_min = 80000, + .tWRCK_min = 20000, + .tWW_min = 100000, + }, + }, + /* Mode 1 */ + { + .type = NAND_NVDDR_IFACE, + .timings.mode = 1, + .timings.nvddr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tAC_min = 3000, + .tAC_max = 25000, + .tADL_min = 400000, + .tCAD_min = 45000, + .tCAH_min = 5000, + .tCALH_min = 5000, + .tCALS_min = 5000, + .tCAS_min = 5000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCK_min = 30000, + .tCS_min = 25000, + .tDH_min = 2500, + .tDQSCK_min = 3000, + .tDQSCK_max = 25000, + .tDQSD_min = 0, + .tDQSD_max = 18000, + .tDQSHZ_max = 20000, + .tDQSQ_max = 2500, + .tDS_min = 3000, + .tDSC_min = 30000, + .tFEAT_max = 1000000, + .tITC_max = 1000000, + .tQHS_max = 3000, + .tRHW_min = 100000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWHR_min = 80000, + .tWRCK_min = 20000, + .tWW_min = 100000, + }, + }, + /* Mode 2 */ + { + .type = NAND_NVDDR_IFACE, + .timings.mode = 2, + .timings.nvddr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tAC_min = 3000, + .tAC_max = 25000, + .tADL_min = 400000, + .tCAD_min = 45000, + .tCAH_min = 4000, + .tCALH_min = 4000, + .tCALS_min = 4000, + .tCAS_min = 4000, + .tCEH_min = 20000, + .tCH_min = 4000, + .tCK_min = 20000, + .tCS_min = 15000, + .tDH_min = 1700, + .tDQSCK_min = 3000, + .tDQSCK_max = 25000, + .tDQSD_min = 0, + .tDQSD_max = 18000, + .tDQSHZ_max = 20000, + .tDQSQ_max = 1700, + .tDS_min = 2000, + .tDSC_min = 20000, + .tFEAT_max = 1000000, + .tITC_max = 1000000, + .tQHS_max = 2000, + .tRHW_min = 100000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWHR_min = 80000, + .tWRCK_min = 20000, + .tWW_min = 100000, + }, + }, + /* Mode 3 */ + { + .type = NAND_NVDDR_IFACE, + .timings.mode = 3, + .timings.nvddr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tAC_min = 3000, + .tAC_max = 25000, + .tADL_min = 400000, + .tCAD_min = 45000, + .tCAH_min = 3000, + .tCALH_min = 3000, + .tCALS_min = 3000, + .tCAS_min = 3000, + .tCEH_min = 20000, + .tCH_min = 3000, + .tCK_min = 15000, + .tCS_min = 15000, + .tDH_min = 1300, + .tDQSCK_min = 3000, + .tDQSCK_max = 25000, + .tDQSD_min = 0, + .tDQSD_max = 18000, + .tDQSHZ_max = 20000, + .tDQSQ_max = 1300, + .tDS_min = 1500, + .tDSC_min = 15000, + .tFEAT_max = 1000000, + .tITC_max = 1000000, + .tQHS_max = 1500, + .tRHW_min = 100000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWHR_min = 80000, + .tWRCK_min = 20000, + .tWW_min = 100000, + }, + }, + /* Mode 4 */ + { + .type = NAND_NVDDR_IFACE, + .timings.mode = 4, + .timings.nvddr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tAC_min = 3000, + .tAC_max = 25000, + .tADL_min = 400000, + .tCAD_min = 45000, + .tCAH_min = 2500, + .tCALH_min = 2500, + .tCALS_min = 2500, + .tCAS_min = 2500, + .tCEH_min = 20000, + .tCH_min = 2500, + .tCK_min = 12000, + .tCS_min = 15000, + .tDH_min = 1100, + .tDQSCK_min = 3000, + .tDQSCK_max = 25000, + .tDQSD_min = 0, + .tDQSD_max = 18000, + .tDQSHZ_max = 20000, + .tDQSQ_max = 1000, + .tDS_min = 1100, + .tDSC_min = 12000, + .tFEAT_max = 1000000, + .tITC_max = 1000000, + .tQHS_max = 1200, + .tRHW_min = 100000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWHR_min = 80000, + .tWRCK_min = 20000, + .tWW_min = 100000, + }, + }, + /* Mode 5 */ + { + .type = NAND_NVDDR_IFACE, + .timings.mode = 5, + .timings.nvddr = { + .tCCS_min = 500000, + .tR_max = 200000000, + .tPROG_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tBERS_max = 1000000ULL * ONFI_DYN_TIMING_MAX, + .tAC_min = 3000, + .tAC_max = 25000, + .tADL_min = 400000, + .tCAD_min = 45000, + .tCAH_min = 2000, + .tCALH_min = 2000, + .tCALS_min = 2000, + .tCAS_min = 2000, + .tCEH_min = 20000, + .tCH_min = 2000, + .tCK_min = 10000, + .tCS_min = 15000, + .tDH_min = 900, + .tDQSCK_min = 3000, + .tDQSCK_max = 25000, + .tDQSD_min = 0, + .tDQSD_max = 18000, + .tDQSHZ_max = 20000, + .tDQSQ_max = 850, + .tDS_min = 900, + .tDSC_min = 10000, + .tFEAT_max = 1000000, + .tITC_max = 1000000, + .tQHS_max = 1000, + .tRHW_min = 100000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWHR_min = 80000, + .tWRCK_min = 20000, + .tWW_min = 100000, + }, + }, +}; + /* All NAND chips share the same reset data interface: SDR mode 0 */ const struct nand_interface_config *nand_get_reset_interface_config(void) { @@ -346,23 +601,60 @@ onfi_find_closest_sdr_mode(const struct nand_sdr_timings *spec_timings) } /** - * onfi_fill_interface_config - Initialize an interface config from a given - * ONFI mode + * onfi_find_closest_nvddr_mode - Derive the closest ONFI NVDDR timing mode + * given a set of timings + * @spec_timings: the timings to challenge + */ +unsigned int +onfi_find_closest_nvddr_mode(const struct nand_nvddr_timings *spec_timings) +{ + const struct nand_nvddr_timings *onfi_timings; + int mode; + + for (mode = ARRAY_SIZE(onfi_nvddr_timings) - 1; mode > 0; mode--) { + onfi_timings = &onfi_nvddr_timings[mode].timings.nvddr; + + if (spec_timings->tCCS_min <= onfi_timings->tCCS_min && + spec_timings->tAC_min <= onfi_timings->tAC_min && + spec_timings->tADL_min <= onfi_timings->tADL_min && + spec_timings->tCAD_min <= onfi_timings->tCAD_min && + spec_timings->tCAH_min <= onfi_timings->tCAH_min && + spec_timings->tCALH_min <= onfi_timings->tCALH_min && + spec_timings->tCALS_min <= onfi_timings->tCALS_min && + spec_timings->tCAS_min <= onfi_timings->tCAS_min && + spec_timings->tCEH_min <= onfi_timings->tCEH_min && + spec_timings->tCH_min <= onfi_timings->tCH_min && + spec_timings->tCK_min <= onfi_timings->tCK_min && + spec_timings->tCS_min <= onfi_timings->tCS_min && + spec_timings->tDH_min <= onfi_timings->tDH_min && + spec_timings->tDQSCK_min <= onfi_timings->tDQSCK_min && + spec_timings->tDQSD_min <= onfi_timings->tDQSD_min && + spec_timings->tDS_min <= onfi_timings->tDS_min && + spec_timings->tDSC_min <= onfi_timings->tDSC_min && + spec_timings->tRHW_min <= onfi_timings->tRHW_min && + spec_timings->tRR_min <= onfi_timings->tRR_min && + spec_timings->tWHR_min <= onfi_timings->tWHR_min && + spec_timings->tWRCK_min <= onfi_timings->tWRCK_min && + spec_timings->tWW_min <= onfi_timings->tWW_min) + return mode; + } + + return 0; +} + +/* + * onfi_fill_sdr_interface_config - Initialize a SDR interface config from a + * given ONFI mode * @chip: The NAND chip * @iface: The interface configuration to fill - * @type: The interface type * @timing_mode: The ONFI timing mode */ -void onfi_fill_interface_config(struct nand_chip *chip, - struct nand_interface_config *iface, - enum nand_interface_type type, - unsigned int timing_mode) +static void onfi_fill_sdr_interface_config(struct nand_chip *chip, + struct nand_interface_config *iface, + unsigned int timing_mode) { struct onfi_params *onfi = chip->parameters.onfi; - if (WARN_ON(type != NAND_SDR_IFACE)) - return; - if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_sdr_timings))) return; @@ -387,6 +679,64 @@ void onfi_fill_interface_config(struct nand_chip *chip, } /** + * onfi_fill_nvddr_interface_config - Initialize a NVDDR interface config from a + * given ONFI mode + * @chip: The NAND chip + * @iface: The interface configuration to fill + * @timing_mode: The ONFI timing mode + */ +static void onfi_fill_nvddr_interface_config(struct nand_chip *chip, + struct nand_interface_config *iface, + unsigned int timing_mode) +{ + struct onfi_params *onfi = chip->parameters.onfi; + + if (WARN_ON(timing_mode >= ARRAY_SIZE(onfi_nvddr_timings))) + return; + + *iface = onfi_nvddr_timings[timing_mode]; + + /* + * Initialize timings that cannot be deduced from timing mode: + * tPROG, tBERS, tR, tCCS and tCAD. + * These information are part of the ONFI parameter page. + */ + if (onfi) { + struct nand_nvddr_timings *timings = &iface->timings.nvddr; + + /* microseconds -> picoseconds */ + timings->tPROG_max = 1000000ULL * onfi->tPROG; + timings->tBERS_max = 1000000ULL * onfi->tBERS; + timings->tR_max = 1000000ULL * onfi->tR; + + /* nanoseconds -> picoseconds */ + timings->tCCS_min = 1000UL * onfi->tCCS; + + if (onfi->fast_tCAD) + timings->tCAD_min = 25000; + } +} + +/** + * onfi_fill_interface_config - Initialize an interface config from a given + * ONFI mode + * @chip: The NAND chip + * @iface: The interface configuration to fill + * @type: The interface type + * @timing_mode: The ONFI timing mode + */ +void onfi_fill_interface_config(struct nand_chip *chip, + struct nand_interface_config *iface, + enum nand_interface_type type, + unsigned int timing_mode) +{ + if (type == NAND_SDR_IFACE) + return onfi_fill_sdr_interface_config(chip, iface, timing_mode); + else + return onfi_fill_nvddr_interface_config(chip, iface, timing_mode); +} + +/** * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND * timings according to the given ONFI timing mode * @mode: ONFI timing mode diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/raw/nand_toshiba.c index 21a5dbc7e0..5b38fa7bdb 100644 --- a/drivers/mtd/nand/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 @@ -252,17 +256,18 @@ 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); - if (!strcmp("TC58TEG5DCLTA00", chip->parameters.model)) - return -EINVAL; /* MLC, not yet supported in barebox */ if (!strncmp("TC58NVG0S3E", chip->parameters.model, sizeof("TC58NVG0S3E") - 1)) tc58nvg0s3e_init(chip); - if (!strncmp("TH58NVG2S3HBAI4", chip->parameters.model, - sizeof("TH58NVG2S3HBAI4") - 1)) + if ((!strncmp("TH58NVG2S3HBAI4", chip->parameters.model, + sizeof("TH58NVG2S3HBAI4") - 1)) || + (!strncmp("TH58NVG3S0HBAI4", chip->parameters.model, + sizeof("TH58NVG3S0HBAI4") - 1))) th58nvg2s3hbai4_init(chip); return 0; diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/raw/nomadik_nand.c index b5ef39223e..1be3021e99 100644 --- a/drivers/mtd/nand/nomadik_nand.c +++ b/drivers/mtd/nand/raw/nomadik_nand.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * drivers/mtd/nand/nomadik_nand.c * @@ -9,16 +10,6 @@ * * Copyright (C) 2009 Alessandro Rubini * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <common.h> @@ -33,8 +24,8 @@ #include <linux/mtd/rawnand.h> #include <io.h> -#include <mach/nand.h> -#include <mach/fsmc.h> +#include <mach/nomadik/nand.h> +#include <mach/nomadik/fsmc.h> #include <errno.h> @@ -167,7 +158,7 @@ static void nomadik_cmd_ctrl(struct nand_chip *nand, int cmd, unsigned int ctrl) writeb(cmd, host->addr_va); } -static int nomadik_nand_probe(struct device_d *dev) +static int nomadik_nand_probe(struct device *dev) { struct nomadik_nand_platform_data *pdata = dev->platform_data; struct nomadik_nand_host *host; @@ -207,7 +198,7 @@ static int nomadik_nand_probe(struct device_d *dev) return PTR_ERR(nand->legacy.IO_ADDR_W); nand->legacy.cmd_ctrl = nomadik_cmd_ctrl; - nand->ecc.mode = NAND_ECC_HW; + nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; mtd_set_ecclayout(mtd, &nomadik_ecc_layout); nand->ecc.calculate = nomadik_ecc512_calc; nand->ecc.correct = nomadik_ecc512_correct; @@ -236,7 +227,7 @@ err: return ret; } -static struct driver_d nomadik_nand_driver = { +static struct driver nomadik_nand_driver = { .probe = nomadik_nand_probe, .name = "nomadik_nand", }; diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/raw/omap_elm.c index 583235fc78..da731e44f3 100644 --- a/drivers/mtd/nand/omap_elm.c +++ b/drivers/mtd/nand/raw/omap_elm.c @@ -66,7 +66,7 @@ struct elm_registers { }; struct elm_info { - struct device_d *dev; + struct device *dev; void __iomem *elm_base; struct list_head list; enum bch_ecc bch_type; @@ -376,7 +376,7 @@ int elm_decode_bch_error_page(u8 *ecc_calc, struct elm_errorvec *err_vec) return 0; } -static int elm_probe(struct device_d *dev) +static int elm_probe(struct device *dev) { struct resource *res; struct elm_info *info; @@ -404,8 +404,9 @@ static struct of_device_id elm_compatible[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, elm_compatible); -static struct driver_d omap_elm_driver = { +static struct driver omap_elm_driver = { .name = "omap-elm", .probe = elm_probe, .of_compatible = DRV_OF_COMPAT(elm_compatible) diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c new file mode 100644 index 0000000000..279b864970 --- /dev/null +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -0,0 +1,1354 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2018 + * Author: Christophe Kerello <christophe.kerello@st.com> + */ + +#include <common.h> +#include <init.h> +#include <of_address.h> +#include <linux/regmap.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/iopoll.h> +#include <linux/reset.h> +#include <mfd/syscon.h> + +#include "internals.h" + +/* Bad block marker length */ +#define FMC2_BBM_LEN 2 + +/* ECC step size */ +#define FMC2_ECC_STEP_SIZE 512 + +/* Max requests done for a 8k nand page size */ +#define FMC2_MAX_SG 16 + +/* Max chip enable */ +#define FMC2_MAX_CE 2 + +#define FMC2_TIMEOUT_MS 5000 + +/* Timings */ +#define FMC2_THIZ 1 +#define FMC2_TIO 8000 +#define FMC2_TSYNC 3000 +#define FMC2_PCR_TIMING_MASK 0xf +#define FMC2_PMEM_PATT_TIMING_MASK 0xff + +/* FMC2 Controller Registers */ +#define FMC2_BCR1 0x0 +#define FMC2_PCR 0x80 +#define FMC2_SR 0x84 +#define FMC2_PMEM 0x88 +#define FMC2_PATT 0x8c +#define FMC2_HECCR 0x94 +#define FMC2_ISR 0x184 +#define FMC2_ICR 0x188 +#define FMC2_CSQCR 0x200 +#define FMC2_CSQCFGR1 0x204 +#define FMC2_CSQCFGR2 0x208 +#define FMC2_CSQCFGR3 0x20c +#define FMC2_CSQAR1 0x210 +#define FMC2_CSQAR2 0x214 +#define FMC2_CSQIER 0x220 +#define FMC2_CSQISR 0x224 +#define FMC2_CSQICR 0x228 +#define FMC2_CSQEMSR 0x230 +#define FMC2_BCHIER 0x250 +#define FMC2_BCHISR 0x254 +#define FMC2_BCHICR 0x258 +#define FMC2_BCHPBR1 0x260 +#define FMC2_BCHPBR2 0x264 +#define FMC2_BCHPBR3 0x268 +#define FMC2_BCHPBR4 0x26c +#define FMC2_BCHDSR0 0x27c +#define FMC2_BCHDSR1 0x280 +#define FMC2_BCHDSR2 0x284 +#define FMC2_BCHDSR3 0x288 +#define FMC2_BCHDSR4 0x28c + +/* Register: FMC2_BCR1 */ +#define FMC2_BCR1_FMC2EN BIT(31) + +/* Register: FMC2_PCR */ +#define FMC2_PCR_PWAITEN BIT(1) +#define FMC2_PCR_PBKEN BIT(2) +#define FMC2_PCR_PWID GENMASK(5, 4) +#define FMC2_PCR_PWID_BUSWIDTH_8 0 +#define FMC2_PCR_PWID_BUSWIDTH_16 1 +#define FMC2_PCR_ECCEN BIT(6) +#define FMC2_PCR_ECCALG BIT(8) +#define FMC2_PCR_TCLR GENMASK(12, 9) +#define FMC2_PCR_TCLR_DEFAULT 0xf +#define FMC2_PCR_TAR GENMASK(16, 13) +#define FMC2_PCR_TAR_DEFAULT 0xf +#define FMC2_PCR_ECCSS GENMASK(19, 17) +#define FMC2_PCR_ECCSS_512 1 +#define FMC2_PCR_ECCSS_2048 3 +#define FMC2_PCR_BCHECC BIT(24) +#define FMC2_PCR_WEN BIT(25) + +/* Register: FMC2_SR */ +#define FMC2_SR_NWRF BIT(6) + +/* Register: FMC2_PMEM */ +#define FMC2_PMEM_MEMSET GENMASK(7, 0) +#define FMC2_PMEM_MEMWAIT GENMASK(15, 8) +#define FMC2_PMEM_MEMHOLD GENMASK(23, 16) +#define FMC2_PMEM_MEMHIZ GENMASK(31, 24) +#define FMC2_PMEM_DEFAULT 0x0a0a0a0a + +/* Register: FMC2_PATT */ +#define FMC2_PATT_ATTSET GENMASK(7, 0) +#define FMC2_PATT_ATTWAIT GENMASK(15, 8) +#define FMC2_PATT_ATTHOLD GENMASK(23, 16) +#define FMC2_PATT_ATTHIZ GENMASK(31, 24) +#define FMC2_PATT_DEFAULT 0x0a0a0a0a + +/* Register: FMC2_ISR */ +#define FMC2_ISR_IHLF BIT(1) + +/* Register: FMC2_BCHISR */ +#define FMC2_BCHISR_DERF BIT(1) +#define FMC2_BCHISR_EPBRF BIT(4) + +/* Register: FMC2_ICR */ +#define FMC2_ICR_CIHLF BIT(1) + +/* Register: FMC2_CSQCR */ +#define FMC2_CSQCR_CSQSTART BIT(0) + +/* Register: FMC2_CSQCFGR1 */ +#define FMC2_CSQCFGR1_CMD2EN BIT(1) +#define FMC2_CSQCFGR1_DMADEN BIT(2) +#define FMC2_CSQCFGR1_ACYNBR GENMASK(6, 4) +#define FMC2_CSQCFGR1_CMD1 GENMASK(15, 8) +#define FMC2_CSQCFGR1_CMD2 GENMASK(23, 16) +#define FMC2_CSQCFGR1_CMD1T BIT(24) +#define FMC2_CSQCFGR1_CMD2T BIT(25) + +/* Register: FMC2_CSQCFGR2 */ +#define FMC2_CSQCFGR2_SQSDTEN BIT(0) +#define FMC2_CSQCFGR2_RCMD2EN BIT(1) +#define FMC2_CSQCFGR2_DMASEN BIT(2) +#define FMC2_CSQCFGR2_RCMD1 GENMASK(15, 8) +#define FMC2_CSQCFGR2_RCMD2 GENMASK(23, 16) +#define FMC2_CSQCFGR2_RCMD1T BIT(24) +#define FMC2_CSQCFGR2_RCMD2T BIT(25) + +/* Register: FMC2_CSQCFGR3 */ +#define FMC2_CSQCFGR3_SNBR GENMASK(13, 8) +#define FMC2_CSQCFGR3_AC1T BIT(16) +#define FMC2_CSQCFGR3_AC2T BIT(17) +#define FMC2_CSQCFGR3_AC3T BIT(18) +#define FMC2_CSQCFGR3_AC4T BIT(19) +#define FMC2_CSQCFGR3_AC5T BIT(20) +#define FMC2_CSQCFGR3_SDT BIT(21) +#define FMC2_CSQCFGR3_RAC1T BIT(22) +#define FMC2_CSQCFGR3_RAC2T BIT(23) + +/* Register: FMC2_CSQCAR1 */ +#define FMC2_CSQCAR1_ADDC1 GENMASK(7, 0) +#define FMC2_CSQCAR1_ADDC2 GENMASK(15, 8) +#define FMC2_CSQCAR1_ADDC3 GENMASK(23, 16) +#define FMC2_CSQCAR1_ADDC4 GENMASK(31, 24) + +/* Register: FMC2_CSQCAR2 */ +#define FMC2_CSQCAR2_ADDC5 GENMASK(7, 0) +#define FMC2_CSQCAR2_NANDCEN GENMASK(11, 10) +#define FMC2_CSQCAR2_SAO GENMASK(31, 16) + +/* Register: FMC2_CSQIER */ +#define FMC2_CSQIER_TCIE BIT(0) + +/* Register: FMC2_CSQICR */ +#define FMC2_CSQICR_CLEAR_IRQ GENMASK(4, 0) + +/* Register: FMC2_CSQEMSR */ +#define FMC2_CSQEMSR_SEM GENMASK(15, 0) + +/* Register: FMC2_BCHIER */ +#define FMC2_BCHIER_DERIE BIT(1) +#define FMC2_BCHIER_EPBRIE BIT(4) + +/* Register: FMC2_BCHICR */ +#define FMC2_BCHICR_CLEAR_IRQ GENMASK(4, 0) + +/* Register: FMC2_BCHDSR0 */ +#define FMC2_BCHDSR0_DUE BIT(0) +#define FMC2_BCHDSR0_DEF BIT(1) +#define FMC2_BCHDSR0_DEN GENMASK(7, 4) + +/* Register: FMC2_BCHDSR1 */ +#define FMC2_BCHDSR1_EBP1 GENMASK(12, 0) +#define FMC2_BCHDSR1_EBP2 GENMASK(28, 16) + +/* Register: FMC2_BCHDSR2 */ +#define FMC2_BCHDSR2_EBP3 GENMASK(12, 0) +#define FMC2_BCHDSR2_EBP4 GENMASK(28, 16) + +/* Register: FMC2_BCHDSR3 */ +#define FMC2_BCHDSR3_EBP5 GENMASK(12, 0) +#define FMC2_BCHDSR3_EBP6 GENMASK(28, 16) + +/* Register: FMC2_BCHDSR4 */ +#define FMC2_BCHDSR4_EBP7 GENMASK(12, 0) +#define FMC2_BCHDSR4_EBP8 GENMASK(28, 16) + +enum stm32_fmc2_ecc { + FMC2_ECC_HAM = 1, + FMC2_ECC_BCH4 = 4, + FMC2_ECC_BCH8 = 8 +}; + +struct stm32_fmc2_timings { + u8 tclr; + u8 tar; + u8 thiz; + u8 twait; + u8 thold_mem; + u8 tset_mem; + u8 thold_att; + u8 tset_att; +}; + +struct stm32_fmc2_nand { + struct nand_chip chip; + struct gpio_desc *wp_gpio; + struct stm32_fmc2_timings timings; + int ncs; + int cs_used[FMC2_MAX_CE]; +}; + +static inline struct stm32_fmc2_nand *to_fmc2_nand(struct nand_chip *chip) +{ + return container_of(chip, struct stm32_fmc2_nand, chip); +} + +struct stm32_fmc2_nfc { + struct nand_controller base; + struct stm32_fmc2_nand nand; + struct device *dev; + struct device *cdev; + struct regmap *regmap; + void __iomem *data_base[FMC2_MAX_CE]; + void __iomem *cmd_base[FMC2_MAX_CE]; + void __iomem *addr_base[FMC2_MAX_CE]; + struct clk *clk; + + u8 cs_assigned; + int cs_sel; +}; + +static inline struct stm32_fmc2_nfc *to_stm32_nfc(struct nand_controller *base) +{ + return container_of(base, struct stm32_fmc2_nfc, base); +} + +static void stm32_fmc2_nfc_timings_init(struct nand_chip *chip) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); + struct stm32_fmc2_timings *timings = &nand->timings; + u32 pmem, patt; + + /* Set tclr/tar timings */ + regmap_update_bits(nfc->regmap, FMC2_PCR, + FMC2_PCR_TCLR | FMC2_PCR_TAR, + FIELD_PREP(FMC2_PCR_TCLR, timings->tclr) | + FIELD_PREP(FMC2_PCR_TAR, timings->tar)); + + /* Set tset/twait/thold/thiz timings in common bank */ + pmem = FIELD_PREP(FMC2_PMEM_MEMSET, timings->tset_mem); + pmem |= FIELD_PREP(FMC2_PMEM_MEMWAIT, timings->twait); + pmem |= FIELD_PREP(FMC2_PMEM_MEMHOLD, timings->thold_mem); + pmem |= FIELD_PREP(FMC2_PMEM_MEMHIZ, timings->thiz); + regmap_write(nfc->regmap, FMC2_PMEM, pmem); + + /* Set tset/twait/thold/thiz timings in attribut bank */ + patt = FIELD_PREP(FMC2_PATT_ATTSET, timings->tset_att); + patt |= FIELD_PREP(FMC2_PATT_ATTWAIT, timings->twait); + patt |= FIELD_PREP(FMC2_PATT_ATTHOLD, timings->thold_att); + patt |= FIELD_PREP(FMC2_PATT_ATTHIZ, timings->thiz); + regmap_write(nfc->regmap, FMC2_PATT, patt); +} + +static void stm32_fmc2_nfc_setup(struct nand_chip *chip) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + u32 pcr = 0, pcr_mask; + + /* Configure ECC algorithm (default configuration is Hamming) */ + pcr_mask = FMC2_PCR_ECCALG; + pcr_mask |= FMC2_PCR_BCHECC; + if (chip->ecc.strength == FMC2_ECC_BCH8) { + pcr |= FMC2_PCR_ECCALG; + pcr |= FMC2_PCR_BCHECC; + } else if (chip->ecc.strength == FMC2_ECC_BCH4) { + pcr |= FMC2_PCR_ECCALG; + } + + /* Set buswidth */ + pcr_mask |= FMC2_PCR_PWID; + if (chip->options & NAND_BUSWIDTH_16) + pcr |= FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16); + + /* Set ECC sector size */ + pcr_mask |= FMC2_PCR_ECCSS; + pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_512); + + regmap_update_bits(nfc->regmap, FMC2_PCR, pcr_mask, pcr); +} + +static void stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); + + if (nand->cs_used[chipnr] == nfc->cs_sel) + return; + + nfc->cs_sel = nand->cs_used[chipnr]; + stm32_fmc2_nfc_setup(chip); + stm32_fmc2_nfc_timings_init(chip); +} + +static void stm32_fmc2_nfc_set_buswidth_16(struct stm32_fmc2_nfc *nfc, bool set) +{ + u32 pcr; + + pcr = set ? FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_16) : + FIELD_PREP(FMC2_PCR_PWID, FMC2_PCR_PWID_BUSWIDTH_8); + + regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_PWID, pcr); +} + +static void stm32_fmc2_nfc_set_ecc(struct stm32_fmc2_nfc *nfc, bool enable) +{ + regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_ECCEN, + enable ? FMC2_PCR_ECCEN : 0); +} + +static void stm32_fmc2_nfc_clear_bch_irq(struct stm32_fmc2_nfc *nfc) +{ + regmap_write(nfc->regmap, FMC2_BCHICR, FMC2_BCHICR_CLEAR_IRQ); +} + +/* + * Enable ECC logic and reset syndrome/parity bits previously calculated + * Syndrome/parity bits is cleared by setting the ECCEN bit to 0 + */ +static void stm32_fmc2_nfc_hwctl(struct nand_chip *chip, int mode) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + + stm32_fmc2_nfc_set_ecc(nfc, false); + + if (chip->ecc.strength != FMC2_ECC_HAM) { + regmap_update_bits(nfc->regmap, FMC2_PCR, FMC2_PCR_WEN, + mode == NAND_ECC_WRITE ? FMC2_PCR_WEN : 0); + + stm32_fmc2_nfc_clear_bch_irq(nfc); + } + + stm32_fmc2_nfc_set_ecc(nfc, true); +} + +/* + * ECC Hamming calculation + * ECC is 3 bytes for 512 bytes of data (supports error correction up to + * max of 1-bit) + */ +static void stm32_fmc2_nfc_ham_set_ecc(const u32 ecc_sta, u8 *ecc) +{ + ecc[0] = ecc_sta; + ecc[1] = ecc_sta >> 8; + ecc[2] = ecc_sta >> 16; +} + +static int stm32_fmc2_nfc_ham_calculate(struct nand_chip *chip, const u8 *data, + u8 *ecc) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + u32 sr, heccr; + int ret; + + ret = regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr, + sr & FMC2_SR_NWRF, + 1000 * FMC2_TIMEOUT_MS); + if (ret) { + dev_err(nfc->dev, "ham timeout\n"); + return ret; + } + + regmap_read(nfc->regmap, FMC2_HECCR, &heccr); + stm32_fmc2_nfc_ham_set_ecc(heccr, ecc); + stm32_fmc2_nfc_set_ecc(nfc, false); + + return 0; +} + +static int stm32_fmc2_nfc_ham_correct(struct nand_chip *chip, u8 *dat, + u8 *read_ecc, u8 *calc_ecc) +{ + u8 bit_position = 0, b0, b1, b2; + u32 byte_addr = 0, b; + u32 i, shifting = 1; + + /* Indicate which bit and byte is faulty (if any) */ + b0 = read_ecc[0] ^ calc_ecc[0]; + b1 = read_ecc[1] ^ calc_ecc[1]; + b2 = read_ecc[2] ^ calc_ecc[2]; + b = b0 | (b1 << 8) | (b2 << 16); + + /* No errors */ + if (likely(!b)) + return 0; + + /* Calculate bit position */ + for (i = 0; i < 3; i++) { + switch (b % 4) { + case 2: + bit_position += shifting; + break; + case 1: + break; + default: + return -EBADMSG; + } + shifting <<= 1; + b >>= 2; + } + + /* Calculate byte position */ + shifting = 1; + for (i = 0; i < 9; i++) { + switch (b % 4) { + case 2: + byte_addr += shifting; + break; + case 1: + break; + default: + return -EBADMSG; + } + shifting <<= 1; + b >>= 2; + } + + /* Flip the bit */ + dat[byte_addr] ^= (1 << bit_position); + + return 1; +} + +/* + * ECC BCH calculation and correction + * ECC is 7/13 bytes for 512 bytes of data (supports error correction up to + * max of 4-bit/8-bit) + */ +static int stm32_fmc2_nfc_bch_calculate(struct nand_chip *chip, const u8 *data, + u8 *ecc) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + u32 bchisr, bchpbr; + int ret; + + /* Wait until the BCH code is ready */ + ret = regmap_read_poll_timeout(nfc->regmap, FMC2_BCHISR, bchisr, + bchisr & FMC2_BCHISR_EPBRF, + 1000 * FMC2_TIMEOUT_MS); + if (ret) { + dev_err(nfc->dev, "bch timeout\n"); + return -ETIMEDOUT; + } + + /* Read parity bits */ + regmap_read(nfc->regmap, FMC2_BCHPBR1, &bchpbr); + ecc[0] = bchpbr; + ecc[1] = bchpbr >> 8; + ecc[2] = bchpbr >> 16; + ecc[3] = bchpbr >> 24; + + regmap_read(nfc->regmap, FMC2_BCHPBR2, &bchpbr); + ecc[4] = bchpbr; + ecc[5] = bchpbr >> 8; + ecc[6] = bchpbr >> 16; + + if (chip->ecc.strength == FMC2_ECC_BCH8) { + ecc[7] = bchpbr >> 24; + + regmap_read(nfc->regmap, FMC2_BCHPBR3, &bchpbr); + ecc[8] = bchpbr; + ecc[9] = bchpbr >> 8; + ecc[10] = bchpbr >> 16; + ecc[11] = bchpbr >> 24; + + regmap_read(nfc->regmap, FMC2_BCHPBR4, &bchpbr); + ecc[12] = bchpbr; + } + + stm32_fmc2_nfc_set_ecc(nfc, false); + + return 0; +} + +static int stm32_fmc2_nfc_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta) +{ + u32 bchdsr0 = ecc_sta[0]; + u32 bchdsr1 = ecc_sta[1]; + u32 bchdsr2 = ecc_sta[2]; + u32 bchdsr3 = ecc_sta[3]; + u32 bchdsr4 = ecc_sta[4]; + u16 pos[8]; + int i, den; + unsigned int nb_errs = 0; + + /* No errors found */ + if (likely(!(bchdsr0 & FMC2_BCHDSR0_DEF))) + return 0; + + /* Too many errors detected */ + if (unlikely(bchdsr0 & FMC2_BCHDSR0_DUE)) + return -EBADMSG; + + pos[0] = FIELD_GET(FMC2_BCHDSR1_EBP1, bchdsr1); + pos[1] = FIELD_GET(FMC2_BCHDSR1_EBP2, bchdsr1); + pos[2] = FIELD_GET(FMC2_BCHDSR2_EBP3, bchdsr2); + pos[3] = FIELD_GET(FMC2_BCHDSR2_EBP4, bchdsr2); + pos[4] = FIELD_GET(FMC2_BCHDSR3_EBP5, bchdsr3); + pos[5] = FIELD_GET(FMC2_BCHDSR3_EBP6, bchdsr3); + pos[6] = FIELD_GET(FMC2_BCHDSR4_EBP7, bchdsr4); + pos[7] = FIELD_GET(FMC2_BCHDSR4_EBP8, bchdsr4); + + den = FIELD_GET(FMC2_BCHDSR0_DEN, bchdsr0); + for (i = 0; i < den; i++) { + if (pos[i] < eccsize * 8) { + change_bit(pos[i], (unsigned long *)dat); + nb_errs++; + } + } + + return nb_errs; +} + +static int stm32_fmc2_nfc_bch_correct(struct nand_chip *chip, u8 *dat, + u8 *read_ecc, u8 *calc_ecc) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + u32 bchisr, ecc_sta[5]; + int ret; + + /* Wait until the decoding error is ready */ + ret = regmap_read_poll_timeout(nfc->regmap, FMC2_BCHISR, bchisr, + bchisr & FMC2_BCHISR_DERF, + 1000 * FMC2_TIMEOUT_MS); + if (ret) { + dev_err(nfc->dev, "bch timeout\n"); + return -ETIMEDOUT; + } + + regmap_bulk_read(nfc->regmap, FMC2_BCHDSR0, ecc_sta, ARRAY_SIZE(ecc_sta)); + + stm32_fmc2_nfc_set_ecc(nfc, false); + + return stm32_fmc2_nfc_bch_decode(chip->ecc.size, dat, ecc_sta); +} + +static int stm32_fmc2_nfc_read_page(struct nand_chip *chip, u8 *buf, + int oob_required, int page) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + int ret, i, s, stat, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + int eccstrength = chip->ecc.strength; + u8 *p = buf; + u8 *ecc_calc = chip->ecc.calc_buf; + u8 *ecc_code = chip->ecc.code_buf; + unsigned int max_bitflips = 0; + + ret = nand_read_page_op(chip, page, 0, NULL, 0); + if (ret) + return ret; + + for (i = mtd->writesize + FMC2_BBM_LEN, s = 0; s < eccsteps; + s++, i += eccbytes, p += eccsize) { + chip->ecc.hwctl(chip, NAND_ECC_READ); + + /* Read the nand page sector (512 bytes) */ + ret = nand_change_read_column_op(chip, s * eccsize, p, + eccsize, false); + if (ret) + return ret; + + /* Read the corresponding ECC bytes */ + ret = nand_change_read_column_op(chip, i, ecc_code, + eccbytes, false); + if (ret) + return ret; + + /* Correct the data */ + stat = chip->ecc.correct(chip, p, ecc_code, ecc_calc); + if (stat == -EBADMSG) + /* Check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + ecc_code, eccbytes, + NULL, 0, + eccstrength); + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } + } + + /* Read oob */ + if (oob_required) { + ret = nand_change_read_column_op(chip, mtd->writesize, + chip->oob_poi, mtd->oobsize, + false); + if (ret) + return ret; + } + + return max_bitflips; +} + +static void stm32_fmc2_nfc_read_data(struct nand_chip *chip, void *buf, + unsigned int len, bool force_8bit) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + void __iomem *io_addr_r = nfc->data_base[nfc->cs_sel]; + + if (force_8bit && chip->options & NAND_BUSWIDTH_16) + /* Reconfigure bus width to 8-bit */ + stm32_fmc2_nfc_set_buswidth_16(nfc, false); + + if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32))) { + if (!IS_ALIGNED((uintptr_t)buf, sizeof(u16)) && len) { + *(u8 *)buf = readb_relaxed(io_addr_r); + buf += sizeof(u8); + len -= sizeof(u8); + } + + if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32)) && + len >= sizeof(u16)) { + *(u16 *)buf = readw_relaxed(io_addr_r); + buf += sizeof(u16); + len -= sizeof(u16); + } + } + + /* Buf is aligned */ + while (len >= sizeof(u32)) { + *(u32 *)buf = readl_relaxed(io_addr_r); + buf += sizeof(u32); + len -= sizeof(u32); + } + + /* Read remaining bytes */ + if (len >= sizeof(u16)) { + *(u16 *)buf = readw_relaxed(io_addr_r); + buf += sizeof(u16); + len -= sizeof(u16); + } + + if (len) + *(u8 *)buf = readb_relaxed(io_addr_r); + + if (force_8bit && chip->options & NAND_BUSWIDTH_16) + /* Reconfigure bus width to 16-bit */ + stm32_fmc2_nfc_set_buswidth_16(nfc, true); +} + +static void stm32_fmc2_nfc_write_data(struct nand_chip *chip, const void *buf, + unsigned int len, bool force_8bit) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + void __iomem *io_addr_w = nfc->data_base[nfc->cs_sel]; + + if (force_8bit && chip->options & NAND_BUSWIDTH_16) + /* Reconfigure bus width to 8-bit */ + stm32_fmc2_nfc_set_buswidth_16(nfc, false); + + if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32))) { + if (!IS_ALIGNED((uintptr_t)buf, sizeof(u16)) && len) { + writeb_relaxed(*(u8 *)buf, io_addr_w); + buf += sizeof(u8); + len -= sizeof(u8); + } + + if (!IS_ALIGNED((uintptr_t)buf, sizeof(u32)) && + len >= sizeof(u16)) { + writew_relaxed(*(u16 *)buf, io_addr_w); + buf += sizeof(u16); + len -= sizeof(u16); + } + } + + /* Buf is aligned */ + while (len >= sizeof(u32)) { + writel_relaxed(*(u32 *)buf, io_addr_w); + buf += sizeof(u32); + len -= sizeof(u32); + } + + /* Write remaining bytes */ + if (len >= sizeof(u16)) { + writew_relaxed(*(u16 *)buf, io_addr_w); + buf += sizeof(u16); + len -= sizeof(u16); + } + + if (len) + writeb_relaxed(*(u8 *)buf, io_addr_w); + + if (force_8bit && chip->options & NAND_BUSWIDTH_16) + /* Reconfigure bus width to 16-bit */ + stm32_fmc2_nfc_set_buswidth_16(nfc, true); +} + +static int stm32_fmc2_nfc_waitrdy(struct nand_chip *chip, + unsigned long timeout_ms) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + const struct nand_sdr_timings *timings; + u32 isr, sr; + + /* Check if there is no pending requests to the NAND flash */ + if (regmap_read_poll_timeout(nfc->regmap, FMC2_SR, sr, + sr & FMC2_SR_NWRF, + 1000 * FMC2_TIMEOUT_MS)) + dev_warn(nfc->dev, "Waitrdy timeout\n"); + + /* Wait tWB before R/B# signal is low */ + timings = nand_get_sdr_timings(nand_get_interface_config(chip)); + ndelay(PSEC_TO_NSEC(timings->tWB_max)); + + /* R/B# signal is low, clear high level flag */ + regmap_write(nfc->regmap, FMC2_ICR, FMC2_ICR_CIHLF); + + /* Wait R/B# signal is high */ + return regmap_read_poll_timeout(nfc->regmap, FMC2_ISR, isr, + isr & FMC2_ISR_IHLF, + 1000 * FMC2_TIMEOUT_MS); +} + +static int stm32_fmc2_nfc_exec_op(struct nand_chip *chip, + const struct nand_operation *op, + bool check_only) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + const struct nand_op_instr *instr = NULL; + unsigned int op_id, i, timeout; + int ret = 0; + + if (check_only) + return 0; + + stm32_fmc2_nfc_select_chip(chip, op->cs); + + for (op_id = 0; op_id < op->ninstrs; op_id++) { + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + writeb_relaxed(instr->ctx.cmd.opcode, + nfc->cmd_base[nfc->cs_sel]); + break; + + case NAND_OP_ADDR_INSTR: + for (i = 0; i < instr->ctx.addr.naddrs; i++) + writeb_relaxed(instr->ctx.addr.addrs[i], + nfc->addr_base[nfc->cs_sel]); + break; + + case NAND_OP_DATA_IN_INSTR: + stm32_fmc2_nfc_read_data(chip, instr->ctx.data.buf.in, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + break; + + case NAND_OP_DATA_OUT_INSTR: + stm32_fmc2_nfc_write_data(chip, instr->ctx.data.buf.out, + instr->ctx.data.len, + instr->ctx.data.force_8bit); + break; + + case NAND_OP_WAITRDY_INSTR: + timeout = instr->ctx.waitrdy.timeout_ms; + ret = stm32_fmc2_nfc_waitrdy(chip, timeout); + break; + } + } + + return ret; +} + +static void stm32_fmc2_nfc_init(struct stm32_fmc2_nfc *nfc) +{ + u32 pcr; + + regmap_read(nfc->regmap, FMC2_PCR, &pcr); + + /* Set CS used to undefined */ + nfc->cs_sel = -1; + + /* Enable wait feature and nand flash memory bank */ + pcr |= FMC2_PCR_PWAITEN; + pcr |= FMC2_PCR_PBKEN; + + /* Set buswidth to 8 bits mode for identification */ + pcr &= ~FMC2_PCR_PWID; + + /* ECC logic is disabled */ + pcr &= ~FMC2_PCR_ECCEN; + + /* Default mode */ + pcr &= ~FMC2_PCR_ECCALG; + pcr &= ~FMC2_PCR_BCHECC; + pcr &= ~FMC2_PCR_WEN; + + /* Set default ECC sector size */ + pcr &= ~FMC2_PCR_ECCSS; + pcr |= FIELD_PREP(FMC2_PCR_ECCSS, FMC2_PCR_ECCSS_2048); + + /* Set default tclr/tar timings */ + pcr &= ~FMC2_PCR_TCLR; + pcr |= FIELD_PREP(FMC2_PCR_TCLR, FMC2_PCR_TCLR_DEFAULT); + pcr &= ~FMC2_PCR_TAR; + pcr |= FIELD_PREP(FMC2_PCR_TAR, FMC2_PCR_TAR_DEFAULT); + + /* Enable FMC2 controller */ + if (nfc->dev == nfc->cdev) + regmap_update_bits(nfc->regmap, FMC2_BCR1, + FMC2_BCR1_FMC2EN, FMC2_BCR1_FMC2EN); + + regmap_write(nfc->regmap, FMC2_PCR, pcr); + regmap_write(nfc->regmap, FMC2_PMEM, FMC2_PMEM_DEFAULT); + regmap_write(nfc->regmap, FMC2_PATT, FMC2_PATT_DEFAULT); +} + +static void stm32_fmc2_nfc_calc_timings(struct nand_chip *chip, + const struct nand_sdr_timings *sdrt) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); + struct stm32_fmc2_timings *tims = &nand->timings; + unsigned long hclk = clk_get_rate(nfc->clk); + unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); + unsigned long timing, tar, tclr, thiz, twait; + unsigned long tset_mem, tset_att, thold_mem, thold_att; + + tar = max_t(unsigned long, hclkp, sdrt->tAR_min); + timing = DIV_ROUND_UP(tar, hclkp) - 1; + tims->tar = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK); + + tclr = max_t(unsigned long, hclkp, sdrt->tCLR_min); + timing = DIV_ROUND_UP(tclr, hclkp) - 1; + tims->tclr = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK); + + tims->thiz = FMC2_THIZ; + thiz = (tims->thiz + 1) * hclkp; + + /* + * tWAIT > tRP + * tWAIT > tWP + * tWAIT > tREA + tIO + */ + twait = max_t(unsigned long, hclkp, sdrt->tRP_min); + twait = max_t(unsigned long, twait, sdrt->tWP_min); + twait = max_t(unsigned long, twait, sdrt->tREA_max + FMC2_TIO); + timing = DIV_ROUND_UP(twait, hclkp); + tims->twait = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tSETUP_MEM > tCS - tWAIT + * tSETUP_MEM > tALS - tWAIT + * tSETUP_MEM > tDS - (tWAIT - tHIZ) + */ + tset_mem = hclkp; + if (sdrt->tCS_min > twait && (tset_mem < sdrt->tCS_min - twait)) + tset_mem = sdrt->tCS_min - twait; + if (sdrt->tALS_min > twait && (tset_mem < sdrt->tALS_min - twait)) + tset_mem = sdrt->tALS_min - twait; + if (twait > thiz && (sdrt->tDS_min > twait - thiz) && + (tset_mem < sdrt->tDS_min - (twait - thiz))) + tset_mem = sdrt->tDS_min - (twait - thiz); + timing = DIV_ROUND_UP(tset_mem, hclkp); + tims->tset_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tHOLD_MEM > tCH + * tHOLD_MEM > tREH - tSETUP_MEM + * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT) + */ + thold_mem = max_t(unsigned long, hclkp, sdrt->tCH_min); + if (sdrt->tREH_min > tset_mem && + (thold_mem < sdrt->tREH_min - tset_mem)) + thold_mem = sdrt->tREH_min - tset_mem; + if ((sdrt->tRC_min > tset_mem + twait) && + (thold_mem < sdrt->tRC_min - (tset_mem + twait))) + thold_mem = sdrt->tRC_min - (tset_mem + twait); + if ((sdrt->tWC_min > tset_mem + twait) && + (thold_mem < sdrt->tWC_min - (tset_mem + twait))) + thold_mem = sdrt->tWC_min - (tset_mem + twait); + timing = DIV_ROUND_UP(thold_mem, hclkp); + tims->thold_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tSETUP_ATT > tCS - tWAIT + * tSETUP_ATT > tCLS - tWAIT + * tSETUP_ATT > tALS - tWAIT + * tSETUP_ATT > tRHW - tHOLD_MEM + * tSETUP_ATT > tDS - (tWAIT - tHIZ) + */ + tset_att = hclkp; + if (sdrt->tCS_min > twait && (tset_att < sdrt->tCS_min - twait)) + tset_att = sdrt->tCS_min - twait; + if (sdrt->tCLS_min > twait && (tset_att < sdrt->tCLS_min - twait)) + tset_att = sdrt->tCLS_min - twait; + if (sdrt->tALS_min > twait && (tset_att < sdrt->tALS_min - twait)) + tset_att = sdrt->tALS_min - twait; + if (sdrt->tRHW_min > thold_mem && + (tset_att < sdrt->tRHW_min - thold_mem)) + tset_att = sdrt->tRHW_min - thold_mem; + if (twait > thiz && (sdrt->tDS_min > twait - thiz) && + (tset_att < sdrt->tDS_min - (twait - thiz))) + tset_att = sdrt->tDS_min - (twait - thiz); + timing = DIV_ROUND_UP(tset_att, hclkp); + tims->tset_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tHOLD_ATT > tALH + * tHOLD_ATT > tCH + * tHOLD_ATT > tCLH + * tHOLD_ATT > tCOH + * tHOLD_ATT > tDH + * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM + * tHOLD_ATT > tADL - tSETUP_MEM + * tHOLD_ATT > tWH - tSETUP_MEM + * tHOLD_ATT > tWHR - tSETUP_MEM + * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT) + * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT) + */ + thold_att = max_t(unsigned long, hclkp, sdrt->tALH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCLH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tCOH_min); + thold_att = max_t(unsigned long, thold_att, sdrt->tDH_min); + if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) && + (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem)) + thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem; + if (sdrt->tADL_min > tset_mem && + (thold_att < sdrt->tADL_min - tset_mem)) + thold_att = sdrt->tADL_min - tset_mem; + if (sdrt->tWH_min > tset_mem && + (thold_att < sdrt->tWH_min - tset_mem)) + thold_att = sdrt->tWH_min - tset_mem; + if (sdrt->tWHR_min > tset_mem && + (thold_att < sdrt->tWHR_min - tset_mem)) + thold_att = sdrt->tWHR_min - tset_mem; + if ((sdrt->tRC_min > tset_att + twait) && + (thold_att < sdrt->tRC_min - (tset_att + twait))) + thold_att = sdrt->tRC_min - (tset_att + twait); + if ((sdrt->tWC_min > tset_att + twait) && + (thold_att < sdrt->tWC_min - (tset_att + twait))) + thold_att = sdrt->tWC_min - (tset_att + twait); + timing = DIV_ROUND_UP(thold_att, hclkp); + tims->thold_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK); +} + +static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr, + const struct nand_interface_config *conf) +{ + const struct nand_sdr_timings *sdrt; + + sdrt = nand_get_sdr_timings(conf); + if (IS_ERR(sdrt)) + return PTR_ERR(sdrt); + + if (conf->timings.mode > 3) + return -EOPNOTSUPP; + + if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) + return 0; + + stm32_fmc2_nfc_calc_timings(chip, sdrt); + stm32_fmc2_nfc_timings_init(chip); + + return 0; +} + +static void stm32_fmc2_nfc_nand_callbacks_setup(struct nand_chip *chip) +{ + /* + * Specific callbacks to read/write a page depending on + * the mode (polling/sequencer) and the algo used (Hamming, BCH). + */ + chip->ecc.hwctl = stm32_fmc2_nfc_hwctl; + if (chip->ecc.strength == FMC2_ECC_HAM) { + /* Hamming is used */ + chip->ecc.calculate = stm32_fmc2_nfc_ham_calculate; + chip->ecc.correct = stm32_fmc2_nfc_ham_correct; + chip->ecc.options |= NAND_ECC_GENERIC_ERASED_CHECK; + } else { + /* BCH is used */ + chip->ecc.calculate = stm32_fmc2_nfc_bch_calculate; + chip->ecc.correct = stm32_fmc2_nfc_bch_correct; + chip->ecc.read_page = stm32_fmc2_nfc_read_page; + } + + /* Specific configurations depending on the algo used */ + if (chip->ecc.strength == FMC2_ECC_HAM) + chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 4 : 3; + else if (chip->ecc.strength == FMC2_ECC_BCH8) + chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 14 : 13; + else + chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 8 : 7; +} + +static int stm32_fmc2_nfc_ooblayout_ecc(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 = ecc->total; + oobregion->offset = FMC2_BBM_LEN; + + return 0; +} + +static int stm32_fmc2_nfc_ooblayout_free(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 - FMC2_BBM_LEN; + oobregion->offset = ecc->total + FMC2_BBM_LEN; + + return 0; +} + +static const struct mtd_ooblayout_ops stm32_fmc2_nfc_ooblayout_ops = { + .ecc = stm32_fmc2_nfc_ooblayout_ecc, + .free = stm32_fmc2_nfc_ooblayout_free, +}; + +static int stm32_fmc2_nfc_calc_ecc_bytes(int step_size, int strength) +{ + /* Hamming */ + if (strength == FMC2_ECC_HAM) + return 4; + + /* BCH8 */ + if (strength == FMC2_ECC_BCH8) + return 14; + + /* BCH4 */ + return 8; +} + +NAND_ECC_CAPS_SINGLE(stm32_fmc2_nfc_ecc_caps, stm32_fmc2_nfc_calc_ecc_bytes, + FMC2_ECC_STEP_SIZE, + FMC2_ECC_HAM, FMC2_ECC_BCH4, FMC2_ECC_BCH8); + +static int stm32_fmc2_nfc_attach_chip(struct nand_chip *chip) +{ + struct stm32_fmc2_nfc *nfc = to_stm32_nfc(chip->controller); + struct mtd_info *mtd = nand_to_mtd(chip); + int ret; + + /* Default ECC settings in case they are not set in the device tree */ + if (!chip->ecc.size) + chip->ecc.size = FMC2_ECC_STEP_SIZE; + + if (!chip->ecc.strength) + chip->ecc.strength = FMC2_ECC_BCH8; + + ret = nand_ecc_choose_conf(chip, &stm32_fmc2_nfc_ecc_caps, + mtd->oobsize - FMC2_BBM_LEN); + if (ret) { + dev_err(nfc->dev, "no valid ECC settings set\n"); + return ret; + } + + if (mtd->writesize / chip->ecc.size > FMC2_MAX_SG) { + dev_err(nfc->dev, "nand page size is not supported\n"); + return -EINVAL; + } + + if (chip->bbt_options & NAND_BBT_USE_FLASH) + chip->bbt_options |= NAND_BBT_NO_OOB; + + stm32_fmc2_nfc_nand_callbacks_setup(chip); + + mtd_set_ooblayout(mtd, &stm32_fmc2_nfc_ooblayout_ops); + + stm32_fmc2_nfc_setup(chip); + + return 0; +} + +static const struct nand_controller_ops stm32_fmc2_nfc_controller_ops = { + .attach_chip = stm32_fmc2_nfc_attach_chip, + .exec_op = stm32_fmc2_nfc_exec_op, + .setup_interface = stm32_fmc2_nfc_setup_interface, +}; + +static void stm32_fmc2_nfc_wp_enable(struct stm32_fmc2_nand *nand) +{ + if (nand->wp_gpio) + gpiod_set_value(nand->wp_gpio, 1); +} + +static void stm32_fmc2_nfc_wp_disable(struct stm32_fmc2_nand *nand) +{ + if (nand->wp_gpio) + gpiod_set_value(nand->wp_gpio, 0); +} + +static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, + struct device_node *dn) +{ + struct stm32_fmc2_nand *nand = &nfc->nand; + u32 cs; + int ret, i; + + if (!of_get_property(dn, "reg", &nand->ncs)) + return -EINVAL; + + nand->ncs /= sizeof(u32); + if (!nand->ncs) { + dev_err(nfc->dev, "invalid reg property size\n"); + return -EINVAL; + } + + for (i = 0; i < nand->ncs; i++) { + ret = of_property_read_u32_index(dn, "reg", i, &cs); + if (ret) { + dev_err(nfc->dev, "could not retrieve reg property: %d\n", + ret); + return ret; + } + + if (cs >= FMC2_MAX_CE) { + dev_err(nfc->dev, "invalid reg value: %d\n", cs); + return -EINVAL; + } + + if (nfc->cs_assigned & BIT(cs)) { + dev_err(nfc->dev, "cs already assigned: %d\n", cs); + return -EINVAL; + } + + nfc->cs_assigned |= BIT(cs); + nand->cs_used[i] = cs; + } + + nand->wp_gpio = dev_gpiod_get(nfc->dev, dn, "wp", GPIOD_OUT_HIGH, "wp"); + if (IS_ERR(nand->wp_gpio)) { + ret = PTR_ERR(nand->wp_gpio); + if (ret != -ENOENT) + return dev_err_probe(nfc->dev, ret, + "failed to request WP GPIO\n"); + + nand->wp_gpio = NULL; + } + + nand_set_flash_node(&nand->chip, dn); + + return 0; +} + +static int stm32_fmc2_nfc_parse_dt(struct stm32_fmc2_nfc *nfc) +{ + struct device_node *dn = nfc->dev->of_node; + struct device_node *child; + int nchips = of_get_child_count(dn); + int ret = 0; + + if (!nchips) { + dev_err(nfc->dev, "NAND chip not defined\n"); + return -EINVAL; + } + + if (nchips > 1) { + dev_err(nfc->dev, "too many NAND chips defined\n"); + return -EINVAL; + } + + for_each_child_of_node(dn, child) { + ret = stm32_fmc2_nfc_parse_child(nfc, child); + if (ret < 0) { + of_node_put(child); + return ret; + } + } + + return ret; +} + +static int stm32_fmc2_nfc_set_cdev(struct stm32_fmc2_nfc *nfc) +{ + struct device *dev = nfc->dev; + bool ebi_found = false; + + if (dev->parent && of_device_is_compatible(dev->parent->of_node, + "st,stm32mp1-fmc2-ebi")) + ebi_found = true; + + if (of_device_is_compatible(dev->of_node, "st,stm32mp1-fmc2-nfc")) { + if (ebi_found) { + nfc->cdev = dev->parent; + + return 0; + } + + return -EINVAL; + } + + if (ebi_found) + return -EINVAL; + + nfc->cdev = dev; + + return 0; +} + +static int __init stm32_fmc2_nfc_probe(struct device *dev) +{ + struct stm32_fmc2_nfc *nfc; + struct stm32_fmc2_nand *nand; + struct mtd_info *mtd; + struct nand_chip *chip; + struct resource cres; + int chip_cs, mem_region, ret; + int start_region = 0; + + nfc = kzalloc(sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = dev; + nand_controller_init(&nfc->base); + nfc->base.ops = &stm32_fmc2_nfc_controller_ops; + + ret = stm32_fmc2_nfc_set_cdev(nfc); + if (ret) + return ret; + + ret = stm32_fmc2_nfc_parse_dt(nfc); + if (ret) + return ret; + + ret = of_address_to_resource(nfc->cdev->of_node, 0, &cres); + if (ret) + return ret; + + nfc->regmap = device_node_to_regmap(nfc->cdev->of_node); + if (IS_ERR(nfc->regmap)) + return PTR_ERR(nfc->regmap); + + if (nfc->dev == nfc->cdev) + start_region = 1; + + for (chip_cs = 0, mem_region = start_region; chip_cs < FMC2_MAX_CE; + chip_cs++, mem_region += 3) { + if (!(nfc->cs_assigned & BIT(chip_cs))) + continue; + + nfc->data_base[chip_cs] = of_iomap(dev->of_node, mem_region); + if (IS_ERR(nfc->data_base[chip_cs])) + return PTR_ERR(nfc->data_base[chip_cs]); + + nfc->cmd_base[chip_cs] = of_iomap(dev->of_node, mem_region + 1); + if (IS_ERR(nfc->cmd_base[chip_cs])) + return PTR_ERR(nfc->cmd_base[chip_cs]); + + nfc->addr_base[chip_cs] = of_iomap(dev->of_node, mem_region + 2); + if (IS_ERR(nfc->addr_base[chip_cs])) + return PTR_ERR(nfc->addr_base[chip_cs]); + } + + nfc->clk = clk_get(nfc->cdev, NULL); + if (IS_ERR(nfc->clk)) + return PTR_ERR(nfc->clk); + + ret = clk_prepare_enable(nfc->clk); + if (ret) { + dev_err(dev, "can not enable the clock\n"); + return ret; + } + + ret = device_reset_us(dev, 2); + if (ret) + goto err_clk_disable; + + stm32_fmc2_nfc_init(nfc); + + nand = &nfc->nand; + chip = &nand->chip; + mtd = nand_to_mtd(chip); + mtd->dev.parent = dev; + + chip->controller = &nfc->base; + chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE; + + /* Default ECC settings */ + chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; + chip->ecc.size = FMC2_ECC_STEP_SIZE; + chip->ecc.strength = FMC2_ECC_BCH8; + + stm32_fmc2_nfc_wp_disable(nand); + + /* Scan to find existence of the device */ + ret = nand_scan(chip, nand->ncs); + if (ret) + goto err_wp_enable; + + ret = add_mtd_nand_device(mtd, "nand"); + if (ret) + goto err_nand_cleanup; + + return 0; + +err_nand_cleanup: + nand_cleanup(chip); + +err_wp_enable: + stm32_fmc2_nfc_wp_enable(nand); + +err_clk_disable: + clk_disable_unprepare(nfc->clk); + + return ret; +} + +static __maybe_unused struct of_device_id stm32_fmc2_nfc_match[] = { + { .compatible = "st,stm32mp15-fmc2", }, + { .compatible = "st,stm32mp1-fmc2-nfc", }, + { } +}; +MODULE_DEVICE_TABLE(of, stm32_fmc2_nfc_match); + +static struct driver stm32_fmc2_nfc_driver = { + .name = "stm32_fmc2_nfc", + .probe = stm32_fmc2_nfc_probe, + .of_compatible = DRV_OF_COMPAT(stm32_fmc2_nfc_match), +}; +coredevice_platform_driver(stm32_fmc2_nfc_driver); |