u-boot.lists.denx.de archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/9] Add DM support for atmel NAND driver
@ 2022-08-29  6:19 Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 1/9] nand: atmel: Add DM based " Balamanikandan Gunasundar
                   ` (9 more replies)
  0 siblings, 10 replies; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

Change include:

- Adapt GPIO descriptor apis for U-Boot. Use
  gpio_request_by_name_nodev, dm_gpio_get_value etc.
- Use U_BOOT_DRIVER instead of platform_driver.
- Replace struct platform_device with struct udevice
- Check the status of nfc exec operation by polling the status
  register instead of interrupt based handling
- DMA operations not supported. Remove it
- Adapt DT parsing to U-Boot APIs

v2:

- Add linux reference version in the commit message from which the
  driver is ported from
- Reword the commit message to describe the changes clearly

Balamanikandan Gunasundar (9):
  nand: atmel: Add DM based NAND driver
  nand: atmel: Add pmecc driver
  mfd: syscon: Add atmel-matrix registers definition
  memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver
  mfd: syscon: atmel-smc: Add driver for atmel SMC
  configs: at91: sam9x60ek: Enable DM based nand driver
  ARM: dts: at91: sam9x60: Add nodes for EBI and NAND
  ARM: dts: at91: sam9x60ek: Enable NAND support
  board: sam9x60ek: remove nand init from board file

 MAINTAINERS                                  |    1 +
 arch/arm/dts/sam9x60.dtsi                    |   42 +
 arch/arm/dts/sam9x60ek.dts                   |  103 +
 board/atmel/sam9x60ek/sam9x60ek.c            |   59 -
 configs/sam9x60ek_mmc_defconfig              |    9 +-
 configs/sam9x60ek_nandflash_defconfig        |    9 +-
 configs/sam9x60ek_qspiflash_defconfig        |    8 +-
 drivers/Kconfig                              |    2 +
 drivers/Makefile                             |    1 +
 drivers/memory/Kconfig                       |    7 +
 drivers/memory/Makefile                      |    1 +
 drivers/memory/atmel_ebi.c                   |   37 +
 drivers/mfd/Kconfig                          |    4 +
 drivers/mfd/Makefile                         |    1 +
 drivers/mfd/atmel-smc.c                      |  364 +++
 drivers/mtd/nand/raw/Kconfig                 |    8 +
 drivers/mtd/nand/raw/Makefile                |    1 +
 drivers/mtd/nand/raw/atmel/Makefile          |    5 +
 drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
 drivers/mtd/nand/raw/atmel/pmecc.c           |  965 ++++++++
 drivers/mtd/nand/raw/atmel/pmecc.h           |   94 +
 include/configs/sam9x60ek.h                  |    5 -
 include/linux/mfd/syscon/atmel-matrix.h      |  112 +
 include/linux/mfd/syscon/atmel-smc.h         |  119 +
 24 files changed, 4177 insertions(+), 73 deletions(-)
 create mode 100644 drivers/memory/atmel_ebi.c
 create mode 100644 drivers/mfd/Kconfig
 create mode 100644 drivers/mfd/Makefile
 create mode 100644 drivers/mfd/atmel-smc.c
 create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
 create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c
 create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c
 create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h
 create mode 100644 include/linux/mfd/syscon/atmel-matrix.h
 create mode 100644 include/linux/mfd/syscon/atmel-smc.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2 1/9] nand: atmel: Add DM based NAND driver
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-09-05  8:55   ` Eugen.Hristev
  2022-08-29  6:19 ` [PATCH v2 2/9] nand: atmel: Add pmecc driver Balamanikandan Gunasundar
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

This implementation is ported from the rework done by Boris Brezillon
in Linux. This porting is done based on linux-5.4-at91. The driver is
tested in sam9x60ek, sama5d3_xplained, sam9x75eb and sama7g54-ddr3-eb.

Changes done includes

- Adapt GPIO descriptor apis for U-Boot. Use
  gpio_request_by_name_nodev, dm_gpio_get_value etc.
- Use U_BOOT_DRIVER instead of platform_driver.
- Replace struct platform_device with struct udevice
- Check the status of nfc exec operation by polling the status
  register instead of interrupt based handling
- DMA operations not supported. Remove it
- Adapt DT parsing to U-Boot APIs

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 drivers/mtd/nand/raw/Kconfig                 |    8 +
 drivers/mtd/nand/raw/Makefile                |    1 +
 drivers/mtd/nand/raw/atmel/Makefile          |    4 +
 drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
 4 files changed, 2306 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
 create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index ce67d1abde..4d023e2893 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -37,6 +37,14 @@ config SYS_NAND_USE_FLASH_BBT
 	help
 	  Enable the BBT (Bad Block Table) usage.
 
+config DM_NAND_ATMEL
+       bool "Support Atmel NAND controller with DM support"
+       select SYS_NAND_SELF_INIT
+       imply SYS_NAND_USE_FLASH_BBT
+       help
+         Enable this driver for NAND flash platforms using an Atmel NAND
+         controller.
+
 config NAND_ATMEL
 	bool "Support Atmel NAND controller"
 	select SYS_NAND_SELF_INIT
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index a398aa9d88..42c1fb25b4 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -48,6 +48,7 @@ ifdef NORMAL_DRIVERS
 obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o
 
 obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
+obj-$(CONFIG_DM_NAND_ATMEL) += atmel/
 obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o
 obj-$(CONFIG_NAND_BRCMNAND) += brcmnand/
 obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
diff --git a/drivers/mtd/nand/raw/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile
new file mode 100644
index 0000000000..6708416983
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_DM_NAND_ATMEL)	+= atmel-nand-controller.o
+
+atmel-nand-controller-objs	:= nand-controller.o
diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
new file mode 100644
index 0000000000..29365c7db0
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
@@ -0,0 +1,2293 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2022 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)
+ *
+ *   Port from Linux
+ *	Balamanikandan Gunasundar(balamanikandan.gunasundar@microchip.com)
+ *	Copyright (C) 2022 Microchip Technology Inc.
+ *
+ * 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 <asm-generic/gpio.h>
+#include <clk.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/of_addr.h>
+#include <dm/of_access.h>
+#include <dm/uclass.h>
+#include <linux/completion.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/mfd/syscon/atmel-smc.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/mtd.h>
+#include <mach/at91_sfr.h>
+#include <nand.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "pmecc.h"
+
+#define NSEC_PER_SEC    1000000000L
+
+#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
+#define MIN_DMA_LEN				128
+
+static struct nand_ecclayout atmel_pmecc_oobinfo;
+
+struct nand_controller_ops {
+	int (*attach_chip)(struct nand_chip *chip);
+	int (*setup_data_interface)(struct mtd_info *mtd, int chipnr,
+				    const struct nand_data_interface *conf);
+};
+
+struct nand_controller {
+	const struct nand_controller_ops *ops;
+};
+
+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;
+		dma_addr_t dma;
+	} io;
+
+	struct atmel_smc_cs_conf smcconf;
+};
+
+struct atmel_nand {
+	struct list_head node;
+	struct udevice *dev;
+	struct nand_chip base;
+	struct atmel_nand_cs *activecs;
+	struct atmel_pmecc_user *pmecc;
+	struct gpio_desc cdgpio;
+	int numcs;
+	struct nand_controller *controller;
+	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 udevice *udev,
+		     const struct atmel_nand_controller_caps *caps);
+	int (*remove)(struct atmel_nand_controller *nc);
+	void (*nand_init)(struct atmel_nand_controller *nc,
+			  struct atmel_nand *nand);
+	int (*ecc_init)(struct nand_chip *chip);
+	int (*setup_data_interface)(struct atmel_nand *nand, int csline,
+				    const struct nand_data_interface *conf);
+};
+
+struct atmel_nand_controller_caps {
+	bool has_dma;
+	bool legacy_of_bindings;
+	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 udevice *dev;
+	struct regmap *smc;
+	struct dma_chan *dmac;
+	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;
+		dma_addr_t dma;
+	} sram;
+	const struct atmel_hsmc_reg_layout *hsmc_layout;
+	struct regmap *io;
+	struct atmel_nfc_op op;
+	struct completion complete;
+	int irq;
+
+	/* Only used when instantiating from legacy DT bindings. */
+	struct clk *clk;
+};
+
+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 void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
+				    int oobsize, int ecc_len)
+{
+	int i;
+
+	layout->eccbytes = ecc_len;
+
+	/* ECC will occupy the last ecc_len bytes continuously */
+	for (i = 0; i < ecc_len; i++)
+		layout->eccpos[i] = oobsize - ecc_len + i;
+
+	layout->oobfree[0].offset = 2;
+	layout->oobfree[0].length =
+		oobsize - ecc_len - layout->oobfree[0].offset;
+}
+
+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, bool poll,
+			  unsigned int timeout_ms)
+{
+	int ret;
+	u32 status;
+
+	if (!timeout_ms)
+		timeout_ms = DEFAULT_TIMEOUT_MS;
+
+	if (poll)
+		ret = regmap_read_poll_timeout(nc->base.smc,
+					       ATMEL_HSMC_NFC_SR, status,
+					       atmel_nfc_op_done(&nc->op,
+								 status),
+					       0, timeout_ms);
+	else
+		return -EOPNOTSUPP;
+
+	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 void iowrite8_rep(void *addr, const uint8_t *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		writeb(buf[i], addr);
+}
+
+static void ioread8_rep(void *addr, uint8_t *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = readb(addr);
+}
+
+static void ioread16_rep(void *addr, void *buf, int len)
+{
+	int i;
+	u16 *p = (u16 *)buf;
+
+	for (i = 0; i < len; i++)
+		p[i] = readw(addr);
+}
+
+static void iowrite16_rep(void *addr, const void *buf, int len)
+{
+	int i;
+	u16 *p = (u16 *)buf;
+
+	for (i = 0; i < len; i++)
+		writew(p[i], addr);
+}
+
+static u8 atmel_nand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+
+	return ioread8(nand->activecs->io.virt);
+}
+
+static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+
+	if (chip->options & NAND_BUSWIDTH_16)
+		iowrite16(byte | (byte << 8), nand->activecs->io.virt);
+	else
+		iowrite8(byte, nand->activecs->io.virt);
+}
+
+static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+
+	if (chip->options & NAND_BUSWIDTH_16)
+		ioread16_rep(nand->activecs->io.virt, buf, len / 2);
+	else
+		ioread8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+
+	if (chip->options & NAND_BUSWIDTH_16)
+		iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
+	else
+		iowrite8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static int atmel_nand_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip  *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+
+	return dm_gpio_get_value(&nand->activecs->rb.gpio);
+}
+
+static void atmel_nand_select_chip(struct mtd_info *mtd, int cs)
+{
+	struct nand_chip *chip =  mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+
+	if (cs < 0 || cs >= nand->numcs) {
+		nand->activecs = NULL;
+		chip->dev_ready = NULL;
+		return;
+	}
+
+	nand->activecs = &nand->cs[cs];
+
+	if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB)
+		chip->dev_ready = atmel_nand_dev_ready;
+}
+
+static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+	struct atmel_hsmc_nand_controller *nc;
+	u32 status;
+
+	nc = to_hsmc_nand_controller(nand->controller);
+
+	regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status);
+
+	return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
+}
+
+static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+	struct atmel_hsmc_nand_controller *nc;
+
+	nc = to_hsmc_nand_controller(nand->controller);
+
+	atmel_nand_select_chip(mtd, cs);
+
+	if (!nand->activecs) {
+		regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+			     ATMEL_HSMC_NFC_CTRL_DIS);
+		return;
+	}
+
+	if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB)
+		chip->dev_ready = atmel_hsmc_nand_dev_ready;
+
+	regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
+			   ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
+			   ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
+			   ATMEL_HSMC_NFC_CFG_RSPARE |
+			   ATMEL_HSMC_NFC_CFG_WSPARE,
+			   ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
+			   ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
+			   ATMEL_HSMC_NFC_CFG_RSPARE);
+	regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+		     ATMEL_HSMC_NFC_CTRL_EN);
+}
+
+static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll)
+{
+	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, poll, 0);
+	if (ret)
+		dev_err(nc->base.dev,
+			"Failed to send NAND command (err = %d)!",
+			ret);
+
+	/* Reset the op state. */
+	memset(&nc->op, 0, sizeof(nc->op));
+
+	return ret;
+}
+
+static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat,
+				     unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+	struct atmel_hsmc_nand_controller *nc;
+
+	nc = to_hsmc_nand_controller(nand->controller);
+
+	if (ctrl & NAND_ALE) {
+		if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
+			return;
+
+		nc->op.addrs[nc->op.naddrs++] = dat;
+	} else if (ctrl & NAND_CLE) {
+		if (nc->op.ncmds > 1)
+			return;
+
+		nc->op.cmds[nc->op.ncmds++] = dat;
+	}
+
+	if (dat == NAND_CMD_NONE) {
+		nc->op.cs = nand->activecs->id;
+		atmel_nfc_exec_op(nc, true);
+	}
+}
+
+static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+				unsigned int ctrl)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+	struct atmel_nand_controller *nc;
+
+	nc = to_nand_controller(nand->controller);
+
+	if ((ctrl & NAND_CTRL_CHANGE) &&
+	    dm_gpio_is_valid(&nand->activecs->csgpio)) {
+		if (ctrl & NAND_NCE)
+			dm_gpio_set_value(&nand->activecs->csgpio, 0);
+		else
+			dm_gpio_set_value(&nand->activecs->csgpio, 1);
+	}
+
+	if (ctrl & NAND_ALE)
+		writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs);
+	else if (ctrl & NAND_CLE)
+		writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
+}
+
+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_nand *nand = to_atmel_nand(chip);
+	struct atmel_hsmc_nand_controller *nc;
+	int ret = -EIO;
+
+	nc = to_hsmc_nand_controller(nand->controller);
+
+	if (ret)
+		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_nand *nand = to_atmel_nand(chip);
+	struct atmel_hsmc_nand_controller *nc;
+	int ret = -EIO;
+
+	nc = to_hsmc_nand_controller(nand->controller);
+
+	if (ret)
+		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_nand *nand = to_atmel_nand(chip);
+	struct atmel_hsmc_nand_controller *nc;
+
+	nc = to_hsmc_nand_controller(nand->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(nand->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(nand->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(nand->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)
+			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;
+
+	atmel_nand_write_buf(mtd, buf, mtd->writesize);
+
+	ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
+	if (ret) {
+		atmel_pmecc_disable(nand->pmecc);
+		return ret;
+	}
+
+	atmel_nand_pmecc_disable(chip, raw);
+
+	atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	return nand_prog_page_end_op(chip);
+}
+
+static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
+				       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 mtd_info *mtd,
+					   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;
+
+	atmel_nand_read_buf(mtd, buf, mtd->writesize);
+	atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
+
+	atmel_nand_pmecc_disable(chip, raw);
+
+	return ret;
+}
+
+static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
+				      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 mtd_info *mtd,
+					  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, status;
+
+	nc = to_hsmc_nand_controller(nand->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, true);
+	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;
+
+	atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	nc->op.cmds[0] = NAND_CMD_PAGEPROG;
+	nc->op.ncmds = 1;
+	nc->op.cs = nand->activecs->id;
+	ret = atmel_nfc_exec_op(nc, true);
+	if (ret)
+		dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
+			ret);
+
+	status = chip->waitfunc(mtd, chip);
+	if (status & NAND_STATUS_FAIL)
+		return -EIO;
+
+	return ret;
+}
+
+static int
+atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd, 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 mtd_info *mtd, 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;
+
+	nc = to_hsmc_nand_controller(nand->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) {
+		nand_read_page_op(chip, page, 0, NULL, 0);
+
+		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, true);
+	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 mtd_info *mtd,
+					   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 mtd_info *mtd,
+					       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 nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
+				 struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+	if (section || !ecc->total)
+		return -ERANGE;
+
+	oobregion->length = ecc->total;
+	oobregion->offset = mtd->oobsize - oobregion->length;
+
+	return 0;
+}
+
+static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
+				  struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->length = mtd->oobsize - ecc->total - 2;
+	oobregion->offset = 2;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+	.ecc = nand_ooblayout_ecc_lp,
+	.rfree = nand_ooblayout_free_lp,
+};
+
+const struct mtd_ooblayout_ops *nand_get_large_page_ooblayout(void)
+{
+	return &nand_ooblayout_lp_ops;
+}
+
+static int atmel_nand_pmecc_init(struct nand_chip *chip)
+{
+	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;
+
+	nc = to_nand_controller(nand->controller);
+
+	if (!nc->pmecc) {
+		dev_err(nc->dev, "HW ECC not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (nc->caps->legacy_of_bindings) {
+		u32 val;
+
+		if (!ofnode_read_u32(nc->dev->node_, "atmel,pmecc-cap", &val))
+			chip->ecc.strength = val;
+
+		if (!ofnode_read_u32(nc->dev->node_,
+				     "atmel,pmecc-sector-size",
+				     &val))
+			chip->ecc.size = val;
+	}
+
+	if (chip->ecc.options & NAND_ECC_MAXIMIZE)
+		req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+	else if (chip->ecc.strength)
+		req.ecc.strength = chip->ecc.strength;
+	else
+		req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+
+	if (chip->ecc.size)
+		req.ecc.sectorsize = chip->ecc.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_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());
+	pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
+				mtd->oobsize,
+				chip->ecc.bytes);
+	chip->ecc.layout = &atmel_pmecc_oobinfo;
+
+	return 0;
+}
+
+static int atmel_nand_ecc_init(struct nand_chip *chip)
+{
+	struct atmel_nand_controller *nc;
+	struct atmel_nand *nand = to_atmel_nand(chip);
+	int ret;
+
+	nc = to_nand_controller(nand->controller);
+
+	switch (chip->ecc.mode) {
+	case NAND_ECC_NONE:
+	case NAND_ECC_SOFT:
+		/*
+		 * Nothing to do, the core will initialize everything for us.
+		 */
+		break;
+
+	case NAND_ECC_HW:
+		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.mode);
+		return -EOPNOTSUPP;
+	}
+
+	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.mode != NAND_ECC_HW)
+		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_data_interface *conf,
+					  struct atmel_smc_cs_conf *smcconf)
+{
+	u32 ncycles, totalcycles, timeps, mckperiodps;
+	struct atmel_nand_controller *nc;
+	int ret;
+
+	nc = to_nand_controller(nand->controller);
+
+	/* DDR interface not supported. */
+	if (conf->type != NAND_SDR_IFACE)
+		return -EOPNOTSUPP;
+
+	/*
+	 * tRC < 30ns implies EDO mode. This controller does not support this
+	 * mode.
+	 */
+	if (conf->timings.sdr.tRC_min < 30000)
+		return -EOPNOTSUPP;
+
+	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_data_interface(struct atmel_nand *nand,
+				    int csline,
+				    const struct nand_data_interface *conf)
+{
+	struct atmel_nand_controller *nc;
+	struct atmel_smc_cs_conf smcconf;
+	struct atmel_nand_cs *cs;
+	int ret;
+
+	nc = to_nand_controller(nand->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_data_interface(struct atmel_nand *nand,
+				     int csline,
+				     const struct nand_data_interface *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->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_data_interface(struct mtd_info *mtd, int csline,
+					   const struct nand_data_interface *conf)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct atmel_nand *nand = to_atmel_nand(chip);
+	struct atmel_nand_controller *nc;
+
+	nc = to_nand_controller(nand->controller);
+
+	if (csline >= nand->numcs ||
+	    (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
+		return -EINVAL;
+
+	return nc->caps->ops->setup_data_interface(nand, csline, conf);
+}
+
+#define NAND_KEEP_TIMINGS       0x00800000
+
+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->controller = &nc->base;
+	nand->controller = &nc->base;
+
+	chip->cmd_ctrl = atmel_nand_cmd_ctrl;
+	chip->read_byte = atmel_nand_read_byte;
+	chip->write_byte = atmel_nand_write_byte;
+	chip->read_buf = atmel_nand_read_buf;
+	chip->write_buf = atmel_nand_write_buf;
+	chip->select_chip = atmel_nand_select_chip;
+	chip->setup_data_interface = atmel_nand_setup_data_interface;
+
+	if (!nc->mck || !nc->caps->ops->setup_data_interface)
+		chip->options |= NAND_KEEP_TIMINGS;
+
+	/* Some NANDs require a longer delay than the default one (20us). */
+	chip->chip_delay = 40;
+
+	/* Default to HW ECC if pmecc is available. */
+	if (nc->pmecc)
+		chip->ecc.mode = NAND_ECC_HW;
+}
+
+static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
+				struct atmel_nand *nand)
+{
+	struct atmel_smc_nand_controller *smc_nc;
+	int i;
+
+	atmel_nand_init(nc, nand);
+
+	smc_nc = to_smc_nand_controller(nand->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 void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
+				 struct atmel_nand *nand)
+{
+	struct nand_chip *chip = &nand->base;
+
+	atmel_nand_init(nc, nand);
+
+	/* Overload some methods for the HSMC controller. */
+	chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl;
+	chip->select_chip = atmel_hsmc_nand_select_chip;
+}
+
+static int atmel_nand_controller_remove_nand(struct atmel_nand *nand)
+{
+	list_del(&nand->node);
+
+	return 0;
+}
+
+static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
+					    ofnode np,
+					    int reg_cells)
+{
+	struct atmel_nand *nand;
+	ofnode n;
+	int numcs = 0;
+	int ret, i;
+	u32 val;
+	fdt32_t faddr;
+	phys_addr_t base;
+
+	/* Count num of nand nodes */
+	ofnode_for_each_subnode(n, ofnode_get_parent(np))
+		numcs++;
+	if (numcs < 1) {
+		dev_err(nc->dev, "Missing or invalid reg property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	nand = devm_kzalloc(nc->dev,
+			    sizeof(struct atmel_nand) +
+			    (numcs * sizeof(struct atmel_nand_cs)),
+			    GFP_KERNEL);
+	if (!nand) {
+		dev_err(nc->dev, "Failed to allocate NAND object\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	nand->numcs = numcs;
+
+	gpio_request_by_name_nodev(np, "det-gpios", 0, &nand->cdgpio,
+				   GPIOD_IS_IN);
+
+	for (i = 0; i < numcs; i++) {
+		ret = ofnode_read_u32(np, "reg", &val);
+		if (ret) {
+			dev_err(nc->dev, "Invalid reg property (err = %d)\n",
+				ret);
+			return ERR_PTR(ret);
+		}
+		nand->cs[i].id = val;
+
+		/* Read base address */
+		struct resource res;
+
+		if (ofnode_read_resource(np, 0, &res)) {
+			dev_err(nc->dev, "Unable to read resource\n");
+			return ERR_PTR(-ENOMEM);
+		}
+
+		faddr = cpu_to_fdt32(val);
+		base = ofnode_translate_address(np, &faddr);
+		nand->cs[i].io.virt = (void *)base;
+
+		if (!ofnode_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 {
+			ret = gpio_request_by_name_nodev(np, "rb-gpios", 0,
+							 &nand->cs[i].rb.gpio,
+							 GPIOD_IS_IN);
+			if (ret) {
+				dev_err(nc->dev, "Failed to get R/B gpio\n");
+				return ERR_PTR(ret);
+			}
+
+			nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
+		}
+
+		ret = gpio_request_by_name_nodev(np, "cs-gpios", 0,
+						 &nand->cs[i].csgpio,
+						 GPIOD_IS_OUT);
+		if (ret)
+			dev_err(nc->dev, "Failed to get CS gpio (%d)\n", ret);
+	}
+
+	nand_set_flash_node(&nand->base, np);
+
+	return nand;
+}
+
+static int nand_attach(struct nand_chip *chip)
+{
+	struct atmel_nand *nand = to_atmel_nand(chip);
+
+	if (nand->controller->ops && nand->controller->ops->attach_chip)
+		return nand->controller->ops->attach_chip(chip);
+
+	return 0;
+}
+
+int atmel_nand_scan(struct mtd_info *mtd, int maxchips)
+{
+	int ret;
+
+	ret = nand_scan_ident(mtd, maxchips, NULL);
+	if (ret)
+		return ret;
+
+	ret = nand_attach(mtd_to_nand(mtd));
+	if (ret)
+		return ret;
+
+	ret = nand_scan_tail(mtd);
+	return ret;
+}
+
+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 (dm_gpio_is_valid(&nand->cdgpio) &&
+	    dm_gpio_get_value(&nand->cdgpio)) {
+		dev_info(nc->dev, "No SmartMedia card inserted.\n");
+		return 0;
+	}
+
+	nc->caps->ops->nand_init(nc, nand);
+
+	ret = atmel_nand_scan(mtd, nand->numcs);
+	if (ret) {
+		dev_err(nc->dev, "NAND scan failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = nand_register(0, mtd);
+	if (ret) {
+		dev_err(nc->dev, "nand register failed: %d\n", ret);
+		return ret;
+	}
+
+	list_add_tail(&nand->node, &nc->chips);
+
+	return 0;
+}
+
+static int
+atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
+{
+	struct atmel_nand *nand, *tmp;
+	int ret;
+
+	list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
+		ret = atmel_nand_controller_remove_nand(nand);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
+{
+	ofnode np;
+	ofnode nand_np;
+	int ret, reg_cells;
+	u32 val;
+
+	/* TODO:
+	 * Add support for legacy nands
+	 */
+
+	np = nc->dev->node_;
+
+	ret = ofnode_read_u32(np, "#address-cells", &val);
+	if (ret) {
+		dev_err(nc->dev, "missing #address-cells property\n");
+		return ret;
+	}
+
+	reg_cells = val;
+
+	ret = ofnode_read_u32(np, "#size-cells", &val);
+	if (ret) {
+		dev_err(nc->dev, "missing #size-cells property\n");
+		return ret;
+	}
+
+	reg_cells += val;
+
+	ofnode_for_each_subnode(nand_np, np) {
+		struct atmel_nand *nand;
+
+		nand = atmel_nand_create(nc, nand_np, reg_cells);
+		if (IS_ERR(nand)) {
+			ret = PTR_ERR(nand);
+			goto err;
+		}
+
+		ret = atmel_nand_controller_add_nand(nc, nand);
+		if (ret)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	atmel_nand_controller_remove_nands(nc);
+
+	return ret;
+}
+
+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 udevice_id atmel_ebi_csa_regmap_of_ids[] = {
+	{
+		.compatible = "atmel,at91sam9260-matrix",
+		.data = (ulong)&at91sam9260_ebi_csa,
+	},
+	{
+		.compatible = "atmel,at91sam9261-matrix",
+		.data = (ulong)&at91sam9261_ebi_csa,
+	},
+	{
+		.compatible = "atmel,at91sam9263-matrix",
+		.data = (ulong)&at91sam9263_ebi_csa,
+	},
+	{
+		.compatible = "atmel,at91sam9rl-matrix",
+		.data = (ulong)&at91sam9rl_ebi_csa,
+	},
+	{
+		.compatible = "atmel,at91sam9g45-matrix",
+		.data = (ulong)&at91sam9g45_ebi_csa,
+	},
+	{
+		.compatible = "atmel,at91sam9n12-matrix",
+		.data = (ulong)&at91sam9n12_ebi_csa,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-matrix",
+		.data = (ulong)&at91sam9x5_ebi_csa,
+	},
+	{
+		.compatible = "microchip,sam9x60-sfr",
+		.data = (ulong)&sam9x60_ebi_csa,
+	},
+	{ /* sentinel */ },
+};
+
+static int atmel_nand_attach_chip(struct nand_chip *chip)
+{
+	struct atmel_nand *nand = to_atmel_nand(chip);
+	struct atmel_nand_controller *nc = to_nand_controller(nand->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	int ret;
+
+	ret = nc->caps->ops->ecc_init(chip);
+	if (ret)
+		return ret;
+
+	if (nc->caps->legacy_of_bindings || !ofnode_valid(nc->dev->node_)) {
+		/*
+		 * We keep the MTD name unchanged to avoid breaking platforms
+		 * where the MTD cmdline parser is used and the bootloader
+		 * has not been updated to use the new naming scheme.
+		 */
+		mtd->name = "atmel_nand";
+	} else 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.
+		 */
+		sprintf(mtd->name, "%s:nand.%d", nc->dev->name, nand->cs[0].id);
+	}
+
+	return 0;
+}
+
+static const struct nand_controller_ops atmel_nand_controller_ops = {
+	.attach_chip = atmel_nand_attach_chip,
+};
+
+static int
+atmel_nand_controller_init(struct atmel_nand_controller *nc,
+			   struct udevice *dev,
+			   const struct atmel_nand_controller_caps *caps)
+{
+	struct ofnode_phandle_args args;
+	int ret;
+
+	nc->base.ops = &atmel_nand_controller_ops;
+	INIT_LIST_HEAD(&nc->chips);
+	nc->dev = dev;
+	nc->caps = caps;
+
+	nc->pmecc = devm_atmel_pmecc_get(dev);
+	if (IS_ERR(nc->pmecc)) {
+		ret = PTR_ERR(nc->pmecc);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "Could not get PMECC object (err = %d)\n",
+				ret);
+		return ret;
+	}
+
+	/* We do not retrieve the SMC syscon when parsing old DTs. */
+	if (nc->caps->legacy_of_bindings)
+		return 0;
+
+	nc->mck = devm_kzalloc(dev, sizeof(nc->mck), GFP_KERNEL);
+	if (!nc->mck)
+		return -ENOMEM;
+
+	clk_get_by_index(dev->parent, 0, nc->mck);
+	if (IS_ERR(nc->mck)) {
+		dev_err(dev, "Failed to retrieve MCK clk\n");
+		return PTR_ERR(nc->mck);
+	}
+
+	ret = ofnode_parse_phandle_with_args(dev->parent->node_,
+					     "atmel,smc", NULL, 0, 0, &args);
+	if (ret) {
+		dev_err(dev, "Missing or invalid atmel,smc property\n");
+		return -EINVAL;
+	}
+
+	nc->smc = syscon_node_to_regmap(args.node);
+	if (IS_ERR(nc->smc)) {
+		ret = PTR_ERR(nc->smc);
+		dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
+		return 0;
+	}
+
+	return 0;
+}
+
+static int
+atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
+{
+	struct udevice *dev = nc->base.dev;
+	struct ofnode_phandle_args args;
+	const struct udevice_id *match = NULL;
+	const char *name;
+	int ret;
+	int len;
+	int i;
+
+	/* We do not retrieve the EBICSA regmap when parsing old DTs. */
+	if (nc->base.caps->legacy_of_bindings)
+		return 0;
+
+	ret = ofnode_parse_phandle_with_args(dev->parent->node_,
+					     nc->base.caps->ebi_csa_regmap_name,
+					     NULL, 0, 0, &args);
+	if (ret) {
+		dev_err(dev, "Unable to read ebi csa regmap\n");
+		return -EINVAL;
+	}
+
+	name = ofnode_get_property(args.node, "compatible", &len);
+
+	for (i = 0; i < ARRAY_SIZE(atmel_ebi_csa_regmap_of_ids); i++) {
+		if (!strcmp(name, atmel_ebi_csa_regmap_of_ids[i].compatible)) {
+			match = &atmel_ebi_csa_regmap_of_ids[i];
+			break;
+		}
+	}
+
+	if (!match) {
+		dev_err(dev, "Unable to find ebi csa conf");
+		return -EINVAL;
+	}
+	nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data;
+
+	nc->ebi_csa_regmap = syscon_node_to_regmap(args.node);
+	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;
+	}
+
+	/* TODO:
+	 * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
+	 * add 4 to ->ebi_csa->offs.
+	 */
+
+	return 0;
+}
+
+static int atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
+{
+	struct udevice *dev = nc->base.dev;
+	struct ofnode_phandle_args args;
+	struct clk smc_clk;
+	int ret;
+	u32 addr;
+
+	ret = ofnode_parse_phandle_with_args(dev->parent->node_,
+					     "atmel,smc", NULL, 0, 0, &args);
+	if (ret) {
+		dev_err(dev, "Missing or invalid atmel,smc property\n");
+		return -EINVAL;
+	}
+
+	nc->hsmc_layout = atmel_hsmc_get_reg_layout(args.node);
+	if (IS_ERR(nc->hsmc_layout)) {
+		dev_err(dev, "Could not get hsmc layout\n");
+		return -EINVAL;
+	}
+
+	/* Enable smc clock */
+	ret = clk_get_by_index_nodev(args.node, 0, &smc_clk);
+	if (ret) {
+		dev_err(dev, "Unable to get smc clock (err = %d)", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(&smc_clk);
+	if (ret)
+		return ret;
+
+	ret = ofnode_parse_phandle_with_args(dev->node_,
+					     "atmel,nfc-io", NULL, 0, 0, &args);
+	if (ret) {
+		dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
+		return -EINVAL;
+	}
+
+	nc->io = syscon_node_to_regmap(args.node);
+	if (IS_ERR(nc->io)) {
+		ret = PTR_ERR(nc->io);
+		dev_err(dev, "Could not get NFC IO regmap\n");
+		return ret;
+	}
+
+	ret = ofnode_parse_phandle_with_args(dev->node_,
+					     "atmel,nfc-sram", NULL, 0, 0, &args);
+	if (ret) {
+		dev_err(dev, "Missing or invalid atmel,nfc-sram property\n");
+		return ret;
+	}
+
+	ret = ofnode_read_u32(args.node, "reg", &addr);
+	if (ret) {
+		dev_err(dev, "Could not read reg addr of nfc sram");
+		return ret;
+	}
+	nc->sram.virt = (void *)addr;
+
+	return 0;
+}
+
+static int
+atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
+{
+	struct atmel_hsmc_nand_controller *hsmc_nc;
+	int ret;
+
+	ret = atmel_nand_controller_remove_nands(nc);
+	if (ret)
+		return ret;
+
+	hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
+
+	if (hsmc_nc->clk) {
+		clk_disable_unprepare(hsmc_nc->clk);
+		devm_clk_put(nc->dev, hsmc_nc->clk);
+	}
+
+	return 0;
+}
+
+static int
+atmel_hsmc_nand_controller_probe(struct udevice *dev,
+				 const struct atmel_nand_controller_caps *caps)
+{
+	struct atmel_hsmc_nand_controller *nc;
+	int ret;
+
+	nc = devm_kzalloc(dev, 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 before registering our IRQ handler. */
+	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);
+
+	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,
+	.remove = atmel_hsmc_nand_controller_remove,
+	.ecc_init = atmel_hsmc_nand_ecc_init,
+	.nand_init = atmel_hsmc_nand_init,
+	.setup_data_interface = atmel_hsmc_nand_setup_data_interface,
+};
+
+static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
+	.has_dma = true,
+	.ale_offs = BIT(21),
+	.cle_offs = BIT(22),
+	.ops = &atmel_hsmc_nc_ops,
+};
+
+static int
+atmel_smc_nand_controller_probe(struct udevice *dev,
+				const struct atmel_nand_controller_caps *caps)
+{
+	struct atmel_smc_nand_controller *nc;
+	int ret;
+
+	nc = devm_kzalloc(dev, 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);
+}
+
+static int
+atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
+{
+	int ret;
+
+	ret = atmel_nand_controller_remove_nands(nc);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * The SMC reg layout of at91rm9200 is completely different which prevents us
+ * from re-using atmel_smc_nand_setup_data_interface() for the
+ * ->setup_data_interface() hook.
+ * At this point, there's no support for the at91rm9200 SMC IP, so we leave
+ * ->setup_data_interface() unassigned.
+ */
+static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
+	.probe = atmel_smc_nand_controller_probe,
+	.remove = atmel_smc_nand_controller_remove,
+	.ecc_init = atmel_nand_ecc_init,
+	.nand_init = atmel_smc_nand_init,
+};
+
+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,
+	.remove = atmel_smc_nand_controller_remove,
+	.ecc_init = atmel_nand_ecc_init,
+	.nand_init = atmel_smc_nand_init,
+	.setup_data_interface = atmel_smc_nand_setup_data_interface,
+};
+
+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 = {
+	.has_dma = true,
+	.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 = {
+	.has_dma = true,
+	.ale_offs = BIT(21),
+	.cle_offs = BIT(22),
+	.ebi_csa_regmap_name = "microchip,sfr",
+	.ops = &atmel_smc_nc_ops,
+};
+
+/* Only used to parse old bindings. */
+static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = {
+	.ale_offs = BIT(21),
+	.cle_offs = BIT(22),
+	.ops = &atmel_smc_nc_ops,
+	.legacy_of_bindings = true,
+};
+
+static const struct udevice_id atmel_nand_controller_of_ids[] = {
+	{
+		.compatible = "atmel,at91rm9200-nand-controller",
+		.data = (ulong)&atmel_rm9200_nc_caps,
+	},
+	{
+		.compatible = "atmel,at91sam9260-nand-controller",
+		.data = (ulong)&atmel_sam9260_nc_caps,
+	},
+	{
+		.compatible = "atmel,at91sam9261-nand-controller",
+		.data = (ulong)&atmel_sam9261_nc_caps,
+	},
+	{
+		.compatible = "atmel,at91sam9g45-nand-controller",
+		.data = (ulong)&atmel_sam9g45_nc_caps,
+	},
+	{
+		.compatible = "atmel,sama5d3-nand-controller",
+		.data = (ulong)&atmel_sama5_nc_caps,
+	},
+	{
+		.compatible = "microchip,sam9x60-nand-controller",
+		.data = (ulong)&microchip_sam9x60_nc_caps,
+	},
+	/* Support for old/deprecated bindings: */
+	{
+		.compatible = "atmel,at91rm9200-nand",
+		.data = (ulong)&atmel_rm9200_nand_caps,
+	},
+	{
+		.compatible = "atmel,sama5d4-nand",
+		.data = (ulong)&atmel_rm9200_nand_caps,
+	},
+	{
+		.compatible = "atmel,sama5d2-nand",
+		.data = (ulong)&atmel_rm9200_nand_caps,
+	},
+	{ /* sentinel */ },
+};
+
+static int atmel_nand_controller_probe(struct udevice *dev)
+{
+	const struct atmel_nand_controller_caps *caps;
+	struct udevice *pmecc_dev;
+
+	caps = (struct atmel_nand_controller_caps *)dev_get_driver_data(dev);
+	if (!caps) {
+		printf("Could not retrieve NFC caps\n");
+		return -EINVAL;
+	}
+
+	/* Probe pmecc driver */
+	if (uclass_get_device(UCLASS_MTD, 1, &pmecc_dev)) {
+		printf("%s: get device fail\n", __func__);
+		return -EINVAL;
+	}
+
+	return caps->ops->probe(dev, caps);
+}
+
+static int atmel_nand_controller_remove(struct udevice *dev)
+{
+	struct atmel_nand_controller *nc;
+
+	nc = (struct atmel_nand_controller *)dev_get_driver_data(dev);
+
+	return nc->caps->ops->remove(nc);
+}
+
+U_BOOT_DRIVER(atmel_nand_controller) = {
+	.name = "atmel-nand-controller",
+	.id = UCLASS_MTD,
+	.of_match = atmel_nand_controller_of_ids,
+	.probe = atmel_nand_controller_probe,
+	.remove = atmel_nand_controller_remove,
+};
+
+void board_nand_init(void)
+{
+	struct udevice *dev;
+	int ret;
+
+	ret = uclass_get_device_by_driver(UCLASS_MTD,
+					  DM_DRIVER_GET(atmel_nand_controller),
+					  &dev);
+	if (ret && ret != -ENODEV)
+		printf("Failed to initialize NAND controller. (error %d)\n",
+		       ret);
+}
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 2/9] nand: atmel: Add pmecc driver
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 1/9] nand: atmel: Add DM based " Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 3/9] mfd: syscon: Add atmel-matrix registers definition Balamanikandan Gunasundar
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

Add driver for atmel pmecc. This implementation is ported from
Linux. The reference taken is linux-5.4-at91.

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 drivers/mtd/nand/raw/atmel/Makefile |   3 +-
 drivers/mtd/nand/raw/atmel/pmecc.c  | 965 ++++++++++++++++++++++++++++
 drivers/mtd/nand/raw/atmel/pmecc.h  |  94 +++
 3 files changed, 1061 insertions(+), 1 deletion(-)
 create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c
 create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h

diff --git a/drivers/mtd/nand/raw/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile
index 6708416983..e044ff55ba 100644
--- a/drivers/mtd/nand/raw/atmel/Makefile
+++ b/drivers/mtd/nand/raw/atmel/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_DM_NAND_ATMEL)	+= atmel-nand-controller.o
+obj-$(CONFIG_DM_NAND_ATMEL)	+= atmel-nand-controller.o atmel-pmecc.o
 
 atmel-nand-controller-objs	:= nand-controller.o
+atmel-pmecc-objs		:= pmecc.o
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.c b/drivers/mtd/nand/raw/atmel/pmecc.c
new file mode 100644
index 0000000000..e2e3f1ee6b
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/pmecc.c
@@ -0,0 +1,965 @@
+// 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 <linux/mtd/rawnand.h>
+#include <dm/of_access.h>
+#include <dm/ofnode.h>
+#include <asm/io.h>
+#include "pmecc.h"
+#include <dm/uclass.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <linux/ioport.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			BIT(4)
+#define PMECC_CFG_NSECTORS(x)			((fls(x) - 1) << 8)
+#define PMECC_CFG_READ_OP			(0 << 12)
+#define PMECC_CFG_WRITE_OP			BIT(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		BIT(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_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;
+};
+
+/* Serialize table access */
+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 = (s16 *)user->gf_tables->alpha_to;
+	s16 *index_of = (s16 *)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 = (s16 *)user->gf_tables->index_of;
+	s16 *alpha_to = (s16 *)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);
+
+#define ATMEL_BASE_PMECC	0xffffe000
+#define ATMEL_BASE_PMERRLOC	0xffffe600
+
+static struct atmel_pmecc *
+atmel_pmecc_create(struct udevice *dev,
+		   const struct atmel_pmecc_caps *caps,
+		   int pmecc_res_idx, int errloc_res_idx,
+		   int timing_res_idx)
+{
+	struct atmel_pmecc *pmecc;
+	struct resource res;
+
+	pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL);
+	if (!pmecc)
+		return ERR_PTR(-ENOMEM);
+
+	pmecc->caps = caps;
+	pmecc->dev = dev;
+	mutex_init(&pmecc->lock);
+
+	ofnode_read_resource(dev->node_, 0, &res);
+	pmecc->regs.base = (void *)res.start;
+	ofnode_read_resource(dev->node_, 1, &res);
+	pmecc->regs.errloc = (void *)res.start;
+
+	pmecc->regs.timing = 0;
+
+	/* Disable all interrupts before registering the PMECC handler. */
+	writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
+	atmel_pmecc_reset(pmecc);
+
+	return pmecc;
+}
+
+static void devm_atmel_pmecc_put(struct udevice *dev, void *res)
+{
+}
+
+static struct atmel_pmecc *atmel_pmecc_get_by_node(struct udevice *dev)
+{
+	struct atmel_pmecc *pmecc, **ptr;
+	int ret;
+
+	pmecc = dev_get_plat(dev);
+	if (!pmecc) {
+		ret = -EPROBE_DEFER;
+		goto err_put_device;
+	}
+
+	ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto err_put_device;
+	}
+
+	*ptr = pmecc;
+
+	devres_add(dev, ptr);
+
+	return pmecc;
+
+err_put_device:
+	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,
+};
+
+struct atmel_pmecc *devm_atmel_pmecc_get(struct udevice *userdev)
+{
+	struct atmel_pmecc *pmecc;
+	struct ofnode_phandle_args args;
+	struct udevice *pdev;
+	int ret;
+
+	if (!userdev)
+		return ERR_PTR(-EINVAL);
+
+	ret = ofnode_parse_phandle_with_args(userdev->node_,
+					     "ecc-engine",
+					     NULL, 0, 0, &args);
+	ret = uclass_get_device_by_ofnode(UCLASS_MTD, args.node, &pdev);
+	if (ret)
+		return NULL;
+
+	pmecc = atmel_pmecc_get_by_node(pdev);
+
+	/* TODO:
+	 * Support old DT bindings
+	 */
+
+	return pmecc;
+}
+EXPORT_SYMBOL(devm_atmel_pmecc_get);
+
+static const struct udevice_id atmel_pmecc_match[] = {
+	{ .compatible = "atmel,at91sam9g45-pmecc", (ulong)&at91sam9g45_caps },
+	{ .compatible = "atmel,sama5d4-pmecc", (ulong)&sama5d4_caps },
+	{ .compatible = "atmel,sama5d2-pmecc", (ulong)&sama5d2_caps },
+	{ /* sentinel */ }
+};
+
+static int atmel_pmecc_probe(struct udevice *dev)
+{
+	const struct atmel_pmecc_caps *caps;
+	struct atmel_pmecc *pmecc;
+
+	caps = (struct atmel_pmecc_caps *)dev_get_driver_data(dev);
+	if (!caps) {
+		dev_err(dev, "Invalid caps\n");
+		return -EINVAL;
+	}
+
+	pmecc = atmel_pmecc_create(dev, caps, 0, 1, 2);
+	if (IS_ERR(pmecc))
+		return PTR_ERR(pmecc);
+
+	dev->plat_ = pmecc;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(atmel_pmecc) = {
+	.name = "atmel-pmecc",
+	.id = UCLASS_MTD,
+	.of_match = atmel_pmecc_match,
+	.probe = atmel_pmecc_probe,
+};
diff --git a/drivers/mtd/nand/raw/atmel/pmecc.h b/drivers/mtd/nand/raw/atmel/pmecc.h
new file mode 100644
index 0000000000..43f96b2f16
--- /dev/null
+++ b/drivers/mtd/nand/raw/atmel/pmecc.h
@@ -0,0 +1,94 @@
+/* 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_suspend_ctx {
+	u32 setup;
+	u32 pulse;
+	u32 cycle;
+	u32 timings;
+	u32 mode;
+};
+
+struct atmel_pmecc {
+	struct udevice *dev;
+	const struct atmel_pmecc_caps *caps;
+
+	struct {
+		void __iomem *base;
+		void __iomem *errloc;
+		void __iomem *timing;
+	} regs;
+
+	/* Mutex used for pmecc enable/disable */
+	struct mutex lock;
+
+	struct atmel_pmecc_suspend_ctx suspend;
+};
+
+struct atmel_pmecc *devm_atmel_pmecc_get(struct udevice *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 */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 3/9] mfd: syscon: Add atmel-matrix registers definition
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 1/9] nand: atmel: Add DM based " Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 2/9] nand: atmel: Add pmecc driver Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 4/9] memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver Balamanikandan Gunasundar
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

This file is copied from Linux. AT91 SoCs have a memory range reserved
for internal bus configuration. Expose those registers so that drivers
can make use of the matrix syscon declared in at91 DTs.

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 include/linux/mfd/syscon/atmel-matrix.h | 112 ++++++++++++++++++++++++
 1 file changed, 112 insertions(+)
 create mode 100644 include/linux/mfd/syscon/atmel-matrix.h

diff --git a/include/linux/mfd/syscon/atmel-matrix.h b/include/linux/mfd/syscon/atmel-matrix.h
new file mode 100644
index 0000000000..dd228cab67
--- /dev/null
+++ b/include/linux/mfd/syscon/atmel-matrix.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Copyright (C) 2014 Atmel Corporation.
+ *
+ * Memory Controllers (MATRIX, EBI) - System peripherals registers.
+ */
+
+#ifndef _LINUX_MFD_SYSCON_ATMEL_MATRIX_H
+#define _LINUX_MFD_SYSCON_ATMEL_MATRIX_H
+
+#define AT91SAM9260_MATRIX_MCFG			0x00
+#define AT91SAM9260_MATRIX_SCFG			0x40
+#define AT91SAM9260_MATRIX_PRS			0x80
+#define AT91SAM9260_MATRIX_MRCR			0x100
+#define AT91SAM9260_MATRIX_EBICSA		0x11c
+
+#define AT91SAM9261_MATRIX_MRCR			0x0
+#define AT91SAM9261_MATRIX_SCFG			0x4
+#define AT91SAM9261_MATRIX_TCR			0x24
+#define AT91SAM9261_MATRIX_EBICSA		0x30
+#define AT91SAM9261_MATRIX_USBPUCR		0x34
+
+#define AT91SAM9263_MATRIX_MCFG			0x00
+#define AT91SAM9263_MATRIX_SCFG			0x40
+#define AT91SAM9263_MATRIX_PRS			0x80
+#define AT91SAM9263_MATRIX_MRCR			0x100
+#define AT91SAM9263_MATRIX_TCR			0x114
+#define AT91SAM9263_MATRIX_EBI0CSA		0x120
+#define AT91SAM9263_MATRIX_EBI1CSA		0x124
+
+#define AT91SAM9RL_MATRIX_MCFG			0x00
+#define AT91SAM9RL_MATRIX_SCFG			0x40
+#define AT91SAM9RL_MATRIX_PRS			0x80
+#define AT91SAM9RL_MATRIX_MRCR			0x100
+#define AT91SAM9RL_MATRIX_TCR			0x114
+#define AT91SAM9RL_MATRIX_EBICSA		0x120
+
+#define AT91SAM9G45_MATRIX_MCFG			0x00
+#define AT91SAM9G45_MATRIX_SCFG			0x40
+#define AT91SAM9G45_MATRIX_PRS			0x80
+#define AT91SAM9G45_MATRIX_MRCR			0x100
+#define AT91SAM9G45_MATRIX_TCR			0x110
+#define AT91SAM9G45_MATRIX_DDRMPR		0x118
+#define AT91SAM9G45_MATRIX_EBICSA		0x128
+
+#define AT91SAM9N12_MATRIX_MCFG			0x00
+#define AT91SAM9N12_MATRIX_SCFG			0x40
+#define AT91SAM9N12_MATRIX_PRS			0x80
+#define AT91SAM9N12_MATRIX_MRCR			0x100
+#define AT91SAM9N12_MATRIX_EBICSA		0x118
+
+#define AT91SAM9X5_MATRIX_MCFG			0x00
+#define AT91SAM9X5_MATRIX_SCFG			0x40
+#define AT91SAM9X5_MATRIX_PRS			0x80
+#define AT91SAM9X5_MATRIX_MRCR			0x100
+#define AT91SAM9X5_MATRIX_EBICSA		0x120
+
+#define SAMA5D3_MATRIX_MCFG			0x00
+#define SAMA5D3_MATRIX_SCFG			0x40
+#define SAMA5D3_MATRIX_PRS			0x80
+#define SAMA5D3_MATRIX_MRCR			0x100
+
+#define AT91_MATRIX_MCFG(o, x)			((o) + ((x) * 0x4))
+#define AT91_MATRIX_ULBT			GENMASK(2, 0)
+#define AT91_MATRIX_ULBT_INFINITE		(0 << 0)
+#define AT91_MATRIX_ULBT_SINGLE			(1 << 0)
+#define AT91_MATRIX_ULBT_FOUR			(2 << 0)
+#define AT91_MATRIX_ULBT_EIGHT			(3 << 0)
+#define AT91_MATRIX_ULBT_SIXTEEN		(4 << 0)
+
+#define AT91_MATRIX_SCFG(o, x)			((o) + ((x) * 0x4))
+#define AT91_MATRIX_SLOT_CYCLE			GENMASK(7,  0)
+#define AT91_MATRIX_DEFMSTR_TYPE		GENMASK(17, 16)
+#define AT91_MATRIX_DEFMSTR_TYPE_NONE		(0 << 16)
+#define AT91_MATRIX_DEFMSTR_TYPE_LAST		(1 << 16)
+#define AT91_MATRIX_DEFMSTR_TYPE_FIXED		(2 << 16)
+#define AT91_MATRIX_FIXED_DEFMSTR		GENMASK(20, 18)
+#define AT91_MATRIX_ARBT			GENMASK(25, 24)
+#define AT91_MATRIX_ARBT_ROUND_ROBIN		(0 << 24)
+#define AT91_MATRIX_ARBT_FIXED_PRIORITY		(1 << 24)
+
+#define AT91_MATRIX_ITCM_SIZE			GENMASK(3, 0)
+#define AT91_MATRIX_ITCM_0			(0 << 0)
+#define AT91_MATRIX_ITCM_16			(5 << 0)
+#define AT91_MATRIX_ITCM_32			(6 << 0)
+#define AT91_MATRIX_ITCM_64			(7 << 0)
+#define	AT91_MATRIX_DTCM_SIZE			GENMASK(7, 4)
+#define	AT91_MATRIX_DTCM_0			(0 << 4)
+#define	AT91_MATRIX_DTCM_16			(5 << 4)
+#define AT91_MATRIX_DTCM_32			(6 << 4)
+#define AT91_MATRIX_DTCM_64			(7 << 4)
+
+#define AT91_MATRIX_PRAS(o, x)			((o) + ((x) * 0x8))
+#define AT91_MATRIX_PRBS(o, x)			((o) + ((x) * 0x8) + 0x4)
+#define AT91_MATRIX_MPR(x)			GENMASK(((x) * 0x4) + 1, ((x) * 0x4))
+
+#define AT91_MATRIX_RCB(x)			BIT(x)
+
+#define AT91_MATRIX_CSA(cs, val)		((val) << (cs))
+#define AT91_MATRIX_DBPUC			BIT(8)
+#define AT91_MATRIX_DBPDC			BIT(9)
+#define AT91_MATRIX_VDDIOMSEL			BIT(16)
+#define AT91_MATRIX_VDDIOMSEL_1_8V		(0 << 16)
+#define AT91_MATRIX_VDDIOMSEL_3_3V		(1 << 16)
+#define AT91_MATRIX_EBI_IOSR			BIT(17)
+#define AT91_MATRIX_DDR_IOSR			BIT(18)
+#define AT91_MATRIX_NFD0_SELECT			BIT(24)
+#define AT91_MATRIX_DDR_MP_EN			BIT(25)
+
+#define AT91_MATRIX_USBPUCR_PUON		BIT(30)
+
+#endif /* _LINUX_MFD_SYSCON_ATMEL_MATRIX_H */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 4/9] memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
                   ` (2 preceding siblings ...)
  2022-08-29  6:19 ` [PATCH v2 3/9] mfd: syscon: Add atmel-matrix registers definition Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC Balamanikandan Gunasundar
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

The EBI is used to access peripherals like NAND, SRAM, NOR etc. Add
this driver to probe the nand flash controller. This is a dummy driver
and not yet a complete device driver for EBI.

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 MAINTAINERS                |  1 +
 drivers/memory/Kconfig     |  7 +++++++
 drivers/memory/Makefile    |  1 +
 drivers/memory/atmel_ebi.c | 37 +++++++++++++++++++++++++++++++++++++
 4 files changed, 46 insertions(+)
 create mode 100644 drivers/memory/atmel_ebi.c

diff --git a/MAINTAINERS b/MAINTAINERS
index fa8c13fc7d..6d3f4c58a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -386,6 +386,7 @@ T:	git https://source.denx.de/u-boot/custodians/u-boot-atmel.git
 F:	arch/arm/mach-at91/
 F:	board/atmel/
 F:	drivers/cpu/at91_cpu.c
+F:	drivers/memory/atmel-ebi.c
 F:	drivers/misc/microchip_flexcom.c
 F:	drivers/timer/atmel_tcb_timer.c
 F:	include/dt-bindings/mfd/atmel-flexcom.h
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 7271892763..759151a452 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -24,4 +24,11 @@ config TI_AEMIF
 	  of 256M bytes of any of these memories can be accessed at a given
 	  time via four chip selects with 64M byte access per chip select.
 
+config ATMEL_EBI
+	bool "Support for Atmel EBI"
+	help
+	  Driver for Atmel EBI controller. This is a dummy
+	  driver. Doesn't provide an access to EBI controller. Select
+	  this option to enable the NAND flash controller driver
+
 endmenu
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index fec52efb60..1d24009e86 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -1,3 +1,4 @@
 
 obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
+obj-$(CONFIG_ATMEL_EBI) += atmel_ebi.o
 obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
diff --git a/drivers/memory/atmel_ebi.c b/drivers/memory/atmel_ebi.c
new file mode 100644
index 0000000000..4739eef1b7
--- /dev/null
+++ b/drivers/memory/atmel_ebi.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
+ */
+
+#include <dm/device.h>
+#include <dm/read.h>
+#include <dm/uclass.h>
+#include <fdtdec.h>
+
+static int atmel_ebi_probe(struct udevice *dev)
+{
+	int ret;
+	struct udevice *ndev;
+
+	ret = uclass_get_device_by_driver(UCLASS_MTD,
+					  DM_DRIVER_GET(atmel_nand_controller),
+					  &ndev);
+	if (ret)
+		printf("Failed to probe nand driver (err = %d)\n", ret);
+
+	return ret;
+}
+
+static const struct udevice_id atmel_ebi_match[] = {
+	{.compatible = "microchip,sam9x60-ebi"},
+	{.compatible = "atmel,sama5d3-ebi"},
+	{ /* Sentinel */ }
+};
+
+U_BOOT_DRIVER(atmel_ebi) = {
+	.name = "atmel_ebi",
+	.id = UCLASS_NOP,
+	.of_match = atmel_ebi_match,
+	.probe = atmel_ebi_probe,
+	.bind = dm_scan_fdt_dev,
+};
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
                   ` (3 preceding siblings ...)
  2022-08-29  6:19 ` [PATCH v2 4/9] memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-31 12:53   ` Michael Nazzareno Trimarchi
  2022-08-29  6:19 ` [PATCH v2 6/9] configs: at91: sam9x60ek: Enable DM based nand driver Balamanikandan Gunasundar
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

Add driver for atmel Static Memory Controller. Add helper functions to
configure SMC. This file is inherited from the work done by Boris
Brezillon for Linux

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 drivers/Kconfig                      |   2 +
 drivers/Makefile                     |   1 +
 drivers/mfd/Kconfig                  |   4 +
 drivers/mfd/Makefile                 |   1 +
 drivers/mfd/atmel-smc.c              | 364 +++++++++++++++++++++++++++
 include/linux/mfd/syscon/atmel-smc.h | 119 +++++++++
 6 files changed, 491 insertions(+)
 create mode 100644 drivers/mfd/Kconfig
 create mode 100644 drivers/mfd/Makefile
 create mode 100644 drivers/mfd/atmel-smc.c
 create mode 100644 include/linux/mfd/syscon/atmel-smc.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 8b6fead351..ffc06ed65e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -60,6 +60,8 @@ source "drivers/mailbox/Kconfig"
 
 source "drivers/memory/Kconfig"
 
+source "drivers/mfd/Kconfig"
+
 source "drivers/misc/Kconfig"
 
 source "drivers/mmc/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index eba9940231..5a8e80d4fe 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_QE) += qe/
 obj-$(CONFIG_U_QE) += qe/
 obj-y += mailbox/
 obj-y += memory/
+obj-y += mfd/
 obj-y += mtd/
 obj-y += pwm/
 obj-y += reset/
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
new file mode 100644
index 0000000000..ae53b02f27
--- /dev/null
+++ b/drivers/mfd/Kconfig
@@ -0,0 +1,4 @@
+config MFD_ATMEL_SMC
+       bool "Atmel Static Memory Controller driver"
+       help
+	Say yes here to support Atmel Static Memory Controller driver.
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
new file mode 100644
index 0000000000..4454815a98
--- /dev/null
+++ b/drivers/mfd/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c
new file mode 100644
index 0000000000..15296f71a1
--- /dev/null
+++ b/drivers/mfd/atmel-smc.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Atmel SMC (Static Memory Controller) helper functions.
+ *
+ * Copyright (C) 2022 Microchip Technology Inc.
+ * Copyright (C) 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ */
+
+#include <clk.h>
+#include <dm/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/mfd/syscon/atmel-smc.h>
+#include <linux/string.h>
+
+/**
+ * atmel_smc_cs_conf_init - initialize a SMC CS conf
+ * @conf: the SMC CS conf to initialize
+ *
+ * Set all fields to 0 so that one can start defining a new config.
+ */
+void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
+{
+	memset(conf, 0, sizeof(*conf));
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
+
+/**
+ * atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
+ *				 format expected by the SMC engine
+ * @ncycles: number of MCK clk cycles
+ * @msbpos: position of the MSB part of the timing field
+ * @msbwidth: width of the MSB part of the timing field
+ * @msbfactor: factor applied to the MSB
+ * @encodedval: param used to store the encoding result
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
+ * helper which called with different parameter depending on the encoding
+ * scheme.
+ *
+ * If the @ncycles value is too big to be encoded, -ERANGE is returned and
+ * the encodedval is contains the maximum val. Otherwise, 0 is returned.
+ */
+static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
+				       unsigned int msbpos,
+				       unsigned int msbwidth,
+				       unsigned int msbfactor,
+				       unsigned int *encodedval)
+{
+	unsigned int lsbmask = GENMASK(msbpos - 1, 0);
+	unsigned int msbmask = GENMASK(msbwidth - 1, 0);
+	unsigned int msb, lsb;
+	int ret = 0;
+
+	msb = ncycles / msbfactor;
+	lsb = ncycles % msbfactor;
+
+	if (lsb > lsbmask) {
+		lsb = 0;
+		msb++;
+	}
+
+	/*
+	 * Let's just put the maximum we can if the requested setting does
+	 * not fit in the register field.
+	 * We still return -ERANGE in case the caller cares.
+	 */
+	if (msb > msbmask) {
+		msb = msbmask;
+		lsb = lsbmask;
+		ret = -ERANGE;
+	}
+
+	*encodedval = (msb << msbpos) | lsb;
+
+	return ret;
+}
+
+/**
+ * atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
+ *				  specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the Txx field in the TIMINGS register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
+ *	     parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Timings Register"), and then stores the result in the
+ * @conf->timings field at @shift position.
+ *
+ * Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
+				 unsigned int shift, unsigned int ncycles)
+{
+	unsigned int val;
+	int ret;
+
+	if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
+	    shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
+	    shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
+	    shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
+	    shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
+		return -EINVAL;
+
+	/*
+	 * The formula described in atmel datasheets (section "HSMC Timings
+	 * Register"):
+	 *
+	 * ncycles = (Txx[3] * 64) + Txx[2:0]
+	 */
+	ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
+	conf->timings &= ~GENMASK(shift + 3, shift);
+	conf->timings |= val << shift;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
+
+/**
+ * atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
+ *				 specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the xx_SETUP field in the SETUP register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
+ *	     parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Setup Register"), and then stores the result in the
+ * @conf->setup field at @shift position.
+ *
+ * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
+				unsigned int shift, unsigned int ncycles)
+{
+	unsigned int val;
+	int ret;
+
+	if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
+	    shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
+		return -EINVAL;
+
+	/*
+	 * The formula described in atmel datasheets (section "SMC Setup
+	 * Register"):
+	 *
+	 * ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
+	 */
+	ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
+	conf->setup &= ~GENMASK(shift + 7, shift);
+	conf->setup |= val << shift;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
+
+/**
+ * atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
+ *				 specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the xx_PULSE field in the PULSE register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
+ *	     parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Pulse Register"), and then stores the result in the
+ * @conf->setup field at @shift position.
+ *
+ * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
+				unsigned int shift, unsigned int ncycles)
+{
+	unsigned int val;
+	int ret;
+
+	if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
+	    shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
+		return -EINVAL;
+
+	/*
+	 * The formula described in atmel datasheets (section "SMC Pulse
+	 * Register"):
+	 *
+	 * ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
+	 */
+	ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
+	conf->pulse &= ~GENMASK(shift + 7, shift);
+	conf->pulse |= val << shift;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
+
+/**
+ * atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
+ *				 specific value
+ * @conf: SMC CS conf descriptor
+ * @shift: the position of the xx_CYCLE field in the CYCLE register
+ * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
+ *	     parameter
+ *
+ * This function encodes the @ncycles value as described in the datasheet
+ * (section "SMC Cycle Register"), and then stores the result in the
+ * @conf->setup field at @shift position.
+ *
+ * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
+ * the field, and 0 otherwise.
+ */
+int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
+				unsigned int shift, unsigned int ncycles)
+{
+	unsigned int val;
+	int ret;
+
+	if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
+		return -EINVAL;
+
+	/*
+	 * The formula described in atmel datasheets (section "SMC Cycle
+	 * Register"):
+	 *
+	 * ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
+	 */
+	ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
+	conf->cycle &= ~GENMASK(shift + 15, shift);
+	conf->cycle |= val << shift;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
+
+/**
+ * atmel_smc_cs_conf_apply - apply an SMC CS conf
+ * @regmap: the SMC regmap
+ * @cs: the CS id
+ * @conf: the SMC CS conf to apply
+ *
+ * Applies an SMC CS configuration.
+ * Only valid on at91sam9/avr32 SoCs.
+ */
+void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
+			     const struct atmel_smc_cs_conf *conf)
+{
+	regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
+	regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
+	regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
+	regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
+
+/**
+ * atmel_hsmc_cs_conf_apply - apply an SMC CS conf
+ * @regmap: the HSMC regmap
+ * @cs: the CS id
+ * @layout: the layout of registers
+ * @conf: the SMC CS conf to apply
+ *
+ * Applies an SMC CS configuration.
+ * Only valid on post-sama5 SoCs.
+ */
+void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
+			      const struct atmel_hsmc_reg_layout *layout,
+			      int cs, const struct atmel_smc_cs_conf *conf)
+{
+	regmap_write(regmap, ATMEL_HSMC_SETUP(layout, cs), conf->setup);
+	regmap_write(regmap, ATMEL_HSMC_PULSE(layout, cs), conf->pulse);
+	regmap_write(regmap, ATMEL_HSMC_CYCLE(layout, cs), conf->cycle);
+	regmap_write(regmap, ATMEL_HSMC_TIMINGS(layout, cs), conf->timings);
+	regmap_write(regmap, ATMEL_HSMC_MODE(layout, cs), conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
+
+/**
+ * atmel_smc_cs_conf_get - retrieve the current SMC CS conf
+ * @regmap: the SMC regmap
+ * @cs: the CS id
+ * @conf: the SMC CS conf object to store the current conf
+ *
+ * Retrieve the SMC CS configuration.
+ * Only valid on at91sam9/avr32 SoCs.
+ */
+void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
+			   struct atmel_smc_cs_conf *conf)
+{
+	regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
+	regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
+	regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
+	regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
+
+/**
+ * atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
+ * @regmap: the HSMC regmap
+ * @cs: the CS id
+ * @layout: the layout of registers
+ * @conf: the SMC CS conf object to store the current conf
+ *
+ * Retrieve the SMC CS configuration.
+ * Only valid on post-sama5 SoCs.
+ */
+void atmel_hsmc_cs_conf_get(struct regmap *regmap,
+			    const struct atmel_hsmc_reg_layout *layout,
+			    int cs, struct atmel_smc_cs_conf *conf)
+{
+	regmap_read(regmap, ATMEL_HSMC_SETUP(layout, cs), &conf->setup);
+	regmap_read(regmap, ATMEL_HSMC_PULSE(layout, cs), &conf->pulse);
+	regmap_read(regmap, ATMEL_HSMC_CYCLE(layout, cs), &conf->cycle);
+	regmap_read(regmap, ATMEL_HSMC_TIMINGS(layout, cs), &conf->timings);
+	regmap_read(regmap, ATMEL_HSMC_MODE(layout, cs), &conf->mode);
+}
+EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
+
+static const struct atmel_hsmc_reg_layout sama5d3_reg_layout = {
+	.timing_regs_offset = 0x600,
+};
+
+static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = {
+	.timing_regs_offset = 0x700,
+};
+
+static const struct udevice_id atmel_smc_ids[] = {
+	{ .compatible = "atmel,at91sam9260-smc", .data = (ulong)0 },
+	{ .compatible = "atmel,sama5d3-smc", .data = (ulong)&sama5d3_reg_layout },
+	{ .compatible = "atmel,sama5d2-smc", .data = (ulong)&sama5d2_reg_layout },
+	{ /* sentinel */ },
+};
+
+/**
+ * atmel_hsmc_get_reg_layout - retrieve the layout of HSMC registers
+ * @np: the HSMC regmap
+ *
+ * Retrieve the layout of HSMC registers.
+ *
+ * Returns NULL in case of SMC, a struct atmel_hsmc_reg_layout pointer
+ * in HSMC case, otherwise ERR_PTR(-EINVAL).
+ */
+const struct atmel_hsmc_reg_layout *
+atmel_hsmc_get_reg_layout(ofnode np)
+{
+	int i;
+	const struct udevice_id *match;
+	const char *name;
+	int len;
+
+	name = ofnode_get_property(np, "compatible", &len);
+
+	for (i = 0; i < ARRAY_SIZE(atmel_smc_ids); i++) {
+		if (!strcmp(name, atmel_smc_ids[i].compatible)) {
+			match = &atmel_smc_ids[i];
+			break;
+		}
+	}
+
+	return match ? (struct atmel_hsmc_reg_layout *)match->data : ERR_PTR(-EINVAL);
+}
diff --git a/include/linux/mfd/syscon/atmel-smc.h b/include/linux/mfd/syscon/atmel-smc.h
new file mode 100644
index 0000000000..74be5a199f
--- /dev/null
+++ b/include/linux/mfd/syscon/atmel-smc.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Atmel SMC (Static Memory Controller) register offsets and bit definitions.
+ *
+ * Copyright (C) 2014 Atmel
+ * Copyright (C) 2014 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ */
+
+#ifndef _LINUX_MFD_SYSCON_ATMEL_SMC_H_
+#define _LINUX_MFD_SYSCON_ATMEL_SMC_H_
+
+#include <linux/kernel.h>
+#include <dm/ofnode.h>
+#include <regmap.h>
+
+#define ATMEL_SMC_SETUP(cs)			(((cs) * 0x10))
+#define ATMEL_HSMC_SETUP(layout, cs)		\
+	((layout)->timing_regs_offset + ((cs) * 0x14))
+#define ATMEL_SMC_PULSE(cs)			(((cs) * 0x10) + 0x4)
+#define ATMEL_HSMC_PULSE(layout, cs)		\
+	((layout)->timing_regs_offset + ((cs) * 0x14) + 0x4)
+#define ATMEL_SMC_CYCLE(cs)			(((cs) * 0x10) + 0x8)
+#define ATMEL_HSMC_CYCLE(layout, cs)			\
+	((layout)->timing_regs_offset + ((cs) * 0x14) + 0x8)
+#define ATMEL_SMC_NWE_SHIFT			0
+#define ATMEL_SMC_NCS_WR_SHIFT			8
+#define ATMEL_SMC_NRD_SHIFT			16
+#define ATMEL_SMC_NCS_RD_SHIFT			24
+
+#define ATMEL_SMC_MODE(cs)			(((cs) * 0x10) + 0xc)
+#define ATMEL_HSMC_MODE(layout, cs)			\
+	((layout)->timing_regs_offset + ((cs) * 0x14) + 0x10)
+#define ATMEL_SMC_MODE_READMODE_MASK		BIT(0)
+#define ATMEL_SMC_MODE_READMODE_NCS		(0 << 0)
+#define ATMEL_SMC_MODE_READMODE_NRD		(1 << 0)
+#define ATMEL_SMC_MODE_WRITEMODE_MASK		BIT(1)
+#define ATMEL_SMC_MODE_WRITEMODE_NCS		(0 << 1)
+#define ATMEL_SMC_MODE_WRITEMODE_NWE		(1 << 1)
+#define ATMEL_SMC_MODE_EXNWMODE_MASK		GENMASK(5, 4)
+#define ATMEL_SMC_MODE_EXNWMODE_DISABLE		(0 << 4)
+#define ATMEL_SMC_MODE_EXNWMODE_FROZEN		(2 << 4)
+#define ATMEL_SMC_MODE_EXNWMODE_READY		(3 << 4)
+#define ATMEL_SMC_MODE_BAT_MASK			BIT(8)
+#define ATMEL_SMC_MODE_BAT_SELECT		(0 << 8)
+#define ATMEL_SMC_MODE_BAT_WRITE		(1 << 8)
+#define ATMEL_SMC_MODE_DBW_MASK			GENMASK(13, 12)
+#define ATMEL_SMC_MODE_DBW_8			(0 << 12)
+#define ATMEL_SMC_MODE_DBW_16			(1 << 12)
+#define ATMEL_SMC_MODE_DBW_32			(2 << 12)
+#define ATMEL_SMC_MODE_TDF_MASK			GENMASK(19, 16)
+#define ATMEL_SMC_MODE_TDF(x)			(((x) - 1) << 16)
+#define ATMEL_SMC_MODE_TDF_MAX			16
+#define ATMEL_SMC_MODE_TDF_MIN			1
+#define ATMEL_SMC_MODE_TDFMODE_OPTIMIZED	BIT(20)
+#define ATMEL_SMC_MODE_PMEN			BIT(24)
+#define ATMEL_SMC_MODE_PS_MASK			GENMASK(29, 28)
+#define ATMEL_SMC_MODE_PS_4			(0 << 28)
+#define ATMEL_SMC_MODE_PS_8			(1 << 28)
+#define ATMEL_SMC_MODE_PS_16			(2 << 28)
+#define ATMEL_SMC_MODE_PS_32			(3 << 28)
+
+#define ATMEL_HSMC_TIMINGS(layout, cs)			\
+	((layout)->timing_regs_offset + ((cs) * 0x14) + 0xc)
+#define ATMEL_HSMC_TIMINGS_OCMS			BIT(12)
+#define ATMEL_HSMC_TIMINGS_RBNSEL(x)		((x) << 28)
+#define ATMEL_HSMC_TIMINGS_NFSEL		BIT(31)
+#define ATMEL_HSMC_TIMINGS_TCLR_SHIFT		0
+#define ATMEL_HSMC_TIMINGS_TADL_SHIFT		4
+#define ATMEL_HSMC_TIMINGS_TAR_SHIFT		8
+#define ATMEL_HSMC_TIMINGS_TRR_SHIFT		16
+#define ATMEL_HSMC_TIMINGS_TWB_SHIFT		24
+
+struct atmel_hsmc_reg_layout {
+	unsigned int timing_regs_offset;
+};
+
+/**
+ * struct atmel_smc_cs_conf - SMC CS config as described in the datasheet.
+ * @setup: NCS/NWE/NRD setup timings (not applicable to at91rm9200)
+ * @pulse: NCS/NWE/NRD pulse timings (not applicable to at91rm9200)
+ * @cycle: NWE/NRD cycle timings (not applicable to at91rm9200)
+ * @timings: advanced NAND related timings (only applicable to HSMC)
+ * @mode: all kind of config parameters (see the fields definition above).
+ *	  The mode fields are different on at91rm9200
+ */
+struct atmel_smc_cs_conf {
+	u32 setup;
+	u32 pulse;
+	u32 cycle;
+	u32 timings;
+	u32 mode;
+};
+
+void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf);
+int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
+				 unsigned int shift,
+				 unsigned int ncycles);
+int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
+				unsigned int shift, unsigned int ncycles);
+int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
+				unsigned int shift, unsigned int ncycles);
+int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
+				unsigned int shift, unsigned int ncycles);
+void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
+			     const struct atmel_smc_cs_conf *conf);
+void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
+			      const struct atmel_hsmc_reg_layout *reglayout,
+			      int cs, const struct atmel_smc_cs_conf *conf);
+void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
+			   struct atmel_smc_cs_conf *conf);
+void atmel_hsmc_cs_conf_get(struct regmap *regmap,
+			    const struct atmel_hsmc_reg_layout *reglayout,
+			    int cs, struct atmel_smc_cs_conf *conf);
+const struct atmel_hsmc_reg_layout *
+atmel_hsmc_get_reg_layout(ofnode np);
+
+#endif /* _LINUX_MFD_SYSCON_ATMEL_SMC_H_ */
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 6/9] configs: at91: sam9x60ek: Enable DM based nand driver
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
                   ` (4 preceding siblings ...)
  2022-08-29  6:19 ` [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 7/9] ARM: dts: at91: sam9x60: Add nodes for EBI and NAND Balamanikandan Gunasundar
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

Enable Device model supported NAND driver and remove legacy Atmel NAND
driver.

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 configs/sam9x60ek_mmc_defconfig       | 9 ++++++---
 configs/sam9x60ek_nandflash_defconfig | 9 ++++++---
 configs/sam9x60ek_qspiflash_defconfig | 8 +++++---
 3 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/configs/sam9x60ek_mmc_defconfig b/configs/sam9x60ek_mmc_defconfig
index c50aa6b5c5..d989681eb6 100644
--- a/configs/sam9x60ek_mmc_defconfig
+++ b/configs/sam9x60ek_mmc_defconfig
@@ -53,6 +53,8 @@ CONFIG_ENV_IS_IN_FAT=y
 CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"
 CONFIG_SYS_RELOC_GD_ENV_ADDR=y
 CONFIG_DM=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
 CONFIG_CLK=y
 CONFIG_CLK_CCF=y
 CONFIG_CLK_AT91=y
@@ -62,15 +64,16 @@ CONFIG_CPU=y
 CONFIG_AT91_GPIO=y
 CONFIG_DM_I2C=y
 CONFIG_SYS_I2C_AT91=y
+CONFIG_ATMEL_EBI=y
+CONFIG_MFD_ATMEL_SMC=y
 CONFIG_I2C_EEPROM=y
 CONFIG_MICROCHIP_FLEXCOM=y
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_ATMEL=y
 CONFIG_MTD=y
+CONFIG_DM_MTD=y
 CONFIG_MTD_RAW_NAND=y
-CONFIG_NAND_ATMEL=y
-CONFIG_ATMEL_NAND_HW_PMECC=y
-CONFIG_PMECC_CAP=8
+CONFIG_DM_NAND_ATMEL=y
 CONFIG_SYS_NAND_ONFI_DETECTION=y
 CONFIG_DM_SPI_FLASH=y
 CONFIG_SF_DEFAULT_SPEED=50000000
diff --git a/configs/sam9x60ek_nandflash_defconfig b/configs/sam9x60ek_nandflash_defconfig
index d8d2383fed..6bc78a73e2 100644
--- a/configs/sam9x60ek_nandflash_defconfig
+++ b/configs/sam9x60ek_nandflash_defconfig
@@ -55,6 +55,8 @@ CONFIG_ENV_IS_IN_NAND=y
 CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
 CONFIG_SYS_RELOC_GD_ENV_ADDR=y
 CONFIG_DM=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
 CONFIG_CLK=y
 CONFIG_CLK_CCF=y
 CONFIG_CLK_AT91=y
@@ -64,14 +66,15 @@ CONFIG_CPU=y
 CONFIG_AT91_GPIO=y
 CONFIG_DM_I2C=y
 CONFIG_SYS_I2C_AT91=y
+CONFIG_ATMEL_EBI=y
+CONFIG_MFD_ATMEL_SMC=y
 CONFIG_I2C_EEPROM=y
 CONFIG_MICROCHIP_FLEXCOM=y
 CONFIG_GENERIC_ATMEL_MCI=y
 CONFIG_MTD=y
+CONFIG_DM_MTD=y
 # CONFIG_SYS_NAND_USE_FLASH_BBT is not set
-CONFIG_NAND_ATMEL=y
-CONFIG_ATMEL_NAND_HW_PMECC=y
-CONFIG_PMECC_CAP=8
+CONFIG_DM_NAND_ATMEL=y
 CONFIG_SYS_NAND_ONFI_DETECTION=y
 CONFIG_DM_SPI_FLASH=y
 CONFIG_SF_DEFAULT_SPEED=50000000
diff --git a/configs/sam9x60ek_qspiflash_defconfig b/configs/sam9x60ek_qspiflash_defconfig
index fc4108cdc4..c17722ab35 100644
--- a/configs/sam9x60ek_qspiflash_defconfig
+++ b/configs/sam9x60ek_qspiflash_defconfig
@@ -55,6 +55,8 @@ CONFIG_ENV_IS_IN_SPI_FLASH=y
 CONFIG_ENV_SPI_MAX_HZ=50000000
 CONFIG_SYS_RELOC_GD_ENV_ADDR=y
 CONFIG_DM=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
 CONFIG_CLK=y
 CONFIG_CLK_CCF=y
 CONFIG_CLK_AT91=y
@@ -64,6 +66,8 @@ CONFIG_CPU=y
 CONFIG_AT91_GPIO=y
 CONFIG_DM_I2C=y
 CONFIG_SYS_I2C_AT91=y
+CONFIG_ATMEL_EBI=y
+CONFIG_MFD_ATMEL_SMC=y
 CONFIG_I2C_EEPROM=y
 CONFIG_MICROCHIP_FLEXCOM=y
 CONFIG_GENERIC_ATMEL_MCI=y
@@ -71,9 +75,7 @@ CONFIG_MTD=y
 CONFIG_DM_MTD=y
 CONFIG_MTD_RAW_NAND=y
 # CONFIG_SYS_NAND_USE_FLASH_BBT is not set
-CONFIG_NAND_ATMEL=y
-CONFIG_ATMEL_NAND_HW_PMECC=y
-CONFIG_PMECC_CAP=8
+CONFIG_DM_NAND_ATMEL=y
 CONFIG_SYS_NAND_ONFI_DETECTION=y
 CONFIG_DM_SPI_FLASH=y
 CONFIG_SPI_FLASH_MACRONIX=y
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 7/9] ARM: dts: at91: sam9x60: Add nodes for EBI and NAND
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
                   ` (5 preceding siblings ...)
  2022-08-29  6:19 ` [PATCH v2 6/9] configs: at91: sam9x60ek: Enable DM based nand driver Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-29  6:19 ` [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support Balamanikandan Gunasundar
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

Add new bindings for EBI and NAND controller

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 arch/arm/dts/sam9x60.dtsi | 42 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/arch/arm/dts/sam9x60.dtsi b/arch/arm/dts/sam9x60.dtsi
index a5c429eb3a..17224ef771 100644
--- a/arch/arm/dts/sam9x60.dtsi
+++ b/arch/arm/dts/sam9x60.dtsi
@@ -69,6 +69,32 @@
 		#size-cells = <1>;
 		ranges;
 
+		ebi: ebi@10000000 {
+			compatible = "microchip,sam9x60-ebi";
+			#address-cells = <2>;
+			#size-cells = <1>;
+			atmel,smc = <&smc>;
+			microchip,sfr = <&sfr>;
+			reg = <0x10000000 0x60000000>;
+			ranges = <0x0 0x0 0x10000000 0x10000000
+				  0x1 0x0 0x20000000 0x10000000
+				  0x2 0x0 0x30000000 0x10000000
+				  0x3 0x0 0x40000000 0x10000000
+				  0x4 0x0 0x50000000 0x10000000
+				  0x5 0x0 0x60000000 0x10000000>;
+			clocks = <&pmc PMC_TYPE_CORE 11>;
+			status = "disabled";
+
+			nand_controller: nand-controller {
+				compatible = "microchip,sam9x60-nand-controller";
+				ecc-engine = <&pmecc>;
+				#address-cells = <2>;
+				#size-cells = <1>;
+				ranges;
+				status = "disabled";
+			};
+		};
+
 		sdhci0: sdhci-host@80000000 {
 			compatible = "microchip,sam9x60-sdhci";
 			reg = <0x80000000 0x300>;
@@ -119,6 +145,11 @@
 				status = "disabled";
 			};
 
+			sfr: sfr@f8050000 {
+				compatible = "microchip,sam9x60-sfr", "syscon";
+				reg = <0xf8050000 0x100>;
+			};
+
 			dbgu: serial@fffff200 {
 				compatible = "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart";
 				reg = <0xfffff200 0x200>;
@@ -182,6 +213,17 @@
 				};
 			};
 
+			pmecc: ecc-engine@ffffe000 {
+				compatible = "microchip,sam9x60-pmecc", "atmel,at91sam9g45-pmecc";
+				reg = <0xffffe000 0x300>,
+				      <0xffffe600 0x100>;
+			};
+
+			smc: smc@ffffea00 {
+				compatible = "microchip,sam9x60-smc", "atmel,at91sam9260-smc", "syscon";
+				reg = <0xffffea00 0x100>;
+			};
+
 			pioA: gpio@fffff400 {
 				compatible = "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio";
 				reg = <0xfffff400 0x200>;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
                   ` (6 preceding siblings ...)
  2022-08-29  6:19 ` [PATCH v2 7/9] ARM: dts: at91: sam9x60: Add nodes for EBI and NAND Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-31 13:14   ` Michael Nazzareno Trimarchi
  2022-08-29  6:19 ` [PATCH v2 9/9] board: sam9x60ek: remove nand init from board file Balamanikandan Gunasundar
  2022-08-29  6:41 ` [PATCH v2 0/9] Add DM support for atmel NAND driver Michael Nazzareno Trimarchi
  9 siblings, 1 reply; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

Enable the EBI and NAND flash controller. Define the pinctrl and
partition table

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 arch/arm/dts/sam9x60ek.dts | 103 +++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/arch/arm/dts/sam9x60ek.dts b/arch/arm/dts/sam9x60ek.dts
index 54c694bd78..6cb81dd90f 100644
--- a/arch/arm/dts/sam9x60ek.dts
+++ b/arch/arm/dts/sam9x60ek.dts
@@ -80,6 +80,44 @@
 			};
 
 			pinctrl {
+					nand {
+						pinctrl_nand_oe_we: nand-oe-we-0 {
+							atmel,pins =
+								<AT91_PIOD 0 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 1 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
+						};
+
+						pinctrl_nand_rb: nand-rb-0 {
+							atmel,pins =
+								<AT91_PIOD 5 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
+						};
+
+						pinctrl_nand_cs: nand-cs-0 {
+							atmel,pins =
+								<AT91_PIOD 4 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
+						};
+					};
+
+					ebi {
+						pinctrl_ebi_data_0_7: ebi-data-lsb-0 {
+							atmel,pins =
+								<AT91_PIOD 6 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 7 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 8 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 9 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 10 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 11 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 12 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 13 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
+						};
+
+						pinctrl_ebi_addr_nand: ebi-addr-0 {
+							atmel,pins =
+								<AT91_PIOD 2 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
+								 AT91_PIOD 3 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
+						};
+					};
+
 					pinctrl_qspi: qspi {
 						atmel,pins =
 							<AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
@@ -106,6 +144,71 @@
 	};
 };
 
+&ebi {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_ebi_addr_nand &pinctrl_ebi_data_0_7>;
+	status = "okay";
+
+	nand_controller: nand-controller {
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_nand_oe_we &pinctrl_nand_cs &pinctrl_nand_rb>;
+		status = "okay";
+
+		nand@3 {
+			reg = <0x3 0x0 0x800000>;
+			rb-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
+			cs-gpios = <&pioD 4 GPIO_ACTIVE_HIGH>;
+			nand-bus-width = <8>;
+			nand-ecc-mode = "hw";
+			nand-ecc-strength = <8>;
+			nand-ecc-step-size = <512>;
+			nand-on-flash-bbt;
+			label = "atmel_nand";
+
+			partitions {
+				compatible = "fixed-partitions";
+				#address-cells = <1>;
+				#size-cells = <1>;
+
+				at91bootstrap@0 {
+					label = "at91bootstrap";
+					reg = <0x0 0x40000>;
+				};
+
+				uboot@40000 {
+					label = "u-boot";
+					reg = <0x40000 0xc0000>;
+				};
+
+				ubootenvred@100000 {
+					label = "U-Boot Env Redundant";
+					reg = <0x100000 0x40000>;
+				};
+
+				ubootenv@140000 {
+					label = "U-Boot Env";
+					reg = <0x140000 0x40000>;
+				};
+
+				dtb@180000 {
+					label = "device tree";
+					reg = <0x180000 0x80000>;
+				};
+
+				kernel@200000 {
+					label = "kernel";
+					reg = <0x200000 0x600000>;
+				};
+
+				rootfs@800000 {
+					label = "rootfs";
+					reg = <0x800000 0x1f800000>;
+				};
+			};
+		};
+	};
+};
+
 &macb0 {
 	phy-mode = "rmii";
 	status = "okay";
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 9/9] board: sam9x60ek: remove nand init from board file
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
                   ` (7 preceding siblings ...)
  2022-08-29  6:19 ` [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support Balamanikandan Gunasundar
@ 2022-08-29  6:19 ` Balamanikandan Gunasundar
  2022-08-29  6:41 ` [PATCH v2 0/9] Add DM support for atmel NAND driver Michael Nazzareno Trimarchi
  9 siblings, 0 replies; 24+ messages in thread
From: Balamanikandan Gunasundar @ 2022-08-29  6:19 UTC (permalink / raw)
  To: u-boot
  Cc: sandeep.sheriker, eugen.hristev, dario.binacchi, michael,
	mihai.sain, sergiu.moga, balamanikandan.gunasundar,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

Move this out of board file as this is done by the DM based NAND flash
driver. The EBI chip select configuration, iomux and timings are
handled by the driver

Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
---
 board/atmel/sam9x60ek/sam9x60ek.c | 59 -------------------------------
 include/configs/sam9x60ek.h       |  5 ---
 2 files changed, 64 deletions(-)

diff --git a/board/atmel/sam9x60ek/sam9x60ek.c b/board/atmel/sam9x60ek/sam9x60ek.c
index 7035fab878..d8d2c3a18f 100644
--- a/board/atmel/sam9x60ek/sam9x60ek.c
+++ b/board/atmel/sam9x60ek/sam9x60ek.c
@@ -24,62 +24,6 @@ DECLARE_GLOBAL_DATA_PTR;
 
 void at91_prepare_cpu_var(void);
 
-#ifdef CONFIG_CMD_NAND
-static void sam9x60ek_nand_hw_init(void)
-{
-	struct at91_smc *smc = (struct at91_smc *)ATMEL_BASE_SMC;
-	struct atmel_sfr *sfr = (struct atmel_sfr *)ATMEL_BASE_SFR;
-	unsigned int csa;
-
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 0, 1);	/* NAND OE */
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 1, 1);	/* NAND WE */
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 2, 0);	/* NAND ALE */
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 3, 0);	/* NAND CLE */
-	/* Enable NandFlash */
-	at91_set_gpio_output(CONFIG_SYS_NAND_ENABLE_PIN, 1);
-	/* Configure RDY/BSY */
-	at91_set_gpio_input(CONFIG_SYS_NAND_READY_PIN, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 6, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 7, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 8, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 9, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 10, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 11, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 12, 1);
-	at91_pio3_set_a_periph(AT91_PIO_PORTD, 13, 1);
-
-	at91_periph_clk_enable(ATMEL_ID_PIOD);
-
-	/* Enable CS3 */
-	csa = readl(&sfr->ebicsa);
-	csa |= AT91_SFR_CCFG_EBI_CSA(3, 1) | AT91_SFR_CCFG_NFD0_ON_D16;
-
-	/* Configure IO drive */
-	csa &= ~AT91_SFR_CCFG_EBI_DRIVE_SAM9X60;
-
-	writel(csa, &sfr->ebicsa);
-
-	/* Configure SMC CS3 for NAND/SmartMedia */
-	writel(AT91_SMC_SETUP_NWE(4), &smc->cs[3].setup);
-
-	writel(AT91_SMC_PULSE_NWE(10) | AT91_SMC_PULSE_NCS_WR(20) |
-	       AT91_SMC_PULSE_NRD(10) | AT91_SMC_PULSE_NCS_RD(20),
-	       &smc->cs[3].pulse);
-
-	writel(AT91_SMC_CYCLE_NWE(20) | AT91_SMC_CYCLE_NRD(20),
-	       &smc->cs[3].cycle);
-
-	writel(AT91_SMC_MODE_RM_NRD | AT91_SMC_MODE_WM_NWE |
-#ifdef CONFIG_SYS_NAND_DBW_16
-	       AT91_SMC_MODE_DBW_16 |
-#else /* CONFIG_SYS_NAND_DBW_8 */
-	       AT91_SMC_MODE_DBW_8 |
-#endif
-	       AT91_SMC_MODE_TDF | AT91_SMC_MODE_TDF_CYCLE(15),
-	       &smc->cs[3].mode);
-}
-#endif
-
 #ifdef CONFIG_BOARD_LATE_INIT
 int board_late_init(void)
 {
@@ -122,9 +66,6 @@ int board_init(void)
 	/* address of boot parameters */
 	gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
 
-#ifdef CONFIG_CMD_NAND
-	sam9x60ek_nand_hw_init();
-#endif
 	return 0;
 }
 
diff --git a/include/configs/sam9x60ek.h b/include/configs/sam9x60ek.h
index b9b56d9f1a..72c089512f 100644
--- a/include/configs/sam9x60ek.h
+++ b/include/configs/sam9x60ek.h
@@ -29,11 +29,6 @@
 /* NAND flash */
 #ifdef CONFIG_CMD_NAND
 #define CONFIG_SYS_MAX_NAND_DEVICE	1
-#define CONFIG_SYS_NAND_BASE		0x40000000
-#define CONFIG_SYS_NAND_MASK_ALE	BIT(21)
-#define CONFIG_SYS_NAND_MASK_CLE	BIT(22)
-#define CONFIG_SYS_NAND_ENABLE_PIN	AT91_PIN_PD4
-#define CONFIG_SYS_NAND_READY_PIN	AT91_PIN_PD5
 #endif
 
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 0/9] Add DM support for atmel NAND driver
  2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
                   ` (8 preceding siblings ...)
  2022-08-29  6:19 ` [PATCH v2 9/9] board: sam9x60ek: remove nand init from board file Balamanikandan Gunasundar
@ 2022-08-29  6:41 ` Michael Nazzareno Trimarchi
  2022-08-30  2:30   ` Simon Glass
  9 siblings, 1 reply; 24+ messages in thread
From: Michael Nazzareno Trimarchi @ 2022-08-29  6:41 UTC (permalink / raw)
  To: Balamanikandan Gunasundar
  Cc: u-boot, sandeep.sheriker, eugen.hristev, dario.binacchi,
	mihai.sain, sergiu.moga, claudiu.beznea, hs, tudor.ambarus, sjg,
	ascull, kettenis, jh80.chung, peng.fan, ye.li, hari.prasathge

Hi

On Mon, Aug 29, 2022 at 8:19 AM Balamanikandan Gunasundar
<balamanikandan.gunasundar@microchip.com> wrote:
>
> Change include:
>
> - Adapt GPIO descriptor apis for U-Boot. Use
>   gpio_request_by_name_nodev, dm_gpio_get_value etc.
> - Use U_BOOT_DRIVER instead of platform_driver.
> - Replace struct platform_device with struct udevice
> - Check the status of nfc exec operation by polling the status
>   register instead of interrupt based handling
> - DMA operations not supported. Remove it
> - Adapt DT parsing to U-Boot APIs
>
> v2:
>
> - Add linux reference version in the commit message from which the
>   driver is ported from
> - Reword the commit message to describe the changes clearly
>

Thank you to repost it again, I will review it and try to include soon

Michael

> Balamanikandan Gunasundar (9):
>   nand: atmel: Add DM based NAND driver
>   nand: atmel: Add pmecc driver
>   mfd: syscon: Add atmel-matrix registers definition
>   memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver
>   mfd: syscon: atmel-smc: Add driver for atmel SMC
>   configs: at91: sam9x60ek: Enable DM based nand driver
>   ARM: dts: at91: sam9x60: Add nodes for EBI and NAND
>   ARM: dts: at91: sam9x60ek: Enable NAND support
>   board: sam9x60ek: remove nand init from board file
>
>  MAINTAINERS                                  |    1 +
>  arch/arm/dts/sam9x60.dtsi                    |   42 +
>  arch/arm/dts/sam9x60ek.dts                   |  103 +
>  board/atmel/sam9x60ek/sam9x60ek.c            |   59 -
>  configs/sam9x60ek_mmc_defconfig              |    9 +-
>  configs/sam9x60ek_nandflash_defconfig        |    9 +-
>  configs/sam9x60ek_qspiflash_defconfig        |    8 +-
>  drivers/Kconfig                              |    2 +
>  drivers/Makefile                             |    1 +
>  drivers/memory/Kconfig                       |    7 +
>  drivers/memory/Makefile                      |    1 +
>  drivers/memory/atmel_ebi.c                   |   37 +
>  drivers/mfd/Kconfig                          |    4 +
>  drivers/mfd/Makefile                         |    1 +
>  drivers/mfd/atmel-smc.c                      |  364 +++
>  drivers/mtd/nand/raw/Kconfig                 |    8 +
>  drivers/mtd/nand/raw/Makefile                |    1 +
>  drivers/mtd/nand/raw/atmel/Makefile          |    5 +
>  drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
>  drivers/mtd/nand/raw/atmel/pmecc.c           |  965 ++++++++
>  drivers/mtd/nand/raw/atmel/pmecc.h           |   94 +
>  include/configs/sam9x60ek.h                  |    5 -
>  include/linux/mfd/syscon/atmel-matrix.h      |  112 +
>  include/linux/mfd/syscon/atmel-smc.h         |  119 +
>  24 files changed, 4177 insertions(+), 73 deletions(-)
>  create mode 100644 drivers/memory/atmel_ebi.c
>  create mode 100644 drivers/mfd/Kconfig
>  create mode 100644 drivers/mfd/Makefile
>  create mode 100644 drivers/mfd/atmel-smc.c
>  create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
>  create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c
>  create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c
>  create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h
>  create mode 100644 include/linux/mfd/syscon/atmel-matrix.h
>  create mode 100644 include/linux/mfd/syscon/atmel-smc.h
>
> --
> 2.34.1
>


-- 
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael@amarulasolutions.com
__________________________________

Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info@amarulasolutions.com
www.amarulasolutions.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 0/9] Add DM support for atmel NAND driver
  2022-08-29  6:41 ` [PATCH v2 0/9] Add DM support for atmel NAND driver Michael Nazzareno Trimarchi
@ 2022-08-30  2:30   ` Simon Glass
  2022-08-31  6:34     ` Eugen.Hristev
  0 siblings, 1 reply; 24+ messages in thread
From: Simon Glass @ 2022-08-30  2:30 UTC (permalink / raw)
  To: Michael Nazzareno Trimarchi
  Cc: Balamanikandan Gunasundar, U-Boot Mailing List,
	Sandeep Sheriker M, Eugen Hristev, Dario Binacchi, mihai.sain,
	sergiu.moga, Claudiu Beznea, Heiko Schocher, tudor.ambarus,
	Andrew Scull, Mark Kettenis, Jaehoon Chung, Peng Fan, Ye Li,
	hari.prasathge

Hi,

On Mon, 29 Aug 2022 at 00:41, Michael Nazzareno Trimarchi
<michael@amarulasolutions.com> wrote:
>
> Hi
>
> On Mon, Aug 29, 2022 at 8:19 AM Balamanikandan Gunasundar
> <balamanikandan.gunasundar@microchip.com> wrote:
> >
> > Change include:
> >
> > - Adapt GPIO descriptor apis for U-Boot. Use
> >   gpio_request_by_name_nodev, dm_gpio_get_value etc.
> > - Use U_BOOT_DRIVER instead of platform_driver.
> > - Replace struct platform_device with struct udevice
> > - Check the status of nfc exec operation by polling the status
> >   register instead of interrupt based handling
> > - DMA operations not supported. Remove it
> > - Adapt DT parsing to U-Boot APIs
> >
> > v2:
> >
> > - Add linux reference version in the commit message from which the
> >   driver is ported from
> > - Reword the commit message to describe the changes clearly
> >
>
> Thank you to repost it again, I will review it and try to include soon
>
> Michael
>
> > Balamanikandan Gunasundar (9):
> >   nand: atmel: Add DM based NAND driver
> >   nand: atmel: Add pmecc driver
> >   mfd: syscon: Add atmel-matrix registers definition
> >   memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver
> >   mfd: syscon: atmel-smc: Add driver for atmel SMC
> >   configs: at91: sam9x60ek: Enable DM based nand driver
> >   ARM: dts: at91: sam9x60: Add nodes for EBI and NAND
> >   ARM: dts: at91: sam9x60ek: Enable NAND support
> >   board: sam9x60ek: remove nand init from board file
> >
> >  MAINTAINERS                                  |    1 +
> >  arch/arm/dts/sam9x60.dtsi                    |   42 +
> >  arch/arm/dts/sam9x60ek.dts                   |  103 +
> >  board/atmel/sam9x60ek/sam9x60ek.c            |   59 -
> >  configs/sam9x60ek_mmc_defconfig              |    9 +-
> >  configs/sam9x60ek_nandflash_defconfig        |    9 +-
> >  configs/sam9x60ek_qspiflash_defconfig        |    8 +-
> >  drivers/Kconfig                              |    2 +
> >  drivers/Makefile                             |    1 +
> >  drivers/memory/Kconfig                       |    7 +
> >  drivers/memory/Makefile                      |    1 +
> >  drivers/memory/atmel_ebi.c                   |   37 +
> >  drivers/mfd/Kconfig                          |    4 +
> >  drivers/mfd/Makefile                         |    1 +
> >  drivers/mfd/atmel-smc.c                      |  364 +++
> >  drivers/mtd/nand/raw/Kconfig                 |    8 +
> >  drivers/mtd/nand/raw/Makefile                |    1 +
> >  drivers/mtd/nand/raw/atmel/Makefile          |    5 +
> >  drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
> >  drivers/mtd/nand/raw/atmel/pmecc.c           |  965 ++++++++
> >  drivers/mtd/nand/raw/atmel/pmecc.h           |   94 +
> >  include/configs/sam9x60ek.h                  |    5 -
> >  include/linux/mfd/syscon/atmel-matrix.h      |  112 +
> >  include/linux/mfd/syscon/atmel-smc.h         |  119 +
> >  24 files changed, 4177 insertions(+), 73 deletions(-)
> >  create mode 100644 drivers/memory/atmel_ebi.c
> >  create mode 100644 drivers/mfd/Kconfig
> >  create mode 100644 drivers/mfd/Makefile
> >  create mode 100644 drivers/mfd/atmel-smc.c
> >  create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
> >  create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c
> >  create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c
> >  create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h
> >  create mode 100644 include/linux/mfd/syscon/atmel-matrix.h
> >  create mode 100644 include/linux/mfd/syscon/atmel-smc.h
> >
> > --
> > 2.34.1
> >

Once this series is in, I wonder if it might be possible to drop the
nand_init() call and have this happen as needed, i.e. lazy init?

Regards,
Simon

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 0/9] Add DM support for atmel NAND driver
  2022-08-30  2:30   ` Simon Glass
@ 2022-08-31  6:34     ` Eugen.Hristev
  2022-08-31  6:37       ` Michael Nazzareno Trimarchi
  2022-08-31 13:46       ` Simon Glass
  0 siblings, 2 replies; 24+ messages in thread
From: Eugen.Hristev @ 2022-08-31  6:34 UTC (permalink / raw)
  To: sjg, michael
  Cc: Balamanikandan.Gunasundar, u-boot, Sandeep.Sheriker,
	dario.binacchi, Mihai.Sain, Sergiu.Moga, Claudiu.Beznea, hs,
	Tudor.Ambarus, ascull, kettenis, jh80.chung, peng.fan, ye.li,
	Hari.PrasathGE

On 8/30/22 5:30 AM, Simon Glass wrote:
> Hi,
> 
> On Mon, 29 Aug 2022 at 00:41, Michael Nazzareno Trimarchi
> <michael@amarulasolutions.com> wrote:
>>
>> Hi
>>
>> On Mon, Aug 29, 2022 at 8:19 AM Balamanikandan Gunasundar
>> <balamanikandan.gunasundar@microchip.com> wrote:
>>>
>>> Change include:
>>>
>>> - Adapt GPIO descriptor apis for U-Boot. Use
>>>    gpio_request_by_name_nodev, dm_gpio_get_value etc.
>>> - Use U_BOOT_DRIVER instead of platform_driver.
>>> - Replace struct platform_device with struct udevice
>>> - Check the status of nfc exec operation by polling the status
>>>    register instead of interrupt based handling
>>> - DMA operations not supported. Remove it
>>> - Adapt DT parsing to U-Boot APIs
>>>
>>> v2:
>>>
>>> - Add linux reference version in the commit message from which the
>>>    driver is ported from
>>> - Reword the commit message to describe the changes clearly
>>>
>>
>> Thank you to repost it again, I will review it and try to include soon
>>
>> Michael
>>
>>> Balamanikandan Gunasundar (9):
>>>    nand: atmel: Add DM based NAND driver
>>>    nand: atmel: Add pmecc driver
>>>    mfd: syscon: Add atmel-matrix registers definition
>>>    memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver
>>>    mfd: syscon: atmel-smc: Add driver for atmel SMC
>>>    configs: at91: sam9x60ek: Enable DM based nand driver
>>>    ARM: dts: at91: sam9x60: Add nodes for EBI and NAND
>>>    ARM: dts: at91: sam9x60ek: Enable NAND support
>>>    board: sam9x60ek: remove nand init from board file
>>>
>>>   MAINTAINERS                                  |    1 +
>>>   arch/arm/dts/sam9x60.dtsi                    |   42 +
>>>   arch/arm/dts/sam9x60ek.dts                   |  103 +
>>>   board/atmel/sam9x60ek/sam9x60ek.c            |   59 -
>>>   configs/sam9x60ek_mmc_defconfig              |    9 +-
>>>   configs/sam9x60ek_nandflash_defconfig        |    9 +-
>>>   configs/sam9x60ek_qspiflash_defconfig        |    8 +-
>>>   drivers/Kconfig                              |    2 +
>>>   drivers/Makefile                             |    1 +
>>>   drivers/memory/Kconfig                       |    7 +
>>>   drivers/memory/Makefile                      |    1 +
>>>   drivers/memory/atmel_ebi.c                   |   37 +
>>>   drivers/mfd/Kconfig                          |    4 +
>>>   drivers/mfd/Makefile                         |    1 +
>>>   drivers/mfd/atmel-smc.c                      |  364 +++
>>>   drivers/mtd/nand/raw/Kconfig                 |    8 +
>>>   drivers/mtd/nand/raw/Makefile                |    1 +
>>>   drivers/mtd/nand/raw/atmel/Makefile          |    5 +
>>>   drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
>>>   drivers/mtd/nand/raw/atmel/pmecc.c           |  965 ++++++++
>>>   drivers/mtd/nand/raw/atmel/pmecc.h           |   94 +
>>>   include/configs/sam9x60ek.h                  |    5 -
>>>   include/linux/mfd/syscon/atmel-matrix.h      |  112 +
>>>   include/linux/mfd/syscon/atmel-smc.h         |  119 +
>>>   24 files changed, 4177 insertions(+), 73 deletions(-)
>>>   create mode 100644 drivers/memory/atmel_ebi.c
>>>   create mode 100644 drivers/mfd/Kconfig
>>>   create mode 100644 drivers/mfd/Makefile
>>>   create mode 100644 drivers/mfd/atmel-smc.c
>>>   create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
>>>   create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c
>>>   create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c
>>>   create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h
>>>   create mode 100644 include/linux/mfd/syscon/atmel-matrix.h
>>>   create mode 100644 include/linux/mfd/syscon/atmel-smc.h
>>>
>>> --
>>> 2.34.1
>>>
> 
> Once this series is in, I wonder if it might be possible to drop the
> nand_init() call and have this happen as needed, i.e. lazy init?
> 
> Regards,
> Simon
> 

Hi Simon,

nand_init() should be called if nand is accessed. Do you have a specific 
place in mind where this call is not appropriate, and should be removed ?

Hi Michael,

If you want to review this series, please do, I would like this to fit 
into the next merge window for 2023.01 , but if it needs more time, 
that's fine.

Eugen

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 0/9] Add DM support for atmel NAND driver
  2022-08-31  6:34     ` Eugen.Hristev
@ 2022-08-31  6:37       ` Michael Nazzareno Trimarchi
  2022-08-31 13:46       ` Simon Glass
  1 sibling, 0 replies; 24+ messages in thread
From: Michael Nazzareno Trimarchi @ 2022-08-31  6:37 UTC (permalink / raw)
  To: Eugen.Hristev
  Cc: sjg, Balamanikandan.Gunasundar, u-boot, Sandeep.Sheriker,
	dario.binacchi, Mihai.Sain, Sergiu.Moga, Claudiu.Beznea, hs,
	Tudor.Ambarus, ascull, kettenis, jh80.chung, peng.fan, ye.li,
	Hari.PrasathGE

Hi

On Wed, Aug 31, 2022 at 8:34 AM <Eugen.Hristev@microchip.com> wrote:
>
> On 8/30/22 5:30 AM, Simon Glass wrote:
> > Hi,
> >
> > On Mon, 29 Aug 2022 at 00:41, Michael Nazzareno Trimarchi
> > <michael@amarulasolutions.com> wrote:
> >>
> >> Hi
> >>
> >> On Mon, Aug 29, 2022 at 8:19 AM Balamanikandan Gunasundar
> >> <balamanikandan.gunasundar@microchip.com> wrote:
> >>>
> >>> Change include:
> >>>
> >>> - Adapt GPIO descriptor apis for U-Boot. Use
> >>>    gpio_request_by_name_nodev, dm_gpio_get_value etc.
> >>> - Use U_BOOT_DRIVER instead of platform_driver.
> >>> - Replace struct platform_device with struct udevice
> >>> - Check the status of nfc exec operation by polling the status
> >>>    register instead of interrupt based handling
> >>> - DMA operations not supported. Remove it
> >>> - Adapt DT parsing to U-Boot APIs
> >>>
> >>> v2:
> >>>
> >>> - Add linux reference version in the commit message from which the
> >>>    driver is ported from
> >>> - Reword the commit message to describe the changes clearly
> >>>
> >>
> >> Thank you to repost it again, I will review it and try to include soon
> >>
> >> Michael
> >>
> >>> Balamanikandan Gunasundar (9):
> >>>    nand: atmel: Add DM based NAND driver
> >>>    nand: atmel: Add pmecc driver
> >>>    mfd: syscon: Add atmel-matrix registers definition
> >>>    memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver
> >>>    mfd: syscon: atmel-smc: Add driver for atmel SMC
> >>>    configs: at91: sam9x60ek: Enable DM based nand driver
> >>>    ARM: dts: at91: sam9x60: Add nodes for EBI and NAND
> >>>    ARM: dts: at91: sam9x60ek: Enable NAND support
> >>>    board: sam9x60ek: remove nand init from board file
> >>>
> >>>   MAINTAINERS                                  |    1 +
> >>>   arch/arm/dts/sam9x60.dtsi                    |   42 +
> >>>   arch/arm/dts/sam9x60ek.dts                   |  103 +
> >>>   board/atmel/sam9x60ek/sam9x60ek.c            |   59 -
> >>>   configs/sam9x60ek_mmc_defconfig              |    9 +-
> >>>   configs/sam9x60ek_nandflash_defconfig        |    9 +-
> >>>   configs/sam9x60ek_qspiflash_defconfig        |    8 +-
> >>>   drivers/Kconfig                              |    2 +
> >>>   drivers/Makefile                             |    1 +
> >>>   drivers/memory/Kconfig                       |    7 +
> >>>   drivers/memory/Makefile                      |    1 +
> >>>   drivers/memory/atmel_ebi.c                   |   37 +
> >>>   drivers/mfd/Kconfig                          |    4 +
> >>>   drivers/mfd/Makefile                         |    1 +
> >>>   drivers/mfd/atmel-smc.c                      |  364 +++
> >>>   drivers/mtd/nand/raw/Kconfig                 |    8 +
> >>>   drivers/mtd/nand/raw/Makefile                |    1 +
> >>>   drivers/mtd/nand/raw/atmel/Makefile          |    5 +
> >>>   drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
> >>>   drivers/mtd/nand/raw/atmel/pmecc.c           |  965 ++++++++
> >>>   drivers/mtd/nand/raw/atmel/pmecc.h           |   94 +
> >>>   include/configs/sam9x60ek.h                  |    5 -
> >>>   include/linux/mfd/syscon/atmel-matrix.h      |  112 +
> >>>   include/linux/mfd/syscon/atmel-smc.h         |  119 +
> >>>   24 files changed, 4177 insertions(+), 73 deletions(-)
> >>>   create mode 100644 drivers/memory/atmel_ebi.c
> >>>   create mode 100644 drivers/mfd/Kconfig
> >>>   create mode 100644 drivers/mfd/Makefile
> >>>   create mode 100644 drivers/mfd/atmel-smc.c
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h
> >>>   create mode 100644 include/linux/mfd/syscon/atmel-matrix.h
> >>>   create mode 100644 include/linux/mfd/syscon/atmel-smc.h
> >>>
> >>> --
> >>> 2.34.1
> >>>
> >
> > Once this series is in, I wonder if it might be possible to drop the
> > nand_init() call and have this happen as needed, i.e. lazy init?
> >
> > Regards,
> > Simon
> >
>
> Hi Simon,
>
> nand_init() should be called if nand is accessed. Do you have a specific
> place in mind where this call is not appropriate, and should be removed ?
>

Those are different topics, I would like to keep them split. Anyway I think that
nand should work as mmc for initialization. I put a note to make it happen.

> Hi Michael,
>
> If you want to review this series, please do, I would like this to fit
> into the next merge window for 2023.01 , but if it needs more time,
> that's fine.

Yes, I will look at it tomorrow

Michael

>
> Eugen



-- 
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael@amarulasolutions.com
__________________________________

Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info@amarulasolutions.com
www.amarulasolutions.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC
  2022-08-29  6:19 ` [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC Balamanikandan Gunasundar
@ 2022-08-31 12:53   ` Michael Nazzareno Trimarchi
  2022-09-30 11:02     ` Michael Nazzareno Trimarchi
  0 siblings, 1 reply; 24+ messages in thread
From: Michael Nazzareno Trimarchi @ 2022-08-31 12:53 UTC (permalink / raw)
  To: Balamanikandan Gunasundar
  Cc: u-boot, sandeep.sheriker, eugen.hristev, dario.binacchi,
	mihai.sain, sergiu.moga, claudiu.beznea, hs, tudor.ambarus, sjg,
	ascull, kettenis, jh80.chung, peng.fan, ye.li, hari.prasathge

Hi

On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
<balamanikandan.gunasundar@microchip.com> wrote:
>
> Add driver for atmel Static Memory Controller. Add helper functions to
> configure SMC. This file is inherited from the work done by Boris
> Brezillon for Linux
>

This does not look like a driver.Those are helper functions. Now I
have no objections
to put it here but it will be the first one under mfd.

I think linux commit message subject give more an idea on what exactly it is

mfd: syscon: atmel-smc: Add new helpers to ease SMC regs manipulation

Michael


> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
> ---
>  drivers/Kconfig                      |   2 +
>  drivers/Makefile                     |   1 +
>  drivers/mfd/Kconfig                  |   4 +
>  drivers/mfd/Makefile                 |   1 +
>  drivers/mfd/atmel-smc.c              | 364 +++++++++++++++++++++++++++
>  include/linux/mfd/syscon/atmel-smc.h | 119 +++++++++
>  6 files changed, 491 insertions(+)
>  create mode 100644 drivers/mfd/Kconfig
>  create mode 100644 drivers/mfd/Makefile
>  create mode 100644 drivers/mfd/atmel-smc.c
>  create mode 100644 include/linux/mfd/syscon/atmel-smc.h
>
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 8b6fead351..ffc06ed65e 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -60,6 +60,8 @@ source "drivers/mailbox/Kconfig"
>
>  source "drivers/memory/Kconfig"
>
> +source "drivers/mfd/Kconfig"
> +
>  source "drivers/misc/Kconfig"
>
>  source "drivers/mmc/Kconfig"
> diff --git a/drivers/Makefile b/drivers/Makefile
> index eba9940231..5a8e80d4fe 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -101,6 +101,7 @@ obj-$(CONFIG_QE) += qe/
>  obj-$(CONFIG_U_QE) += qe/
>  obj-y += mailbox/
>  obj-y += memory/
> +obj-y += mfd/
>  obj-y += mtd/
>  obj-y += pwm/
>  obj-y += reset/
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> new file mode 100644
> index 0000000000..ae53b02f27
> --- /dev/null
> +++ b/drivers/mfd/Kconfig
> @@ -0,0 +1,4 @@
> +config MFD_ATMEL_SMC
> +       bool "Atmel Static Memory Controller driver"
> +       help
> +       Say yes here to support Atmel Static Memory Controller driver.
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> new file mode 100644
> index 0000000000..4454815a98
> --- /dev/null
> +++ b/drivers/mfd/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
> diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c
> new file mode 100644
> index 0000000000..15296f71a1
> --- /dev/null
> +++ b/drivers/mfd/atmel-smc.c
> @@ -0,0 +1,364 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Atmel SMC (Static Memory Controller) helper functions.
> + *
> + * Copyright (C) 2022 Microchip Technology Inc.
> + * Copyright (C) 2017 Free Electrons
> + *
> + * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
> + */
> +
> +#include <clk.h>
> +#include <dm/device.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/mfd/syscon/atmel-smc.h>
> +#include <linux/string.h>
> +
> +/**
> + * atmel_smc_cs_conf_init - initialize a SMC CS conf
> + * @conf: the SMC CS conf to initialize
> + *
> + * Set all fields to 0 so that one can start defining a new config.
> + */
> +void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
> +{
> +       memset(conf, 0, sizeof(*conf));
> +}
> +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
> +
> +/**
> + * atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
> + *                              format expected by the SMC engine
> + * @ncycles: number of MCK clk cycles
> + * @msbpos: position of the MSB part of the timing field
> + * @msbwidth: width of the MSB part of the timing field
> + * @msbfactor: factor applied to the MSB
> + * @encodedval: param used to store the encoding result
> + *
> + * This function encodes the @ncycles value as described in the datasheet
> + * (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
> + * helper which called with different parameter depending on the encoding
> + * scheme.
> + *
> + * If the @ncycles value is too big to be encoded, -ERANGE is returned and
> + * the encodedval is contains the maximum val. Otherwise, 0 is returned.
> + */
> +static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
> +                                      unsigned int msbpos,
> +                                      unsigned int msbwidth,
> +                                      unsigned int msbfactor,
> +                                      unsigned int *encodedval)
> +{
> +       unsigned int lsbmask = GENMASK(msbpos - 1, 0);
> +       unsigned int msbmask = GENMASK(msbwidth - 1, 0);
> +       unsigned int msb, lsb;
> +       int ret = 0;
> +
> +       msb = ncycles / msbfactor;
> +       lsb = ncycles % msbfactor;
> +
> +       if (lsb > lsbmask) {
> +               lsb = 0;
> +               msb++;
> +       }
> +
> +       /*
> +        * Let's just put the maximum we can if the requested setting does
> +        * not fit in the register field.
> +        * We still return -ERANGE in case the caller cares.
> +        */
> +       if (msb > msbmask) {
> +               msb = msbmask;
> +               lsb = lsbmask;
> +               ret = -ERANGE;
> +       }
> +
> +       *encodedval = (msb << msbpos) | lsb;
> +
> +       return ret;
> +}
> +
> +/**
> + * atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
> + *                               specific value
> + * @conf: SMC CS conf descriptor
> + * @shift: the position of the Txx field in the TIMINGS register
> + * @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
> + *          parameter
> + *
> + * This function encodes the @ncycles value as described in the datasheet
> + * (section "SMC Timings Register"), and then stores the result in the
> + * @conf->timings field at @shift position.
> + *
> + * Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
> + * the field, and 0 otherwise.
> + */
> +int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
> +                                unsigned int shift, unsigned int ncycles)
> +{
> +       unsigned int val;
> +       int ret;
> +
> +       if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
> +           shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
> +           shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
> +           shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
> +           shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
> +               return -EINVAL;
> +
> +       /*
> +        * The formula described in atmel datasheets (section "HSMC Timings
> +        * Register"):
> +        *
> +        * ncycles = (Txx[3] * 64) + Txx[2:0]
> +        */
> +       ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
> +       conf->timings &= ~GENMASK(shift + 3, shift);
> +       conf->timings |= val << shift;
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
> +
> +/**
> + * atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
> + *                              specific value
> + * @conf: SMC CS conf descriptor
> + * @shift: the position of the xx_SETUP field in the SETUP register
> + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
> + *          parameter
> + *
> + * This function encodes the @ncycles value as described in the datasheet
> + * (section "SMC Setup Register"), and then stores the result in the
> + * @conf->setup field at @shift position.
> + *
> + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
> + * the field, and 0 otherwise.
> + */
> +int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
> +                               unsigned int shift, unsigned int ncycles)
> +{
> +       unsigned int val;
> +       int ret;
> +
> +       if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
> +           shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
> +               return -EINVAL;
> +
> +       /*
> +        * The formula described in atmel datasheets (section "SMC Setup
> +        * Register"):
> +        *
> +        * ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
> +        */
> +       ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
> +       conf->setup &= ~GENMASK(shift + 7, shift);
> +       conf->setup |= val << shift;
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
> +
> +/**
> + * atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
> + *                              specific value
> + * @conf: SMC CS conf descriptor
> + * @shift: the position of the xx_PULSE field in the PULSE register
> + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
> + *          parameter
> + *
> + * This function encodes the @ncycles value as described in the datasheet
> + * (section "SMC Pulse Register"), and then stores the result in the
> + * @conf->setup field at @shift position.
> + *
> + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
> + * the field, and 0 otherwise.
> + */
> +int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
> +                               unsigned int shift, unsigned int ncycles)
> +{
> +       unsigned int val;
> +       int ret;
> +
> +       if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
> +           shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
> +               return -EINVAL;
> +
> +       /*
> +        * The formula described in atmel datasheets (section "SMC Pulse
> +        * Register"):
> +        *
> +        * ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
> +        */
> +       ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
> +       conf->pulse &= ~GENMASK(shift + 7, shift);
> +       conf->pulse |= val << shift;
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
> +
> +/**
> + * atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
> + *                              specific value
> + * @conf: SMC CS conf descriptor
> + * @shift: the position of the xx_CYCLE field in the CYCLE register
> + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
> + *          parameter
> + *
> + * This function encodes the @ncycles value as described in the datasheet
> + * (section "SMC Cycle Register"), and then stores the result in the
> + * @conf->setup field at @shift position.
> + *
> + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
> + * the field, and 0 otherwise.
> + */
> +int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
> +                               unsigned int shift, unsigned int ncycles)
> +{
> +       unsigned int val;
> +       int ret;
> +
> +       if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
> +               return -EINVAL;
> +
> +       /*
> +        * The formula described in atmel datasheets (section "SMC Cycle
> +        * Register"):
> +        *
> +        * ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
> +        */
> +       ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
> +       conf->cycle &= ~GENMASK(shift + 15, shift);
> +       conf->cycle |= val << shift;
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
> +
> +/**
> + * atmel_smc_cs_conf_apply - apply an SMC CS conf
> + * @regmap: the SMC regmap
> + * @cs: the CS id
> + * @conf: the SMC CS conf to apply
> + *
> + * Applies an SMC CS configuration.
> + * Only valid on at91sam9/avr32 SoCs.
> + */
> +void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
> +                            const struct atmel_smc_cs_conf *conf)
> +{
> +       regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
> +       regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
> +       regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
> +       regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
> +}
> +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
> +
> +/**
> + * atmel_hsmc_cs_conf_apply - apply an SMC CS conf
> + * @regmap: the HSMC regmap
> + * @cs: the CS id
> + * @layout: the layout of registers
> + * @conf: the SMC CS conf to apply
> + *
> + * Applies an SMC CS configuration.
> + * Only valid on post-sama5 SoCs.
> + */
> +void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
> +                             const struct atmel_hsmc_reg_layout *layout,
> +                             int cs, const struct atmel_smc_cs_conf *conf)
> +{
> +       regmap_write(regmap, ATMEL_HSMC_SETUP(layout, cs), conf->setup);
> +       regmap_write(regmap, ATMEL_HSMC_PULSE(layout, cs), conf->pulse);
> +       regmap_write(regmap, ATMEL_HSMC_CYCLE(layout, cs), conf->cycle);
> +       regmap_write(regmap, ATMEL_HSMC_TIMINGS(layout, cs), conf->timings);
> +       regmap_write(regmap, ATMEL_HSMC_MODE(layout, cs), conf->mode);
> +}
> +EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
> +
> +/**
> + * atmel_smc_cs_conf_get - retrieve the current SMC CS conf
> + * @regmap: the SMC regmap
> + * @cs: the CS id
> + * @conf: the SMC CS conf object to store the current conf
> + *
> + * Retrieve the SMC CS configuration.
> + * Only valid on at91sam9/avr32 SoCs.
> + */
> +void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
> +                          struct atmel_smc_cs_conf *conf)
> +{
> +       regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
> +       regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
> +       regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
> +       regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
> +}
> +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
> +
> +/**
> + * atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
> + * @regmap: the HSMC regmap
> + * @cs: the CS id
> + * @layout: the layout of registers
> + * @conf: the SMC CS conf object to store the current conf
> + *
> + * Retrieve the SMC CS configuration.
> + * Only valid on post-sama5 SoCs.
> + */
> +void atmel_hsmc_cs_conf_get(struct regmap *regmap,
> +                           const struct atmel_hsmc_reg_layout *layout,
> +                           int cs, struct atmel_smc_cs_conf *conf)
> +{
> +       regmap_read(regmap, ATMEL_HSMC_SETUP(layout, cs), &conf->setup);
> +       regmap_read(regmap, ATMEL_HSMC_PULSE(layout, cs), &conf->pulse);
> +       regmap_read(regmap, ATMEL_HSMC_CYCLE(layout, cs), &conf->cycle);
> +       regmap_read(regmap, ATMEL_HSMC_TIMINGS(layout, cs), &conf->timings);
> +       regmap_read(regmap, ATMEL_HSMC_MODE(layout, cs), &conf->mode);
> +}
> +EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
> +
> +static const struct atmel_hsmc_reg_layout sama5d3_reg_layout = {
> +       .timing_regs_offset = 0x600,
> +};
> +
> +static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = {
> +       .timing_regs_offset = 0x700,
> +};
> +
> +static const struct udevice_id atmel_smc_ids[] = {
> +       { .compatible = "atmel,at91sam9260-smc", .data = (ulong)0 },
> +       { .compatible = "atmel,sama5d3-smc", .data = (ulong)&sama5d3_reg_layout },
> +       { .compatible = "atmel,sama5d2-smc", .data = (ulong)&sama5d2_reg_layout },
> +       { /* sentinel */ },
> +};
> +
> +/**
> + * atmel_hsmc_get_reg_layout - retrieve the layout of HSMC registers
> + * @np: the HSMC regmap
> + *
> + * Retrieve the layout of HSMC registers.
> + *
> + * Returns NULL in case of SMC, a struct atmel_hsmc_reg_layout pointer
> + * in HSMC case, otherwise ERR_PTR(-EINVAL).
> + */
> +const struct atmel_hsmc_reg_layout *
> +atmel_hsmc_get_reg_layout(ofnode np)
> +{
> +       int i;
> +       const struct udevice_id *match;
> +       const char *name;
> +       int len;
> +
> +       name = ofnode_get_property(np, "compatible", &len);
> +
> +       for (i = 0; i < ARRAY_SIZE(atmel_smc_ids); i++) {
> +               if (!strcmp(name, atmel_smc_ids[i].compatible)) {
> +                       match = &atmel_smc_ids[i];
> +                       break;
> +               }
> +       }
> +
> +       return match ? (struct atmel_hsmc_reg_layout *)match->data : ERR_PTR(-EINVAL);
> +}
> diff --git a/include/linux/mfd/syscon/atmel-smc.h b/include/linux/mfd/syscon/atmel-smc.h
> new file mode 100644
> index 0000000000..74be5a199f
> --- /dev/null
> +++ b/include/linux/mfd/syscon/atmel-smc.h
> @@ -0,0 +1,119 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Atmel SMC (Static Memory Controller) register offsets and bit definitions.
> + *
> + * Copyright (C) 2014 Atmel
> + * Copyright (C) 2014 Free Electrons
> + *
> + * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
> + */
> +
> +#ifndef _LINUX_MFD_SYSCON_ATMEL_SMC_H_
> +#define _LINUX_MFD_SYSCON_ATMEL_SMC_H_
> +
> +#include <linux/kernel.h>
> +#include <dm/ofnode.h>
> +#include <regmap.h>
> +
> +#define ATMEL_SMC_SETUP(cs)                    (((cs) * 0x10))
> +#define ATMEL_HSMC_SETUP(layout, cs)           \
> +       ((layout)->timing_regs_offset + ((cs) * 0x14))
> +#define ATMEL_SMC_PULSE(cs)                    (((cs) * 0x10) + 0x4)
> +#define ATMEL_HSMC_PULSE(layout, cs)           \
> +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0x4)
> +#define ATMEL_SMC_CYCLE(cs)                    (((cs) * 0x10) + 0x8)
> +#define ATMEL_HSMC_CYCLE(layout, cs)                   \
> +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0x8)
> +#define ATMEL_SMC_NWE_SHIFT                    0
> +#define ATMEL_SMC_NCS_WR_SHIFT                 8
> +#define ATMEL_SMC_NRD_SHIFT                    16
> +#define ATMEL_SMC_NCS_RD_SHIFT                 24
> +
> +#define ATMEL_SMC_MODE(cs)                     (((cs) * 0x10) + 0xc)
> +#define ATMEL_HSMC_MODE(layout, cs)                    \
> +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0x10)
> +#define ATMEL_SMC_MODE_READMODE_MASK           BIT(0)
> +#define ATMEL_SMC_MODE_READMODE_NCS            (0 << 0)
> +#define ATMEL_SMC_MODE_READMODE_NRD            (1 << 0)
> +#define ATMEL_SMC_MODE_WRITEMODE_MASK          BIT(1)
> +#define ATMEL_SMC_MODE_WRITEMODE_NCS           (0 << 1)
> +#define ATMEL_SMC_MODE_WRITEMODE_NWE           (1 << 1)
> +#define ATMEL_SMC_MODE_EXNWMODE_MASK           GENMASK(5, 4)
> +#define ATMEL_SMC_MODE_EXNWMODE_DISABLE                (0 << 4)
> +#define ATMEL_SMC_MODE_EXNWMODE_FROZEN         (2 << 4)
> +#define ATMEL_SMC_MODE_EXNWMODE_READY          (3 << 4)
> +#define ATMEL_SMC_MODE_BAT_MASK                        BIT(8)
> +#define ATMEL_SMC_MODE_BAT_SELECT              (0 << 8)
> +#define ATMEL_SMC_MODE_BAT_WRITE               (1 << 8)
> +#define ATMEL_SMC_MODE_DBW_MASK                        GENMASK(13, 12)
> +#define ATMEL_SMC_MODE_DBW_8                   (0 << 12)
> +#define ATMEL_SMC_MODE_DBW_16                  (1 << 12)
> +#define ATMEL_SMC_MODE_DBW_32                  (2 << 12)
> +#define ATMEL_SMC_MODE_TDF_MASK                        GENMASK(19, 16)
> +#define ATMEL_SMC_MODE_TDF(x)                  (((x) - 1) << 16)
> +#define ATMEL_SMC_MODE_TDF_MAX                 16
> +#define ATMEL_SMC_MODE_TDF_MIN                 1
> +#define ATMEL_SMC_MODE_TDFMODE_OPTIMIZED       BIT(20)
> +#define ATMEL_SMC_MODE_PMEN                    BIT(24)
> +#define ATMEL_SMC_MODE_PS_MASK                 GENMASK(29, 28)
> +#define ATMEL_SMC_MODE_PS_4                    (0 << 28)
> +#define ATMEL_SMC_MODE_PS_8                    (1 << 28)
> +#define ATMEL_SMC_MODE_PS_16                   (2 << 28)
> +#define ATMEL_SMC_MODE_PS_32                   (3 << 28)
> +
> +#define ATMEL_HSMC_TIMINGS(layout, cs)                 \
> +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0xc)
> +#define ATMEL_HSMC_TIMINGS_OCMS                        BIT(12)
> +#define ATMEL_HSMC_TIMINGS_RBNSEL(x)           ((x) << 28)
> +#define ATMEL_HSMC_TIMINGS_NFSEL               BIT(31)
> +#define ATMEL_HSMC_TIMINGS_TCLR_SHIFT          0
> +#define ATMEL_HSMC_TIMINGS_TADL_SHIFT          4
> +#define ATMEL_HSMC_TIMINGS_TAR_SHIFT           8
> +#define ATMEL_HSMC_TIMINGS_TRR_SHIFT           16
> +#define ATMEL_HSMC_TIMINGS_TWB_SHIFT           24
> +
> +struct atmel_hsmc_reg_layout {
> +       unsigned int timing_regs_offset;
> +};
> +
> +/**
> + * struct atmel_smc_cs_conf - SMC CS config as described in the datasheet.
> + * @setup: NCS/NWE/NRD setup timings (not applicable to at91rm9200)
> + * @pulse: NCS/NWE/NRD pulse timings (not applicable to at91rm9200)
> + * @cycle: NWE/NRD cycle timings (not applicable to at91rm9200)
> + * @timings: advanced NAND related timings (only applicable to HSMC)
> + * @mode: all kind of config parameters (see the fields definition above).
> + *       The mode fields are different on at91rm9200
> + */
> +struct atmel_smc_cs_conf {
> +       u32 setup;
> +       u32 pulse;
> +       u32 cycle;
> +       u32 timings;
> +       u32 mode;
> +};
> +
> +void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf);
> +int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
> +                                unsigned int shift,
> +                                unsigned int ncycles);
> +int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
> +                               unsigned int shift, unsigned int ncycles);
> +int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
> +                               unsigned int shift, unsigned int ncycles);
> +int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
> +                               unsigned int shift, unsigned int ncycles);
> +void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
> +                            const struct atmel_smc_cs_conf *conf);
> +void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
> +                             const struct atmel_hsmc_reg_layout *reglayout,
> +                             int cs, const struct atmel_smc_cs_conf *conf);
> +void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
> +                          struct atmel_smc_cs_conf *conf);
> +void atmel_hsmc_cs_conf_get(struct regmap *regmap,
> +                           const struct atmel_hsmc_reg_layout *reglayout,
> +                           int cs, struct atmel_smc_cs_conf *conf);
> +const struct atmel_hsmc_reg_layout *
> +atmel_hsmc_get_reg_layout(ofnode np);
> +
> +#endif /* _LINUX_MFD_SYSCON_ATMEL_SMC_H_ */
> --
> 2.34.1
>


-- 
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael@amarulasolutions.com
__________________________________

Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info@amarulasolutions.com
www.amarulasolutions.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support
  2022-08-29  6:19 ` [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support Balamanikandan Gunasundar
@ 2022-08-31 13:14   ` Michael Nazzareno Trimarchi
  2022-08-31 13:31     ` Eugen.Hristev
  0 siblings, 1 reply; 24+ messages in thread
From: Michael Nazzareno Trimarchi @ 2022-08-31 13:14 UTC (permalink / raw)
  To: Balamanikandan Gunasundar
  Cc: u-boot, sandeep.sheriker, eugen.hristev, dario.binacchi,
	mihai.sain, sergiu.moga, claudiu.beznea, hs, tudor.ambarus, sjg,
	ascull, kettenis, jh80.chung, peng.fan, ye.li, hari.prasathge

Hi

On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
<balamanikandan.gunasundar@microchip.com> wrote:
>
> Enable the EBI and NAND flash controller. Define the pinctrl and
> partition table
>
> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
> ---
>  arch/arm/dts/sam9x60ek.dts | 103 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 103 insertions(+)
>
> diff --git a/arch/arm/dts/sam9x60ek.dts b/arch/arm/dts/sam9x60ek.dts
> index 54c694bd78..6cb81dd90f 100644
> --- a/arch/arm/dts/sam9x60ek.dts
> +++ b/arch/arm/dts/sam9x60ek.dts
> @@ -80,6 +80,44 @@
>                         };
>
>                         pinctrl {
> +                                       nand {

I can see two tabs here. You don't need it. The indentation go far on the right

> +                                               pinctrl_nand_oe_we: nand-oe-we-0 {
> +                                                       atmel,pins =
> +                                                               <AT91_PIOD 0 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 1 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> +                                               };
> +
> +                                               pinctrl_nand_rb: nand-rb-0 {
> +                                                       atmel,pins =
> +                                                               <AT91_PIOD 5 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
> +                                               };
> +
> +                                               pinctrl_nand_cs: nand-cs-0 {
> +                                                       atmel,pins =
> +                                                               <AT91_PIOD 4 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
> +                                               };
> +                                       };
> +
> +                                       ebi {
> +                                               pinctrl_ebi_data_0_7: ebi-data-lsb-0 {
> +                                                       atmel,pins =
> +                                                               <AT91_PIOD 6 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 7 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 8 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 9 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 10 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 11 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 12 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 13 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> +                                               };
> +
> +                                               pinctrl_ebi_addr_nand: ebi-addr-0 {
> +                                                       atmel,pins =
> +                                                               <AT91_PIOD 2 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> +                                                                AT91_PIOD 3 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> +                                               };
> +                                       };
> +

Please remove the ebi and nand block so you have one tab less. Then I
suggest to align to linux dts and refer to pinctrl instead to create
another level of indentation

>                                         pinctrl_qspi: qspi {
>                                                 atmel,pins =
>                                                         <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
This part can be tab back

Michael

> @@ -106,6 +144,71 @@
>         };
>  };
>
> +&ebi {
> +       pinctrl-names = "default";
> +       pinctrl-0 = <&pinctrl_ebi_addr_nand &pinctrl_ebi_data_0_7>;
> +       status = "okay";
> +
> +       nand_controller: nand-controller {
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&pinctrl_nand_oe_we &pinctrl_nand_cs &pinctrl_nand_rb>;
> +               status = "okay";
> +
> +               nand@3 {
> +                       reg = <0x3 0x0 0x800000>;
> +                       rb-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
> +                       cs-gpios = <&pioD 4 GPIO_ACTIVE_HIGH>;
> +                       nand-bus-width = <8>;
> +                       nand-ecc-mode = "hw";
> +                       nand-ecc-strength = <8>;
> +                       nand-ecc-step-size = <512>;
> +                       nand-on-flash-bbt;
> +                       label = "atmel_nand";
> +
> +                       partitions {
> +                               compatible = "fixed-partitions";
> +                               #address-cells = <1>;
> +                               #size-cells = <1>;
> +
> +                               at91bootstrap@0 {
> +                                       label = "at91bootstrap";
> +                                       reg = <0x0 0x40000>;
> +                               };
> +
> +                               uboot@40000 {
> +                                       label = "u-boot";
> +                                       reg = <0x40000 0xc0000>;
> +                               };
> +
> +                               ubootenvred@100000 {
> +                                       label = "U-Boot Env Redundant";
> +                                       reg = <0x100000 0x40000>;
> +                               };
> +
> +                               ubootenv@140000 {
> +                                       label = "U-Boot Env";
> +                                       reg = <0x140000 0x40000>;
> +                               };
> +
> +                               dtb@180000 {
> +                                       label = "device tree";
> +                                       reg = <0x180000 0x80000>;
> +                               };
> +
> +                               kernel@200000 {
> +                                       label = "kernel";
> +                                       reg = <0x200000 0x600000>;
> +                               };
> +
> +                               rootfs@800000 {
> +                                       label = "rootfs";
> +                                       reg = <0x800000 0x1f800000>;
> +                               };
> +                       };
> +               };
> +       };
> +};
> +
>  &macb0 {
>         phy-mode = "rmii";
>         status = "okay";
> --
> 2.34.1
>


-- 
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael@amarulasolutions.com
__________________________________

Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info@amarulasolutions.com
www.amarulasolutions.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support
  2022-08-31 13:14   ` Michael Nazzareno Trimarchi
@ 2022-08-31 13:31     ` Eugen.Hristev
  2022-08-31 14:19       ` Michael Nazzareno Trimarchi
  0 siblings, 1 reply; 24+ messages in thread
From: Eugen.Hristev @ 2022-08-31 13:31 UTC (permalink / raw)
  To: michael, Balamanikandan.Gunasundar
  Cc: u-boot, Sandeep.Sheriker, dario.binacchi, Mihai.Sain,
	Sergiu.Moga, Claudiu.Beznea, hs, Tudor.Ambarus, sjg, ascull,
	kettenis, jh80.chung, peng.fan, ye.li, Hari.PrasathGE

On 8/31/22 4:14 PM, Michael Nazzareno Trimarchi wrote:
> Hi
> 
> On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
> <balamanikandan.gunasundar@microchip.com> wrote:
>>
>> Enable the EBI and NAND flash controller. Define the pinctrl and
>> partition table
>>
>> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
>> ---
>>   arch/arm/dts/sam9x60ek.dts | 103 +++++++++++++++++++++++++++++++++++++
>>   1 file changed, 103 insertions(+)
>>
>> diff --git a/arch/arm/dts/sam9x60ek.dts b/arch/arm/dts/sam9x60ek.dts
>> index 54c694bd78..6cb81dd90f 100644
>> --- a/arch/arm/dts/sam9x60ek.dts
>> +++ b/arch/arm/dts/sam9x60ek.dts
>> @@ -80,6 +80,44 @@
>>                          };
>>
>>                          pinctrl {
>> +                                       nand {
> 
> I can see two tabs here. You don't need it. The indentation go far on the right
> 
>> +                                               pinctrl_nand_oe_we: nand-oe-we-0 {
>> +                                                       atmel,pins =
>> +                                                               <AT91_PIOD 0 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 1 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
>> +                                               };
>> +
>> +                                               pinctrl_nand_rb: nand-rb-0 {
>> +                                                       atmel,pins =
>> +                                                               <AT91_PIOD 5 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
>> +                                               };
>> +
>> +                                               pinctrl_nand_cs: nand-cs-0 {
>> +                                                       atmel,pins =
>> +                                                               <AT91_PIOD 4 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
>> +                                               };
>> +                                       };
>> +
>> +                                       ebi {
>> +                                               pinctrl_ebi_data_0_7: ebi-data-lsb-0 {
>> +                                                       atmel,pins =
>> +                                                               <AT91_PIOD 6 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 7 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 8 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 9 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 10 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 11 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 12 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 13 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
>> +                                               };
>> +
>> +                                               pinctrl_ebi_addr_nand: ebi-addr-0 {
>> +                                                       atmel,pins =
>> +                                                               <AT91_PIOD 2 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>> +                                                                AT91_PIOD 3 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
>> +                                               };
>> +                                       };
>> +
> 
> Please remove the ebi and nand block so you have one tab less. Then I
> suggest to align to linux dts and refer to pinctrl instead to create
> another level of indentation

Hello Michael,

In Linux I see the nand and ebi block. We have to keep the same DT as in 
Linux. Do you agree ?

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/at91-sam9x60ek.dts#n378

Eugen

> 
>>                                          pinctrl_qspi: qspi {
>>                                                  atmel,pins =
>>                                                          <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
> This part can be tab back
> 
> Michael
> 
>> @@ -106,6 +144,71 @@
>>          };
>>   };
>>
>> +&ebi {
>> +       pinctrl-names = "default";
>> +       pinctrl-0 = <&pinctrl_ebi_addr_nand &pinctrl_ebi_data_0_7>;
>> +       status = "okay";
>> +
>> +       nand_controller: nand-controller {
>> +               pinctrl-names = "default";
>> +               pinctrl-0 = <&pinctrl_nand_oe_we &pinctrl_nand_cs &pinctrl_nand_rb>;
>> +               status = "okay";
>> +
>> +               nand@3 {
>> +                       reg = <0x3 0x0 0x800000>;
>> +                       rb-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
>> +                       cs-gpios = <&pioD 4 GPIO_ACTIVE_HIGH>;
>> +                       nand-bus-width = <8>;
>> +                       nand-ecc-mode = "hw";
>> +                       nand-ecc-strength = <8>;
>> +                       nand-ecc-step-size = <512>;
>> +                       nand-on-flash-bbt;
>> +                       label = "atmel_nand";
>> +
>> +                       partitions {
>> +                               compatible = "fixed-partitions";
>> +                               #address-cells = <1>;
>> +                               #size-cells = <1>;
>> +
>> +                               at91bootstrap@0 {
>> +                                       label = "at91bootstrap";
>> +                                       reg = <0x0 0x40000>;
>> +                               };
>> +
>> +                               uboot@40000 {
>> +                                       label = "u-boot";
>> +                                       reg = <0x40000 0xc0000>;
>> +                               };
>> +
>> +                               ubootenvred@100000 {
>> +                                       label = "U-Boot Env Redundant";
>> +                                       reg = <0x100000 0x40000>;
>> +                               };
>> +
>> +                               ubootenv@140000 {
>> +                                       label = "U-Boot Env";
>> +                                       reg = <0x140000 0x40000>;
>> +                               };
>> +
>> +                               dtb@180000 {
>> +                                       label = "device tree";
>> +                                       reg = <0x180000 0x80000>;
>> +                               };
>> +
>> +                               kernel@200000 {
>> +                                       label = "kernel";
>> +                                       reg = <0x200000 0x600000>;
>> +                               };
>> +
>> +                               rootfs@800000 {
>> +                                       label = "rootfs";
>> +                                       reg = <0x800000 0x1f800000>;
>> +                               };
>> +                       };
>> +               };
>> +       };
>> +};
>> +
>>   &macb0 {
>>          phy-mode = "rmii";
>>          status = "okay";
>> --
>> 2.34.1
>>
> 
> 
> --
> Michael Nazzareno Trimarchi
> Co-Founder & Chief Executive Officer
> M. +39 347 913 2170
> michael@amarulasolutions.com
> __________________________________
> 
> Amarula Solutions BV
> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> T. +31 (0)85 111 9172
> info@amarulasolutions.com
> www.amarulasolutions.com
> 


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 0/9] Add DM support for atmel NAND driver
  2022-08-31  6:34     ` Eugen.Hristev
  2022-08-31  6:37       ` Michael Nazzareno Trimarchi
@ 2022-08-31 13:46       ` Simon Glass
  1 sibling, 0 replies; 24+ messages in thread
From: Simon Glass @ 2022-08-31 13:46 UTC (permalink / raw)
  To: Eugen Hristev
  Cc: Michael Trimarchi, Balamanikandan Gunasundar,
	U-Boot Mailing List, Sandeep Sheriker M, Dario Binacchi,
	Mihai.Sain, Sergiu.Moga, Claudiu Beznea, Heiko Schocher,
	Tudor.Ambarus, Andrew Scull, Mark Kettenis, Jaehoon Chung,
	Peng Fan, Ye Li, Hari.PrasathGE

Hi Eugen,

On Wed, 31 Aug 2022 at 00:34, <Eugen.Hristev@microchip.com> wrote:
>
> On 8/30/22 5:30 AM, Simon Glass wrote:
> > Hi,
> >
> > On Mon, 29 Aug 2022 at 00:41, Michael Nazzareno Trimarchi
> > <michael@amarulasolutions.com> wrote:
> >>
> >> Hi
> >>
> >> On Mon, Aug 29, 2022 at 8:19 AM Balamanikandan Gunasundar
> >> <balamanikandan.gunasundar@microchip.com> wrote:
> >>>
> >>> Change include:
> >>>
> >>> - Adapt GPIO descriptor apis for U-Boot. Use
> >>>    gpio_request_by_name_nodev, dm_gpio_get_value etc.
> >>> - Use U_BOOT_DRIVER instead of platform_driver.
> >>> - Replace struct platform_device with struct udevice
> >>> - Check the status of nfc exec operation by polling the status
> >>>    register instead of interrupt based handling
> >>> - DMA operations not supported. Remove it
> >>> - Adapt DT parsing to U-Boot APIs
> >>>
> >>> v2:
> >>>
> >>> - Add linux reference version in the commit message from which the
> >>>    driver is ported from
> >>> - Reword the commit message to describe the changes clearly
> >>>
> >>
> >> Thank you to repost it again, I will review it and try to include soon
> >>
> >> Michael
> >>
> >>> Balamanikandan Gunasundar (9):
> >>>    nand: atmel: Add DM based NAND driver
> >>>    nand: atmel: Add pmecc driver
> >>>    mfd: syscon: Add atmel-matrix registers definition
> >>>    memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver
> >>>    mfd: syscon: atmel-smc: Add driver for atmel SMC
> >>>    configs: at91: sam9x60ek: Enable DM based nand driver
> >>>    ARM: dts: at91: sam9x60: Add nodes for EBI and NAND
> >>>    ARM: dts: at91: sam9x60ek: Enable NAND support
> >>>    board: sam9x60ek: remove nand init from board file
> >>>
> >>>   MAINTAINERS                                  |    1 +
> >>>   arch/arm/dts/sam9x60.dtsi                    |   42 +
> >>>   arch/arm/dts/sam9x60ek.dts                   |  103 +
> >>>   board/atmel/sam9x60ek/sam9x60ek.c            |   59 -
> >>>   configs/sam9x60ek_mmc_defconfig              |    9 +-
> >>>   configs/sam9x60ek_nandflash_defconfig        |    9 +-
> >>>   configs/sam9x60ek_qspiflash_defconfig        |    8 +-
> >>>   drivers/Kconfig                              |    2 +
> >>>   drivers/Makefile                             |    1 +
> >>>   drivers/memory/Kconfig                       |    7 +
> >>>   drivers/memory/Makefile                      |    1 +
> >>>   drivers/memory/atmel_ebi.c                   |   37 +
> >>>   drivers/mfd/Kconfig                          |    4 +
> >>>   drivers/mfd/Makefile                         |    1 +
> >>>   drivers/mfd/atmel-smc.c                      |  364 +++
> >>>   drivers/mtd/nand/raw/Kconfig                 |    8 +
> >>>   drivers/mtd/nand/raw/Makefile                |    1 +
> >>>   drivers/mtd/nand/raw/atmel/Makefile          |    5 +
> >>>   drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
> >>>   drivers/mtd/nand/raw/atmel/pmecc.c           |  965 ++++++++
> >>>   drivers/mtd/nand/raw/atmel/pmecc.h           |   94 +
> >>>   include/configs/sam9x60ek.h                  |    5 -
> >>>   include/linux/mfd/syscon/atmel-matrix.h      |  112 +
> >>>   include/linux/mfd/syscon/atmel-smc.h         |  119 +
> >>>   24 files changed, 4177 insertions(+), 73 deletions(-)
> >>>   create mode 100644 drivers/memory/atmel_ebi.c
> >>>   create mode 100644 drivers/mfd/Kconfig
> >>>   create mode 100644 drivers/mfd/Makefile
> >>>   create mode 100644 drivers/mfd/atmel-smc.c
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.c
> >>>   create mode 100644 drivers/mtd/nand/raw/atmel/pmecc.h
> >>>   create mode 100644 include/linux/mfd/syscon/atmel-matrix.h
> >>>   create mode 100644 include/linux/mfd/syscon/atmel-smc.h
> >>>
> >>> --
> >>> 2.34.1
> >>>
> >
> > Once this series is in, I wonder if it might be possible to drop the
> > nand_init() call and have this happen as needed, i.e. lazy init?
> >
> > Regards,
> > Simon
> >
>
> Hi Simon,
>
> nand_init() should be called if nand is accessed. Do you have a specific
> place in mind where this call is not appropriate, and should be removed ?

Well I mean dropping the initr_nand() so we don't init nand unless
needed. Or perhaps it should be a Kconfig to enable this.

But note this is nothing to do with your series, just something for the future.


>
> Hi Michael,
>
> If you want to review this series, please do, I would like this to fit
> into the next merge window for 2023.01 , but if it needs more time,
> that's fine.
>
> Eugen

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support
  2022-08-31 13:31     ` Eugen.Hristev
@ 2022-08-31 14:19       ` Michael Nazzareno Trimarchi
  2022-09-01  5:57         ` Eugen.Hristev
  0 siblings, 1 reply; 24+ messages in thread
From: Michael Nazzareno Trimarchi @ 2022-08-31 14:19 UTC (permalink / raw)
  To: Eugen.Hristev
  Cc: Balamanikandan.Gunasundar, u-boot, Sandeep.Sheriker,
	dario.binacchi, Mihai.Sain, Sergiu.Moga, Claudiu.Beznea, hs,
	Tudor.Ambarus, sjg, ascull, kettenis, jh80.chung, peng.fan,
	ye.li, Hari.PrasathGE

Hi

On Wed, Aug 31, 2022 at 3:31 PM <Eugen.Hristev@microchip.com> wrote:
>
> On 8/31/22 4:14 PM, Michael Nazzareno Trimarchi wrote:
> > Hi
> >
> > On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
> > <balamanikandan.gunasundar@microchip.com> wrote:
> >>
> >> Enable the EBI and NAND flash controller. Define the pinctrl and
> >> partition table
> >>
> >> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
> >> ---
> >>   arch/arm/dts/sam9x60ek.dts | 103 +++++++++++++++++++++++++++++++++++++
> >>   1 file changed, 103 insertions(+)
> >>
> >> diff --git a/arch/arm/dts/sam9x60ek.dts b/arch/arm/dts/sam9x60ek.dts
> >> index 54c694bd78..6cb81dd90f 100644
> >> --- a/arch/arm/dts/sam9x60ek.dts
> >> +++ b/arch/arm/dts/sam9x60ek.dts
> >> @@ -80,6 +80,44 @@
> >>                          };
> >>
> >>                          pinctrl {
> >> +                                       nand {
> >
> > I can see two tabs here. You don't need it. The indentation go far on the right
> >
> >> +                                               pinctrl_nand_oe_we: nand-oe-we-0 {
> >> +                                                       atmel,pins =
> >> +                                                               <AT91_PIOD 0 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 1 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> >> +                                               };
> >> +
> >> +                                               pinctrl_nand_rb: nand-rb-0 {
> >> +                                                       atmel,pins =
> >> +                                                               <AT91_PIOD 5 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
> >> +                                               };
> >> +
> >> +                                               pinctrl_nand_cs: nand-cs-0 {
> >> +                                                       atmel,pins =
> >> +                                                               <AT91_PIOD 4 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
> >> +                                               };
> >> +                                       };
> >> +
> >> +                                       ebi {
> >> +                                               pinctrl_ebi_data_0_7: ebi-data-lsb-0 {
> >> +                                                       atmel,pins =
> >> +                                                               <AT91_PIOD 6 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 7 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 8 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 9 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 10 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 11 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 12 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 13 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> >> +                                               };
> >> +
> >> +                                               pinctrl_ebi_addr_nand: ebi-addr-0 {
> >> +                                                       atmel,pins =
> >> +                                                               <AT91_PIOD 2 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >> +                                                                AT91_PIOD 3 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> >> +                                               };
> >> +                                       };
> >> +
> >
> > Please remove the ebi and nand block so you have one tab less. Then I
> > suggest to align to linux dts and refer to pinctrl instead to create
> > another level of indentation
>
> Hello Michael,
>
> In Linux I see the nand and ebi block. We have to keep the same DT as in
> Linux. Do you agree ?
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/at91-sam9x60ek.dts#n378
>

I have seen it already but I don't think that dtsi is aligned with it.
So you don't have pinctrl: pinctrl@0ffff400 and you can refer
to this node as it is. Here the question is if we need to continue to
have a no-align dts component inside the pinctrl and add more
elements in it. If you look at that file pinctrl_qspi: qspi { is not
in any block and for the linux should be even before. It should
be coherent in what we have or in what linux have now. This does not
block driver review so I think that can be acked by microchip
maintainers in this part.

Michael


> Eugen
>
> >
> >>                                          pinctrl_qspi: qspi {
> >>                                                  atmel,pins =
> >>                                                          <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
> > This part can be tab back
> >
> > Michael
> >
> >> @@ -106,6 +144,71 @@
> >>          };
> >>   };
> >>
> >> +&ebi {
> >> +       pinctrl-names = "default";
> >> +       pinctrl-0 = <&pinctrl_ebi_addr_nand &pinctrl_ebi_data_0_7>;
> >> +       status = "okay";
> >> +
> >> +       nand_controller: nand-controller {
> >> +               pinctrl-names = "default";
> >> +               pinctrl-0 = <&pinctrl_nand_oe_we &pinctrl_nand_cs &pinctrl_nand_rb>;
> >> +               status = "okay";
> >> +
> >> +               nand@3 {
> >> +                       reg = <0x3 0x0 0x800000>;
> >> +                       rb-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
> >> +                       cs-gpios = <&pioD 4 GPIO_ACTIVE_HIGH>;
> >> +                       nand-bus-width = <8>;
> >> +                       nand-ecc-mode = "hw";
> >> +                       nand-ecc-strength = <8>;
> >> +                       nand-ecc-step-size = <512>;
> >> +                       nand-on-flash-bbt;
> >> +                       label = "atmel_nand";
> >> +
> >> +                       partitions {
> >> +                               compatible = "fixed-partitions";
> >> +                               #address-cells = <1>;
> >> +                               #size-cells = <1>;
> >> +
> >> +                               at91bootstrap@0 {
> >> +                                       label = "at91bootstrap";
> >> +                                       reg = <0x0 0x40000>;
> >> +                               };
> >> +
> >> +                               uboot@40000 {
> >> +                                       label = "u-boot";
> >> +                                       reg = <0x40000 0xc0000>;
> >> +                               };
> >> +
> >> +                               ubootenvred@100000 {
> >> +                                       label = "U-Boot Env Redundant";
> >> +                                       reg = <0x100000 0x40000>;
> >> +                               };
> >> +
> >> +                               ubootenv@140000 {
> >> +                                       label = "U-Boot Env";
> >> +                                       reg = <0x140000 0x40000>;
> >> +                               };
> >> +
> >> +                               dtb@180000 {
> >> +                                       label = "device tree";
> >> +                                       reg = <0x180000 0x80000>;
> >> +                               };
> >> +
> >> +                               kernel@200000 {
> >> +                                       label = "kernel";
> >> +                                       reg = <0x200000 0x600000>;
> >> +                               };
> >> +
> >> +                               rootfs@800000 {
> >> +                                       label = "rootfs";
> >> +                                       reg = <0x800000 0x1f800000>;
> >> +                               };
> >> +                       };
> >> +               };
> >> +       };
> >> +};
> >> +
> >>   &macb0 {
> >>          phy-mode = "rmii";
> >>          status = "okay";
> >> --
> >> 2.34.1
> >>
> >
> >
> > --
> > Michael Nazzareno Trimarchi
> > Co-Founder & Chief Executive Officer
> > M. +39 347 913 2170
> > michael@amarulasolutions.com
> > __________________________________
> >
> > Amarula Solutions BV
> > Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> > T. +31 (0)85 111 9172
> > info@amarulasolutions.com
> > www.amarulasolutions.com
> >
>


-- 
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael@amarulasolutions.com
__________________________________

Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info@amarulasolutions.com
www.amarulasolutions.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support
  2022-08-31 14:19       ` Michael Nazzareno Trimarchi
@ 2022-09-01  5:57         ` Eugen.Hristev
  2022-09-20  8:41           ` Dario Binacchi
  0 siblings, 1 reply; 24+ messages in thread
From: Eugen.Hristev @ 2022-09-01  5:57 UTC (permalink / raw)
  To: michael
  Cc: Balamanikandan.Gunasundar, u-boot, Sandeep.Sheriker,
	dario.binacchi, Mihai.Sain, Sergiu.Moga, Claudiu.Beznea, hs,
	Tudor.Ambarus, sjg, ascull, kettenis, jh80.chung, peng.fan,
	ye.li, Hari.PrasathGE

On 8/31/22 5:19 PM, Michael Nazzareno Trimarchi wrote:
> Hi
> 
> On Wed, Aug 31, 2022 at 3:31 PM <Eugen.Hristev@microchip.com> wrote:
>>
>> On 8/31/22 4:14 PM, Michael Nazzareno Trimarchi wrote:
>>> Hi
>>>
>>> On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
>>> <balamanikandan.gunasundar@microchip.com> wrote:
>>>>
>>>> Enable the EBI and NAND flash controller. Define the pinctrl and
>>>> partition table
>>>>
>>>> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
>>>> ---
>>>>    arch/arm/dts/sam9x60ek.dts | 103 +++++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 103 insertions(+)
>>>>
>>>> diff --git a/arch/arm/dts/sam9x60ek.dts b/arch/arm/dts/sam9x60ek.dts
>>>> index 54c694bd78..6cb81dd90f 100644
>>>> --- a/arch/arm/dts/sam9x60ek.dts
>>>> +++ b/arch/arm/dts/sam9x60ek.dts
>>>> @@ -80,6 +80,44 @@
>>>>                           };
>>>>
>>>>                           pinctrl {
>>>> +                                       nand {
>>>
>>> I can see two tabs here. You don't need it. The indentation go far on the right
>>>
>>>> +                                               pinctrl_nand_oe_we: nand-oe-we-0 {
>>>> +                                                       atmel,pins =
>>>> +                                                               <AT91_PIOD 0 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 1 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
>>>> +                                               };
>>>> +
>>>> +                                               pinctrl_nand_rb: nand-rb-0 {
>>>> +                                                       atmel,pins =
>>>> +                                                               <AT91_PIOD 5 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
>>>> +                                               };
>>>> +
>>>> +                                               pinctrl_nand_cs: nand-cs-0 {
>>>> +                                                       atmel,pins =
>>>> +                                                               <AT91_PIOD 4 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
>>>> +                                               };
>>>> +                                       };
>>>> +
>>>> +                                       ebi {
>>>> +                                               pinctrl_ebi_data_0_7: ebi-data-lsb-0 {
>>>> +                                                       atmel,pins =
>>>> +                                                               <AT91_PIOD 6 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 7 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 8 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 9 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 10 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 11 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 12 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 13 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
>>>> +                                               };
>>>> +
>>>> +                                               pinctrl_ebi_addr_nand: ebi-addr-0 {
>>>> +                                                       atmel,pins =
>>>> +                                                               <AT91_PIOD 2 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
>>>> +                                                                AT91_PIOD 3 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
>>>> +                                               };
>>>> +                                       };
>>>> +
>>>
>>> Please remove the ebi and nand block so you have one tab less. Then I
>>> suggest to align to linux dts and refer to pinctrl instead to create
>>> another level of indentation
>>
>> Hello Michael,
>>
>> In Linux I see the nand and ebi block. We have to keep the same DT as in
>> Linux. Do you agree ?
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/at91-sam9x60ek.dts#n378
>>
> 
> I have seen it already but I don't think that dtsi is aligned with it.
> So you don't have pinctrl: pinctrl@0ffff400 and you can refer
> to this node as it is. Here the question is if we need to continue to
> have a no-align dts component inside the pinctrl and add more
> elements in it. If you look at that file pinctrl_qspi: qspi { is not
> in any block and for the linux should be even before. It should
> be coherent in what we have or in what linux have now. This does not
> block driver review so I think that can be acked by microchip
> maintainers in this part.
> 

Hi Michael,

I have difficulties to follow your reasoning. Could you detail it a bit 
so I can understand what is the problem you are referring to ?

As I see it, the DT in the patch is aligned with Linux. The nodes are 
identical.

Eugen

> Michael
> 
> 
>> Eugen
>>
>>>
>>>>                                           pinctrl_qspi: qspi {
>>>>                                                   atmel,pins =
>>>>                                                           <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
>>> This part can be tab back
>>>
>>> Michael
>>>
>>>> @@ -106,6 +144,71 @@
>>>>           };
>>>>    };
>>>>
>>>> +&ebi {
>>>> +       pinctrl-names = "default";
>>>> +       pinctrl-0 = <&pinctrl_ebi_addr_nand &pinctrl_ebi_data_0_7>;
>>>> +       status = "okay";
>>>> +
>>>> +       nand_controller: nand-controller {
>>>> +               pinctrl-names = "default";
>>>> +               pinctrl-0 = <&pinctrl_nand_oe_we &pinctrl_nand_cs &pinctrl_nand_rb>;
>>>> +               status = "okay";
>>>> +
>>>> +               nand@3 {
>>>> +                       reg = <0x3 0x0 0x800000>;
>>>> +                       rb-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
>>>> +                       cs-gpios = <&pioD 4 GPIO_ACTIVE_HIGH>;
>>>> +                       nand-bus-width = <8>;
>>>> +                       nand-ecc-mode = "hw";
>>>> +                       nand-ecc-strength = <8>;
>>>> +                       nand-ecc-step-size = <512>;
>>>> +                       nand-on-flash-bbt;
>>>> +                       label = "atmel_nand";
>>>> +
>>>> +                       partitions {
>>>> +                               compatible = "fixed-partitions";
>>>> +                               #address-cells = <1>;
>>>> +                               #size-cells = <1>;
>>>> +
>>>> +                               at91bootstrap@0 {
>>>> +                                       label = "at91bootstrap";
>>>> +                                       reg = <0x0 0x40000>;
>>>> +                               };
>>>> +
>>>> +                               uboot@40000 {
>>>> +                                       label = "u-boot";
>>>> +                                       reg = <0x40000 0xc0000>;
>>>> +                               };
>>>> +
>>>> +                               ubootenvred@100000 {
>>>> +                                       label = "U-Boot Env Redundant";
>>>> +                                       reg = <0x100000 0x40000>;
>>>> +                               };
>>>> +
>>>> +                               ubootenv@140000 {
>>>> +                                       label = "U-Boot Env";
>>>> +                                       reg = <0x140000 0x40000>;
>>>> +                               };
>>>> +
>>>> +                               dtb@180000 {
>>>> +                                       label = "device tree";
>>>> +                                       reg = <0x180000 0x80000>;
>>>> +                               };
>>>> +
>>>> +                               kernel@200000 {
>>>> +                                       label = "kernel";
>>>> +                                       reg = <0x200000 0x600000>;
>>>> +                               };
>>>> +
>>>> +                               rootfs@800000 {
>>>> +                                       label = "rootfs";
>>>> +                                       reg = <0x800000 0x1f800000>;
>>>> +                               };
>>>> +                       };
>>>> +               };
>>>> +       };
>>>> +};
>>>> +
>>>>    &macb0 {
>>>>           phy-mode = "rmii";
>>>>           status = "okay";
>>>> --
>>>> 2.34.1
>>>>
>>>
>>>
>>> --
>>> Michael Nazzareno Trimarchi
>>> Co-Founder & Chief Executive Officer
>>> M. +39 347 913 2170
>>> michael@amarulasolutions.com
>>> __________________________________
>>>
>>> Amarula Solutions BV
>>> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
>>> T. +31 (0)85 111 9172
>>> info@amarulasolutions.com
>>> www.amarulasolutions.com
>>>
>>
> 
> 
> --
> Michael Nazzareno Trimarchi
> Co-Founder & Chief Executive Officer
> M. +39 347 913 2170
> michael@amarulasolutions.com
> __________________________________
> 
> Amarula Solutions BV
> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> T. +31 (0)85 111 9172
> info@amarulasolutions.com
> www.amarulasolutions.com
> 


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 1/9] nand: atmel: Add DM based NAND driver
  2022-08-29  6:19 ` [PATCH v2 1/9] nand: atmel: Add DM based " Balamanikandan Gunasundar
@ 2022-09-05  8:55   ` Eugen.Hristev
  0 siblings, 0 replies; 24+ messages in thread
From: Eugen.Hristev @ 2022-09-05  8:55 UTC (permalink / raw)
  To: Balamanikandan.Gunasundar, u-boot
  Cc: Sandeep.Sheriker, dario.binacchi, michael, Mihai.Sain,
	Sergiu.Moga, Claudiu.Beznea, hs, Tudor.Ambarus, sjg, ascull,
	kettenis, jh80.chung, peng.fan, ye.li, Hari.PrasathGE

On 8/29/22 9:19 AM, Balamanikandan Gunasundar wrote:
> This implementation is ported from the rework done by Boris Brezillon
> in Linux. This porting is done based on linux-5.4-at91. The driver is
> tested in sam9x60ek, sama5d3_xplained, sam9x75eb and sama7g54-ddr3-eb.
> 
> Changes done includes
> 
> - Adapt GPIO descriptor apis for U-Boot. Use
>    gpio_request_by_name_nodev, dm_gpio_get_value etc.
> - Use U_BOOT_DRIVER instead of platform_driver.
> - Replace struct platform_device with struct udevice
> - Check the status of nfc exec operation by polling the status
>    register instead of interrupt based handling
> - DMA operations not supported. Remove it
> - Adapt DT parsing to U-Boot APIs
> 
> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
> ---
>   drivers/mtd/nand/raw/Kconfig                 |    8 +
>   drivers/mtd/nand/raw/Makefile                |    1 +
>   drivers/mtd/nand/raw/atmel/Makefile          |    4 +
>   drivers/mtd/nand/raw/atmel/nand-controller.c | 2293 ++++++++++++++++++
>   4 files changed, 2306 insertions(+)
>   create mode 100644 drivers/mtd/nand/raw/atmel/Makefile
>   create mode 100644 drivers/mtd/nand/raw/atmel/nand-controller.c
> 
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index ce67d1abde..4d023e2893 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -37,6 +37,14 @@ config SYS_NAND_USE_FLASH_BBT
>   	help
>   	  Enable the BBT (Bad Block Table) usage.
>   
> +config DM_NAND_ATMEL
> +       bool "Support Atmel NAND controller with DM support"
> +       select SYS_NAND_SELF_INIT
> +       imply SYS_NAND_USE_FLASH_BBT
> +       help
> +         Enable this driver for NAND flash platforms using an Atmel NAND
> +         controller.
> +
>   config NAND_ATMEL
>   	bool "Support Atmel NAND controller"
>   	select SYS_NAND_SELF_INIT
> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
> index a398aa9d88..42c1fb25b4 100644
> --- a/drivers/mtd/nand/raw/Makefile
> +++ b/drivers/mtd/nand/raw/Makefile
> @@ -48,6 +48,7 @@ ifdef NORMAL_DRIVERS
>   obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o
>   
>   obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
> +obj-$(CONFIG_DM_NAND_ATMEL) += atmel/
>   obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o
>   obj-$(CONFIG_NAND_BRCMNAND) += brcmnand/
>   obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o
> diff --git a/drivers/mtd/nand/raw/atmel/Makefile b/drivers/mtd/nand/raw/atmel/Makefile
> new file mode 100644
> index 0000000000..6708416983
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/atmel/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_DM_NAND_ATMEL)	+= atmel-nand-controller.o
> +
> +atmel-nand-controller-objs	:= nand-controller.o
> diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c
> new file mode 100644
> index 0000000000..29365c7db0
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c
> @@ -0,0 +1,2293 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2022 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)
> + *
> + *   Port from Linux
> + *	Balamanikandan Gunasundar(balamanikandan.gunasundar@microchip.com)
> + *	Copyright (C) 2022 Microchip Technology Inc.
> + *
> + * 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 <asm-generic/gpio.h>
> +#include <clk.h>
> +#include <dm/device_compat.h>
> +#include <dm/devres.h>
> +#include <dm/of_addr.h>
> +#include <dm/of_access.h>
> +#include <dm/uclass.h>
> +#include <linux/completion.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/ioport.h>
> +#include <linux/mfd/syscon/atmel-matrix.h>
> +#include <linux/mfd/syscon/atmel-smc.h>
> +#include <linux/mtd/rawnand.h>
> +#include <linux/mtd/mtd.h>
> +#include <mach/at91_sfr.h>
> +#include <nand.h>
> +#include <regmap.h>
> +#include <syscon.h>
> +
> +#include "pmecc.h"
> +
> +#define NSEC_PER_SEC    1000000000L
> +
> +#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
> +#define MIN_DMA_LEN				128
> +
> +static struct nand_ecclayout atmel_pmecc_oobinfo;
> +
> +struct nand_controller_ops {
> +	int (*attach_chip)(struct nand_chip *chip);
> +	int (*setup_data_interface)(struct mtd_info *mtd, int chipnr,
> +				    const struct nand_data_interface *conf);
> +};
> +
> +struct nand_controller {
> +	const struct nand_controller_ops *ops;
> +};
> +
> +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;
> +		dma_addr_t dma;
> +	} io;
> +
> +	struct atmel_smc_cs_conf smcconf;
> +};
> +
> +struct atmel_nand {
> +	struct list_head node;
> +	struct udevice *dev;
> +	struct nand_chip base;
> +	struct atmel_nand_cs *activecs;
> +	struct atmel_pmecc_user *pmecc;
> +	struct gpio_desc cdgpio;
> +	int numcs;
> +	struct nand_controller *controller;
> +	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 udevice *udev,
> +		     const struct atmel_nand_controller_caps *caps);
> +	int (*remove)(struct atmel_nand_controller *nc);
> +	void (*nand_init)(struct atmel_nand_controller *nc,
> +			  struct atmel_nand *nand);
> +	int (*ecc_init)(struct nand_chip *chip);
> +	int (*setup_data_interface)(struct atmel_nand *nand, int csline,
> +				    const struct nand_data_interface *conf);
> +};
> +
> +struct atmel_nand_controller_caps {
> +	bool has_dma;
> +	bool legacy_of_bindings;
> +	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 udevice *dev;
> +	struct regmap *smc;
> +	struct dma_chan *dmac;
> +	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;
> +		dma_addr_t dma;
> +	} sram;
> +	const struct atmel_hsmc_reg_layout *hsmc_layout;
> +	struct regmap *io;
> +	struct atmel_nfc_op op;
> +	struct completion complete;
> +	int irq;
> +
> +	/* Only used when instantiating from legacy DT bindings. */
> +	struct clk *clk;
> +};
> +
> +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 void pmecc_config_ecc_layout(struct nand_ecclayout *layout,
> +				    int oobsize, int ecc_len)
> +{
> +	int i;
> +
> +	layout->eccbytes = ecc_len;
> +
> +	/* ECC will occupy the last ecc_len bytes continuously */
> +	for (i = 0; i < ecc_len; i++)
> +		layout->eccpos[i] = oobsize - ecc_len + i;
> +
> +	layout->oobfree[0].offset = 2;
> +	layout->oobfree[0].length =
> +		oobsize - ecc_len - layout->oobfree[0].offset;
> +}
> +
> +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, bool poll,
> +			  unsigned int timeout_ms)
> +{
> +	int ret;
> +	u32 status;
> +
> +	if (!timeout_ms)
> +		timeout_ms = DEFAULT_TIMEOUT_MS;
> +
> +	if (poll)
> +		ret = regmap_read_poll_timeout(nc->base.smc,
> +					       ATMEL_HSMC_NFC_SR, status,
> +					       atmel_nfc_op_done(&nc->op,
> +								 status),
> +					       0, timeout_ms);
> +	else
> +		return -EOPNOTSUPP;
> +
> +	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 void iowrite8_rep(void *addr, const uint8_t *buf, int len)
> +{
> +	int i;
> +
> +	for (i = 0; i < len; i++)
> +		writeb(buf[i], addr);
> +}
> +
> +static void ioread8_rep(void *addr, uint8_t *buf, int len)
> +{
> +	int i;
> +
> +	for (i = 0; i < len; i++)
> +		buf[i] = readb(addr);
> +}
> +
> +static void ioread16_rep(void *addr, void *buf, int len)
> +{
> +	int i;
> +	u16 *p = (u16 *)buf;
> +
> +	for (i = 0; i < len; i++)
> +		p[i] = readw(addr);
> +}
> +
> +static void iowrite16_rep(void *addr, const void *buf, int len)
> +{
> +	int i;
> +	u16 *p = (u16 *)buf;
> +
> +	for (i = 0; i < len; i++)
> +		writew(p[i], addr);
> +}
> +
> +static u8 atmel_nand_read_byte(struct mtd_info *mtd)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +
> +	return ioread8(nand->activecs->io.virt);
> +}
> +
> +static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +
> +	if (chip->options & NAND_BUSWIDTH_16)
> +		iowrite16(byte | (byte << 8), nand->activecs->io.virt);
> +	else
> +		iowrite8(byte, nand->activecs->io.virt);
> +}
> +
> +static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +
> +	if (chip->options & NAND_BUSWIDTH_16)
> +		ioread16_rep(nand->activecs->io.virt, buf, len / 2);
> +	else
> +		ioread8_rep(nand->activecs->io.virt, buf, len);
> +}
> +
> +static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +
> +	if (chip->options & NAND_BUSWIDTH_16)
> +		iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
> +	else
> +		iowrite8_rep(nand->activecs->io.virt, buf, len);
> +}
> +
> +static int atmel_nand_dev_ready(struct mtd_info *mtd)
> +{
> +	struct nand_chip  *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +
> +	return dm_gpio_get_value(&nand->activecs->rb.gpio);
> +}
> +
> +static void atmel_nand_select_chip(struct mtd_info *mtd, int cs)
> +{
> +	struct nand_chip *chip =  mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +
> +	if (cs < 0 || cs >= nand->numcs) {
> +		nand->activecs = NULL;
> +		chip->dev_ready = NULL;
> +		return;
> +	}
> +
> +	nand->activecs = &nand->cs[cs];
> +
> +	if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB)
> +		chip->dev_ready = atmel_nand_dev_ready;
> +}
> +
> +static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +	struct atmel_hsmc_nand_controller *nc;
> +	u32 status;
> +
> +	nc = to_hsmc_nand_controller(nand->controller);
> +
> +	regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status);
> +
> +	return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
> +}
> +
> +static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +	struct atmel_hsmc_nand_controller *nc;
> +
> +	nc = to_hsmc_nand_controller(nand->controller);
> +
> +	atmel_nand_select_chip(mtd, cs);
> +
> +	if (!nand->activecs) {
> +		regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
> +			     ATMEL_HSMC_NFC_CTRL_DIS);
> +		return;
> +	}
> +
> +	if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB)
> +		chip->dev_ready = atmel_hsmc_nand_dev_ready;
> +
> +	regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
> +			   ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
> +			   ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
> +			   ATMEL_HSMC_NFC_CFG_RSPARE |
> +			   ATMEL_HSMC_NFC_CFG_WSPARE,
> +			   ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
> +			   ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
> +			   ATMEL_HSMC_NFC_CFG_RSPARE);
> +	regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
> +		     ATMEL_HSMC_NFC_CTRL_EN);
> +}
> +
> +static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll)
> +{
> +	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, poll, 0);
> +	if (ret)
> +		dev_err(nc->base.dev,
> +			"Failed to send NAND command (err = %d)!",
> +			ret);
> +
> +	/* Reset the op state. */
> +	memset(&nc->op, 0, sizeof(nc->op));
> +
> +	return ret;
> +}
> +
> +static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat,
> +				     unsigned int ctrl)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +	struct atmel_hsmc_nand_controller *nc;
> +
> +	nc = to_hsmc_nand_controller(nand->controller);
> +
> +	if (ctrl & NAND_ALE) {
> +		if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
> +			return;
> +
> +		nc->op.addrs[nc->op.naddrs++] = dat;
> +	} else if (ctrl & NAND_CLE) {
> +		if (nc->op.ncmds > 1)
> +			return;
> +
> +		nc->op.cmds[nc->op.ncmds++] = dat;
> +	}
> +
> +	if (dat == NAND_CMD_NONE) {
> +		nc->op.cs = nand->activecs->id;
> +		atmel_nfc_exec_op(nc, true);
> +	}
> +}
> +
> +static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
> +				unsigned int ctrl)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +	struct atmel_nand_controller *nc;
> +
> +	nc = to_nand_controller(nand->controller);
> +
> +	if ((ctrl & NAND_CTRL_CHANGE) &&
> +	    dm_gpio_is_valid(&nand->activecs->csgpio)) {
> +		if (ctrl & NAND_NCE)
> +			dm_gpio_set_value(&nand->activecs->csgpio, 0);
> +		else
> +			dm_gpio_set_value(&nand->activecs->csgpio, 1);
> +	}
> +
> +	if (ctrl & NAND_ALE)
> +		writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs);
> +	else if (ctrl & NAND_CLE)
> +		writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
> +}
> +
> +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_nand *nand = to_atmel_nand(chip);
> +	struct atmel_hsmc_nand_controller *nc;
> +	int ret = -EIO;
> +
> +	nc = to_hsmc_nand_controller(nand->controller);
> +
> +	if (ret)
> +		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_nand *nand = to_atmel_nand(chip);
> +	struct atmel_hsmc_nand_controller *nc;
> +	int ret = -EIO;
> +
> +	nc = to_hsmc_nand_controller(nand->controller);
> +
> +	if (ret)
> +		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_nand *nand = to_atmel_nand(chip);
> +	struct atmel_hsmc_nand_controller *nc;
> +
> +	nc = to_hsmc_nand_controller(nand->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(nand->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(nand->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(nand->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)
> +			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;
> +
> +	atmel_nand_write_buf(mtd, buf, mtd->writesize);
> +
> +	ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
> +	if (ret) {
> +		atmel_pmecc_disable(nand->pmecc);
> +		return ret;
> +	}
> +
> +	atmel_nand_pmecc_disable(chip, raw);
> +
> +	atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
> +
> +	return nand_prog_page_end_op(chip);
> +}
> +
> +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
> +				       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 mtd_info *mtd,
> +					   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;
> +
> +	atmel_nand_read_buf(mtd, buf, mtd->writesize);
> +	atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
> +
> +	ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
> +
> +	atmel_nand_pmecc_disable(chip, raw);
> +
> +	return ret;
> +}
> +
> +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
> +				      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 mtd_info *mtd,
> +					  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, status;
> +
> +	nc = to_hsmc_nand_controller(nand->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, true);
> +	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;
> +
> +	atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
> +
> +	nc->op.cmds[0] = NAND_CMD_PAGEPROG;
> +	nc->op.ncmds = 1;
> +	nc->op.cs = nand->activecs->id;
> +	ret = atmel_nfc_exec_op(nc, true);
> +	if (ret)
> +		dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
> +			ret);
> +
> +	status = chip->waitfunc(mtd, chip);
> +	if (status & NAND_STATUS_FAIL)
> +		return -EIO;
> +
> +	return ret;
> +}
> +
> +static int
> +atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd, 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 mtd_info *mtd, 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;
> +
> +	nc = to_hsmc_nand_controller(nand->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) {
> +		nand_read_page_op(chip, page, 0, NULL, 0);
> +
> +		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, true);
> +	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 mtd_info *mtd,
> +					   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 mtd_info *mtd,
> +					       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 nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
> +				 struct mtd_oob_region *oobregion)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> +	if (section || !ecc->total)
> +		return -ERANGE;
> +
> +	oobregion->length = ecc->total;
> +	oobregion->offset = mtd->oobsize - oobregion->length;
> +
> +	return 0;
> +}
> +
> +static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
> +				  struct mtd_oob_region *oobregion)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> +	if (section)
> +		return -ERANGE;
> +
> +	oobregion->length = mtd->oobsize - ecc->total - 2;
> +	oobregion->offset = 2;
> +
> +	return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
> +	.ecc = nand_ooblayout_ecc_lp,
> +	.rfree = nand_ooblayout_free_lp,
> +};
> +
> +const struct mtd_ooblayout_ops *nand_get_large_page_ooblayout(void)
> +{
> +	return &nand_ooblayout_lp_ops;
> +}
> +
> +static int atmel_nand_pmecc_init(struct nand_chip *chip)
> +{
> +	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;
> +
> +	nc = to_nand_controller(nand->controller);
> +
> +	if (!nc->pmecc) {
> +		dev_err(nc->dev, "HW ECC not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (nc->caps->legacy_of_bindings) {
> +		u32 val;
> +
> +		if (!ofnode_read_u32(nc->dev->node_, "atmel,pmecc-cap", &val))
> +			chip->ecc.strength = val;
> +
> +		if (!ofnode_read_u32(nc->dev->node_,
> +				     "atmel,pmecc-sector-size",
> +				     &val))
> +			chip->ecc.size = val;
> +	}
> +
> +	if (chip->ecc.options & NAND_ECC_MAXIMIZE)
> +		req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
> +	else if (chip->ecc.strength)
> +		req.ecc.strength = chip->ecc.strength;
> +	else
> +		req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
> +
> +	if (chip->ecc.size)
> +		req.ecc.sectorsize = chip->ecc.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_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());
> +	pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
> +				mtd->oobsize,
> +				chip->ecc.bytes);
> +	chip->ecc.layout = &atmel_pmecc_oobinfo;
> +
> +	return 0;
> +}
> +
> +static int atmel_nand_ecc_init(struct nand_chip *chip)
> +{
> +	struct atmel_nand_controller *nc;
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +	int ret;
> +
> +	nc = to_nand_controller(nand->controller);
> +
> +	switch (chip->ecc.mode) {
> +	case NAND_ECC_NONE:
> +	case NAND_ECC_SOFT:
> +		/*
> +		 * Nothing to do, the core will initialize everything for us.
> +		 */
> +		break;
> +
> +	case NAND_ECC_HW:
> +		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.mode);
> +		return -EOPNOTSUPP;
> +	}
> +
> +	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.mode != NAND_ECC_HW)
> +		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_data_interface *conf,
> +					  struct atmel_smc_cs_conf *smcconf)
> +{
> +	u32 ncycles, totalcycles, timeps, mckperiodps;
> +	struct atmel_nand_controller *nc;
> +	int ret;
> +
> +	nc = to_nand_controller(nand->controller);
> +
> +	/* DDR interface not supported. */
> +	if (conf->type != NAND_SDR_IFACE)
> +		return -EOPNOTSUPP;
> +
> +	/*
> +	 * tRC < 30ns implies EDO mode. This controller does not support this
> +	 * mode.
> +	 */
> +	if (conf->timings.sdr.tRC_min < 30000)
> +		return -EOPNOTSUPP;
> +
> +	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_data_interface(struct atmel_nand *nand,
> +				    int csline,
> +				    const struct nand_data_interface *conf)
> +{
> +	struct atmel_nand_controller *nc;
> +	struct atmel_smc_cs_conf smcconf;
> +	struct atmel_nand_cs *cs;
> +	int ret;
> +
> +	nc = to_nand_controller(nand->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_data_interface(struct atmel_nand *nand,
> +				     int csline,
> +				     const struct nand_data_interface *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->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_data_interface(struct mtd_info *mtd, int csline,
> +					   const struct nand_data_interface *conf)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +	struct atmel_nand_controller *nc;
> +
> +	nc = to_nand_controller(nand->controller);
> +
> +	if (csline >= nand->numcs ||
> +	    (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
> +		return -EINVAL;
> +
> +	return nc->caps->ops->setup_data_interface(nand, csline, conf);
> +}
> +
> +#define NAND_KEEP_TIMINGS       0x00800000
> +
> +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->controller = &nc->base;
> +	nand->controller = &nc->base;
> +
> +	chip->cmd_ctrl = atmel_nand_cmd_ctrl;
> +	chip->read_byte = atmel_nand_read_byte;
> +	chip->write_byte = atmel_nand_write_byte;
> +	chip->read_buf = atmel_nand_read_buf;
> +	chip->write_buf = atmel_nand_write_buf;
> +	chip->select_chip = atmel_nand_select_chip;
> +	chip->setup_data_interface = atmel_nand_setup_data_interface;
> +
> +	if (!nc->mck || !nc->caps->ops->setup_data_interface)
> +		chip->options |= NAND_KEEP_TIMINGS;
> +
> +	/* Some NANDs require a longer delay than the default one (20us). */
> +	chip->chip_delay = 40;
> +
> +	/* Default to HW ECC if pmecc is available. */
> +	if (nc->pmecc)
> +		chip->ecc.mode = NAND_ECC_HW;
> +}
> +
> +static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
> +				struct atmel_nand *nand)
> +{
> +	struct atmel_smc_nand_controller *smc_nc;
> +	int i;
> +
> +	atmel_nand_init(nc, nand);
> +
> +	smc_nc = to_smc_nand_controller(nand->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 void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
> +				 struct atmel_nand *nand)
> +{
> +	struct nand_chip *chip = &nand->base;
> +
> +	atmel_nand_init(nc, nand);
> +
> +	/* Overload some methods for the HSMC controller. */
> +	chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl;
> +	chip->select_chip = atmel_hsmc_nand_select_chip;
> +}
> +
> +static int atmel_nand_controller_remove_nand(struct atmel_nand *nand)
> +{
> +	list_del(&nand->node);
> +
> +	return 0;
> +}
> +
> +static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
> +					    ofnode np,
> +					    int reg_cells)
> +{
> +	struct atmel_nand *nand;
> +	ofnode n;
> +	int numcs = 0;
> +	int ret, i;
> +	u32 val;
> +	fdt32_t faddr;
> +	phys_addr_t base;
> +
> +	/* Count num of nand nodes */
> +	ofnode_for_each_subnode(n, ofnode_get_parent(np))
> +		numcs++;
> +	if (numcs < 1) {
> +		dev_err(nc->dev, "Missing or invalid reg property\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	nand = devm_kzalloc(nc->dev,
> +			    sizeof(struct atmel_nand) +
> +			    (numcs * sizeof(struct atmel_nand_cs)),
> +			    GFP_KERNEL);
> +	if (!nand) {
> +		dev_err(nc->dev, "Failed to allocate NAND object\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	nand->numcs = numcs;
> +
> +	gpio_request_by_name_nodev(np, "det-gpios", 0, &nand->cdgpio,
> +				   GPIOD_IS_IN);
> +
> +	for (i = 0; i < numcs; i++) {
> +		ret = ofnode_read_u32(np, "reg", &val);
> +		if (ret) {
> +			dev_err(nc->dev, "Invalid reg property (err = %d)\n",
> +				ret);
> +			return ERR_PTR(ret);
> +		}
> +		nand->cs[i].id = val;
> +
> +		/* Read base address */
> +		struct resource res;
> +
> +		if (ofnode_read_resource(np, 0, &res)) {
> +			dev_err(nc->dev, "Unable to read resource\n");
> +			return ERR_PTR(-ENOMEM);
> +		}
> +
> +		faddr = cpu_to_fdt32(val);
> +		base = ofnode_translate_address(np, &faddr);
> +		nand->cs[i].io.virt = (void *)base;
> +
> +		if (!ofnode_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 {
> +			ret = gpio_request_by_name_nodev(np, "rb-gpios", 0,
> +							 &nand->cs[i].rb.gpio,
> +							 GPIOD_IS_IN);
> +			if (ret) {
> +				dev_err(nc->dev, "Failed to get R/B gpio\n");
> +				return ERR_PTR(ret);
> +			}
> +
> +			nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
> +		}
> +
> +		ret = gpio_request_by_name_nodev(np, "cs-gpios", 0,
> +						 &nand->cs[i].csgpio,
> +						 GPIOD_IS_OUT);
> +		if (ret)
> +			dev_err(nc->dev, "Failed to get CS gpio (%d)\n", ret);

Hi Bala,

According to the bindings, rb-gpios and cs-gpios are optional:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/mtd/atmel-nand.txt#n41

So please remove the return of error when RB-gpios are not found, and 
the error message when CS-gpios are not found.

I think that if some optional things are not found, there should not be 
any message printed.


Eugen


> +	}
> +
> +	nand_set_flash_node(&nand->base, np);
> +
> +	return nand;
> +}
> +
> +static int nand_attach(struct nand_chip *chip)
> +{
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +
> +	if (nand->controller->ops && nand->controller->ops->attach_chip)
> +		return nand->controller->ops->attach_chip(chip);
> +
> +	return 0;
> +}
> +
> +int atmel_nand_scan(struct mtd_info *mtd, int maxchips)
> +{
> +	int ret;
> +
> +	ret = nand_scan_ident(mtd, maxchips, NULL);
> +	if (ret)
> +		return ret;
> +
> +	ret = nand_attach(mtd_to_nand(mtd));
> +	if (ret)
> +		return ret;
> +
> +	ret = nand_scan_tail(mtd);
> +	return ret;
> +}
> +
> +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 (dm_gpio_is_valid(&nand->cdgpio) &&
> +	    dm_gpio_get_value(&nand->cdgpio)) {
> +		dev_info(nc->dev, "No SmartMedia card inserted.\n");
> +		return 0;
> +	}
> +
> +	nc->caps->ops->nand_init(nc, nand);
> +
> +	ret = atmel_nand_scan(mtd, nand->numcs);
> +	if (ret) {
> +		dev_err(nc->dev, "NAND scan failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = nand_register(0, mtd);
> +	if (ret) {
> +		dev_err(nc->dev, "nand register failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	list_add_tail(&nand->node, &nc->chips);
> +
> +	return 0;
> +}
> +
> +static int
> +atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
> +{
> +	struct atmel_nand *nand, *tmp;
> +	int ret;
> +
> +	list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
> +		ret = atmel_nand_controller_remove_nand(nand);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
> +{
> +	ofnode np;
> +	ofnode nand_np;
> +	int ret, reg_cells;
> +	u32 val;
> +
> +	/* TODO:
> +	 * Add support for legacy nands
> +	 */
> +
> +	np = nc->dev->node_;
> +
> +	ret = ofnode_read_u32(np, "#address-cells", &val);
> +	if (ret) {
> +		dev_err(nc->dev, "missing #address-cells property\n");
> +		return ret;
> +	}
> +
> +	reg_cells = val;
> +
> +	ret = ofnode_read_u32(np, "#size-cells", &val);
> +	if (ret) {
> +		dev_err(nc->dev, "missing #size-cells property\n");
> +		return ret;
> +	}
> +
> +	reg_cells += val;
> +
> +	ofnode_for_each_subnode(nand_np, np) {
> +		struct atmel_nand *nand;
> +
> +		nand = atmel_nand_create(nc, nand_np, reg_cells);
> +		if (IS_ERR(nand)) {
> +			ret = PTR_ERR(nand);
> +			goto err;
> +		}
> +
> +		ret = atmel_nand_controller_add_nand(nc, nand);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	atmel_nand_controller_remove_nands(nc);
> +
> +	return ret;
> +}
> +
> +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 udevice_id atmel_ebi_csa_regmap_of_ids[] = {
> +	{
> +		.compatible = "atmel,at91sam9260-matrix",
> +		.data = (ulong)&at91sam9260_ebi_csa,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9261-matrix",
> +		.data = (ulong)&at91sam9261_ebi_csa,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9263-matrix",
> +		.data = (ulong)&at91sam9263_ebi_csa,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9rl-matrix",
> +		.data = (ulong)&at91sam9rl_ebi_csa,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9g45-matrix",
> +		.data = (ulong)&at91sam9g45_ebi_csa,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9n12-matrix",
> +		.data = (ulong)&at91sam9n12_ebi_csa,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9x5-matrix",
> +		.data = (ulong)&at91sam9x5_ebi_csa,
> +	},
> +	{
> +		.compatible = "microchip,sam9x60-sfr",
> +		.data = (ulong)&sam9x60_ebi_csa,
> +	},
> +	{ /* sentinel */ },
> +};
> +
> +static int atmel_nand_attach_chip(struct nand_chip *chip)
> +{
> +	struct atmel_nand *nand = to_atmel_nand(chip);
> +	struct atmel_nand_controller *nc = to_nand_controller(nand->controller);
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	int ret;
> +
> +	ret = nc->caps->ops->ecc_init(chip);
> +	if (ret)
> +		return ret;
> +
> +	if (nc->caps->legacy_of_bindings || !ofnode_valid(nc->dev->node_)) {
> +		/*
> +		 * We keep the MTD name unchanged to avoid breaking platforms
> +		 * where the MTD cmdline parser is used and the bootloader
> +		 * has not been updated to use the new naming scheme.
> +		 */
> +		mtd->name = "atmel_nand";
> +	} else 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.
> +		 */
> +		sprintf(mtd->name, "%s:nand.%d", nc->dev->name, nand->cs[0].id);
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct nand_controller_ops atmel_nand_controller_ops = {
> +	.attach_chip = atmel_nand_attach_chip,
> +};
> +
> +static int
> +atmel_nand_controller_init(struct atmel_nand_controller *nc,
> +			   struct udevice *dev,
> +			   const struct atmel_nand_controller_caps *caps)
> +{
> +	struct ofnode_phandle_args args;
> +	int ret;
> +
> +	nc->base.ops = &atmel_nand_controller_ops;
> +	INIT_LIST_HEAD(&nc->chips);
> +	nc->dev = dev;
> +	nc->caps = caps;
> +
> +	nc->pmecc = devm_atmel_pmecc_get(dev);
> +	if (IS_ERR(nc->pmecc)) {
> +		ret = PTR_ERR(nc->pmecc);
> +		if (ret != -EPROBE_DEFER)
> +			dev_err(dev, "Could not get PMECC object (err = %d)\n",
> +				ret);
> +		return ret;
> +	}
> +
> +	/* We do not retrieve the SMC syscon when parsing old DTs. */
> +	if (nc->caps->legacy_of_bindings)
> +		return 0;
> +
> +	nc->mck = devm_kzalloc(dev, sizeof(nc->mck), GFP_KERNEL);
> +	if (!nc->mck)
> +		return -ENOMEM;
> +
> +	clk_get_by_index(dev->parent, 0, nc->mck);
> +	if (IS_ERR(nc->mck)) {
> +		dev_err(dev, "Failed to retrieve MCK clk\n");
> +		return PTR_ERR(nc->mck);
> +	}
> +
> +	ret = ofnode_parse_phandle_with_args(dev->parent->node_,
> +					     "atmel,smc", NULL, 0, 0, &args);
> +	if (ret) {
> +		dev_err(dev, "Missing or invalid atmel,smc property\n");
> +		return -EINVAL;
> +	}
> +
> +	nc->smc = syscon_node_to_regmap(args.node);
> +	if (IS_ERR(nc->smc)) {
> +		ret = PTR_ERR(nc->smc);
> +		dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
> +		return 0;
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
> +{
> +	struct udevice *dev = nc->base.dev;
> +	struct ofnode_phandle_args args;
> +	const struct udevice_id *match = NULL;
> +	const char *name;
> +	int ret;
> +	int len;
> +	int i;
> +
> +	/* We do not retrieve the EBICSA regmap when parsing old DTs. */
> +	if (nc->base.caps->legacy_of_bindings)
> +		return 0;
> +
> +	ret = ofnode_parse_phandle_with_args(dev->parent->node_,
> +					     nc->base.caps->ebi_csa_regmap_name,
> +					     NULL, 0, 0, &args);
> +	if (ret) {
> +		dev_err(dev, "Unable to read ebi csa regmap\n");
> +		return -EINVAL;
> +	}
> +
> +	name = ofnode_get_property(args.node, "compatible", &len);
> +
> +	for (i = 0; i < ARRAY_SIZE(atmel_ebi_csa_regmap_of_ids); i++) {
> +		if (!strcmp(name, atmel_ebi_csa_regmap_of_ids[i].compatible)) {
> +			match = &atmel_ebi_csa_regmap_of_ids[i];
> +			break;
> +		}
> +	}
> +
> +	if (!match) {
> +		dev_err(dev, "Unable to find ebi csa conf");
> +		return -EINVAL;
> +	}
> +	nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data;
> +
> +	nc->ebi_csa_regmap = syscon_node_to_regmap(args.node);
> +	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;
> +	}
> +
> +	/* TODO:
> +	 * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
> +	 * add 4 to ->ebi_csa->offs.
> +	 */
> +
> +	return 0;
> +}
> +
> +static int atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
> +{
> +	struct udevice *dev = nc->base.dev;
> +	struct ofnode_phandle_args args;
> +	struct clk smc_clk;
> +	int ret;
> +	u32 addr;
> +
> +	ret = ofnode_parse_phandle_with_args(dev->parent->node_,
> +					     "atmel,smc", NULL, 0, 0, &args);
> +	if (ret) {
> +		dev_err(dev, "Missing or invalid atmel,smc property\n");
> +		return -EINVAL;
> +	}
> +
> +	nc->hsmc_layout = atmel_hsmc_get_reg_layout(args.node);
> +	if (IS_ERR(nc->hsmc_layout)) {
> +		dev_err(dev, "Could not get hsmc layout\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Enable smc clock */
> +	ret = clk_get_by_index_nodev(args.node, 0, &smc_clk);
> +	if (ret) {
> +		dev_err(dev, "Unable to get smc clock (err = %d)", ret);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(&smc_clk);
> +	if (ret)
> +		return ret;
> +
> +	ret = ofnode_parse_phandle_with_args(dev->node_,
> +					     "atmel,nfc-io", NULL, 0, 0, &args);
> +	if (ret) {
> +		dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
> +		return -EINVAL;
> +	}
> +
> +	nc->io = syscon_node_to_regmap(args.node);
> +	if (IS_ERR(nc->io)) {
> +		ret = PTR_ERR(nc->io);
> +		dev_err(dev, "Could not get NFC IO regmap\n");
> +		return ret;
> +	}
> +
> +	ret = ofnode_parse_phandle_with_args(dev->node_,
> +					     "atmel,nfc-sram", NULL, 0, 0, &args);
> +	if (ret) {
> +		dev_err(dev, "Missing or invalid atmel,nfc-sram property\n");
> +		return ret;
> +	}
> +
> +	ret = ofnode_read_u32(args.node, "reg", &addr);
> +	if (ret) {
> +		dev_err(dev, "Could not read reg addr of nfc sram");
> +		return ret;
> +	}
> +	nc->sram.virt = (void *)addr;
> +
> +	return 0;
> +}
> +
> +static int
> +atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
> +{
> +	struct atmel_hsmc_nand_controller *hsmc_nc;
> +	int ret;
> +
> +	ret = atmel_nand_controller_remove_nands(nc);
> +	if (ret)
> +		return ret;
> +
> +	hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
> +
> +	if (hsmc_nc->clk) {
> +		clk_disable_unprepare(hsmc_nc->clk);
> +		devm_clk_put(nc->dev, hsmc_nc->clk);
> +	}
> +
> +	return 0;
> +}
> +
> +static int
> +atmel_hsmc_nand_controller_probe(struct udevice *dev,
> +				 const struct atmel_nand_controller_caps *caps)
> +{
> +	struct atmel_hsmc_nand_controller *nc;
> +	int ret;
> +
> +	nc = devm_kzalloc(dev, 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 before registering our IRQ handler. */
> +	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);
> +
> +	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,
> +	.remove = atmel_hsmc_nand_controller_remove,
> +	.ecc_init = atmel_hsmc_nand_ecc_init,
> +	.nand_init = atmel_hsmc_nand_init,
> +	.setup_data_interface = atmel_hsmc_nand_setup_data_interface,
> +};
> +
> +static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
> +	.has_dma = true,
> +	.ale_offs = BIT(21),
> +	.cle_offs = BIT(22),
> +	.ops = &atmel_hsmc_nc_ops,
> +};
> +
> +static int
> +atmel_smc_nand_controller_probe(struct udevice *dev,
> +				const struct atmel_nand_controller_caps *caps)
> +{
> +	struct atmel_smc_nand_controller *nc;
> +	int ret;
> +
> +	nc = devm_kzalloc(dev, 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);
> +}
> +
> +static int
> +atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
> +{
> +	int ret;
> +
> +	ret = atmel_nand_controller_remove_nands(nc);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/*
> + * The SMC reg layout of at91rm9200 is completely different which prevents us
> + * from re-using atmel_smc_nand_setup_data_interface() for the
> + * ->setup_data_interface() hook.
> + * At this point, there's no support for the at91rm9200 SMC IP, so we leave
> + * ->setup_data_interface() unassigned.
> + */
> +static const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
> +	.probe = atmel_smc_nand_controller_probe,
> +	.remove = atmel_smc_nand_controller_remove,
> +	.ecc_init = atmel_nand_ecc_init,
> +	.nand_init = atmel_smc_nand_init,
> +};
> +
> +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,
> +	.remove = atmel_smc_nand_controller_remove,
> +	.ecc_init = atmel_nand_ecc_init,
> +	.nand_init = atmel_smc_nand_init,
> +	.setup_data_interface = atmel_smc_nand_setup_data_interface,
> +};
> +
> +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 = {
> +	.has_dma = true,
> +	.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 = {
> +	.has_dma = true,
> +	.ale_offs = BIT(21),
> +	.cle_offs = BIT(22),
> +	.ebi_csa_regmap_name = "microchip,sfr",
> +	.ops = &atmel_smc_nc_ops,
> +};
> +
> +/* Only used to parse old bindings. */
> +static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = {
> +	.ale_offs = BIT(21),
> +	.cle_offs = BIT(22),
> +	.ops = &atmel_smc_nc_ops,
> +	.legacy_of_bindings = true,
> +};
> +
> +static const struct udevice_id atmel_nand_controller_of_ids[] = {
> +	{
> +		.compatible = "atmel,at91rm9200-nand-controller",
> +		.data = (ulong)&atmel_rm9200_nc_caps,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9260-nand-controller",
> +		.data = (ulong)&atmel_sam9260_nc_caps,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9261-nand-controller",
> +		.data = (ulong)&atmel_sam9261_nc_caps,
> +	},
> +	{
> +		.compatible = "atmel,at91sam9g45-nand-controller",
> +		.data = (ulong)&atmel_sam9g45_nc_caps,
> +	},
> +	{
> +		.compatible = "atmel,sama5d3-nand-controller",
> +		.data = (ulong)&atmel_sama5_nc_caps,
> +	},
> +	{
> +		.compatible = "microchip,sam9x60-nand-controller",
> +		.data = (ulong)&microchip_sam9x60_nc_caps,
> +	},
> +	/* Support for old/deprecated bindings: */
> +	{
> +		.compatible = "atmel,at91rm9200-nand",
> +		.data = (ulong)&atmel_rm9200_nand_caps,
> +	},
> +	{
> +		.compatible = "atmel,sama5d4-nand",
> +		.data = (ulong)&atmel_rm9200_nand_caps,
> +	},
> +	{
> +		.compatible = "atmel,sama5d2-nand",
> +		.data = (ulong)&atmel_rm9200_nand_caps,
> +	},
> +	{ /* sentinel */ },
> +};
> +
> +static int atmel_nand_controller_probe(struct udevice *dev)
> +{
> +	const struct atmel_nand_controller_caps *caps;
> +	struct udevice *pmecc_dev;
> +
> +	caps = (struct atmel_nand_controller_caps *)dev_get_driver_data(dev);
> +	if (!caps) {
> +		printf("Could not retrieve NFC caps\n");
> +		return -EINVAL;
> +	}
> +
> +	/* Probe pmecc driver */
> +	if (uclass_get_device(UCLASS_MTD, 1, &pmecc_dev)) {
> +		printf("%s: get device fail\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	return caps->ops->probe(dev, caps);
> +}
> +
> +static int atmel_nand_controller_remove(struct udevice *dev)
> +{
> +	struct atmel_nand_controller *nc;
> +
> +	nc = (struct atmel_nand_controller *)dev_get_driver_data(dev);
> +
> +	return nc->caps->ops->remove(nc);
> +}
> +
> +U_BOOT_DRIVER(atmel_nand_controller) = {
> +	.name = "atmel-nand-controller",
> +	.id = UCLASS_MTD,
> +	.of_match = atmel_nand_controller_of_ids,
> +	.probe = atmel_nand_controller_probe,
> +	.remove = atmel_nand_controller_remove,
> +};
> +
> +void board_nand_init(void)
> +{
> +	struct udevice *dev;
> +	int ret;
> +
> +	ret = uclass_get_device_by_driver(UCLASS_MTD,
> +					  DM_DRIVER_GET(atmel_nand_controller),
> +					  &dev);
> +	if (ret && ret != -ENODEV)
> +		printf("Failed to initialize NAND controller. (error %d)\n",
> +		       ret);
> +}
> 


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support
  2022-09-01  5:57         ` Eugen.Hristev
@ 2022-09-20  8:41           ` Dario Binacchi
  0 siblings, 0 replies; 24+ messages in thread
From: Dario Binacchi @ 2022-09-20  8:41 UTC (permalink / raw)
  To: Eugen.Hristev
  Cc: michael, Balamanikandan.Gunasundar, u-boot, Sandeep.Sheriker,
	Mihai.Sain, Sergiu.Moga, Claudiu.Beznea, hs, Tudor.Ambarus, sjg,
	ascull, kettenis, jh80.chung, peng.fan, ye.li, Hari.PrasathGE

Hi Eugen,

On Thu, Sep 1, 2022 at 7:57 AM <Eugen.Hristev@microchip.com> wrote:
>
> On 8/31/22 5:19 PM, Michael Nazzareno Trimarchi wrote:
> > Hi
> >
> > On Wed, Aug 31, 2022 at 3:31 PM <Eugen.Hristev@microchip.com> wrote:
> >>
> >> On 8/31/22 4:14 PM, Michael Nazzareno Trimarchi wrote:
> >>> Hi
> >>>
> >>> On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
> >>> <balamanikandan.gunasundar@microchip.com> wrote:
> >>>>
> >>>> Enable the EBI and NAND flash controller. Define the pinctrl and
> >>>> partition table
> >>>>
> >>>> Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
> >>>> ---
> >>>>    arch/arm/dts/sam9x60ek.dts | 103 +++++++++++++++++++++++++++++++++++++
> >>>>    1 file changed, 103 insertions(+)
> >>>>
> >>>> diff --git a/arch/arm/dts/sam9x60ek.dts b/arch/arm/dts/sam9x60ek.dts
> >>>> index 54c694bd78..6cb81dd90f 100644
> >>>> --- a/arch/arm/dts/sam9x60ek.dts
> >>>> +++ b/arch/arm/dts/sam9x60ek.dts
> >>>> @@ -80,6 +80,44 @@
> >>>>                           };
> >>>>
> >>>>                           pinctrl {
> >>>> +                                       nand {
> >>>
> >>> I can see two tabs here. You don't need it. The indentation go far on the right
> >>>
> >>>> +                                               pinctrl_nand_oe_we: nand-oe-we-0 {
> >>>> +                                                       atmel,pins =
> >>>> +                                                               <AT91_PIOD 0 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 1 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> >>>> +                                               };
> >>>> +
> >>>> +                                               pinctrl_nand_rb: nand-rb-0 {
> >>>> +                                                       atmel,pins =
> >>>> +                                                               <AT91_PIOD 5 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
> >>>> +                                               };
> >>>> +
> >>>> +                                               pinctrl_nand_cs: nand-cs-0 {
> >>>> +                                                       atmel,pins =
> >>>> +                                                               <AT91_PIOD 4 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
> >>>> +                                               };
> >>>> +                                       };
> >>>> +
> >>>> +                                       ebi {
> >>>> +                                               pinctrl_ebi_data_0_7: ebi-data-lsb-0 {
> >>>> +                                                       atmel,pins =
> >>>> +                                                               <AT91_PIOD 6 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 7 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 8 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 9 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 10 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 11 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 12 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 13 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> >>>> +                                               };
> >>>> +
> >>>> +                                               pinctrl_ebi_addr_nand: ebi-addr-0 {
> >>>> +                                                       atmel,pins =
> >>>> +                                                               <AT91_PIOD 2 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
> >>>> +                                                                AT91_PIOD 3 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
> >>>> +                                               };
> >>>> +                                       };
> >>>> +
> >>>
> >>> Please remove the ebi and nand block so you have one tab less. Then I
> >>> suggest to align to linux dts and refer to pinctrl instead to create
> >>> another level of indentation
> >>
> >> Hello Michael,
> >>
> >> In Linux I see the nand and ebi block. We have to keep the same DT as in
> >> Linux. Do you agree ?
> >>
> >> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm/boot/dts/at91-sam9x60ek.dts#n378
> >>
> >
> > I have seen it already but I don't think that dtsi is aligned with it.
> > So you don't have pinctrl: pinctrl@0ffff400 and you can refer
> > to this node as it is. Here the question is if we need to continue to
> > have a no-align dts component inside the pinctrl and add more
> > elements in it. If you look at that file pinctrl_qspi: qspi { is not
> > in any block and for the linux should be even before. It should
> > be coherent in what we have or in what linux have now. This does not
> > block driver review so I think that can be acked by microchip
> > maintainers in this part.
> >
>
> Hi Michael,
>
> I have difficulties to follow your reasoning. Could you detail it a bit
> so I can understand what is the problem you are referring to ?
>
> As I see it, the DT in the patch is aligned with Linux. The nodes are
> identical.

The "pinctrl" node in linux mainline is not a subnode of "apb" but is
referenced.
This improves the indentation. Therefore, it is better to add the NAND
subnodes to a
referenced pinctrl node. In the meantime, I have submitted a patch
that fixes the
indentation for the current subnodes of pinctrl.

Thanks and regards,
Dario

>
> Eugen
>
> > Michael
> >
> >
> >> Eugen
> >>
> >>>
> >>>>                                           pinctrl_qspi: qspi {
> >>>>                                                   atmel,pins =
> >>>>                                                           <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
> >>> This part can be tab back
> >>>
> >>> Michael
> >>>
> >>>> @@ -106,6 +144,71 @@
> >>>>           };
> >>>>    };
> >>>>
> >>>> +&ebi {
> >>>> +       pinctrl-names = "default";
> >>>> +       pinctrl-0 = <&pinctrl_ebi_addr_nand &pinctrl_ebi_data_0_7>;
> >>>> +       status = "okay";
> >>>> +
> >>>> +       nand_controller: nand-controller {
> >>>> +               pinctrl-names = "default";
> >>>> +               pinctrl-0 = <&pinctrl_nand_oe_we &pinctrl_nand_cs &pinctrl_nand_rb>;
> >>>> +               status = "okay";
> >>>> +
> >>>> +               nand@3 {
> >>>> +                       reg = <0x3 0x0 0x800000>;
> >>>> +                       rb-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
> >>>> +                       cs-gpios = <&pioD 4 GPIO_ACTIVE_HIGH>;
> >>>> +                       nand-bus-width = <8>;
> >>>> +                       nand-ecc-mode = "hw";
> >>>> +                       nand-ecc-strength = <8>;
> >>>> +                       nand-ecc-step-size = <512>;
> >>>> +                       nand-on-flash-bbt;
> >>>> +                       label = "atmel_nand";
> >>>> +
> >>>> +                       partitions {
> >>>> +                               compatible = "fixed-partitions";
> >>>> +                               #address-cells = <1>;
> >>>> +                               #size-cells = <1>;
> >>>> +
> >>>> +                               at91bootstrap@0 {
> >>>> +                                       label = "at91bootstrap";
> >>>> +                                       reg = <0x0 0x40000>;
> >>>> +                               };
> >>>> +
> >>>> +                               uboot@40000 {
> >>>> +                                       label = "u-boot";
> >>>> +                                       reg = <0x40000 0xc0000>;
> >>>> +                               };
> >>>> +
> >>>> +                               ubootenvred@100000 {
> >>>> +                                       label = "U-Boot Env Redundant";
> >>>> +                                       reg = <0x100000 0x40000>;
> >>>> +                               };
> >>>> +
> >>>> +                               ubootenv@140000 {
> >>>> +                                       label = "U-Boot Env";
> >>>> +                                       reg = <0x140000 0x40000>;
> >>>> +                               };
> >>>> +
> >>>> +                               dtb@180000 {
> >>>> +                                       label = "device tree";
> >>>> +                                       reg = <0x180000 0x80000>;
> >>>> +                               };
> >>>> +
> >>>> +                               kernel@200000 {
> >>>> +                                       label = "kernel";
> >>>> +                                       reg = <0x200000 0x600000>;
> >>>> +                               };
> >>>> +
> >>>> +                               rootfs@800000 {
> >>>> +                                       label = "rootfs";
> >>>> +                                       reg = <0x800000 0x1f800000>;
> >>>> +                               };
> >>>> +                       };
> >>>> +               };
> >>>> +       };
> >>>> +};
> >>>> +
> >>>>    &macb0 {
> >>>>           phy-mode = "rmii";
> >>>>           status = "okay";
> >>>> --
> >>>> 2.34.1
> >>>>
> >>>
> >>>
> >>> --
> >>> Michael Nazzareno Trimarchi
> >>> Co-Founder & Chief Executive Officer
> >>> M. +39 347 913 2170
> >>> michael@amarulasolutions.com
> >>> __________________________________
> >>>
> >>> Amarula Solutions BV
> >>> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> >>> T. +31 (0)85 111 9172
> >>> info@amarulasolutions.com
> >>> www.amarulasolutions.com
> >>>
> >>
> >
> >
> > --
> > Michael Nazzareno Trimarchi
> > Co-Founder & Chief Executive Officer
> > M. +39 347 913 2170
> > michael@amarulasolutions.com
> > __________________________________
> >
> > Amarula Solutions BV
> > Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> > T. +31 (0)85 111 9172
> > info@amarulasolutions.com
> > www.amarulasolutions.com
> >
>


-- 

Dario Binacchi

Embedded Linux Developer

dario.binacchi@amarulasolutions.com

__________________________________


Amarula Solutions SRL

Via Le Canevare 30, 31100 Treviso, Veneto, IT

T. +39 042 243 5310
info@amarulasolutions.com

www.amarulasolutions.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC
  2022-08-31 12:53   ` Michael Nazzareno Trimarchi
@ 2022-09-30 11:02     ` Michael Nazzareno Trimarchi
  2022-09-30 14:00       ` Tom Rini
  0 siblings, 1 reply; 24+ messages in thread
From: Michael Nazzareno Trimarchi @ 2022-09-30 11:02 UTC (permalink / raw)
  To: Balamanikandan Gunasundar, Tom Rini
  Cc: u-boot, sandeep.sheriker, eugen.hristev, dario.binacchi,
	mihai.sain, sergiu.moga, claudiu.beznea, hs, tudor.ambarus, sjg,
	ascull, kettenis, jh80.chung, peng.fan, ye.li, hari.prasathge

Hi Tom

On Wed, Aug 31, 2022 at 2:53 PM Michael Nazzareno Trimarchi
<michael@amarulasolutions.com> wrote:
>
> Hi
>
> On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
> <balamanikandan.gunasundar@microchip.com> wrote:
> >
> > Add driver for atmel Static Memory Controller. Add helper functions to
> > configure SMC. This file is inherited from the work done by Boris
> > Brezillon for Linux
> >
>
> This does not look like a driver.Those are helper functions. Now I
> have no objections
> to put it here but it will be the first one under mfd.
>
> I think linux commit message subject give more an idea on what exactly it is
>
> mfd: syscon: atmel-smc: Add new helpers to ease SMC regs manipulation
>

Apart dts things in those patches, It's nice to know if adding mfd in
uboot it's fine for you. At the moment
this new file has only helper and it's not a driver. It's in same
place in linux kernel

Michael

> Michael
>
>
> > Signed-off-by: Balamanikandan Gunasundar <balamanikandan.gunasundar@microchip.com>
> > ---
> >  drivers/Kconfig                      |   2 +
> >  drivers/Makefile                     |   1 +
> >  drivers/mfd/Kconfig                  |   4 +
> >  drivers/mfd/Makefile                 |   1 +
> >  drivers/mfd/atmel-smc.c              | 364 +++++++++++++++++++++++++++
> >  include/linux/mfd/syscon/atmel-smc.h | 119 +++++++++
> >  6 files changed, 491 insertions(+)
> >  create mode 100644 drivers/mfd/Kconfig
> >  create mode 100644 drivers/mfd/Makefile
> >  create mode 100644 drivers/mfd/atmel-smc.c
> >  create mode 100644 include/linux/mfd/syscon/atmel-smc.h
> >
> > diff --git a/drivers/Kconfig b/drivers/Kconfig
> > index 8b6fead351..ffc06ed65e 100644
> > --- a/drivers/Kconfig
> > +++ b/drivers/Kconfig
> > @@ -60,6 +60,8 @@ source "drivers/mailbox/Kconfig"
> >
> >  source "drivers/memory/Kconfig"
> >
> > +source "drivers/mfd/Kconfig"
> > +
> >  source "drivers/misc/Kconfig"
> >
> >  source "drivers/mmc/Kconfig"
> > diff --git a/drivers/Makefile b/drivers/Makefile
> > index eba9940231..5a8e80d4fe 100644
> > --- a/drivers/Makefile
> > +++ b/drivers/Makefile
> > @@ -101,6 +101,7 @@ obj-$(CONFIG_QE) += qe/
> >  obj-$(CONFIG_U_QE) += qe/
> >  obj-y += mailbox/
> >  obj-y += memory/
> > +obj-y += mfd/
> >  obj-y += mtd/
> >  obj-y += pwm/
> >  obj-y += reset/
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > new file mode 100644
> > index 0000000000..ae53b02f27
> > --- /dev/null
> > +++ b/drivers/mfd/Kconfig
> > @@ -0,0 +1,4 @@
> > +config MFD_ATMEL_SMC
> > +       bool "Atmel Static Memory Controller driver"
> > +       help
> > +       Say yes here to support Atmel Static Memory Controller driver.
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > new file mode 100644
> > index 0000000000..4454815a98
> > --- /dev/null
> > +++ b/drivers/mfd/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
> > diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c
> > new file mode 100644
> > index 0000000000..15296f71a1
> > --- /dev/null
> > +++ b/drivers/mfd/atmel-smc.c
> > @@ -0,0 +1,364 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Atmel SMC (Static Memory Controller) helper functions.
> > + *
> > + * Copyright (C) 2022 Microchip Technology Inc.
> > + * Copyright (C) 2017 Free Electrons
> > + *
> > + * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
> > + */
> > +
> > +#include <clk.h>
> > +#include <dm/device.h>
> > +#include <linux/err.h>
> > +#include <linux/errno.h>
> > +#include <linux/mfd/syscon/atmel-smc.h>
> > +#include <linux/string.h>
> > +
> > +/**
> > + * atmel_smc_cs_conf_init - initialize a SMC CS conf
> > + * @conf: the SMC CS conf to initialize
> > + *
> > + * Set all fields to 0 so that one can start defining a new config.
> > + */
> > +void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
> > +{
> > +       memset(conf, 0, sizeof(*conf));
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
> > +
> > +/**
> > + * atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
> > + *                              format expected by the SMC engine
> > + * @ncycles: number of MCK clk cycles
> > + * @msbpos: position of the MSB part of the timing field
> > + * @msbwidth: width of the MSB part of the timing field
> > + * @msbfactor: factor applied to the MSB
> > + * @encodedval: param used to store the encoding result
> > + *
> > + * This function encodes the @ncycles value as described in the datasheet
> > + * (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
> > + * helper which called with different parameter depending on the encoding
> > + * scheme.
> > + *
> > + * If the @ncycles value is too big to be encoded, -ERANGE is returned and
> > + * the encodedval is contains the maximum val. Otherwise, 0 is returned.
> > + */
> > +static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
> > +                                      unsigned int msbpos,
> > +                                      unsigned int msbwidth,
> > +                                      unsigned int msbfactor,
> > +                                      unsigned int *encodedval)
> > +{
> > +       unsigned int lsbmask = GENMASK(msbpos - 1, 0);
> > +       unsigned int msbmask = GENMASK(msbwidth - 1, 0);
> > +       unsigned int msb, lsb;
> > +       int ret = 0;
> > +
> > +       msb = ncycles / msbfactor;
> > +       lsb = ncycles % msbfactor;
> > +
> > +       if (lsb > lsbmask) {
> > +               lsb = 0;
> > +               msb++;
> > +       }
> > +
> > +       /*
> > +        * Let's just put the maximum we can if the requested setting does
> > +        * not fit in the register field.
> > +        * We still return -ERANGE in case the caller cares.
> > +        */
> > +       if (msb > msbmask) {
> > +               msb = msbmask;
> > +               lsb = lsbmask;
> > +               ret = -ERANGE;
> > +       }
> > +
> > +       *encodedval = (msb << msbpos) | lsb;
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
> > + *                               specific value
> > + * @conf: SMC CS conf descriptor
> > + * @shift: the position of the Txx field in the TIMINGS register
> > + * @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
> > + *          parameter
> > + *
> > + * This function encodes the @ncycles value as described in the datasheet
> > + * (section "SMC Timings Register"), and then stores the result in the
> > + * @conf->timings field at @shift position.
> > + *
> > + * Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
> > + * the field, and 0 otherwise.
> > + */
> > +int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
> > +                                unsigned int shift, unsigned int ncycles)
> > +{
> > +       unsigned int val;
> > +       int ret;
> > +
> > +       if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
> > +           shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
> > +           shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
> > +           shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
> > +           shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
> > +               return -EINVAL;
> > +
> > +       /*
> > +        * The formula described in atmel datasheets (section "HSMC Timings
> > +        * Register"):
> > +        *
> > +        * ncycles = (Txx[3] * 64) + Txx[2:0]
> > +        */
> > +       ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
> > +       conf->timings &= ~GENMASK(shift + 3, shift);
> > +       conf->timings |= val << shift;
> > +
> > +       return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
> > +
> > +/**
> > + * atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
> > + *                              specific value
> > + * @conf: SMC CS conf descriptor
> > + * @shift: the position of the xx_SETUP field in the SETUP register
> > + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
> > + *          parameter
> > + *
> > + * This function encodes the @ncycles value as described in the datasheet
> > + * (section "SMC Setup Register"), and then stores the result in the
> > + * @conf->setup field at @shift position.
> > + *
> > + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
> > + * the field, and 0 otherwise.
> > + */
> > +int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
> > +                               unsigned int shift, unsigned int ncycles)
> > +{
> > +       unsigned int val;
> > +       int ret;
> > +
> > +       if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
> > +           shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
> > +               return -EINVAL;
> > +
> > +       /*
> > +        * The formula described in atmel datasheets (section "SMC Setup
> > +        * Register"):
> > +        *
> > +        * ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
> > +        */
> > +       ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
> > +       conf->setup &= ~GENMASK(shift + 7, shift);
> > +       conf->setup |= val << shift;
> > +
> > +       return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
> > +
> > +/**
> > + * atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
> > + *                              specific value
> > + * @conf: SMC CS conf descriptor
> > + * @shift: the position of the xx_PULSE field in the PULSE register
> > + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
> > + *          parameter
> > + *
> > + * This function encodes the @ncycles value as described in the datasheet
> > + * (section "SMC Pulse Register"), and then stores the result in the
> > + * @conf->setup field at @shift position.
> > + *
> > + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
> > + * the field, and 0 otherwise.
> > + */
> > +int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
> > +                               unsigned int shift, unsigned int ncycles)
> > +{
> > +       unsigned int val;
> > +       int ret;
> > +
> > +       if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
> > +           shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
> > +               return -EINVAL;
> > +
> > +       /*
> > +        * The formula described in atmel datasheets (section "SMC Pulse
> > +        * Register"):
> > +        *
> > +        * ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
> > +        */
> > +       ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
> > +       conf->pulse &= ~GENMASK(shift + 7, shift);
> > +       conf->pulse |= val << shift;
> > +
> > +       return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
> > +
> > +/**
> > + * atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
> > + *                              specific value
> > + * @conf: SMC CS conf descriptor
> > + * @shift: the position of the xx_CYCLE field in the CYCLE register
> > + * @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
> > + *          parameter
> > + *
> > + * This function encodes the @ncycles value as described in the datasheet
> > + * (section "SMC Cycle Register"), and then stores the result in the
> > + * @conf->setup field at @shift position.
> > + *
> > + * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
> > + * the field, and 0 otherwise.
> > + */
> > +int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
> > +                               unsigned int shift, unsigned int ncycles)
> > +{
> > +       unsigned int val;
> > +       int ret;
> > +
> > +       if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
> > +               return -EINVAL;
> > +
> > +       /*
> > +        * The formula described in atmel datasheets (section "SMC Cycle
> > +        * Register"):
> > +        *
> > +        * ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
> > +        */
> > +       ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
> > +       conf->cycle &= ~GENMASK(shift + 15, shift);
> > +       conf->cycle |= val << shift;
> > +
> > +       return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
> > +
> > +/**
> > + * atmel_smc_cs_conf_apply - apply an SMC CS conf
> > + * @regmap: the SMC regmap
> > + * @cs: the CS id
> > + * @conf: the SMC CS conf to apply
> > + *
> > + * Applies an SMC CS configuration.
> > + * Only valid on at91sam9/avr32 SoCs.
> > + */
> > +void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
> > +                            const struct atmel_smc_cs_conf *conf)
> > +{
> > +       regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
> > +       regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
> > +       regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
> > +       regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
> > +
> > +/**
> > + * atmel_hsmc_cs_conf_apply - apply an SMC CS conf
> > + * @regmap: the HSMC regmap
> > + * @cs: the CS id
> > + * @layout: the layout of registers
> > + * @conf: the SMC CS conf to apply
> > + *
> > + * Applies an SMC CS configuration.
> > + * Only valid on post-sama5 SoCs.
> > + */
> > +void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
> > +                             const struct atmel_hsmc_reg_layout *layout,
> > +                             int cs, const struct atmel_smc_cs_conf *conf)
> > +{
> > +       regmap_write(regmap, ATMEL_HSMC_SETUP(layout, cs), conf->setup);
> > +       regmap_write(regmap, ATMEL_HSMC_PULSE(layout, cs), conf->pulse);
> > +       regmap_write(regmap, ATMEL_HSMC_CYCLE(layout, cs), conf->cycle);
> > +       regmap_write(regmap, ATMEL_HSMC_TIMINGS(layout, cs), conf->timings);
> > +       regmap_write(regmap, ATMEL_HSMC_MODE(layout, cs), conf->mode);
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
> > +
> > +/**
> > + * atmel_smc_cs_conf_get - retrieve the current SMC CS conf
> > + * @regmap: the SMC regmap
> > + * @cs: the CS id
> > + * @conf: the SMC CS conf object to store the current conf
> > + *
> > + * Retrieve the SMC CS configuration.
> > + * Only valid on at91sam9/avr32 SoCs.
> > + */
> > +void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
> > +                          struct atmel_smc_cs_conf *conf)
> > +{
> > +       regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
> > +       regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
> > +       regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
> > +       regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
> > +
> > +/**
> > + * atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
> > + * @regmap: the HSMC regmap
> > + * @cs: the CS id
> > + * @layout: the layout of registers
> > + * @conf: the SMC CS conf object to store the current conf
> > + *
> > + * Retrieve the SMC CS configuration.
> > + * Only valid on post-sama5 SoCs.
> > + */
> > +void atmel_hsmc_cs_conf_get(struct regmap *regmap,
> > +                           const struct atmel_hsmc_reg_layout *layout,
> > +                           int cs, struct atmel_smc_cs_conf *conf)
> > +{
> > +       regmap_read(regmap, ATMEL_HSMC_SETUP(layout, cs), &conf->setup);
> > +       regmap_read(regmap, ATMEL_HSMC_PULSE(layout, cs), &conf->pulse);
> > +       regmap_read(regmap, ATMEL_HSMC_CYCLE(layout, cs), &conf->cycle);
> > +       regmap_read(regmap, ATMEL_HSMC_TIMINGS(layout, cs), &conf->timings);
> > +       regmap_read(regmap, ATMEL_HSMC_MODE(layout, cs), &conf->mode);
> > +}
> > +EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
> > +
> > +static const struct atmel_hsmc_reg_layout sama5d3_reg_layout = {
> > +       .timing_regs_offset = 0x600,
> > +};
> > +
> > +static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = {
> > +       .timing_regs_offset = 0x700,
> > +};
> > +
> > +static const struct udevice_id atmel_smc_ids[] = {
> > +       { .compatible = "atmel,at91sam9260-smc", .data = (ulong)0 },
> > +       { .compatible = "atmel,sama5d3-smc", .data = (ulong)&sama5d3_reg_layout },
> > +       { .compatible = "atmel,sama5d2-smc", .data = (ulong)&sama5d2_reg_layout },
> > +       { /* sentinel */ },
> > +};
> > +
> > +/**
> > + * atmel_hsmc_get_reg_layout - retrieve the layout of HSMC registers
> > + * @np: the HSMC regmap
> > + *
> > + * Retrieve the layout of HSMC registers.
> > + *
> > + * Returns NULL in case of SMC, a struct atmel_hsmc_reg_layout pointer
> > + * in HSMC case, otherwise ERR_PTR(-EINVAL).
> > + */
> > +const struct atmel_hsmc_reg_layout *
> > +atmel_hsmc_get_reg_layout(ofnode np)
> > +{
> > +       int i;
> > +       const struct udevice_id *match;
> > +       const char *name;
> > +       int len;
> > +
> > +       name = ofnode_get_property(np, "compatible", &len);
> > +
> > +       for (i = 0; i < ARRAY_SIZE(atmel_smc_ids); i++) {
> > +               if (!strcmp(name, atmel_smc_ids[i].compatible)) {
> > +                       match = &atmel_smc_ids[i];
> > +                       break;
> > +               }
> > +       }
> > +
> > +       return match ? (struct atmel_hsmc_reg_layout *)match->data : ERR_PTR(-EINVAL);
> > +}
> > diff --git a/include/linux/mfd/syscon/atmel-smc.h b/include/linux/mfd/syscon/atmel-smc.h
> > new file mode 100644
> > index 0000000000..74be5a199f
> > --- /dev/null
> > +++ b/include/linux/mfd/syscon/atmel-smc.h
> > @@ -0,0 +1,119 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Atmel SMC (Static Memory Controller) register offsets and bit definitions.
> > + *
> > + * Copyright (C) 2014 Atmel
> > + * Copyright (C) 2014 Free Electrons
> > + *
> > + * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
> > + */
> > +
> > +#ifndef _LINUX_MFD_SYSCON_ATMEL_SMC_H_
> > +#define _LINUX_MFD_SYSCON_ATMEL_SMC_H_
> > +
> > +#include <linux/kernel.h>
> > +#include <dm/ofnode.h>
> > +#include <regmap.h>
> > +
> > +#define ATMEL_SMC_SETUP(cs)                    (((cs) * 0x10))
> > +#define ATMEL_HSMC_SETUP(layout, cs)           \
> > +       ((layout)->timing_regs_offset + ((cs) * 0x14))
> > +#define ATMEL_SMC_PULSE(cs)                    (((cs) * 0x10) + 0x4)
> > +#define ATMEL_HSMC_PULSE(layout, cs)           \
> > +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0x4)
> > +#define ATMEL_SMC_CYCLE(cs)                    (((cs) * 0x10) + 0x8)
> > +#define ATMEL_HSMC_CYCLE(layout, cs)                   \
> > +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0x8)
> > +#define ATMEL_SMC_NWE_SHIFT                    0
> > +#define ATMEL_SMC_NCS_WR_SHIFT                 8
> > +#define ATMEL_SMC_NRD_SHIFT                    16
> > +#define ATMEL_SMC_NCS_RD_SHIFT                 24
> > +
> > +#define ATMEL_SMC_MODE(cs)                     (((cs) * 0x10) + 0xc)
> > +#define ATMEL_HSMC_MODE(layout, cs)                    \
> > +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0x10)
> > +#define ATMEL_SMC_MODE_READMODE_MASK           BIT(0)
> > +#define ATMEL_SMC_MODE_READMODE_NCS            (0 << 0)
> > +#define ATMEL_SMC_MODE_READMODE_NRD            (1 << 0)
> > +#define ATMEL_SMC_MODE_WRITEMODE_MASK          BIT(1)
> > +#define ATMEL_SMC_MODE_WRITEMODE_NCS           (0 << 1)
> > +#define ATMEL_SMC_MODE_WRITEMODE_NWE           (1 << 1)
> > +#define ATMEL_SMC_MODE_EXNWMODE_MASK           GENMASK(5, 4)
> > +#define ATMEL_SMC_MODE_EXNWMODE_DISABLE                (0 << 4)
> > +#define ATMEL_SMC_MODE_EXNWMODE_FROZEN         (2 << 4)
> > +#define ATMEL_SMC_MODE_EXNWMODE_READY          (3 << 4)
> > +#define ATMEL_SMC_MODE_BAT_MASK                        BIT(8)
> > +#define ATMEL_SMC_MODE_BAT_SELECT              (0 << 8)
> > +#define ATMEL_SMC_MODE_BAT_WRITE               (1 << 8)
> > +#define ATMEL_SMC_MODE_DBW_MASK                        GENMASK(13, 12)
> > +#define ATMEL_SMC_MODE_DBW_8                   (0 << 12)
> > +#define ATMEL_SMC_MODE_DBW_16                  (1 << 12)
> > +#define ATMEL_SMC_MODE_DBW_32                  (2 << 12)
> > +#define ATMEL_SMC_MODE_TDF_MASK                        GENMASK(19, 16)
> > +#define ATMEL_SMC_MODE_TDF(x)                  (((x) - 1) << 16)
> > +#define ATMEL_SMC_MODE_TDF_MAX                 16
> > +#define ATMEL_SMC_MODE_TDF_MIN                 1
> > +#define ATMEL_SMC_MODE_TDFMODE_OPTIMIZED       BIT(20)
> > +#define ATMEL_SMC_MODE_PMEN                    BIT(24)
> > +#define ATMEL_SMC_MODE_PS_MASK                 GENMASK(29, 28)
> > +#define ATMEL_SMC_MODE_PS_4                    (0 << 28)
> > +#define ATMEL_SMC_MODE_PS_8                    (1 << 28)
> > +#define ATMEL_SMC_MODE_PS_16                   (2 << 28)
> > +#define ATMEL_SMC_MODE_PS_32                   (3 << 28)
> > +
> > +#define ATMEL_HSMC_TIMINGS(layout, cs)                 \
> > +       ((layout)->timing_regs_offset + ((cs) * 0x14) + 0xc)
> > +#define ATMEL_HSMC_TIMINGS_OCMS                        BIT(12)
> > +#define ATMEL_HSMC_TIMINGS_RBNSEL(x)           ((x) << 28)
> > +#define ATMEL_HSMC_TIMINGS_NFSEL               BIT(31)
> > +#define ATMEL_HSMC_TIMINGS_TCLR_SHIFT          0
> > +#define ATMEL_HSMC_TIMINGS_TADL_SHIFT          4
> > +#define ATMEL_HSMC_TIMINGS_TAR_SHIFT           8
> > +#define ATMEL_HSMC_TIMINGS_TRR_SHIFT           16
> > +#define ATMEL_HSMC_TIMINGS_TWB_SHIFT           24
> > +
> > +struct atmel_hsmc_reg_layout {
> > +       unsigned int timing_regs_offset;
> > +};
> > +
> > +/**
> > + * struct atmel_smc_cs_conf - SMC CS config as described in the datasheet.
> > + * @setup: NCS/NWE/NRD setup timings (not applicable to at91rm9200)
> > + * @pulse: NCS/NWE/NRD pulse timings (not applicable to at91rm9200)
> > + * @cycle: NWE/NRD cycle timings (not applicable to at91rm9200)
> > + * @timings: advanced NAND related timings (only applicable to HSMC)
> > + * @mode: all kind of config parameters (see the fields definition above).
> > + *       The mode fields are different on at91rm9200
> > + */
> > +struct atmel_smc_cs_conf {
> > +       u32 setup;
> > +       u32 pulse;
> > +       u32 cycle;
> > +       u32 timings;
> > +       u32 mode;
> > +};
> > +
> > +void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf);
> > +int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
> > +                                unsigned int shift,
> > +                                unsigned int ncycles);
> > +int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
> > +                               unsigned int shift, unsigned int ncycles);
> > +int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
> > +                               unsigned int shift, unsigned int ncycles);
> > +int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
> > +                               unsigned int shift, unsigned int ncycles);
> > +void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
> > +                            const struct atmel_smc_cs_conf *conf);
> > +void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
> > +                             const struct atmel_hsmc_reg_layout *reglayout,
> > +                             int cs, const struct atmel_smc_cs_conf *conf);
> > +void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
> > +                          struct atmel_smc_cs_conf *conf);
> > +void atmel_hsmc_cs_conf_get(struct regmap *regmap,
> > +                           const struct atmel_hsmc_reg_layout *reglayout,
> > +                           int cs, struct atmel_smc_cs_conf *conf);
> > +const struct atmel_hsmc_reg_layout *
> > +atmel_hsmc_get_reg_layout(ofnode np);
> > +
> > +#endif /* _LINUX_MFD_SYSCON_ATMEL_SMC_H_ */
> > --
> > 2.34.1
> >
>
>
> --
> Michael Nazzareno Trimarchi
> Co-Founder & Chief Executive Officer
> M. +39 347 913 2170
> michael@amarulasolutions.com
> __________________________________
>
> Amarula Solutions BV
> Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
> T. +31 (0)85 111 9172
> info@amarulasolutions.com
> www.amarulasolutions.com



-- 
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael@amarulasolutions.com
__________________________________

Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info@amarulasolutions.com
www.amarulasolutions.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC
  2022-09-30 11:02     ` Michael Nazzareno Trimarchi
@ 2022-09-30 14:00       ` Tom Rini
  0 siblings, 0 replies; 24+ messages in thread
From: Tom Rini @ 2022-09-30 14:00 UTC (permalink / raw)
  To: Michael Nazzareno Trimarchi
  Cc: Balamanikandan Gunasundar, u-boot, sandeep.sheriker,
	eugen.hristev, dario.binacchi, mihai.sain, sergiu.moga,
	claudiu.beznea, hs, tudor.ambarus, sjg, ascull, kettenis,
	jh80.chung, peng.fan, ye.li, hari.prasathge

[-- Attachment #1: Type: text/plain, Size: 1115 bytes --]

On Fri, Sep 30, 2022 at 01:02:44PM +0200, Michael Nazzareno Trimarchi wrote:
> Hi Tom
> 
> On Wed, Aug 31, 2022 at 2:53 PM Michael Nazzareno Trimarchi
> <michael@amarulasolutions.com> wrote:
> >
> > Hi
> >
> > On Mon, Aug 29, 2022 at 8:20 AM Balamanikandan Gunasundar
> > <balamanikandan.gunasundar@microchip.com> wrote:
> > >
> > > Add driver for atmel Static Memory Controller. Add helper functions to
> > > configure SMC. This file is inherited from the work done by Boris
> > > Brezillon for Linux
> > >
> >
> > This does not look like a driver.Those are helper functions. Now I
> > have no objections
> > to put it here but it will be the first one under mfd.
> >
> > I think linux commit message subject give more an idea on what exactly it is
> >
> > mfd: syscon: atmel-smc: Add new helpers to ease SMC regs manipulation
> >
> 
> Apart dts things in those patches, It's nice to know if adding mfd in
> uboot it's fine for you. At the moment
> this new file has only helper and it's not a driver. It's in same
> place in linux kernel

Yes, I think this makes sense.

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2022-09-30 14:01 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-29  6:19 [PATCH v2 0/9] Add DM support for atmel NAND driver Balamanikandan Gunasundar
2022-08-29  6:19 ` [PATCH v2 1/9] nand: atmel: Add DM based " Balamanikandan Gunasundar
2022-09-05  8:55   ` Eugen.Hristev
2022-08-29  6:19 ` [PATCH v2 2/9] nand: atmel: Add pmecc driver Balamanikandan Gunasundar
2022-08-29  6:19 ` [PATCH v2 3/9] mfd: syscon: Add atmel-matrix registers definition Balamanikandan Gunasundar
2022-08-29  6:19 ` [PATCH v2 4/9] memory: atmel-ebi: add Atmel EBI (External Bus Interface) driver Balamanikandan Gunasundar
2022-08-29  6:19 ` [PATCH v2 5/9] mfd: syscon: atmel-smc: Add driver for atmel SMC Balamanikandan Gunasundar
2022-08-31 12:53   ` Michael Nazzareno Trimarchi
2022-09-30 11:02     ` Michael Nazzareno Trimarchi
2022-09-30 14:00       ` Tom Rini
2022-08-29  6:19 ` [PATCH v2 6/9] configs: at91: sam9x60ek: Enable DM based nand driver Balamanikandan Gunasundar
2022-08-29  6:19 ` [PATCH v2 7/9] ARM: dts: at91: sam9x60: Add nodes for EBI and NAND Balamanikandan Gunasundar
2022-08-29  6:19 ` [PATCH v2 8/9] ARM: dts: at91: sam9x60ek: Enable NAND support Balamanikandan Gunasundar
2022-08-31 13:14   ` Michael Nazzareno Trimarchi
2022-08-31 13:31     ` Eugen.Hristev
2022-08-31 14:19       ` Michael Nazzareno Trimarchi
2022-09-01  5:57         ` Eugen.Hristev
2022-09-20  8:41           ` Dario Binacchi
2022-08-29  6:19 ` [PATCH v2 9/9] board: sam9x60ek: remove nand init from board file Balamanikandan Gunasundar
2022-08-29  6:41 ` [PATCH v2 0/9] Add DM support for atmel NAND driver Michael Nazzareno Trimarchi
2022-08-30  2:30   ` Simon Glass
2022-08-31  6:34     ` Eugen.Hristev
2022-08-31  6:37       ` Michael Nazzareno Trimarchi
2022-08-31 13:46       ` Simon Glass

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).