summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Kconfig37
-rw-r--r--drivers/mtd/nand/Makefile5
-rw-r--r--drivers/mtd/nand/atmel/Makefile3
-rw-r--r--drivers/mtd/nand/atmel/atmel_nand_ecc.h (renamed from drivers/mtd/nand/atmel_nand_ecc.h)0
-rw-r--r--drivers/mtd/nand/atmel/legacy.c (renamed from drivers/mtd/nand/atmel_nand.c)31
-rw-r--r--drivers/mtd/nand/atmel/nand-controller.c2049
-rw-r--r--drivers/mtd/nand/atmel/pmecc.c993
-rw-r--r--drivers/mtd/nand/atmel/pmecc.h70
-rw-r--r--drivers/mtd/nand/denali.h2
-rw-r--r--drivers/mtd/nand/nand_base.c86
-rw-r--r--drivers/mtd/nand/nand_denali_dt.c78
-rw-r--r--drivers/mtd/nand/nand_esmt.c10
-rw-r--r--drivers/mtd/nand/nand_fsl_ifc.c11
-rw-r--r--drivers/mtd/nand/nand_hynix.c40
-rw-r--r--drivers/mtd/nand/nand_imx.c13
-rw-r--r--drivers/mtd/nand/nand_jedec.c4
-rw-r--r--drivers/mtd/nand/nand_micron.c16
-rw-r--r--drivers/mtd/nand/nand_mrvl_nfc.c11
-rw-r--r--drivers/mtd/nand/nand_mxs.c588
-rw-r--r--drivers/mtd/nand/nand_omap_gpmc.c14
-rw-r--r--drivers/mtd/nand/nand_onfi.c8
-rw-r--r--drivers/mtd/nand/nand_orion.c7
-rw-r--r--drivers/mtd/nand/nand_s3c24xx.c649
-rw-r--r--drivers/mtd/nand/nand_samsung.c18
-rw-r--r--drivers/mtd/nand/nand_toshiba.c12
-rw-r--r--drivers/mtd/nand/nomadik_nand.c8
-rw-r--r--drivers/mtd/nand/omap_elm.c7
-rw-r--r--drivers/mtd/nand/stm32_fmc2_nand.c1354
28 files changed, 5035 insertions, 1089 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2cd52f3820..19f4322f65 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -40,7 +40,7 @@ config NAND_IMX
"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.
+ 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
@@ -65,6 +65,7 @@ config NAND_MXS
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
@@ -94,25 +95,41 @@ config NAND_MRVL_NFC
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"
- depends on ARCH_AT91
+ 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 || COMPILE_TEST
+ depends on NAND_ATMEL_LEGACY
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"
default n
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1584e86694..a0207d328b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -16,10 +16,9 @@ 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_STM32) += stm32_fmc2_nand.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_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
diff --git a/drivers/mtd/nand/atmel/Makefile b/drivers/mtd/nand/atmel/Makefile
new file mode 100644
index 0000000000..0f739c3f31
--- /dev/null
+++ b/drivers/mtd/nand/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/atmel/atmel_nand_ecc.h
index c7864d96dd..c7864d96dd 100644
--- a/drivers/mtd/nand/atmel_nand_ecc.h
+++ b/drivers/mtd/nand/atmel/atmel_nand_ecc.h
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel/legacy.c
index 6a54add93b..cee9e49be0 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel/legacy.c
@@ -33,7 +33,7 @@
#include <linux/err.h>
#include <io.h>
-#include <mach/board.h>
+#include <mach/at91/board.h>
#include <errno.h>
@@ -75,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;
@@ -835,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;
@@ -909,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:
@@ -1195,7 +1195,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;
@@ -1253,7 +1253,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;
@@ -1281,8 +1281,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 {
@@ -1336,19 +1336,14 @@ 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.algo = NAND_ECC_ALGO_HAMMING;
}
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;
@@ -1433,7 +1428,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/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c
new file mode 100644
index 0000000000..5188a11cbe
--- /dev/null
+++ b/drivers/mtd/nand/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 = &microchip_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/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
new file mode 100644
index 0000000000..1b89607a33
--- /dev/null
+++ b/drivers/mtd/nand/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/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h
new file mode 100644
index 0000000000..6178a35e9d
--- /dev/null
+++ b/drivers/mtd/nand/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/denali.h
index 7a721ffbbc..ed489d010b 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -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/nand_base.c b/drivers/mtd/nand/nand_base.c
index 4c90ad9757..2599e8c8c2 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -36,6 +36,7 @@
#include <asm/byteorder.h>
#include <io.h>
#include <malloc.h>
+#include <linux/gpio/consumer.h>
#include <module.h>
#include <of_mtd.h>
@@ -91,11 +92,16 @@ static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
return 0;
}
-const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
+static 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);
+
+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)
@@ -127,11 +133,16 @@ static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
return 0;
}
-const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+static 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);
+
+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
@@ -785,6 +796,27 @@ 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 *gpio,
+ unsigned long timeout_ms)
+{
+ return gpiod_poll_timeout_us(gpio, true, timeout_ms * USEC_PER_MSEC);
+};
+EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);
+
static bool nand_supports_get_features(struct nand_chip *chip, int addr)
{
return (chip->parameters.supports_set_get_features &&
@@ -4651,8 +4683,8 @@ 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);
+ chip->base.ecc.requirements.strength = NAND_ECC_STRENGTH(type);
+ chip->base.ecc.requirements.step_size = NAND_ECC_STEP(type);
chip->parameters.model = strdup(type->name);
if (!chip->parameters.model)
@@ -4914,9 +4946,9 @@ free_detect_allocation:
}
static const char * const nand_ecc_algos[] = {
- [NAND_ECC_HAMMING] = "hamming",
- [NAND_ECC_BCH] = "bch",
- [NAND_ECC_RS] = "rs",
+ [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)
@@ -4927,7 +4959,7 @@ static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
err = of_property_read_string(np, "nand-ecc-algo", &pm);
if (!err) {
- for (ecc_algo = NAND_ECC_HAMMING;
+ for (ecc_algo = NAND_ECC_ALGO_HAMMING;
ecc_algo < ARRAY_SIZE(nand_ecc_algos);
ecc_algo++) {
if (!strcasecmp(pm, nand_ecc_algos[ecc_algo]))
@@ -4942,12 +4974,12 @@ static enum nand_ecc_algo of_get_nand_ecc_algo(struct device_node *np)
err = of_property_read_string(np, "nand-ecc-mode", &pm);
if (!err) {
if (!strcasecmp(pm, "soft"))
- return NAND_ECC_HAMMING;
+ return NAND_ECC_ALGO_HAMMING;
else if (!strcasecmp(pm, "soft_bch"))
- return NAND_ECC_BCH;
+ return NAND_ECC_ALGO_BCH;
}
- return NAND_ECC_UNKNOWN;
+ return NAND_ECC_ALGO_UNKNOWN;
}
static int nand_dt_init(struct nand_chip *chip)
@@ -4976,7 +5008,7 @@ static int nand_dt_init(struct nand_chip *chip)
if (ecc_mode >= 0)
chip->ecc.mode = ecc_mode;
- if (ecc_algo != NAND_ECC_UNKNOWN)
+ if (ecc_algo != NAND_ECC_ALGO_UNKNOWN)
chip->ecc.algo = ecc_algo;
if (ecc_strength >= 0)
@@ -5105,7 +5137,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
return -EINVAL;
switch (ecc->algo) {
- case NAND_ECC_HAMMING:
+ case NAND_ECC_ALGO_HAMMING:
ecc->calculate = nand_calculate_ecc;
ecc->correct = nand_correct_data;
ecc->read_page = nand_read_page_swecc;
@@ -5126,7 +5158,7 @@ static int nand_set_ecc_soft_ops(struct nand_chip *chip)
ecc->options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
return 0;
- case NAND_ECC_BCH:
+ case NAND_ECC_ALGO_BCH:
if (!mtd_nand_has_bch()) {
WARN(1, "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
return -EINVAL;
@@ -5270,8 +5302,8 @@ nand_match_ecc_req(struct nand_chip *chip,
{
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 = chip->base.ecc.requirements.step_size;
+ int req_strength = chip->base.ecc.requirements.strength;
int req_corr, step_size, strength, nsteps, ecc_bytes, ecc_bytes_total;
int best_step, best_strength, best_ecc_bytes;
int best_ecc_bytes_total = INT_MAX;
@@ -5464,7 +5496,7 @@ static bool nand_ecc_strength_good(struct nand_chip *chip)
struct nand_ecc_ctrl *ecc = &chip->ecc;
int corr, ds_corr;
- if (ecc->size == 0 || chip->base.eccreq.step_size == 0)
+ if (ecc->size == 0 || chip->base.ecc.requirements.step_size == 0)
/* Not enough information */
return true;
@@ -5473,10 +5505,10 @@ static bool nand_ecc_strength_good(struct nand_chip *chip)
* the correction density.
*/
corr = (mtd->writesize * ecc->strength) / ecc->size;
- ds_corr = (mtd->writesize * chip->base.eccreq.strength) /
- chip->base.eccreq.step_size;
+ ds_corr = (mtd->writesize * chip->base.ecc.requirements.strength) /
+ chip->base.ecc.requirements.step_size;
- return corr >= ds_corr && ecc->strength >= chip->base.eccreq.strength;
+ return corr >= ds_corr && ecc->strength >= chip->base.ecc.requirements.strength;
}
static int rawnand_erase(struct nand_device *nand, const struct nand_pos *pos)
@@ -5566,7 +5598,7 @@ 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->mode == NAND_ECC_SOFT && ecc->algo == NAND_ECC_ALGO_BCH)) {
switch (mtd->oobsize) {
case 8:
case 16:
@@ -5662,7 +5694,7 @@ int nand_scan_tail(struct nand_chip *chip)
pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
ecc->size, mtd->writesize);
ecc->mode = NAND_ECC_SOFT;
- ecc->algo = NAND_ECC_HAMMING;
+ ecc->algo = NAND_ECC_ALGO_HAMMING;
case NAND_ECC_SOFT:
ret = nand_set_ecc_soft_ops(chip);
if (ret) {
@@ -5752,8 +5784,8 @@ int nand_scan_tail(struct nand_chip *chip)
if (!nand_ecc_strength_good(chip))
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);
+ chip->base.ecc.requirements.strength,
+ chip->base.ecc.requirements.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)) {
@@ -5913,7 +5945,7 @@ 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)
+ chip->ecc.algo == NAND_ECC_ALGO_BCH)
nand_bch_free((struct nand_bch_control *)chip->ecc.priv);
nanddev_cleanup(&chip->base);
diff --git a/drivers/mtd/nand/nand_denali_dt.c b/drivers/mtd/nand/nand_denali_dt.c
index 5cb62cd733..d21cdc9756 100644
--- a/drivers/mtd/nand/nand_denali_dt.c
+++ b/drivers/mtd/nand/nand_denali_dt.c
@@ -14,6 +14,7 @@
#include <io.h>
#include <of_mtd.h>
#include <errno.h>
+#include <globalvar.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
@@ -43,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
@@ -70,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);
}
@@ -125,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;
@@ -189,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;
@@ -211,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_esmt.c b/drivers/mtd/nand/nand_esmt.c
index 2331eb174e..cd635c27ef 100644
--- a/drivers/mtd/nand/nand_esmt.c
+++ b/drivers/mtd/nand/nand_esmt.c
@@ -14,20 +14,20 @@ static void esmt_nand_decode_id(struct nand_chip *chip)
/* Extract ECC requirements from 5th id byte. */
if (chip->id.len >= 5 && nand_is_slc(chip)) {
- chip->base.eccreq.step_size = 512;
+ chip->base.ecc.requirements.step_size = 512;
switch (chip->id.data[4] & 0x3) {
case 0x0:
- chip->base.eccreq.strength = 4;
+ chip->base.ecc.requirements.strength = 4;
break;
case 0x1:
- chip->base.eccreq.strength = 2;
+ chip->base.ecc.requirements.strength = 2;
break;
case 0x2:
- chip->base.eccreq.strength = 1;
+ chip->base.ecc.requirements.strength = 1;
break;
default:
WARN(1, "Could not get ECC info");
- chip->base.eccreq.step_size = 0;
+ chip->base.ecc.requirements.step_size = 0;
break;
}
}
diff --git a/drivers/mtd/nand/nand_fsl_ifc.c b/drivers/mtd/nand/nand_fsl_ifc.c
index 64dc9c225f..3b14b4ae15 100644
--- a/drivers/mtd/nand/nand_fsl_ifc.c
+++ b/drivers/mtd/nand/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 */
@@ -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 */
@@ -964,7 +964,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops);
} else {
nand->ecc.mode = NAND_ECC_SOFT;
- nand->ecc.algo = NAND_ECC_HAMMING;
+ 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/nand_hynix.c
index 0422ed53aa..fef9207495 100644
--- a/drivers/mtd/nand/nand_hynix.c
+++ b/drivers/mtd/nand/nand_hynix.c
@@ -498,30 +498,30 @@ static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
if (valid_jedecid) {
/* Reference: H27UCG8T2E datasheet */
- chip->base.eccreq.step_size = 1024;
+ chip->base.ecc.requirements.step_size = 1024;
switch (ecc_level) {
case 0:
- chip->base.eccreq.step_size = 0;
- chip->base.eccreq.strength = 0;
+ chip->base.ecc.requirements.step_size = 0;
+ chip->base.ecc.requirements.strength = 0;
break;
case 1:
- chip->base.eccreq.strength = 4;
+ chip->base.ecc.requirements.strength = 4;
break;
case 2:
- chip->base.eccreq.strength = 24;
+ chip->base.ecc.requirements.strength = 24;
break;
case 3:
- chip->base.eccreq.strength = 32;
+ chip->base.ecc.requirements.strength = 32;
break;
case 4:
- chip->base.eccreq.strength = 40;
+ chip->base.ecc.requirements.strength = 40;
break;
case 5:
- chip->base.eccreq.strength = 50;
+ chip->base.ecc.requirements.strength = 50;
break;
case 6:
- chip->base.eccreq.strength = 60;
+ chip->base.ecc.requirements.strength = 60;
break;
default:
/*
@@ -542,14 +542,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;
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1 << ecc_level;
} else if (ecc_level < 7) {
if (ecc_level == 5)
- chip->base.eccreq.step_size = 2048;
+ chip->base.ecc.requirements.step_size = 2048;
else
- chip->base.eccreq.step_size = 1024;
- chip->base.eccreq.strength = 24;
+ chip->base.ecc.requirements.step_size = 1024;
+ chip->base.ecc.requirements.strength = 24;
} else {
/*
* We should never reach this case, but if that
@@ -562,14 +562,14 @@ 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;
+ chip->base.ecc.requirements.step_size = 0;
+ chip->base.ecc.requirements.strength = 0;
} else if (ecc_level < 5) {
- chip->base.eccreq.step_size = 512;
- chip->base.eccreq.strength = 1 << (ecc_level - 1);
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1 << (ecc_level - 1);
} else {
- chip->base.eccreq.step_size = 1024;
- chip->base.eccreq.strength = 24 +
+ chip->base.ecc.requirements.step_size = 1024;
+ chip->base.ecc.requirements.strength = 24 +
(8 * (ecc_level - 5));
}
}
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index 66c53670d3..23b9c52e0f 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -18,8 +18,8 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/rawnand.h>
#include <linux/clk.h>
-#include <mach/generic.h>
-#include <mach/imx-nand.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx-nand.h>
#include <io.h>
#include <of_mtd.h>
#include <errno.h>
@@ -27,7 +27,7 @@
struct imx_nand_host {
struct nand_chip nand;
struct mtd_partition *parts;
- struct device_d *dev;
+ struct device *dev;
void *spare0;
void *main_area0;
@@ -1106,7 +1106,7 @@ static struct nand_bbt_descr bbt_mirror_descr = {
static int __init mxcnd_probe_dt(struct imx_nand_host *host)
{
- struct device_node *np = host->dev->device_node;
+ struct device_node *np = host->dev->of_node;
int buswidth;
if (!IS_ENABLED(CONFIG_OFDEVICE))
@@ -1240,7 +1240,7 @@ out:
* @return The function always returns 0.
*/
-static int __init imxnd_probe(struct device_d *dev)
+static int __init imxnd_probe(struct device *dev)
{
struct resource *iores;
struct nand_chip *this;
@@ -1480,8 +1480,9 @@ static __maybe_unused struct of_device_id imx_nand_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, imx_nand_compatible);
-static struct driver_d imx_nand_driver = {
+static struct driver imx_nand_driver = {
.name = "imx_nand",
.probe = imxnd_probe,
.of_compatible = DRV_OF_COMPAT(imx_nand_compatible),
diff --git a/drivers/mtd/nand/nand_jedec.c b/drivers/mtd/nand/nand_jedec.c
index 48997bd5f5..2b21e2d5b5 100644
--- a/drivers/mtd/nand/nand_jedec.c
+++ b/drivers/mtd/nand/nand_jedec.c
@@ -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_micron.c b/drivers/mtd/nand/nand_micron.c
index d59be7ca7b..758316e681 100644
--- a/drivers/mtd/nand/nand_micron.c
+++ b/drivers/mtd/nand/nand_micron.c
@@ -426,7 +426,8 @@ 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 (chip->base.ecc.requirements.strength != 4 &&
+ chip->base.ecc.requirements.strength != 8)
return MICRON_ON_DIE_UNSUPPORTED;
/* 0x2 means on-die ECC is available. */
@@ -467,7 +468,8 @@ 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 (chip->base.ecc.requirements.strength != 4 &&
+ chip->base.ecc.requirements.strength != 8)
return MICRON_ON_DIE_UNSUPPORTED;
return MICRON_ON_DIE_SUPPORTED;
@@ -524,7 +526,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 (chip->base.ecc.requirements.strength == 4) {
micron->ecc.rawbuf = kmalloc(mtd->writesize +
mtd->oobsize,
GFP_KERNEL);
@@ -534,17 +536,17 @@ static int micron_nand_init(struct nand_chip *chip)
}
}
- if (chip->base.eccreq.strength == 4)
+ if (chip->base.ecc.requirements.strength == 4)
mtd_set_ooblayout(mtd,
&micron_nand_on_die_4_ooblayout_ops);
else
mtd_set_ooblayout(mtd,
&micron_nand_on_die_8_ooblayout_ops);
- chip->ecc.bytes = chip->base.eccreq.strength * 2;
+ chip->ecc.bytes = chip->base.ecc.requirements.strength * 2;
chip->ecc.size = 512;
- chip->ecc.strength = chip->base.eccreq.strength;
- chip->ecc.algo = NAND_ECC_BCH;
+ chip->ecc.strength = chip->base.ecc.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/nand_mrvl_nfc.c
index 064853b344..27ca4456c8 100644
--- a/drivers/mtd/nand/nand_mrvl_nfc.c
+++ b/drivers/mtd/nand/nand_mrvl_nfc.c
@@ -137,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 */
@@ -299,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)
@@ -1123,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;
@@ -1188,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;
@@ -1219,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;
@@ -1250,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/nand_mxs.c
index 6f707f9b17..c2a7d036d6 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -21,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>
@@ -31,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,
@@ -194,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;
@@ -220,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)
@@ -236,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;
@@ -254,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;
@@ -442,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;
@@ -482,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);
@@ -597,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;
@@ -613,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.
@@ -639,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;
@@ -674,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;
@@ -692,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");
@@ -732,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);
@@ -943,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);
@@ -984,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;
@@ -998,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;
@@ -1235,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
@@ -1323,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);
@@ -1363,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;
}
@@ -2130,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;
@@ -2272,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_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c
index 9c4f267196..ab36183005 100644
--- a/drivers/mtd/nand/nand_omap_gpmc.c
+++ b/drivers/mtd/nand/nand_omap_gpmc.c
@@ -12,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
@@ -54,7 +54,7 @@
* Copyright (c) 2004 Micron Technology Inc.
* Copyright (c) 2004 David Brownell
*
- *
+ *
*/
#include <common.h>
@@ -68,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"
@@ -109,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;
@@ -1184,7 +1184,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;
@@ -1343,7 +1343,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/nand_onfi.c
index c6b187be02..5dd29ba6ba 100644
--- a/drivers/mtd/nand/nand_onfi.c
+++ b/drivers/mtd/nand/nand_onfi.c
@@ -95,8 +95,8 @@ 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;
+ chip->base.ecc.requirements.strength = ecc->ecc_bits;
+ chip->base.ecc.requirements.step_size = 1 << ecc->codeword_size;
ret = 0;
ext_out:
@@ -266,8 +266,8 @@ 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;
+ chip->base.ecc.requirements.strength = p->ecc_bits;
+ chip->base.ecc.requirements.step_size = 512;
} else if (onfi_version >= 21 &&
(le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) {
diff --git a/drivers/mtd/nand/nand_orion.c b/drivers/mtd/nand/nand_orion.c
index bb41b006e2..ff3642939a 100644
--- a/drivers/mtd/nand/nand_orion.c
+++ b/drivers/mtd/nand/nand_orion.c
@@ -73,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;
@@ -147,8 +147,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_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c
deleted file mode 100644
index b0f16f1d62..0000000000
--- a/drivers/mtd/nand/nand_s3c24xx.c
+++ /dev/null
@@ -1,649 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/* 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
- *
- */
-
-#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);
-}
-
-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/nand_samsung.c b/drivers/mtd/nand/nand_samsung.c
index 3a4a19e808..ee993af1e5 100644
--- a/drivers/mtd/nand/nand_samsung.c
+++ b/drivers/mtd/nand/nand_samsung.c
@@ -71,23 +71,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;
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1 << extid;
} else {
- chip->base.eccreq.step_size = 1024;
+ chip->base.ecc.requirements.step_size = 1024;
switch (extid) {
case 5:
- chip->base.eccreq.strength = 24;
+ chip->base.ecc.requirements.strength = 24;
break;
case 6:
- chip->base.eccreq.strength = 40;
+ chip->base.ecc.requirements.strength = 40;
break;
case 7:
- chip->base.eccreq.strength = 60;
+ chip->base.ecc.requirements.strength = 60;
break;
default:
WARN(1, "Could not decode ECC info");
- chip->base.eccreq.step_size = 0;
+ chip->base.ecc.requirements.step_size = 0;
}
}
} else {
@@ -97,8 +97,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;
+ chip->base.ecc.requirements.step_size = 512;
+ chip->base.ecc.requirements.strength = 1;
break;
/* K9F1G08U0E 21nm chips do not support subpage write */
diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c
index 21a5dbc7e0..3fe0347bfd 100644
--- a/drivers/mtd/nand/nand_toshiba.c
+++ b/drivers/mtd/nand/nand_toshiba.c
@@ -140,7 +140,7 @@ 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)
@@ -175,20 +175,20 @@ 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;
+ chip->base.ecc.requirements.step_size = 512;
switch (chip->id.data[5] & 0x7) {
case 0x4:
- chip->base.eccreq.strength = 1;
+ chip->base.ecc.requirements.strength = 1;
break;
case 0x5:
- chip->base.eccreq.strength = 4;
+ chip->base.ecc.requirements.strength = 4;
break;
case 0x6:
- chip->base.eccreq.strength = 8;
+ chip->base.ecc.requirements.strength = 8;
break;
default:
WARN(1, "Could not get ECC info");
- chip->base.eccreq.step_size = 0;
+ chip->base.ecc.requirements.step_size = 0;
break;
}
}
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
index 8f58f5997c..940ed9809e 100644
--- a/drivers/mtd/nand/nomadik_nand.c
+++ b/drivers/mtd/nand/nomadik_nand.c
@@ -24,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>
@@ -158,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;
@@ -227,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/omap_elm.c
index 583235fc78..da731e44f3 100644
--- a/drivers/mtd/nand/omap_elm.c
+++ b/drivers/mtd/nand/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/stm32_fmc2_nand.c b/drivers/mtd/nand/stm32_fmc2_nand.c
new file mode 100644
index 0000000000..47b012cc9e
--- /dev/null
+++ b/drivers/mtd/nand/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.mode = NAND_ECC_HW;
+ 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);