18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2017 ATMEL
48c2ecf20Sopenharmony_ci * Copyright 2017 Free Electrons
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Derived from the atmel_nand.c driver which contained the following
98c2ecf20Sopenharmony_ci * copyrights:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *   Copyright 2003 Rick Bronson
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *   Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
148c2ecf20Sopenharmony_ci *	Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *   Derived from drivers/mtd/spia.c (removed in v3.8)
178c2ecf20Sopenharmony_ci *	Copyright 2000 Steven J. Hill (sjhill@cotw.com)
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci *   Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
218c2ecf20Sopenharmony_ci *	Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci *   Derived from Das U-Boot source code
248c2ecf20Sopenharmony_ci *	(u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
258c2ecf20Sopenharmony_ci *	Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *   Add Programmable Multibit ECC support for various AT91 SoC
288c2ecf20Sopenharmony_ci *	Copyright 2012 ATMEL, Hong Xu
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci *   Add Nand Flash Controller support for SAMA5 SoC
318c2ecf20Sopenharmony_ci *	Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * A few words about the naming convention in this file. This convention
348c2ecf20Sopenharmony_ci * applies to structure and function names.
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * Prefixes:
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * - atmel_nand_: all generic structures/functions
398c2ecf20Sopenharmony_ci * - atmel_smc_nand_: all structures/functions specific to the SMC interface
408c2ecf20Sopenharmony_ci *		      (at91sam9 and avr32 SoCs)
418c2ecf20Sopenharmony_ci * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface
428c2ecf20Sopenharmony_ci *		       (sama5 SoCs and later)
438c2ecf20Sopenharmony_ci * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block
448c2ecf20Sopenharmony_ci *		 that is available in the HSMC block
458c2ecf20Sopenharmony_ci * - <soc>_nand_: all SoC specific structures/functions
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#include <linux/clk.h>
498c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
508c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
518c2ecf20Sopenharmony_ci#include <linux/genalloc.h>
528c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
538c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
548c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
558c2ecf20Sopenharmony_ci#include <linux/mfd/syscon/atmel-matrix.h>
568c2ecf20Sopenharmony_ci#include <linux/mfd/syscon/atmel-smc.h>
578c2ecf20Sopenharmony_ci#include <linux/module.h>
588c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h>
598c2ecf20Sopenharmony_ci#include <linux/of_address.h>
608c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
618c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
628c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
638c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
648c2ecf20Sopenharmony_ci#include <linux/regmap.h>
658c2ecf20Sopenharmony_ci#include <soc/at91/atmel-sfr.h>
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#include "pmecc.h"
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG			0x0
708c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x)		(((x) / 4) << 24)
718c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK	GENMASK(30, 24)
728c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul)	(((cyc) << 16) | ((mul) << 20))
738c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_DTO_MAX		GENMASK(22, 16)
748c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_RBEDGE		BIT(13)
758c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE		BIT(12)
768c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_RSPARE		BIT(9)
778c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_WSPARE		BIT(8)
788c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK	GENMASK(2, 0)
798c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x)		(fls((x) / 512) - 1)
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL			0x4
828c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL_EN			BIT(0)
838c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL_DIS			BIT(1)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR			0x8
868c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_IER			0xc
878c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_IDR			0x10
888c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_IMR			0x14
898c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_ENABLED		BIT(1)
908c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RB_RISE		BIT(4)
918c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RB_FALL		BIT(5)
928c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_BUSY			BIT(8)
938c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_WR			BIT(11)
948c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_CSID			GENMASK(14, 12)
958c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_XFRDONE		BIT(16)
968c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_CMDDONE		BIT(17)
978c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_DTOE			BIT(20)
988c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_UNDEF			BIT(21)
998c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_AWB			BIT(22)
1008c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_NFCASE		BIT(23)
1018c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_ERRORS		(ATMEL_HSMC_NFC_SR_DTOE | \
1028c2ecf20Sopenharmony_ci						 ATMEL_HSMC_NFC_SR_UNDEF | \
1038c2ecf20Sopenharmony_ci						 ATMEL_HSMC_NFC_SR_AWB | \
1048c2ecf20Sopenharmony_ci						 ATMEL_HSMC_NFC_SR_NFCASE)
1058c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RBEDGE(x)		BIT((x) + 24)
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_ADDR			0x18
1088c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_BANK			0x1c
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#define ATMEL_NFC_MAX_RB_ID			7
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci#define ATMEL_NFC_SRAM_SIZE			0x2400
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#define ATMEL_NFC_CMD(pos, cmd)			((cmd) << (((pos) * 8) + 2))
1158c2ecf20Sopenharmony_ci#define ATMEL_NFC_VCMD2				BIT(18)
1168c2ecf20Sopenharmony_ci#define ATMEL_NFC_ACYCLE(naddrs)		((naddrs) << 19)
1178c2ecf20Sopenharmony_ci#define ATMEL_NFC_CSID(cs)			((cs) << 22)
1188c2ecf20Sopenharmony_ci#define ATMEL_NFC_DATAEN			BIT(25)
1198c2ecf20Sopenharmony_ci#define ATMEL_NFC_NFCWR				BIT(26)
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#define ATMEL_NFC_MAX_ADDR_CYCLES		5
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define ATMEL_NAND_ALE_OFFSET			BIT(21)
1248c2ecf20Sopenharmony_ci#define ATMEL_NAND_CLE_OFFSET			BIT(22)
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci#define DEFAULT_TIMEOUT_MS			1000
1278c2ecf20Sopenharmony_ci#define MIN_DMA_LEN				128
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic bool atmel_nand_avoid_dma __read_mostly;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(avoiddma, "Avoid using DMA");
1328c2ecf20Sopenharmony_cimodule_param_named(avoiddma, atmel_nand_avoid_dma, bool, 0400);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cienum atmel_nand_rb_type {
1358c2ecf20Sopenharmony_ci	ATMEL_NAND_NO_RB,
1368c2ecf20Sopenharmony_ci	ATMEL_NAND_NATIVE_RB,
1378c2ecf20Sopenharmony_ci	ATMEL_NAND_GPIO_RB,
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistruct atmel_nand_rb {
1418c2ecf20Sopenharmony_ci	enum atmel_nand_rb_type type;
1428c2ecf20Sopenharmony_ci	union {
1438c2ecf20Sopenharmony_ci		struct gpio_desc *gpio;
1448c2ecf20Sopenharmony_ci		int id;
1458c2ecf20Sopenharmony_ci	};
1468c2ecf20Sopenharmony_ci};
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistruct atmel_nand_cs {
1498c2ecf20Sopenharmony_ci	int id;
1508c2ecf20Sopenharmony_ci	struct atmel_nand_rb rb;
1518c2ecf20Sopenharmony_ci	struct gpio_desc *csgpio;
1528c2ecf20Sopenharmony_ci	struct {
1538c2ecf20Sopenharmony_ci		void __iomem *virt;
1548c2ecf20Sopenharmony_ci		dma_addr_t dma;
1558c2ecf20Sopenharmony_ci	} io;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	struct atmel_smc_cs_conf smcconf;
1588c2ecf20Sopenharmony_ci};
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistruct atmel_nand {
1618c2ecf20Sopenharmony_ci	struct list_head node;
1628c2ecf20Sopenharmony_ci	struct device *dev;
1638c2ecf20Sopenharmony_ci	struct nand_chip base;
1648c2ecf20Sopenharmony_ci	struct atmel_nand_cs *activecs;
1658c2ecf20Sopenharmony_ci	struct atmel_pmecc_user *pmecc;
1668c2ecf20Sopenharmony_ci	struct gpio_desc *cdgpio;
1678c2ecf20Sopenharmony_ci	int numcs;
1688c2ecf20Sopenharmony_ci	struct atmel_nand_cs cs[];
1698c2ecf20Sopenharmony_ci};
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	return container_of(chip, struct atmel_nand, base);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cienum atmel_nfc_data_xfer {
1778c2ecf20Sopenharmony_ci	ATMEL_NFC_NO_DATA,
1788c2ecf20Sopenharmony_ci	ATMEL_NFC_READ_DATA,
1798c2ecf20Sopenharmony_ci	ATMEL_NFC_WRITE_DATA,
1808c2ecf20Sopenharmony_ci};
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistruct atmel_nfc_op {
1838c2ecf20Sopenharmony_ci	u8 cs;
1848c2ecf20Sopenharmony_ci	u8 ncmds;
1858c2ecf20Sopenharmony_ci	u8 cmds[2];
1868c2ecf20Sopenharmony_ci	u8 naddrs;
1878c2ecf20Sopenharmony_ci	u8 addrs[5];
1888c2ecf20Sopenharmony_ci	enum atmel_nfc_data_xfer data;
1898c2ecf20Sopenharmony_ci	u32 wait;
1908c2ecf20Sopenharmony_ci	u32 errors;
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistruct atmel_nand_controller;
1948c2ecf20Sopenharmony_cistruct atmel_nand_controller_caps;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistruct atmel_nand_controller_ops {
1978c2ecf20Sopenharmony_ci	int (*probe)(struct platform_device *pdev,
1988c2ecf20Sopenharmony_ci		     const struct atmel_nand_controller_caps *caps);
1998c2ecf20Sopenharmony_ci	int (*remove)(struct atmel_nand_controller *nc);
2008c2ecf20Sopenharmony_ci	void (*nand_init)(struct atmel_nand_controller *nc,
2018c2ecf20Sopenharmony_ci			  struct atmel_nand *nand);
2028c2ecf20Sopenharmony_ci	int (*ecc_init)(struct nand_chip *chip);
2038c2ecf20Sopenharmony_ci	int (*setup_interface)(struct atmel_nand *nand, int csline,
2048c2ecf20Sopenharmony_ci			       const struct nand_interface_config *conf);
2058c2ecf20Sopenharmony_ci	int (*exec_op)(struct atmel_nand *nand,
2068c2ecf20Sopenharmony_ci		       const struct nand_operation *op, bool check_only);
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistruct atmel_nand_controller_caps {
2108c2ecf20Sopenharmony_ci	bool has_dma;
2118c2ecf20Sopenharmony_ci	bool legacy_of_bindings;
2128c2ecf20Sopenharmony_ci	u32 ale_offs;
2138c2ecf20Sopenharmony_ci	u32 cle_offs;
2148c2ecf20Sopenharmony_ci	const char *ebi_csa_regmap_name;
2158c2ecf20Sopenharmony_ci	const struct atmel_nand_controller_ops *ops;
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistruct atmel_nand_controller {
2198c2ecf20Sopenharmony_ci	struct nand_controller base;
2208c2ecf20Sopenharmony_ci	const struct atmel_nand_controller_caps *caps;
2218c2ecf20Sopenharmony_ci	struct device *dev;
2228c2ecf20Sopenharmony_ci	struct regmap *smc;
2238c2ecf20Sopenharmony_ci	struct dma_chan *dmac;
2248c2ecf20Sopenharmony_ci	struct atmel_pmecc *pmecc;
2258c2ecf20Sopenharmony_ci	struct list_head chips;
2268c2ecf20Sopenharmony_ci	struct clk *mck;
2278c2ecf20Sopenharmony_ci};
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic inline struct atmel_nand_controller *
2308c2ecf20Sopenharmony_cito_nand_controller(struct nand_controller *ctl)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	return container_of(ctl, struct atmel_nand_controller, base);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistruct atmel_smc_nand_ebi_csa_cfg {
2368c2ecf20Sopenharmony_ci	u32 offs;
2378c2ecf20Sopenharmony_ci	u32 nfd0_on_d16;
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistruct atmel_smc_nand_controller {
2418c2ecf20Sopenharmony_ci	struct atmel_nand_controller base;
2428c2ecf20Sopenharmony_ci	struct regmap *ebi_csa_regmap;
2438c2ecf20Sopenharmony_ci	struct atmel_smc_nand_ebi_csa_cfg *ebi_csa;
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic inline struct atmel_smc_nand_controller *
2478c2ecf20Sopenharmony_cito_smc_nand_controller(struct nand_controller *ctl)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	return container_of(to_nand_controller(ctl),
2508c2ecf20Sopenharmony_ci			    struct atmel_smc_nand_controller, base);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistruct atmel_hsmc_nand_controller {
2548c2ecf20Sopenharmony_ci	struct atmel_nand_controller base;
2558c2ecf20Sopenharmony_ci	struct {
2568c2ecf20Sopenharmony_ci		struct gen_pool *pool;
2578c2ecf20Sopenharmony_ci		void __iomem *virt;
2588c2ecf20Sopenharmony_ci		dma_addr_t dma;
2598c2ecf20Sopenharmony_ci	} sram;
2608c2ecf20Sopenharmony_ci	const struct atmel_hsmc_reg_layout *hsmc_layout;
2618c2ecf20Sopenharmony_ci	struct regmap *io;
2628c2ecf20Sopenharmony_ci	struct atmel_nfc_op op;
2638c2ecf20Sopenharmony_ci	struct completion complete;
2648c2ecf20Sopenharmony_ci	u32 cfg;
2658c2ecf20Sopenharmony_ci	int irq;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	/* Only used when instantiating from legacy DT bindings. */
2688c2ecf20Sopenharmony_ci	struct clk *clk;
2698c2ecf20Sopenharmony_ci};
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic inline struct atmel_hsmc_nand_controller *
2728c2ecf20Sopenharmony_cito_hsmc_nand_controller(struct nand_controller *ctl)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	return container_of(to_nand_controller(ctl),
2758c2ecf20Sopenharmony_ci			    struct atmel_hsmc_nand_controller, base);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS;
2818c2ecf20Sopenharmony_ci	op->wait ^= status & op->wait;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return !op->wait || op->errors;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic irqreturn_t atmel_nfc_interrupt(int irq, void *data)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc = data;
2898c2ecf20Sopenharmony_ci	u32 sr, rcvd;
2908c2ecf20Sopenharmony_ci	bool done;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
2958c2ecf20Sopenharmony_ci	done = atmel_nfc_op_done(&nc->op, sr);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (rcvd)
2988c2ecf20Sopenharmony_ci		regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (done)
3018c2ecf20Sopenharmony_ci		complete(&nc->complete);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	return rcvd ? IRQ_HANDLED : IRQ_NONE;
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll,
3078c2ecf20Sopenharmony_ci			  unsigned int timeout_ms)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	int ret;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (!timeout_ms)
3128c2ecf20Sopenharmony_ci		timeout_ms = DEFAULT_TIMEOUT_MS;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (poll) {
3158c2ecf20Sopenharmony_ci		u32 status;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		ret = regmap_read_poll_timeout(nc->base.smc,
3188c2ecf20Sopenharmony_ci					       ATMEL_HSMC_NFC_SR, status,
3198c2ecf20Sopenharmony_ci					       atmel_nfc_op_done(&nc->op,
3208c2ecf20Sopenharmony_ci								 status),
3218c2ecf20Sopenharmony_ci					       0, timeout_ms * 1000);
3228c2ecf20Sopenharmony_ci	} else {
3238c2ecf20Sopenharmony_ci		init_completion(&nc->complete);
3248c2ecf20Sopenharmony_ci		regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER,
3258c2ecf20Sopenharmony_ci			     nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
3268c2ecf20Sopenharmony_ci		ret = wait_for_completion_timeout(&nc->complete,
3278c2ecf20Sopenharmony_ci						msecs_to_jiffies(timeout_ms));
3288c2ecf20Sopenharmony_ci		if (!ret)
3298c2ecf20Sopenharmony_ci			ret = -ETIMEDOUT;
3308c2ecf20Sopenharmony_ci		else
3318c2ecf20Sopenharmony_ci			ret = 0;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) {
3378c2ecf20Sopenharmony_ci		dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n");
3388c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) {
3428c2ecf20Sopenharmony_ci		dev_err(nc->base.dev, "Access to an undefined area\n");
3438c2ecf20Sopenharmony_ci		ret = -EIO;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) {
3478c2ecf20Sopenharmony_ci		dev_err(nc->base.dev, "Access while busy\n");
3488c2ecf20Sopenharmony_ci		ret = -EIO;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) {
3528c2ecf20Sopenharmony_ci		dev_err(nc->base.dev, "Wrong access size\n");
3538c2ecf20Sopenharmony_ci		ret = -EIO;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return ret;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void atmel_nand_dma_transfer_finished(void *data)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct completion *finished = data;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	complete(finished);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
3678c2ecf20Sopenharmony_ci				   void *buf, dma_addr_t dev_dma, size_t len,
3688c2ecf20Sopenharmony_ci				   enum dma_data_direction dir)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(finished);
3718c2ecf20Sopenharmony_ci	dma_addr_t src_dma, dst_dma, buf_dma;
3728c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
3738c2ecf20Sopenharmony_ci	dma_cookie_t cookie;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	buf_dma = dma_map_single(nc->dev, buf, len, dir);
3768c2ecf20Sopenharmony_ci	if (dma_mapping_error(nc->dev, dev_dma)) {
3778c2ecf20Sopenharmony_ci		dev_err(nc->dev,
3788c2ecf20Sopenharmony_ci			"Failed to prepare a buffer for DMA access\n");
3798c2ecf20Sopenharmony_ci		goto err;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (dir == DMA_FROM_DEVICE) {
3838c2ecf20Sopenharmony_ci		src_dma = dev_dma;
3848c2ecf20Sopenharmony_ci		dst_dma = buf_dma;
3858c2ecf20Sopenharmony_ci	} else {
3868c2ecf20Sopenharmony_ci		src_dma = buf_dma;
3878c2ecf20Sopenharmony_ci		dst_dma = dev_dma;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len,
3918c2ecf20Sopenharmony_ci				       DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
3928c2ecf20Sopenharmony_ci	if (!tx) {
3938c2ecf20Sopenharmony_ci		dev_err(nc->dev, "Failed to prepare DMA memcpy\n");
3948c2ecf20Sopenharmony_ci		goto err_unmap;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	tx->callback = atmel_nand_dma_transfer_finished;
3988c2ecf20Sopenharmony_ci	tx->callback_param = &finished;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	cookie = dmaengine_submit(tx);
4018c2ecf20Sopenharmony_ci	if (dma_submit_error(cookie)) {
4028c2ecf20Sopenharmony_ci		dev_err(nc->dev, "Failed to do DMA tx_submit\n");
4038c2ecf20Sopenharmony_ci		goto err_unmap;
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	dma_async_issue_pending(nc->dmac);
4078c2ecf20Sopenharmony_ci	wait_for_completion(&finished);
4088c2ecf20Sopenharmony_ci	dma_unmap_single(nc->dev, buf_dma, len, dir);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cierr_unmap:
4138c2ecf20Sopenharmony_ci	dma_unmap_single(nc->dev, buf_dma, len, dir);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cierr:
4168c2ecf20Sopenharmony_ci	dev_dbg(nc->dev, "Fall back to CPU I/O\n");
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return -EIO;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	u8 *addrs = nc->op.addrs;
4248c2ecf20Sopenharmony_ci	unsigned int op = 0;
4258c2ecf20Sopenharmony_ci	u32 addr, val;
4268c2ecf20Sopenharmony_ci	int i, ret;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	for (i = 0; i < nc->op.ncmds; i++)
4318c2ecf20Sopenharmony_ci		op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
4348c2ecf20Sopenharmony_ci		regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	op |= ATMEL_NFC_CSID(nc->op.cs) |
4378c2ecf20Sopenharmony_ci	      ATMEL_NFC_ACYCLE(nc->op.naddrs);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (nc->op.ncmds > 1)
4408c2ecf20Sopenharmony_ci		op |= ATMEL_NFC_VCMD2;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) |
4438c2ecf20Sopenharmony_ci	       (addrs[3] << 24);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (nc->op.data != ATMEL_NFC_NO_DATA) {
4468c2ecf20Sopenharmony_ci		op |= ATMEL_NFC_DATAEN;
4478c2ecf20Sopenharmony_ci		nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		if (nc->op.data == ATMEL_NFC_WRITE_DATA)
4508c2ecf20Sopenharmony_ci			op |= ATMEL_NFC_NFCWR;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/* Clear all flags. */
4548c2ecf20Sopenharmony_ci	regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* Send the command. */
4578c2ecf20Sopenharmony_ci	regmap_write(nc->io, op, addr);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	ret = atmel_nfc_wait(nc, poll, 0);
4608c2ecf20Sopenharmony_ci	if (ret)
4618c2ecf20Sopenharmony_ci		dev_err(nc->base.dev,
4628c2ecf20Sopenharmony_ci			"Failed to send NAND command (err = %d)!",
4638c2ecf20Sopenharmony_ci			ret);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	/* Reset the op state. */
4668c2ecf20Sopenharmony_ci	memset(&nc->op, 0, sizeof(nc->op));
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return ret;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic void atmel_nand_data_in(struct atmel_nand *nand, void *buf,
4728c2ecf20Sopenharmony_ci			       unsigned int len, bool force_8bit)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	nc = to_nand_controller(nand->base.controller);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	/*
4798c2ecf20Sopenharmony_ci	 * If the controller supports DMA, the buffer address is DMA-able and
4808c2ecf20Sopenharmony_ci	 * len is long enough to make DMA transfers profitable, let's trigger
4818c2ecf20Sopenharmony_ci	 * a DMA transfer. If it fails, fallback to PIO mode.
4828c2ecf20Sopenharmony_ci	 */
4838c2ecf20Sopenharmony_ci	if (nc->dmac && virt_addr_valid(buf) &&
4848c2ecf20Sopenharmony_ci	    len >= MIN_DMA_LEN && !force_8bit &&
4858c2ecf20Sopenharmony_ci	    !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len,
4868c2ecf20Sopenharmony_ci				     DMA_FROM_DEVICE))
4878c2ecf20Sopenharmony_ci		return;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit)
4908c2ecf20Sopenharmony_ci		ioread16_rep(nand->activecs->io.virt, buf, len / 2);
4918c2ecf20Sopenharmony_ci	else
4928c2ecf20Sopenharmony_ci		ioread8_rep(nand->activecs->io.virt, buf, len);
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic void atmel_nand_data_out(struct atmel_nand *nand, const void *buf,
4968c2ecf20Sopenharmony_ci				unsigned int len, bool force_8bit)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	nc = to_nand_controller(nand->base.controller);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/*
5038c2ecf20Sopenharmony_ci	 * If the controller supports DMA, the buffer address is DMA-able and
5048c2ecf20Sopenharmony_ci	 * len is long enough to make DMA transfers profitable, let's trigger
5058c2ecf20Sopenharmony_ci	 * a DMA transfer. If it fails, fallback to PIO mode.
5068c2ecf20Sopenharmony_ci	 */
5078c2ecf20Sopenharmony_ci	if (nc->dmac && virt_addr_valid(buf) &&
5088c2ecf20Sopenharmony_ci	    len >= MIN_DMA_LEN && !force_8bit &&
5098c2ecf20Sopenharmony_ci	    !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma,
5108c2ecf20Sopenharmony_ci				     len, DMA_TO_DEVICE))
5118c2ecf20Sopenharmony_ci		return;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit)
5148c2ecf20Sopenharmony_ci		iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
5158c2ecf20Sopenharmony_ci	else
5168c2ecf20Sopenharmony_ci		iowrite8_rep(nand->activecs->io.virt, buf, len);
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int atmel_nand_waitrdy(struct atmel_nand *nand, unsigned int timeout_ms)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	if (nand->activecs->rb.type == ATMEL_NAND_NO_RB)
5228c2ecf20Sopenharmony_ci		return nand_soft_waitrdy(&nand->base, timeout_ms);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	return nand_gpio_waitrdy(&nand->base, nand->activecs->rb.gpio,
5258c2ecf20Sopenharmony_ci				 timeout_ms);
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_waitrdy(struct atmel_nand *nand,
5298c2ecf20Sopenharmony_ci				   unsigned int timeout_ms)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
5328c2ecf20Sopenharmony_ci	u32 status, mask;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB)
5358c2ecf20Sopenharmony_ci		return atmel_nand_waitrdy(nand, timeout_ms);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(nand->base.controller);
5388c2ecf20Sopenharmony_ci	mask = ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
5398c2ecf20Sopenharmony_ci	return regmap_read_poll_timeout_atomic(nc->base.smc, ATMEL_HSMC_NFC_SR,
5408c2ecf20Sopenharmony_ci					       status, status & mask,
5418c2ecf20Sopenharmony_ci					       10, timeout_ms * 1000);
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic void atmel_nand_select_target(struct atmel_nand *nand,
5458c2ecf20Sopenharmony_ci				     unsigned int cs)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	nand->activecs = &nand->cs[cs];
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void atmel_hsmc_nand_select_target(struct atmel_nand *nand,
5518c2ecf20Sopenharmony_ci					  unsigned int cs)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&nand->base);
5548c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
5558c2ecf20Sopenharmony_ci	u32 cfg = ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
5568c2ecf20Sopenharmony_ci		  ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
5578c2ecf20Sopenharmony_ci		  ATMEL_HSMC_NFC_CFG_RSPARE;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	nand->activecs = &nand->cs[cs];
5608c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(nand->base.controller);
5618c2ecf20Sopenharmony_ci	if (nc->cfg == cfg)
5628c2ecf20Sopenharmony_ci		return;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
5658c2ecf20Sopenharmony_ci			   ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
5668c2ecf20Sopenharmony_ci			   ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
5678c2ecf20Sopenharmony_ci			   ATMEL_HSMC_NFC_CFG_RSPARE |
5688c2ecf20Sopenharmony_ci			   ATMEL_HSMC_NFC_CFG_WSPARE,
5698c2ecf20Sopenharmony_ci			   cfg);
5708c2ecf20Sopenharmony_ci	nc->cfg = cfg;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_cistatic int atmel_smc_nand_exec_instr(struct atmel_nand *nand,
5748c2ecf20Sopenharmony_ci				     const struct nand_op_instr *instr)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
5778c2ecf20Sopenharmony_ci	unsigned int i;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	nc = to_nand_controller(nand->base.controller);
5808c2ecf20Sopenharmony_ci	switch (instr->type) {
5818c2ecf20Sopenharmony_ci	case NAND_OP_CMD_INSTR:
5828c2ecf20Sopenharmony_ci		writeb(instr->ctx.cmd.opcode,
5838c2ecf20Sopenharmony_ci		       nand->activecs->io.virt + nc->caps->cle_offs);
5848c2ecf20Sopenharmony_ci		return 0;
5858c2ecf20Sopenharmony_ci	case NAND_OP_ADDR_INSTR:
5868c2ecf20Sopenharmony_ci		for (i = 0; i < instr->ctx.addr.naddrs; i++)
5878c2ecf20Sopenharmony_ci			writeb(instr->ctx.addr.addrs[i],
5888c2ecf20Sopenharmony_ci			       nand->activecs->io.virt + nc->caps->ale_offs);
5898c2ecf20Sopenharmony_ci		return 0;
5908c2ecf20Sopenharmony_ci	case NAND_OP_DATA_IN_INSTR:
5918c2ecf20Sopenharmony_ci		atmel_nand_data_in(nand, instr->ctx.data.buf.in,
5928c2ecf20Sopenharmony_ci				   instr->ctx.data.len,
5938c2ecf20Sopenharmony_ci				   instr->ctx.data.force_8bit);
5948c2ecf20Sopenharmony_ci		return 0;
5958c2ecf20Sopenharmony_ci	case NAND_OP_DATA_OUT_INSTR:
5968c2ecf20Sopenharmony_ci		atmel_nand_data_out(nand, instr->ctx.data.buf.out,
5978c2ecf20Sopenharmony_ci				    instr->ctx.data.len,
5988c2ecf20Sopenharmony_ci				    instr->ctx.data.force_8bit);
5998c2ecf20Sopenharmony_ci		return 0;
6008c2ecf20Sopenharmony_ci	case NAND_OP_WAITRDY_INSTR:
6018c2ecf20Sopenharmony_ci		return atmel_nand_waitrdy(nand,
6028c2ecf20Sopenharmony_ci					  instr->ctx.waitrdy.timeout_ms);
6038c2ecf20Sopenharmony_ci	default:
6048c2ecf20Sopenharmony_ci		break;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return -EINVAL;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int atmel_smc_nand_exec_op(struct atmel_nand *nand,
6118c2ecf20Sopenharmony_ci				  const struct nand_operation *op,
6128c2ecf20Sopenharmony_ci				  bool check_only)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	unsigned int i;
6158c2ecf20Sopenharmony_ci	int ret = 0;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	if (check_only)
6188c2ecf20Sopenharmony_ci		return 0;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	atmel_nand_select_target(nand, op->cs);
6218c2ecf20Sopenharmony_ci	gpiod_set_value(nand->activecs->csgpio, 0);
6228c2ecf20Sopenharmony_ci	for (i = 0; i < op->ninstrs; i++) {
6238c2ecf20Sopenharmony_ci		ret = atmel_smc_nand_exec_instr(nand, &op->instrs[i]);
6248c2ecf20Sopenharmony_ci		if (ret)
6258c2ecf20Sopenharmony_ci			break;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci	gpiod_set_value(nand->activecs->csgpio, 1);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return ret;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic int atmel_hsmc_exec_cmd_addr(struct nand_chip *chip,
6338c2ecf20Sopenharmony_ci				    const struct nand_subop *subop)
6348c2ecf20Sopenharmony_ci{
6358c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
6368c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
6378c2ecf20Sopenharmony_ci	unsigned int i, j;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(chip->controller);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	nc->op.cs = nand->activecs->id;
6428c2ecf20Sopenharmony_ci	for (i = 0; i < subop->ninstrs; i++) {
6438c2ecf20Sopenharmony_ci		const struct nand_op_instr *instr = &subop->instrs[i];
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci		if (instr->type == NAND_OP_CMD_INSTR) {
6468c2ecf20Sopenharmony_ci			nc->op.cmds[nc->op.ncmds++] = instr->ctx.cmd.opcode;
6478c2ecf20Sopenharmony_ci			continue;
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		for (j = nand_subop_get_addr_start_off(subop, i);
6518c2ecf20Sopenharmony_ci		     j < nand_subop_get_num_addr_cyc(subop, i); j++) {
6528c2ecf20Sopenharmony_ci			nc->op.addrs[nc->op.naddrs] = instr->ctx.addr.addrs[j];
6538c2ecf20Sopenharmony_ci			nc->op.naddrs++;
6548c2ecf20Sopenharmony_ci		}
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return atmel_nfc_exec_op(nc, true);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic int atmel_hsmc_exec_rw(struct nand_chip *chip,
6618c2ecf20Sopenharmony_ci			      const struct nand_subop *subop)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	const struct nand_op_instr *instr = subop->instrs;
6648c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (instr->type == NAND_OP_DATA_IN_INSTR)
6678c2ecf20Sopenharmony_ci		atmel_nand_data_in(nand, instr->ctx.data.buf.in,
6688c2ecf20Sopenharmony_ci				   instr->ctx.data.len,
6698c2ecf20Sopenharmony_ci				   instr->ctx.data.force_8bit);
6708c2ecf20Sopenharmony_ci	else
6718c2ecf20Sopenharmony_ci		atmel_nand_data_out(nand, instr->ctx.data.buf.out,
6728c2ecf20Sopenharmony_ci				    instr->ctx.data.len,
6738c2ecf20Sopenharmony_ci				    instr->ctx.data.force_8bit);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	return 0;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cistatic int atmel_hsmc_exec_waitrdy(struct nand_chip *chip,
6798c2ecf20Sopenharmony_ci				   const struct nand_subop *subop)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	const struct nand_op_instr *instr = subop->instrs;
6828c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	return atmel_hsmc_nand_waitrdy(nand, instr->ctx.waitrdy.timeout_ms);
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic const struct nand_op_parser atmel_hsmc_op_parser = NAND_OP_PARSER(
6888c2ecf20Sopenharmony_ci	NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_cmd_addr,
6898c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_CMD_ELEM(true),
6908c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5),
6918c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_CMD_ELEM(true)),
6928c2ecf20Sopenharmony_ci	NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw,
6938c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 0)),
6948c2ecf20Sopenharmony_ci	NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw,
6958c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 0)),
6968c2ecf20Sopenharmony_ci	NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_waitrdy,
6978c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
6988c2ecf20Sopenharmony_ci);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_exec_op(struct atmel_nand *nand,
7018c2ecf20Sopenharmony_ci				   const struct nand_operation *op,
7028c2ecf20Sopenharmony_ci				   bool check_only)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	int ret;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	if (check_only)
7078c2ecf20Sopenharmony_ci		return nand_op_parser_exec_op(&nand->base,
7088c2ecf20Sopenharmony_ci					      &atmel_hsmc_op_parser, op, true);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	atmel_hsmc_nand_select_target(nand, op->cs);
7118c2ecf20Sopenharmony_ci	ret = nand_op_parser_exec_op(&nand->base, &atmel_hsmc_op_parser, op,
7128c2ecf20Sopenharmony_ci				     false);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	return ret;
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
7188c2ecf20Sopenharmony_ci				   bool oob_required)
7198c2ecf20Sopenharmony_ci{
7208c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
7218c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
7228c2ecf20Sopenharmony_ci	int ret = -EIO;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(chip->controller);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (nc->base.dmac)
7278c2ecf20Sopenharmony_ci		ret = atmel_nand_dma_transfer(&nc->base, (void *)buf,
7288c2ecf20Sopenharmony_ci					      nc->sram.dma, mtd->writesize,
7298c2ecf20Sopenharmony_ci					      DMA_TO_DEVICE);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	/* Falling back to CPU copy. */
7328c2ecf20Sopenharmony_ci	if (ret)
7338c2ecf20Sopenharmony_ci		memcpy_toio(nc->sram.virt, buf, mtd->writesize);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (oob_required)
7368c2ecf20Sopenharmony_ci		memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi,
7378c2ecf20Sopenharmony_ci			    mtd->oobsize);
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf,
7418c2ecf20Sopenharmony_ci				     bool oob_required)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
7448c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
7458c2ecf20Sopenharmony_ci	int ret = -EIO;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(chip->controller);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (nc->base.dmac)
7508c2ecf20Sopenharmony_ci		ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma,
7518c2ecf20Sopenharmony_ci					      mtd->writesize, DMA_FROM_DEVICE);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	/* Falling back to CPU copy. */
7548c2ecf20Sopenharmony_ci	if (ret)
7558c2ecf20Sopenharmony_ci		memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (oob_required)
7588c2ecf20Sopenharmony_ci		memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,
7598c2ecf20Sopenharmony_ci			      mtd->oobsize);
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistatic void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
7658c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(chip->controller);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (column >= 0) {
7708c2ecf20Sopenharmony_ci		nc->op.addrs[nc->op.naddrs++] = column;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci		/*
7738c2ecf20Sopenharmony_ci		 * 2 address cycles for the column offset on large page NANDs.
7748c2ecf20Sopenharmony_ci		 */
7758c2ecf20Sopenharmony_ci		if (mtd->writesize > 512)
7768c2ecf20Sopenharmony_ci			nc->op.addrs[nc->op.naddrs++] = column >> 8;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	if (page >= 0) {
7808c2ecf20Sopenharmony_ci		nc->op.addrs[nc->op.naddrs++] = page;
7818c2ecf20Sopenharmony_ci		nc->op.addrs[nc->op.naddrs++] = page >> 8;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		if (chip->options & NAND_ROW_ADDR_3)
7848c2ecf20Sopenharmony_ci			nc->op.addrs[nc->op.naddrs++] = page >> 16;
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
7918c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
7928c2ecf20Sopenharmony_ci	int ret;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	nc = to_nand_controller(chip->controller);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (raw)
7978c2ecf20Sopenharmony_ci		return 0;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	ret = atmel_pmecc_enable(nand->pmecc, op);
8008c2ecf20Sopenharmony_ci	if (ret)
8018c2ecf20Sopenharmony_ci		dev_err(nc->dev,
8028c2ecf20Sopenharmony_ci			"Failed to enable ECC engine (err = %d)\n", ret);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	return ret;
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (!raw)
8128c2ecf20Sopenharmony_ci		atmel_pmecc_disable(nand->pmecc);
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
8188c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
8198c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
8208c2ecf20Sopenharmony_ci	struct mtd_oob_region oobregion;
8218c2ecf20Sopenharmony_ci	void *eccbuf;
8228c2ecf20Sopenharmony_ci	int ret, i;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	nc = to_nand_controller(chip->controller);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (raw)
8278c2ecf20Sopenharmony_ci		return 0;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	ret = atmel_pmecc_wait_rdy(nand->pmecc);
8308c2ecf20Sopenharmony_ci	if (ret) {
8318c2ecf20Sopenharmony_ci		dev_err(nc->dev,
8328c2ecf20Sopenharmony_ci			"Failed to transfer NAND page data (err = %d)\n",
8338c2ecf20Sopenharmony_ci			ret);
8348c2ecf20Sopenharmony_ci		return ret;
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	mtd_ooblayout_ecc(mtd, 0, &oobregion);
8388c2ecf20Sopenharmony_ci	eccbuf = chip->oob_poi + oobregion.offset;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	for (i = 0; i < chip->ecc.steps; i++) {
8418c2ecf20Sopenharmony_ci		atmel_pmecc_get_generated_eccbytes(nand->pmecc, i,
8428c2ecf20Sopenharmony_ci						   eccbuf);
8438c2ecf20Sopenharmony_ci		eccbuf += chip->ecc.bytes;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	return 0;
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf,
8508c2ecf20Sopenharmony_ci					 bool raw)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
8538c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
8548c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
8558c2ecf20Sopenharmony_ci	struct mtd_oob_region oobregion;
8568c2ecf20Sopenharmony_ci	int ret, i, max_bitflips = 0;
8578c2ecf20Sopenharmony_ci	void *databuf, *eccbuf;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	nc = to_nand_controller(chip->controller);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	if (raw)
8628c2ecf20Sopenharmony_ci		return 0;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	ret = atmel_pmecc_wait_rdy(nand->pmecc);
8658c2ecf20Sopenharmony_ci	if (ret) {
8668c2ecf20Sopenharmony_ci		dev_err(nc->dev,
8678c2ecf20Sopenharmony_ci			"Failed to read NAND page data (err = %d)\n",
8688c2ecf20Sopenharmony_ci			ret);
8698c2ecf20Sopenharmony_ci		return ret;
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	mtd_ooblayout_ecc(mtd, 0, &oobregion);
8738c2ecf20Sopenharmony_ci	eccbuf = chip->oob_poi + oobregion.offset;
8748c2ecf20Sopenharmony_ci	databuf = buf;
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	for (i = 0; i < chip->ecc.steps; i++) {
8778c2ecf20Sopenharmony_ci		ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf,
8788c2ecf20Sopenharmony_ci						 eccbuf);
8798c2ecf20Sopenharmony_ci		if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc))
8808c2ecf20Sopenharmony_ci			ret = nand_check_erased_ecc_chunk(databuf,
8818c2ecf20Sopenharmony_ci							  chip->ecc.size,
8828c2ecf20Sopenharmony_ci							  eccbuf,
8838c2ecf20Sopenharmony_ci							  chip->ecc.bytes,
8848c2ecf20Sopenharmony_ci							  NULL, 0,
8858c2ecf20Sopenharmony_ci							  chip->ecc.strength);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci		if (ret >= 0) {
8888c2ecf20Sopenharmony_ci			mtd->ecc_stats.corrected += ret;
8898c2ecf20Sopenharmony_ci			max_bitflips = max(ret, max_bitflips);
8908c2ecf20Sopenharmony_ci		} else {
8918c2ecf20Sopenharmony_ci			mtd->ecc_stats.failed++;
8928c2ecf20Sopenharmony_ci		}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci		databuf += chip->ecc.size;
8958c2ecf20Sopenharmony_ci		eccbuf += chip->ecc.bytes;
8968c2ecf20Sopenharmony_ci	}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	return max_bitflips;
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,
9028c2ecf20Sopenharmony_ci				     bool oob_required, int page, bool raw)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
9058c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
9068c2ecf20Sopenharmony_ci	int ret;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
9118c2ecf20Sopenharmony_ci	if (ret)
9128c2ecf20Sopenharmony_ci		return ret;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	nand_write_data_op(chip, buf, mtd->writesize, false);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
9178c2ecf20Sopenharmony_ci	if (ret) {
9188c2ecf20Sopenharmony_ci		atmel_pmecc_disable(nand->pmecc);
9198c2ecf20Sopenharmony_ci		return ret;
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	atmel_nand_pmecc_disable(chip, raw);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_write_page(struct nand_chip *chip, const u8 *buf,
9308c2ecf20Sopenharmony_ci				       int oob_required, int page)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_write_page_raw(struct nand_chip *chip,
9368c2ecf20Sopenharmony_ci					   const u8 *buf, int oob_required,
9378c2ecf20Sopenharmony_ci					   int page)
9388c2ecf20Sopenharmony_ci{
9398c2ecf20Sopenharmony_ci	return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true);
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
9438c2ecf20Sopenharmony_ci				    bool oob_required, int page, bool raw)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
9468c2ecf20Sopenharmony_ci	int ret;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	nand_read_page_op(chip, page, 0, NULL, 0);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
9518c2ecf20Sopenharmony_ci	if (ret)
9528c2ecf20Sopenharmony_ci		return ret;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	ret = nand_read_data_op(chip, buf, mtd->writesize, false, false);
9558c2ecf20Sopenharmony_ci	if (ret)
9568c2ecf20Sopenharmony_ci		goto out_disable;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, false);
9598c2ecf20Sopenharmony_ci	if (ret)
9608c2ecf20Sopenharmony_ci		goto out_disable;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ciout_disable:
9658c2ecf20Sopenharmony_ci	atmel_nand_pmecc_disable(chip, raw);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	return ret;
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf,
9718c2ecf20Sopenharmony_ci				      int oob_required, int page)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false);
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_read_page_raw(struct nand_chip *chip, u8 *buf,
9778c2ecf20Sopenharmony_ci					  int oob_required, int page)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true);
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
9838c2ecf20Sopenharmony_ci					  const u8 *buf, bool oob_required,
9848c2ecf20Sopenharmony_ci					  int page, bool raw)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
9878c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
9888c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
9898c2ecf20Sopenharmony_ci	int ret;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	atmel_hsmc_nand_select_target(nand, chip->cur_cs);
9928c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(chip->controller);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	atmel_nfc_copy_to_sram(chip, buf, false);
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	nc->op.cmds[0] = NAND_CMD_SEQIN;
9978c2ecf20Sopenharmony_ci	nc->op.ncmds = 1;
9988c2ecf20Sopenharmony_ci	atmel_nfc_set_op_addr(chip, page, 0x0);
9998c2ecf20Sopenharmony_ci	nc->op.cs = nand->activecs->id;
10008c2ecf20Sopenharmony_ci	nc->op.data = ATMEL_NFC_WRITE_DATA;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
10038c2ecf20Sopenharmony_ci	if (ret)
10048c2ecf20Sopenharmony_ci		return ret;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	ret = atmel_nfc_exec_op(nc, false);
10078c2ecf20Sopenharmony_ci	if (ret) {
10088c2ecf20Sopenharmony_ci		atmel_nand_pmecc_disable(chip, raw);
10098c2ecf20Sopenharmony_ci		dev_err(nc->base.dev,
10108c2ecf20Sopenharmony_ci			"Failed to transfer NAND page data (err = %d)\n",
10118c2ecf20Sopenharmony_ci			ret);
10128c2ecf20Sopenharmony_ci		return ret;
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	atmel_nand_pmecc_disable(chip, raw);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	if (ret)
10208c2ecf20Sopenharmony_ci		return ret;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
10258c2ecf20Sopenharmony_ci}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_page(struct nand_chip *chip,
10288c2ecf20Sopenharmony_ci					    const u8 *buf, int oob_required,
10298c2ecf20Sopenharmony_ci					    int page)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
10328c2ecf20Sopenharmony_ci					      false);
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_page_raw(struct nand_chip *chip,
10368c2ecf20Sopenharmony_ci						const u8 *buf,
10378c2ecf20Sopenharmony_ci						int oob_required, int page)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
10408c2ecf20Sopenharmony_ci					      true);
10418c2ecf20Sopenharmony_ci}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
10448c2ecf20Sopenharmony_ci					 bool oob_required, int page,
10458c2ecf20Sopenharmony_ci					 bool raw)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
10488c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
10498c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
10508c2ecf20Sopenharmony_ci	int ret;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	atmel_hsmc_nand_select_target(nand, chip->cur_cs);
10538c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(chip->controller);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	/*
10568c2ecf20Sopenharmony_ci	 * Optimized read page accessors only work when the NAND R/B pin is
10578c2ecf20Sopenharmony_ci	 * connected to a native SoC R/B pin. If that's not the case, fallback
10588c2ecf20Sopenharmony_ci	 * to the non-optimized one.
10598c2ecf20Sopenharmony_ci	 */
10608c2ecf20Sopenharmony_ci	if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB)
10618c2ecf20Sopenharmony_ci		return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page,
10628c2ecf20Sopenharmony_ci						raw);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	if (mtd->writesize > 512)
10678c2ecf20Sopenharmony_ci		nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	atmel_nfc_set_op_addr(chip, page, 0x0);
10708c2ecf20Sopenharmony_ci	nc->op.cs = nand->activecs->id;
10718c2ecf20Sopenharmony_ci	nc->op.data = ATMEL_NFC_READ_DATA;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
10748c2ecf20Sopenharmony_ci	if (ret)
10758c2ecf20Sopenharmony_ci		return ret;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	ret = atmel_nfc_exec_op(nc, false);
10788c2ecf20Sopenharmony_ci	if (ret) {
10798c2ecf20Sopenharmony_ci		atmel_nand_pmecc_disable(chip, raw);
10808c2ecf20Sopenharmony_ci		dev_err(nc->base.dev,
10818c2ecf20Sopenharmony_ci			"Failed to load NAND page data (err = %d)\n",
10828c2ecf20Sopenharmony_ci			ret);
10838c2ecf20Sopenharmony_ci		return ret;
10848c2ecf20Sopenharmony_ci	}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	atmel_nfc_copy_from_sram(chip, buf, true);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	atmel_nand_pmecc_disable(chip, raw);
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	return ret;
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf,
10968c2ecf20Sopenharmony_ci					   int oob_required, int page)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
10998c2ecf20Sopenharmony_ci					     false);
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_page_raw(struct nand_chip *chip,
11038c2ecf20Sopenharmony_ci					       u8 *buf, int oob_required,
11048c2ecf20Sopenharmony_ci					       int page)
11058c2ecf20Sopenharmony_ci{
11068c2ecf20Sopenharmony_ci	return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
11078c2ecf20Sopenharmony_ci					     true);
11088c2ecf20Sopenharmony_ci}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_init(struct nand_chip *chip)
11118c2ecf20Sopenharmony_ci{
11128c2ecf20Sopenharmony_ci	const struct nand_ecc_props *requirements =
11138c2ecf20Sopenharmony_ci		nanddev_get_ecc_requirements(&chip->base);
11148c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
11158c2ecf20Sopenharmony_ci	struct nand_device *nanddev = mtd_to_nanddev(mtd);
11168c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
11178c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
11188c2ecf20Sopenharmony_ci	struct atmel_pmecc_user_req req;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	nc = to_nand_controller(chip->controller);
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (!nc->pmecc) {
11238c2ecf20Sopenharmony_ci		dev_err(nc->dev, "HW ECC not supported\n");
11248c2ecf20Sopenharmony_ci		return -ENOTSUPP;
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	if (nc->caps->legacy_of_bindings) {
11288c2ecf20Sopenharmony_ci		u32 val;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci		if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap",
11318c2ecf20Sopenharmony_ci					  &val))
11328c2ecf20Sopenharmony_ci			chip->ecc.strength = val;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci		if (!of_property_read_u32(nc->dev->of_node,
11358c2ecf20Sopenharmony_ci					  "atmel,pmecc-sector-size",
11368c2ecf20Sopenharmony_ci					  &val))
11378c2ecf20Sopenharmony_ci			chip->ecc.size = val;
11388c2ecf20Sopenharmony_ci	}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH)
11418c2ecf20Sopenharmony_ci		req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
11428c2ecf20Sopenharmony_ci	else if (chip->ecc.strength)
11438c2ecf20Sopenharmony_ci		req.ecc.strength = chip->ecc.strength;
11448c2ecf20Sopenharmony_ci	else if (requirements->strength)
11458c2ecf20Sopenharmony_ci		req.ecc.strength = requirements->strength;
11468c2ecf20Sopenharmony_ci	else
11478c2ecf20Sopenharmony_ci		req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	if (chip->ecc.size)
11508c2ecf20Sopenharmony_ci		req.ecc.sectorsize = chip->ecc.size;
11518c2ecf20Sopenharmony_ci	else if (requirements->step_size)
11528c2ecf20Sopenharmony_ci		req.ecc.sectorsize = requirements->step_size;
11538c2ecf20Sopenharmony_ci	else
11548c2ecf20Sopenharmony_ci		req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	req.pagesize = mtd->writesize;
11578c2ecf20Sopenharmony_ci	req.oobsize = mtd->oobsize;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	if (mtd->writesize <= 512) {
11608c2ecf20Sopenharmony_ci		req.ecc.bytes = 4;
11618c2ecf20Sopenharmony_ci		req.ecc.ooboffset = 0;
11628c2ecf20Sopenharmony_ci	} else {
11638c2ecf20Sopenharmony_ci		req.ecc.bytes = mtd->oobsize - 2;
11648c2ecf20Sopenharmony_ci		req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO;
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req);
11688c2ecf20Sopenharmony_ci	if (IS_ERR(nand->pmecc))
11698c2ecf20Sopenharmony_ci		return PTR_ERR(nand->pmecc);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	chip->ecc.algo = NAND_ECC_ALGO_BCH;
11728c2ecf20Sopenharmony_ci	chip->ecc.size = req.ecc.sectorsize;
11738c2ecf20Sopenharmony_ci	chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors;
11748c2ecf20Sopenharmony_ci	chip->ecc.strength = req.ecc.strength;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	chip->options |= NAND_NO_SUBPAGE_WRITE;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	return 0;
11818c2ecf20Sopenharmony_ci}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_cistatic int atmel_nand_ecc_init(struct nand_chip *chip)
11848c2ecf20Sopenharmony_ci{
11858c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
11868c2ecf20Sopenharmony_ci	int ret;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	nc = to_nand_controller(chip->controller);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	switch (chip->ecc.engine_type) {
11918c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_NONE:
11928c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_SOFT:
11938c2ecf20Sopenharmony_ci		/*
11948c2ecf20Sopenharmony_ci		 * Nothing to do, the core will initialize everything for us.
11958c2ecf20Sopenharmony_ci		 */
11968c2ecf20Sopenharmony_ci		break;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_HOST:
11998c2ecf20Sopenharmony_ci		ret = atmel_nand_pmecc_init(chip);
12008c2ecf20Sopenharmony_ci		if (ret)
12018c2ecf20Sopenharmony_ci			return ret;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci		chip->ecc.read_page = atmel_nand_pmecc_read_page;
12048c2ecf20Sopenharmony_ci		chip->ecc.write_page = atmel_nand_pmecc_write_page;
12058c2ecf20Sopenharmony_ci		chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw;
12068c2ecf20Sopenharmony_ci		chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw;
12078c2ecf20Sopenharmony_ci		break;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	default:
12108c2ecf20Sopenharmony_ci		/* Other modes are not supported. */
12118c2ecf20Sopenharmony_ci		dev_err(nc->dev, "Unsupported ECC mode: %d\n",
12128c2ecf20Sopenharmony_ci			chip->ecc.engine_type);
12138c2ecf20Sopenharmony_ci		return -ENOTSUPP;
12148c2ecf20Sopenharmony_ci	}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	return 0;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_ecc_init(struct nand_chip *chip)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	int ret;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	ret = atmel_nand_ecc_init(chip);
12248c2ecf20Sopenharmony_ci	if (ret)
12258c2ecf20Sopenharmony_ci		return ret;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
12288c2ecf20Sopenharmony_ci		return 0;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	/* Adjust the ECC operations for the HSMC IP. */
12318c2ecf20Sopenharmony_ci	chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page;
12328c2ecf20Sopenharmony_ci	chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page;
12338c2ecf20Sopenharmony_ci	chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw;
12348c2ecf20Sopenharmony_ci	chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	return 0;
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_cistatic int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
12408c2ecf20Sopenharmony_ci					const struct nand_interface_config *conf,
12418c2ecf20Sopenharmony_ci					struct atmel_smc_cs_conf *smcconf)
12428c2ecf20Sopenharmony_ci{
12438c2ecf20Sopenharmony_ci	u32 ncycles, totalcycles, timeps, mckperiodps;
12448c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
12458c2ecf20Sopenharmony_ci	int ret;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	nc = to_nand_controller(nand->base.controller);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	/* DDR interface not supported. */
12508c2ecf20Sopenharmony_ci	if (!nand_interface_is_sdr(conf))
12518c2ecf20Sopenharmony_ci		return -ENOTSUPP;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	/*
12548c2ecf20Sopenharmony_ci	 * tRC < 30ns implies EDO mode. This controller does not support this
12558c2ecf20Sopenharmony_ci	 * mode.
12568c2ecf20Sopenharmony_ci	 */
12578c2ecf20Sopenharmony_ci	if (conf->timings.sdr.tRC_min < 30000)
12588c2ecf20Sopenharmony_ci		return -ENOTSUPP;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	atmel_smc_cs_conf_init(smcconf);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck);
12638c2ecf20Sopenharmony_ci	mckperiodps *= 1000;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	/*
12668c2ecf20Sopenharmony_ci	 * Set write pulse timing. This one is easy to extract:
12678c2ecf20Sopenharmony_ci	 *
12688c2ecf20Sopenharmony_ci	 * NWE_PULSE = tWP
12698c2ecf20Sopenharmony_ci	 */
12708c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps);
12718c2ecf20Sopenharmony_ci	totalcycles = ncycles;
12728c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT,
12738c2ecf20Sopenharmony_ci					  ncycles);
12748c2ecf20Sopenharmony_ci	if (ret)
12758c2ecf20Sopenharmony_ci		return ret;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	/*
12788c2ecf20Sopenharmony_ci	 * The write setup timing depends on the operation done on the NAND.
12798c2ecf20Sopenharmony_ci	 * All operations goes through the same data bus, but the operation
12808c2ecf20Sopenharmony_ci	 * type depends on the address we are writing to (ALE/CLE address
12818c2ecf20Sopenharmony_ci	 * lines).
12828c2ecf20Sopenharmony_ci	 * Since we have no way to differentiate the different operations at
12838c2ecf20Sopenharmony_ci	 * the SMC level, we must consider the worst case (the biggest setup
12848c2ecf20Sopenharmony_ci	 * time among all operation types):
12858c2ecf20Sopenharmony_ci	 *
12868c2ecf20Sopenharmony_ci	 * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE
12878c2ecf20Sopenharmony_ci	 */
12888c2ecf20Sopenharmony_ci	timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min,
12898c2ecf20Sopenharmony_ci		      conf->timings.sdr.tALS_min);
12908c2ecf20Sopenharmony_ci	timeps = max(timeps, conf->timings.sdr.tDS_min);
12918c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(timeps, mckperiodps);
12928c2ecf20Sopenharmony_ci	ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0;
12938c2ecf20Sopenharmony_ci	totalcycles += ncycles;
12948c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT,
12958c2ecf20Sopenharmony_ci					  ncycles);
12968c2ecf20Sopenharmony_ci	if (ret)
12978c2ecf20Sopenharmony_ci		return ret;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	/*
13008c2ecf20Sopenharmony_ci	 * As for the write setup timing, the write hold timing depends on the
13018c2ecf20Sopenharmony_ci	 * operation done on the NAND:
13028c2ecf20Sopenharmony_ci	 *
13038c2ecf20Sopenharmony_ci	 * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH)
13048c2ecf20Sopenharmony_ci	 */
13058c2ecf20Sopenharmony_ci	timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min,
13068c2ecf20Sopenharmony_ci		      conf->timings.sdr.tALH_min);
13078c2ecf20Sopenharmony_ci	timeps = max3(timeps, conf->timings.sdr.tDH_min,
13088c2ecf20Sopenharmony_ci		      conf->timings.sdr.tWH_min);
13098c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(timeps, mckperiodps);
13108c2ecf20Sopenharmony_ci	totalcycles += ncycles;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	/*
13138c2ecf20Sopenharmony_ci	 * The write cycle timing is directly matching tWC, but is also
13148c2ecf20Sopenharmony_ci	 * dependent on the other timings on the setup and hold timings we
13158c2ecf20Sopenharmony_ci	 * calculated earlier, which gives:
13168c2ecf20Sopenharmony_ci	 *
13178c2ecf20Sopenharmony_ci	 * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD)
13188c2ecf20Sopenharmony_ci	 */
13198c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps);
13208c2ecf20Sopenharmony_ci	ncycles = max(totalcycles, ncycles);
13218c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT,
13228c2ecf20Sopenharmony_ci					  ncycles);
13238c2ecf20Sopenharmony_ci	if (ret)
13248c2ecf20Sopenharmony_ci		return ret;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	/*
13278c2ecf20Sopenharmony_ci	 * We don't want the CS line to be toggled between each byte/word
13288c2ecf20Sopenharmony_ci	 * transfer to the NAND. The only way to guarantee that is to have the
13298c2ecf20Sopenharmony_ci	 * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
13308c2ecf20Sopenharmony_ci	 *
13318c2ecf20Sopenharmony_ci	 * NCS_WR_PULSE = NWE_CYCLE
13328c2ecf20Sopenharmony_ci	 */
13338c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT,
13348c2ecf20Sopenharmony_ci					  ncycles);
13358c2ecf20Sopenharmony_ci	if (ret)
13368c2ecf20Sopenharmony_ci		return ret;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	/*
13398c2ecf20Sopenharmony_ci	 * As for the write setup timing, the read hold timing depends on the
13408c2ecf20Sopenharmony_ci	 * operation done on the NAND:
13418c2ecf20Sopenharmony_ci	 *
13428c2ecf20Sopenharmony_ci	 * NRD_HOLD = max(tREH, tRHOH)
13438c2ecf20Sopenharmony_ci	 */
13448c2ecf20Sopenharmony_ci	timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min);
13458c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(timeps, mckperiodps);
13468c2ecf20Sopenharmony_ci	totalcycles = ncycles;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	/*
13498c2ecf20Sopenharmony_ci	 * TDF = tRHZ - NRD_HOLD
13508c2ecf20Sopenharmony_ci	 */
13518c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps);
13528c2ecf20Sopenharmony_ci	ncycles -= totalcycles;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	/*
13558c2ecf20Sopenharmony_ci	 * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and
13568c2ecf20Sopenharmony_ci	 * we might end up with a config that does not fit in the TDF field.
13578c2ecf20Sopenharmony_ci	 * Just take the max value in this case and hope that the NAND is more
13588c2ecf20Sopenharmony_ci	 * tolerant than advertised.
13598c2ecf20Sopenharmony_ci	 */
13608c2ecf20Sopenharmony_ci	if (ncycles > ATMEL_SMC_MODE_TDF_MAX)
13618c2ecf20Sopenharmony_ci		ncycles = ATMEL_SMC_MODE_TDF_MAX;
13628c2ecf20Sopenharmony_ci	else if (ncycles < ATMEL_SMC_MODE_TDF_MIN)
13638c2ecf20Sopenharmony_ci		ncycles = ATMEL_SMC_MODE_TDF_MIN;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci	smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) |
13668c2ecf20Sopenharmony_ci			 ATMEL_SMC_MODE_TDFMODE_OPTIMIZED;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	/*
13698c2ecf20Sopenharmony_ci	 * Read pulse timing directly matches tRP:
13708c2ecf20Sopenharmony_ci	 *
13718c2ecf20Sopenharmony_ci	 * NRD_PULSE = tRP
13728c2ecf20Sopenharmony_ci	 */
13738c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps);
13748c2ecf20Sopenharmony_ci	totalcycles += ncycles;
13758c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT,
13768c2ecf20Sopenharmony_ci					  ncycles);
13778c2ecf20Sopenharmony_ci	if (ret)
13788c2ecf20Sopenharmony_ci		return ret;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	/*
13818c2ecf20Sopenharmony_ci	 * The write cycle timing is directly matching tWC, but is also
13828c2ecf20Sopenharmony_ci	 * dependent on the setup and hold timings we calculated earlier,
13838c2ecf20Sopenharmony_ci	 * which gives:
13848c2ecf20Sopenharmony_ci	 *
13858c2ecf20Sopenharmony_ci	 * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD)
13868c2ecf20Sopenharmony_ci	 *
13878c2ecf20Sopenharmony_ci	 * NRD_SETUP is always 0.
13888c2ecf20Sopenharmony_ci	 */
13898c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps);
13908c2ecf20Sopenharmony_ci	ncycles = max(totalcycles, ncycles);
13918c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT,
13928c2ecf20Sopenharmony_ci					  ncycles);
13938c2ecf20Sopenharmony_ci	if (ret)
13948c2ecf20Sopenharmony_ci		return ret;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	/*
13978c2ecf20Sopenharmony_ci	 * We don't want the CS line to be toggled between each byte/word
13988c2ecf20Sopenharmony_ci	 * transfer from the NAND. The only way to guarantee that is to have
13998c2ecf20Sopenharmony_ci	 * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means:
14008c2ecf20Sopenharmony_ci	 *
14018c2ecf20Sopenharmony_ci	 * NCS_RD_PULSE = NRD_CYCLE
14028c2ecf20Sopenharmony_ci	 */
14038c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT,
14048c2ecf20Sopenharmony_ci					  ncycles);
14058c2ecf20Sopenharmony_ci	if (ret)
14068c2ecf20Sopenharmony_ci		return ret;
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	/* Txxx timings are directly matching tXXX ones. */
14098c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps);
14108c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_timing(smcconf,
14118c2ecf20Sopenharmony_ci					   ATMEL_HSMC_TIMINGS_TCLR_SHIFT,
14128c2ecf20Sopenharmony_ci					   ncycles);
14138c2ecf20Sopenharmony_ci	if (ret)
14148c2ecf20Sopenharmony_ci		return ret;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps);
14178c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_timing(smcconf,
14188c2ecf20Sopenharmony_ci					   ATMEL_HSMC_TIMINGS_TADL_SHIFT,
14198c2ecf20Sopenharmony_ci					   ncycles);
14208c2ecf20Sopenharmony_ci	/*
14218c2ecf20Sopenharmony_ci	 * Version 4 of the ONFI spec mandates that tADL be at least 400
14228c2ecf20Sopenharmony_ci	 * nanoseconds, but, depending on the master clock rate, 400 ns may not
14238c2ecf20Sopenharmony_ci	 * fit in the tADL field of the SMC reg. We need to relax the check and
14248c2ecf20Sopenharmony_ci	 * accept the -ERANGE return code.
14258c2ecf20Sopenharmony_ci	 *
14268c2ecf20Sopenharmony_ci	 * Note that previous versions of the ONFI spec had a lower tADL_min
14278c2ecf20Sopenharmony_ci	 * (100 or 200 ns). It's not clear why this timing constraint got
14288c2ecf20Sopenharmony_ci	 * increased but it seems most NANDs are fine with values lower than
14298c2ecf20Sopenharmony_ci	 * 400ns, so we should be safe.
14308c2ecf20Sopenharmony_ci	 */
14318c2ecf20Sopenharmony_ci	if (ret && ret != -ERANGE)
14328c2ecf20Sopenharmony_ci		return ret;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps);
14358c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_timing(smcconf,
14368c2ecf20Sopenharmony_ci					   ATMEL_HSMC_TIMINGS_TAR_SHIFT,
14378c2ecf20Sopenharmony_ci					   ncycles);
14388c2ecf20Sopenharmony_ci	if (ret)
14398c2ecf20Sopenharmony_ci		return ret;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps);
14428c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_timing(smcconf,
14438c2ecf20Sopenharmony_ci					   ATMEL_HSMC_TIMINGS_TRR_SHIFT,
14448c2ecf20Sopenharmony_ci					   ncycles);
14458c2ecf20Sopenharmony_ci	if (ret)
14468c2ecf20Sopenharmony_ci		return ret;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps);
14498c2ecf20Sopenharmony_ci	ret = atmel_smc_cs_conf_set_timing(smcconf,
14508c2ecf20Sopenharmony_ci					   ATMEL_HSMC_TIMINGS_TWB_SHIFT,
14518c2ecf20Sopenharmony_ci					   ncycles);
14528c2ecf20Sopenharmony_ci	if (ret)
14538c2ecf20Sopenharmony_ci		return ret;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	/* Attach the CS line to the NFC logic. */
14568c2ecf20Sopenharmony_ci	smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	/* Set the appropriate data bus width. */
14598c2ecf20Sopenharmony_ci	if (nand->base.options & NAND_BUSWIDTH_16)
14608c2ecf20Sopenharmony_ci		smcconf->mode |= ATMEL_SMC_MODE_DBW_16;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	/* Operate in NRD/NWE READ/WRITEMODE. */
14638c2ecf20Sopenharmony_ci	smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD |
14648c2ecf20Sopenharmony_ci			 ATMEL_SMC_MODE_WRITEMODE_NWE;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	return 0;
14678c2ecf20Sopenharmony_ci}
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_cistatic int atmel_smc_nand_setup_interface(struct atmel_nand *nand,
14708c2ecf20Sopenharmony_ci					int csline,
14718c2ecf20Sopenharmony_ci					const struct nand_interface_config *conf)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
14748c2ecf20Sopenharmony_ci	struct atmel_smc_cs_conf smcconf;
14758c2ecf20Sopenharmony_ci	struct atmel_nand_cs *cs;
14768c2ecf20Sopenharmony_ci	int ret;
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	nc = to_nand_controller(nand->base.controller);
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
14818c2ecf20Sopenharmony_ci	if (ret)
14828c2ecf20Sopenharmony_ci		return ret;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
14858c2ecf20Sopenharmony_ci		return 0;
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	cs = &nand->cs[csline];
14888c2ecf20Sopenharmony_ci	cs->smcconf = smcconf;
14898c2ecf20Sopenharmony_ci	atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	return 0;
14928c2ecf20Sopenharmony_ci}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_setup_interface(struct atmel_nand *nand,
14958c2ecf20Sopenharmony_ci					int csline,
14968c2ecf20Sopenharmony_ci					const struct nand_interface_config *conf)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
14998c2ecf20Sopenharmony_ci	struct atmel_smc_cs_conf smcconf;
15008c2ecf20Sopenharmony_ci	struct atmel_nand_cs *cs;
15018c2ecf20Sopenharmony_ci	int ret;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	nc = to_hsmc_nand_controller(nand->base.controller);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf);
15068c2ecf20Sopenharmony_ci	if (ret)
15078c2ecf20Sopenharmony_ci		return ret;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
15108c2ecf20Sopenharmony_ci		return 0;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	cs = &nand->cs[csline];
15138c2ecf20Sopenharmony_ci	cs->smcconf = smcconf;
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	if (cs->rb.type == ATMEL_NAND_NATIVE_RB)
15168c2ecf20Sopenharmony_ci		cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id);
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id,
15198c2ecf20Sopenharmony_ci				 &cs->smcconf);
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	return 0;
15228c2ecf20Sopenharmony_ci}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_cistatic int atmel_nand_setup_interface(struct nand_chip *chip, int csline,
15258c2ecf20Sopenharmony_ci				      const struct nand_interface_config *conf)
15268c2ecf20Sopenharmony_ci{
15278c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
15288c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	nc = to_nand_controller(nand->base.controller);
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	if (csline >= nand->numcs ||
15338c2ecf20Sopenharmony_ci	    (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY))
15348c2ecf20Sopenharmony_ci		return -EINVAL;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	return nc->caps->ops->setup_interface(nand, csline, conf);
15378c2ecf20Sopenharmony_ci}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_cistatic int atmel_nand_exec_op(struct nand_chip *chip,
15408c2ecf20Sopenharmony_ci			      const struct nand_operation *op,
15418c2ecf20Sopenharmony_ci			      bool check_only)
15428c2ecf20Sopenharmony_ci{
15438c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
15448c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	nc = to_nand_controller(nand->base.controller);
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	return nc->caps->ops->exec_op(nand, op, check_only);
15498c2ecf20Sopenharmony_ci}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_cistatic void atmel_nand_init(struct atmel_nand_controller *nc,
15528c2ecf20Sopenharmony_ci			    struct atmel_nand *nand)
15538c2ecf20Sopenharmony_ci{
15548c2ecf20Sopenharmony_ci	struct nand_chip *chip = &nand->base;
15558c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	mtd->dev.parent = nc->dev;
15588c2ecf20Sopenharmony_ci	nand->base.controller = &nc->base;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	if (!nc->mck || !nc->caps->ops->setup_interface)
15618c2ecf20Sopenharmony_ci		chip->options |= NAND_KEEP_TIMINGS;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	/*
15648c2ecf20Sopenharmony_ci	 * Use a bounce buffer when the buffer passed by the MTD user is not
15658c2ecf20Sopenharmony_ci	 * suitable for DMA.
15668c2ecf20Sopenharmony_ci	 */
15678c2ecf20Sopenharmony_ci	if (nc->dmac)
15688c2ecf20Sopenharmony_ci		chip->options |= NAND_USES_DMA;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	/* Default to HW ECC if pmecc is available. */
15718c2ecf20Sopenharmony_ci	if (nc->pmecc)
15728c2ecf20Sopenharmony_ci		chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
15738c2ecf20Sopenharmony_ci}
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_cistatic void atmel_smc_nand_init(struct atmel_nand_controller *nc,
15768c2ecf20Sopenharmony_ci				struct atmel_nand *nand)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	struct nand_chip *chip = &nand->base;
15798c2ecf20Sopenharmony_ci	struct atmel_smc_nand_controller *smc_nc;
15808c2ecf20Sopenharmony_ci	int i;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	atmel_nand_init(nc, nand);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	smc_nc = to_smc_nand_controller(chip->controller);
15858c2ecf20Sopenharmony_ci	if (!smc_nc->ebi_csa_regmap)
15868c2ecf20Sopenharmony_ci		return;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	/* Attach the CS to the NAND Flash logic. */
15898c2ecf20Sopenharmony_ci	for (i = 0; i < nand->numcs; i++)
15908c2ecf20Sopenharmony_ci		regmap_update_bits(smc_nc->ebi_csa_regmap,
15918c2ecf20Sopenharmony_ci				   smc_nc->ebi_csa->offs,
15928c2ecf20Sopenharmony_ci				   BIT(nand->cs[i].id), BIT(nand->cs[i].id));
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	if (smc_nc->ebi_csa->nfd0_on_d16)
15958c2ecf20Sopenharmony_ci		regmap_update_bits(smc_nc->ebi_csa_regmap,
15968c2ecf20Sopenharmony_ci				   smc_nc->ebi_csa->offs,
15978c2ecf20Sopenharmony_ci				   smc_nc->ebi_csa->nfd0_on_d16,
15988c2ecf20Sopenharmony_ci				   smc_nc->ebi_csa->nfd0_on_d16);
15998c2ecf20Sopenharmony_ci}
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_cistatic int atmel_nand_controller_remove_nand(struct atmel_nand *nand)
16028c2ecf20Sopenharmony_ci{
16038c2ecf20Sopenharmony_ci	struct nand_chip *chip = &nand->base;
16048c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
16058c2ecf20Sopenharmony_ci	int ret;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	ret = mtd_device_unregister(mtd);
16088c2ecf20Sopenharmony_ci	if (ret)
16098c2ecf20Sopenharmony_ci		return ret;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	nand_cleanup(chip);
16128c2ecf20Sopenharmony_ci	list_del(&nand->node);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	return 0;
16158c2ecf20Sopenharmony_ci}
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_cistatic struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
16188c2ecf20Sopenharmony_ci					    struct device_node *np,
16198c2ecf20Sopenharmony_ci					    int reg_cells)
16208c2ecf20Sopenharmony_ci{
16218c2ecf20Sopenharmony_ci	struct atmel_nand *nand;
16228c2ecf20Sopenharmony_ci	struct gpio_desc *gpio;
16238c2ecf20Sopenharmony_ci	int numcs, ret, i;
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	numcs = of_property_count_elems_of_size(np, "reg",
16268c2ecf20Sopenharmony_ci						reg_cells * sizeof(u32));
16278c2ecf20Sopenharmony_ci	if (numcs < 1) {
16288c2ecf20Sopenharmony_ci		dev_err(nc->dev, "Missing or invalid reg property\n");
16298c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
16308c2ecf20Sopenharmony_ci	}
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	nand = devm_kzalloc(nc->dev, struct_size(nand, cs, numcs), GFP_KERNEL);
16338c2ecf20Sopenharmony_ci	if (!nand) {
16348c2ecf20Sopenharmony_ci		dev_err(nc->dev, "Failed to allocate NAND object\n");
16358c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
16368c2ecf20Sopenharmony_ci	}
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	nand->numcs = numcs;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	gpio = devm_fwnode_gpiod_get(nc->dev, of_fwnode_handle(np),
16418c2ecf20Sopenharmony_ci				     "det", GPIOD_IN, "nand-det");
16428c2ecf20Sopenharmony_ci	if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
16438c2ecf20Sopenharmony_ci		dev_err(nc->dev,
16448c2ecf20Sopenharmony_ci			"Failed to get detect gpio (err = %ld)\n",
16458c2ecf20Sopenharmony_ci			PTR_ERR(gpio));
16468c2ecf20Sopenharmony_ci		return ERR_CAST(gpio);
16478c2ecf20Sopenharmony_ci	}
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	if (!IS_ERR(gpio))
16508c2ecf20Sopenharmony_ci		nand->cdgpio = gpio;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	for (i = 0; i < numcs; i++) {
16538c2ecf20Sopenharmony_ci		struct resource res;
16548c2ecf20Sopenharmony_ci		u32 val;
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci		ret = of_address_to_resource(np, 0, &res);
16578c2ecf20Sopenharmony_ci		if (ret) {
16588c2ecf20Sopenharmony_ci			dev_err(nc->dev, "Invalid reg property (err = %d)\n",
16598c2ecf20Sopenharmony_ci				ret);
16608c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
16618c2ecf20Sopenharmony_ci		}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci		ret = of_property_read_u32_index(np, "reg", i * reg_cells,
16648c2ecf20Sopenharmony_ci						 &val);
16658c2ecf20Sopenharmony_ci		if (ret) {
16668c2ecf20Sopenharmony_ci			dev_err(nc->dev, "Invalid reg property (err = %d)\n",
16678c2ecf20Sopenharmony_ci				ret);
16688c2ecf20Sopenharmony_ci			return ERR_PTR(ret);
16698c2ecf20Sopenharmony_ci		}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci		nand->cs[i].id = val;
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci		nand->cs[i].io.dma = res.start;
16748c2ecf20Sopenharmony_ci		nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res);
16758c2ecf20Sopenharmony_ci		if (IS_ERR(nand->cs[i].io.virt))
16768c2ecf20Sopenharmony_ci			return ERR_CAST(nand->cs[i].io.virt);
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci		if (!of_property_read_u32(np, "atmel,rb", &val)) {
16798c2ecf20Sopenharmony_ci			if (val > ATMEL_NFC_MAX_RB_ID)
16808c2ecf20Sopenharmony_ci				return ERR_PTR(-EINVAL);
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci			nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB;
16838c2ecf20Sopenharmony_ci			nand->cs[i].rb.id = val;
16848c2ecf20Sopenharmony_ci		} else {
16858c2ecf20Sopenharmony_ci			gpio = devm_fwnode_gpiod_get_index(nc->dev,
16868c2ecf20Sopenharmony_ci							   of_fwnode_handle(np),
16878c2ecf20Sopenharmony_ci							   "rb", i, GPIOD_IN,
16888c2ecf20Sopenharmony_ci							   "nand-rb");
16898c2ecf20Sopenharmony_ci			if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
16908c2ecf20Sopenharmony_ci				dev_err(nc->dev,
16918c2ecf20Sopenharmony_ci					"Failed to get R/B gpio (err = %ld)\n",
16928c2ecf20Sopenharmony_ci					PTR_ERR(gpio));
16938c2ecf20Sopenharmony_ci				return ERR_CAST(gpio);
16948c2ecf20Sopenharmony_ci			}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci			if (!IS_ERR(gpio)) {
16978c2ecf20Sopenharmony_ci				nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
16988c2ecf20Sopenharmony_ci				nand->cs[i].rb.gpio = gpio;
16998c2ecf20Sopenharmony_ci			}
17008c2ecf20Sopenharmony_ci		}
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci		gpio = devm_fwnode_gpiod_get_index(nc->dev,
17038c2ecf20Sopenharmony_ci						   of_fwnode_handle(np),
17048c2ecf20Sopenharmony_ci						   "cs", i, GPIOD_OUT_HIGH,
17058c2ecf20Sopenharmony_ci						   "nand-cs");
17068c2ecf20Sopenharmony_ci		if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
17078c2ecf20Sopenharmony_ci			dev_err(nc->dev,
17088c2ecf20Sopenharmony_ci				"Failed to get CS gpio (err = %ld)\n",
17098c2ecf20Sopenharmony_ci				PTR_ERR(gpio));
17108c2ecf20Sopenharmony_ci			return ERR_CAST(gpio);
17118c2ecf20Sopenharmony_ci		}
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci		if (!IS_ERR(gpio))
17148c2ecf20Sopenharmony_ci			nand->cs[i].csgpio = gpio;
17158c2ecf20Sopenharmony_ci	}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	nand_set_flash_node(&nand->base, np);
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	return nand;
17208c2ecf20Sopenharmony_ci}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_cistatic int
17238c2ecf20Sopenharmony_ciatmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
17248c2ecf20Sopenharmony_ci			       struct atmel_nand *nand)
17258c2ecf20Sopenharmony_ci{
17268c2ecf20Sopenharmony_ci	struct nand_chip *chip = &nand->base;
17278c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
17288c2ecf20Sopenharmony_ci	int ret;
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	/* No card inserted, skip this NAND. */
17318c2ecf20Sopenharmony_ci	if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) {
17328c2ecf20Sopenharmony_ci		dev_info(nc->dev, "No SmartMedia card inserted.\n");
17338c2ecf20Sopenharmony_ci		return 0;
17348c2ecf20Sopenharmony_ci	}
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	nc->caps->ops->nand_init(nc, nand);
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	ret = nand_scan(chip, nand->numcs);
17398c2ecf20Sopenharmony_ci	if (ret) {
17408c2ecf20Sopenharmony_ci		dev_err(nc->dev, "NAND scan failed: %d\n", ret);
17418c2ecf20Sopenharmony_ci		return ret;
17428c2ecf20Sopenharmony_ci	}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
17458c2ecf20Sopenharmony_ci	if (ret) {
17468c2ecf20Sopenharmony_ci		dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
17478c2ecf20Sopenharmony_ci		nand_cleanup(chip);
17488c2ecf20Sopenharmony_ci		return ret;
17498c2ecf20Sopenharmony_ci	}
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	list_add_tail(&nand->node, &nc->chips);
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	return 0;
17548c2ecf20Sopenharmony_ci}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_cistatic int
17578c2ecf20Sopenharmony_ciatmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
17588c2ecf20Sopenharmony_ci{
17598c2ecf20Sopenharmony_ci	struct atmel_nand *nand, *tmp;
17608c2ecf20Sopenharmony_ci	int ret;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
17638c2ecf20Sopenharmony_ci		ret = atmel_nand_controller_remove_nand(nand);
17648c2ecf20Sopenharmony_ci		if (ret)
17658c2ecf20Sopenharmony_ci			return ret;
17668c2ecf20Sopenharmony_ci	}
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	return 0;
17698c2ecf20Sopenharmony_ci}
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_cistatic int
17728c2ecf20Sopenharmony_ciatmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
17738c2ecf20Sopenharmony_ci{
17748c2ecf20Sopenharmony_ci	struct device *dev = nc->dev;
17758c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
17768c2ecf20Sopenharmony_ci	struct atmel_nand *nand;
17778c2ecf20Sopenharmony_ci	struct gpio_desc *gpio;
17788c2ecf20Sopenharmony_ci	struct resource *res;
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	/*
17818c2ecf20Sopenharmony_ci	 * Legacy bindings only allow connecting a single NAND with a unique CS
17828c2ecf20Sopenharmony_ci	 * line to the controller.
17838c2ecf20Sopenharmony_ci	 */
17848c2ecf20Sopenharmony_ci	nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs),
17858c2ecf20Sopenharmony_ci			    GFP_KERNEL);
17868c2ecf20Sopenharmony_ci	if (!nand)
17878c2ecf20Sopenharmony_ci		return -ENOMEM;
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	nand->numcs = 1;
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
17928c2ecf20Sopenharmony_ci	nand->cs[0].io.virt = devm_ioremap_resource(dev, res);
17938c2ecf20Sopenharmony_ci	if (IS_ERR(nand->cs[0].io.virt))
17948c2ecf20Sopenharmony_ci		return PTR_ERR(nand->cs[0].io.virt);
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_ci	nand->cs[0].io.dma = res->start;
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	/*
17998c2ecf20Sopenharmony_ci	 * The old driver was hardcoding the CS id to 3 for all sama5
18008c2ecf20Sopenharmony_ci	 * controllers. Since this id is only meaningful for the sama5
18018c2ecf20Sopenharmony_ci	 * controller we can safely assign this id to 3 no matter the
18028c2ecf20Sopenharmony_ci	 * controller.
18038c2ecf20Sopenharmony_ci	 * If one wants to connect a NAND to a different CS line, he will
18048c2ecf20Sopenharmony_ci	 * have to use the new bindings.
18058c2ecf20Sopenharmony_ci	 */
18068c2ecf20Sopenharmony_ci	nand->cs[0].id = 3;
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	/* R/B GPIO. */
18098c2ecf20Sopenharmony_ci	gpio = devm_gpiod_get_index_optional(dev, NULL, 0,  GPIOD_IN);
18108c2ecf20Sopenharmony_ci	if (IS_ERR(gpio)) {
18118c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get R/B gpio (err = %ld)\n",
18128c2ecf20Sopenharmony_ci			PTR_ERR(gpio));
18138c2ecf20Sopenharmony_ci		return PTR_ERR(gpio);
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	if (gpio) {
18178c2ecf20Sopenharmony_ci		nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB;
18188c2ecf20Sopenharmony_ci		nand->cs[0].rb.gpio = gpio;
18198c2ecf20Sopenharmony_ci	}
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	/* CS GPIO. */
18228c2ecf20Sopenharmony_ci	gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH);
18238c2ecf20Sopenharmony_ci	if (IS_ERR(gpio)) {
18248c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get CS gpio (err = %ld)\n",
18258c2ecf20Sopenharmony_ci			PTR_ERR(gpio));
18268c2ecf20Sopenharmony_ci		return PTR_ERR(gpio);
18278c2ecf20Sopenharmony_ci	}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	nand->cs[0].csgpio = gpio;
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	/* Card detect GPIO. */
18328c2ecf20Sopenharmony_ci	gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN);
18338c2ecf20Sopenharmony_ci	if (IS_ERR(gpio)) {
18348c2ecf20Sopenharmony_ci		dev_err(dev,
18358c2ecf20Sopenharmony_ci			"Failed to get detect gpio (err = %ld)\n",
18368c2ecf20Sopenharmony_ci			PTR_ERR(gpio));
18378c2ecf20Sopenharmony_ci		return PTR_ERR(gpio);
18388c2ecf20Sopenharmony_ci	}
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	nand->cdgpio = gpio;
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci	nand_set_flash_node(&nand->base, nc->dev->of_node);
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	return atmel_nand_controller_add_nand(nc, nand);
18458c2ecf20Sopenharmony_ci}
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_cistatic int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
18488c2ecf20Sopenharmony_ci{
18498c2ecf20Sopenharmony_ci	struct device_node *np, *nand_np;
18508c2ecf20Sopenharmony_ci	struct device *dev = nc->dev;
18518c2ecf20Sopenharmony_ci	int ret, reg_cells;
18528c2ecf20Sopenharmony_ci	u32 val;
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	/* We do not retrieve the SMC syscon when parsing old DTs. */
18558c2ecf20Sopenharmony_ci	if (nc->caps->legacy_of_bindings)
18568c2ecf20Sopenharmony_ci		return atmel_nand_controller_legacy_add_nands(nc);
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci	np = dev->of_node;
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "#address-cells", &val);
18618c2ecf20Sopenharmony_ci	if (ret) {
18628c2ecf20Sopenharmony_ci		dev_err(dev, "missing #address-cells property\n");
18638c2ecf20Sopenharmony_ci		return ret;
18648c2ecf20Sopenharmony_ci	}
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	reg_cells = val;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "#size-cells", &val);
18698c2ecf20Sopenharmony_ci	if (ret) {
18708c2ecf20Sopenharmony_ci		dev_err(dev, "missing #size-cells property\n");
18718c2ecf20Sopenharmony_ci		return ret;
18728c2ecf20Sopenharmony_ci	}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	reg_cells += val;
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	for_each_child_of_node(np, nand_np) {
18778c2ecf20Sopenharmony_ci		struct atmel_nand *nand;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci		nand = atmel_nand_create(nc, nand_np, reg_cells);
18808c2ecf20Sopenharmony_ci		if (IS_ERR(nand)) {
18818c2ecf20Sopenharmony_ci			ret = PTR_ERR(nand);
18828c2ecf20Sopenharmony_ci			goto err;
18838c2ecf20Sopenharmony_ci		}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci		ret = atmel_nand_controller_add_nand(nc, nand);
18868c2ecf20Sopenharmony_ci		if (ret)
18878c2ecf20Sopenharmony_ci			goto err;
18888c2ecf20Sopenharmony_ci	}
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	return 0;
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_cierr:
18938c2ecf20Sopenharmony_ci	atmel_nand_controller_remove_nands(nc);
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	return ret;
18968c2ecf20Sopenharmony_ci}
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_cistatic void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc)
18998c2ecf20Sopenharmony_ci{
19008c2ecf20Sopenharmony_ci	if (nc->dmac)
19018c2ecf20Sopenharmony_ci		dma_release_channel(nc->dmac);
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	clk_put(nc->mck);
19048c2ecf20Sopenharmony_ci}
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9260_ebi_csa = {
19078c2ecf20Sopenharmony_ci	.offs = AT91SAM9260_MATRIX_EBICSA,
19088c2ecf20Sopenharmony_ci};
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9261_ebi_csa = {
19118c2ecf20Sopenharmony_ci	.offs = AT91SAM9261_MATRIX_EBICSA,
19128c2ecf20Sopenharmony_ci};
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9263_ebi_csa = {
19158c2ecf20Sopenharmony_ci	.offs = AT91SAM9263_MATRIX_EBI0CSA,
19168c2ecf20Sopenharmony_ci};
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9rl_ebi_csa = {
19198c2ecf20Sopenharmony_ci	.offs = AT91SAM9RL_MATRIX_EBICSA,
19208c2ecf20Sopenharmony_ci};
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9g45_ebi_csa = {
19238c2ecf20Sopenharmony_ci	.offs = AT91SAM9G45_MATRIX_EBICSA,
19248c2ecf20Sopenharmony_ci};
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9n12_ebi_csa = {
19278c2ecf20Sopenharmony_ci	.offs = AT91SAM9N12_MATRIX_EBICSA,
19288c2ecf20Sopenharmony_ci};
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9x5_ebi_csa = {
19318c2ecf20Sopenharmony_ci	.offs = AT91SAM9X5_MATRIX_EBICSA,
19328c2ecf20Sopenharmony_ci};
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = {
19358c2ecf20Sopenharmony_ci	.offs = AT91_SFR_CCFG_EBICSA,
19368c2ecf20Sopenharmony_ci	.nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16,
19378c2ecf20Sopenharmony_ci};
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_ebi_csa_regmap_of_ids[] = {
19408c2ecf20Sopenharmony_ci	{
19418c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9260-matrix",
19428c2ecf20Sopenharmony_ci		.data = &at91sam9260_ebi_csa,
19438c2ecf20Sopenharmony_ci	},
19448c2ecf20Sopenharmony_ci	{
19458c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9261-matrix",
19468c2ecf20Sopenharmony_ci		.data = &at91sam9261_ebi_csa,
19478c2ecf20Sopenharmony_ci	},
19488c2ecf20Sopenharmony_ci	{
19498c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9263-matrix",
19508c2ecf20Sopenharmony_ci		.data = &at91sam9263_ebi_csa,
19518c2ecf20Sopenharmony_ci	},
19528c2ecf20Sopenharmony_ci	{
19538c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9rl-matrix",
19548c2ecf20Sopenharmony_ci		.data = &at91sam9rl_ebi_csa,
19558c2ecf20Sopenharmony_ci	},
19568c2ecf20Sopenharmony_ci	{
19578c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9g45-matrix",
19588c2ecf20Sopenharmony_ci		.data = &at91sam9g45_ebi_csa,
19598c2ecf20Sopenharmony_ci	},
19608c2ecf20Sopenharmony_ci	{
19618c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9n12-matrix",
19628c2ecf20Sopenharmony_ci		.data = &at91sam9n12_ebi_csa,
19638c2ecf20Sopenharmony_ci	},
19648c2ecf20Sopenharmony_ci	{
19658c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9x5-matrix",
19668c2ecf20Sopenharmony_ci		.data = &at91sam9x5_ebi_csa,
19678c2ecf20Sopenharmony_ci	},
19688c2ecf20Sopenharmony_ci	{
19698c2ecf20Sopenharmony_ci		.compatible = "microchip,sam9x60-sfr",
19708c2ecf20Sopenharmony_ci		.data = &sam9x60_ebi_csa,
19718c2ecf20Sopenharmony_ci	},
19728c2ecf20Sopenharmony_ci	{ /* sentinel */ },
19738c2ecf20Sopenharmony_ci};
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_cistatic int atmel_nand_attach_chip(struct nand_chip *chip)
19768c2ecf20Sopenharmony_ci{
19778c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc = to_nand_controller(chip->controller);
19788c2ecf20Sopenharmony_ci	struct atmel_nand *nand = to_atmel_nand(chip);
19798c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
19808c2ecf20Sopenharmony_ci	int ret;
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	ret = nc->caps->ops->ecc_init(chip);
19838c2ecf20Sopenharmony_ci	if (ret)
19848c2ecf20Sopenharmony_ci		return ret;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci	if (nc->caps->legacy_of_bindings || !nc->dev->of_node) {
19878c2ecf20Sopenharmony_ci		/*
19888c2ecf20Sopenharmony_ci		 * We keep the MTD name unchanged to avoid breaking platforms
19898c2ecf20Sopenharmony_ci		 * where the MTD cmdline parser is used and the bootloader
19908c2ecf20Sopenharmony_ci		 * has not been updated to use the new naming scheme.
19918c2ecf20Sopenharmony_ci		 */
19928c2ecf20Sopenharmony_ci		mtd->name = "atmel_nand";
19938c2ecf20Sopenharmony_ci	} else if (!mtd->name) {
19948c2ecf20Sopenharmony_ci		/*
19958c2ecf20Sopenharmony_ci		 * If the new bindings are used and the bootloader has not been
19968c2ecf20Sopenharmony_ci		 * updated to pass a new mtdparts parameter on the cmdline, you
19978c2ecf20Sopenharmony_ci		 * should define the following property in your nand node:
19988c2ecf20Sopenharmony_ci		 *
19998c2ecf20Sopenharmony_ci		 *	label = "atmel_nand";
20008c2ecf20Sopenharmony_ci		 *
20018c2ecf20Sopenharmony_ci		 * This way, mtd->name will be set by the core when
20028c2ecf20Sopenharmony_ci		 * nand_set_flash_node() is called.
20038c2ecf20Sopenharmony_ci		 */
20048c2ecf20Sopenharmony_ci		mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL,
20058c2ecf20Sopenharmony_ci					   "%s:nand.%d", dev_name(nc->dev),
20068c2ecf20Sopenharmony_ci					   nand->cs[0].id);
20078c2ecf20Sopenharmony_ci		if (!mtd->name) {
20088c2ecf20Sopenharmony_ci			dev_err(nc->dev, "Failed to allocate mtd->name\n");
20098c2ecf20Sopenharmony_ci			return -ENOMEM;
20108c2ecf20Sopenharmony_ci		}
20118c2ecf20Sopenharmony_ci	}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	return 0;
20148c2ecf20Sopenharmony_ci}
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_cistatic const struct nand_controller_ops atmel_nand_controller_ops = {
20178c2ecf20Sopenharmony_ci	.attach_chip = atmel_nand_attach_chip,
20188c2ecf20Sopenharmony_ci	.setup_interface = atmel_nand_setup_interface,
20198c2ecf20Sopenharmony_ci	.exec_op = atmel_nand_exec_op,
20208c2ecf20Sopenharmony_ci};
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_cistatic int atmel_nand_controller_init(struct atmel_nand_controller *nc,
20238c2ecf20Sopenharmony_ci				struct platform_device *pdev,
20248c2ecf20Sopenharmony_ci				const struct atmel_nand_controller_caps *caps)
20258c2ecf20Sopenharmony_ci{
20268c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
20278c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
20288c2ecf20Sopenharmony_ci	int ret;
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	nand_controller_init(&nc->base);
20318c2ecf20Sopenharmony_ci	nc->base.ops = &atmel_nand_controller_ops;
20328c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&nc->chips);
20338c2ecf20Sopenharmony_ci	nc->dev = dev;
20348c2ecf20Sopenharmony_ci	nc->caps = caps;
20358c2ecf20Sopenharmony_ci
20368c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, nc);
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	nc->pmecc = devm_atmel_pmecc_get(dev);
20398c2ecf20Sopenharmony_ci	if (IS_ERR(nc->pmecc))
20408c2ecf20Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(nc->pmecc),
20418c2ecf20Sopenharmony_ci				     "Could not get PMECC object\n");
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	if (nc->caps->has_dma && !atmel_nand_avoid_dma) {
20448c2ecf20Sopenharmony_ci		dma_cap_mask_t mask;
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci		dma_cap_zero(mask);
20478c2ecf20Sopenharmony_ci		dma_cap_set(DMA_MEMCPY, mask);
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci		nc->dmac = dma_request_channel(mask, NULL, NULL);
20508c2ecf20Sopenharmony_ci		if (!nc->dmac)
20518c2ecf20Sopenharmony_ci			dev_err(nc->dev, "Failed to request DMA channel\n");
20528c2ecf20Sopenharmony_ci	}
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci	/* We do not retrieve the SMC syscon when parsing old DTs. */
20558c2ecf20Sopenharmony_ci	if (nc->caps->legacy_of_bindings)
20568c2ecf20Sopenharmony_ci		return 0;
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci	nc->mck = of_clk_get(dev->parent->of_node, 0);
20598c2ecf20Sopenharmony_ci	if (IS_ERR(nc->mck)) {
20608c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to retrieve MCK clk\n");
20618c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->mck);
20628c2ecf20Sopenharmony_ci		goto out_release_dma;
20638c2ecf20Sopenharmony_ci	}
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
20668c2ecf20Sopenharmony_ci	if (!np) {
20678c2ecf20Sopenharmony_ci		dev_err(dev, "Missing or invalid atmel,smc property\n");
20688c2ecf20Sopenharmony_ci		ret = -EINVAL;
20698c2ecf20Sopenharmony_ci		goto out_release_dma;
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	nc->smc = syscon_node_to_regmap(np);
20738c2ecf20Sopenharmony_ci	of_node_put(np);
20748c2ecf20Sopenharmony_ci	if (IS_ERR(nc->smc)) {
20758c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->smc);
20768c2ecf20Sopenharmony_ci		dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
20778c2ecf20Sopenharmony_ci		goto out_release_dma;
20788c2ecf20Sopenharmony_ci	}
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	return 0;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ciout_release_dma:
20838c2ecf20Sopenharmony_ci	if (nc->dmac)
20848c2ecf20Sopenharmony_ci		dma_release_channel(nc->dmac);
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	return ret;
20878c2ecf20Sopenharmony_ci}
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_cistatic int
20908c2ecf20Sopenharmony_ciatmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
20918c2ecf20Sopenharmony_ci{
20928c2ecf20Sopenharmony_ci	struct device *dev = nc->base.dev;
20938c2ecf20Sopenharmony_ci	const struct of_device_id *match;
20948c2ecf20Sopenharmony_ci	struct device_node *np;
20958c2ecf20Sopenharmony_ci	int ret;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	/* We do not retrieve the EBICSA regmap when parsing old DTs. */
20988c2ecf20Sopenharmony_ci	if (nc->base.caps->legacy_of_bindings)
20998c2ecf20Sopenharmony_ci		return 0;
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	np = of_parse_phandle(dev->parent->of_node,
21028c2ecf20Sopenharmony_ci			      nc->base.caps->ebi_csa_regmap_name, 0);
21038c2ecf20Sopenharmony_ci	if (!np)
21048c2ecf20Sopenharmony_ci		return 0;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	match = of_match_node(atmel_ebi_csa_regmap_of_ids, np);
21078c2ecf20Sopenharmony_ci	if (!match) {
21088c2ecf20Sopenharmony_ci		of_node_put(np);
21098c2ecf20Sopenharmony_ci		return 0;
21108c2ecf20Sopenharmony_ci	}
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	nc->ebi_csa_regmap = syscon_node_to_regmap(np);
21138c2ecf20Sopenharmony_ci	of_node_put(np);
21148c2ecf20Sopenharmony_ci	if (IS_ERR(nc->ebi_csa_regmap)) {
21158c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->ebi_csa_regmap);
21168c2ecf20Sopenharmony_ci		dev_err(dev, "Could not get EBICSA regmap (err = %d)\n", ret);
21178c2ecf20Sopenharmony_ci		return ret;
21188c2ecf20Sopenharmony_ci	}
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci	nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data;
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	/*
21238c2ecf20Sopenharmony_ci	 * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
21248c2ecf20Sopenharmony_ci	 * add 4 to ->ebi_csa->offs.
21258c2ecf20Sopenharmony_ci	 */
21268c2ecf20Sopenharmony_ci	if (of_device_is_compatible(dev->parent->of_node,
21278c2ecf20Sopenharmony_ci				    "atmel,at91sam9263-ebi1"))
21288c2ecf20Sopenharmony_ci		nc->ebi_csa->offs += 4;
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci	return 0;
21318c2ecf20Sopenharmony_ci}
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_cistatic int
21348c2ecf20Sopenharmony_ciatmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
21358c2ecf20Sopenharmony_ci{
21368c2ecf20Sopenharmony_ci	struct regmap_config regmap_conf = {
21378c2ecf20Sopenharmony_ci		.reg_bits = 32,
21388c2ecf20Sopenharmony_ci		.val_bits = 32,
21398c2ecf20Sopenharmony_ci		.reg_stride = 4,
21408c2ecf20Sopenharmony_ci	};
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci	struct device *dev = nc->base.dev;
21438c2ecf20Sopenharmony_ci	struct device_node *nand_np, *nfc_np;
21448c2ecf20Sopenharmony_ci	void __iomem *iomem;
21458c2ecf20Sopenharmony_ci	struct resource res;
21468c2ecf20Sopenharmony_ci	int ret;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	nand_np = dev->of_node;
21498c2ecf20Sopenharmony_ci	nfc_np = of_get_compatible_child(dev->of_node, "atmel,sama5d3-nfc");
21508c2ecf20Sopenharmony_ci	if (!nfc_np) {
21518c2ecf20Sopenharmony_ci		dev_err(dev, "Could not find device node for sama5d3-nfc\n");
21528c2ecf20Sopenharmony_ci		return -ENODEV;
21538c2ecf20Sopenharmony_ci	}
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci	nc->clk = of_clk_get(nfc_np, 0);
21568c2ecf20Sopenharmony_ci	if (IS_ERR(nc->clk)) {
21578c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->clk);
21588c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n",
21598c2ecf20Sopenharmony_ci			ret);
21608c2ecf20Sopenharmony_ci		goto out;
21618c2ecf20Sopenharmony_ci	}
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(nc->clk);
21648c2ecf20Sopenharmony_ci	if (ret) {
21658c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n",
21668c2ecf20Sopenharmony_ci			ret);
21678c2ecf20Sopenharmony_ci		goto out;
21688c2ecf20Sopenharmony_ci	}
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci	nc->irq = of_irq_get(nand_np, 0);
21718c2ecf20Sopenharmony_ci	if (nc->irq <= 0) {
21728c2ecf20Sopenharmony_ci		ret = nc->irq ?: -ENXIO;
21738c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
21748c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to get IRQ number (err = %d)\n",
21758c2ecf20Sopenharmony_ci				ret);
21768c2ecf20Sopenharmony_ci		goto out;
21778c2ecf20Sopenharmony_ci	}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	ret = of_address_to_resource(nfc_np, 0, &res);
21808c2ecf20Sopenharmony_ci	if (ret) {
21818c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n",
21828c2ecf20Sopenharmony_ci			ret);
21838c2ecf20Sopenharmony_ci		goto out;
21848c2ecf20Sopenharmony_ci	}
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	iomem = devm_ioremap_resource(dev, &res);
21878c2ecf20Sopenharmony_ci	if (IS_ERR(iomem)) {
21888c2ecf20Sopenharmony_ci		ret = PTR_ERR(iomem);
21898c2ecf20Sopenharmony_ci		goto out;
21908c2ecf20Sopenharmony_ci	}
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	regmap_conf.name = "nfc-io";
21938c2ecf20Sopenharmony_ci	regmap_conf.max_register = resource_size(&res) - 4;
21948c2ecf20Sopenharmony_ci	nc->io = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
21958c2ecf20Sopenharmony_ci	if (IS_ERR(nc->io)) {
21968c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->io);
21978c2ecf20Sopenharmony_ci		dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
21988c2ecf20Sopenharmony_ci			ret);
21998c2ecf20Sopenharmony_ci		goto out;
22008c2ecf20Sopenharmony_ci	}
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	ret = of_address_to_resource(nfc_np, 1, &res);
22038c2ecf20Sopenharmony_ci	if (ret) {
22048c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n",
22058c2ecf20Sopenharmony_ci			ret);
22068c2ecf20Sopenharmony_ci		goto out;
22078c2ecf20Sopenharmony_ci	}
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	iomem = devm_ioremap_resource(dev, &res);
22108c2ecf20Sopenharmony_ci	if (IS_ERR(iomem)) {
22118c2ecf20Sopenharmony_ci		ret = PTR_ERR(iomem);
22128c2ecf20Sopenharmony_ci		goto out;
22138c2ecf20Sopenharmony_ci	}
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	regmap_conf.name = "smc";
22168c2ecf20Sopenharmony_ci	regmap_conf.max_register = resource_size(&res) - 4;
22178c2ecf20Sopenharmony_ci	nc->base.smc = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
22188c2ecf20Sopenharmony_ci	if (IS_ERR(nc->base.smc)) {
22198c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->base.smc);
22208c2ecf20Sopenharmony_ci		dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
22218c2ecf20Sopenharmony_ci			ret);
22228c2ecf20Sopenharmony_ci		goto out;
22238c2ecf20Sopenharmony_ci	}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	ret = of_address_to_resource(nfc_np, 2, &res);
22268c2ecf20Sopenharmony_ci	if (ret) {
22278c2ecf20Sopenharmony_ci		dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n",
22288c2ecf20Sopenharmony_ci			ret);
22298c2ecf20Sopenharmony_ci		goto out;
22308c2ecf20Sopenharmony_ci	}
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci	nc->sram.virt = devm_ioremap_resource(dev, &res);
22338c2ecf20Sopenharmony_ci	if (IS_ERR(nc->sram.virt)) {
22348c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->sram.virt);
22358c2ecf20Sopenharmony_ci		goto out;
22368c2ecf20Sopenharmony_ci	}
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	nc->sram.dma = res.start;
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ciout:
22418c2ecf20Sopenharmony_ci	of_node_put(nfc_np);
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	return ret;
22448c2ecf20Sopenharmony_ci}
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_cistatic int
22478c2ecf20Sopenharmony_ciatmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
22488c2ecf20Sopenharmony_ci{
22498c2ecf20Sopenharmony_ci	struct device *dev = nc->base.dev;
22508c2ecf20Sopenharmony_ci	struct device_node *np;
22518c2ecf20Sopenharmony_ci	int ret;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci	np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
22548c2ecf20Sopenharmony_ci	if (!np) {
22558c2ecf20Sopenharmony_ci		dev_err(dev, "Missing or invalid atmel,smc property\n");
22568c2ecf20Sopenharmony_ci		return -EINVAL;
22578c2ecf20Sopenharmony_ci	}
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci	nc->hsmc_layout = atmel_hsmc_get_reg_layout(np);
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	nc->irq = of_irq_get(np, 0);
22628c2ecf20Sopenharmony_ci	of_node_put(np);
22638c2ecf20Sopenharmony_ci	if (nc->irq <= 0) {
22648c2ecf20Sopenharmony_ci		ret = nc->irq ?: -ENXIO;
22658c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
22668c2ecf20Sopenharmony_ci			dev_err(dev, "Failed to get IRQ number (err = %d)\n",
22678c2ecf20Sopenharmony_ci				ret);
22688c2ecf20Sopenharmony_ci		return ret;
22698c2ecf20Sopenharmony_ci	}
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0);
22728c2ecf20Sopenharmony_ci	if (!np) {
22738c2ecf20Sopenharmony_ci		dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
22748c2ecf20Sopenharmony_ci		return -EINVAL;
22758c2ecf20Sopenharmony_ci	}
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci	nc->io = syscon_node_to_regmap(np);
22788c2ecf20Sopenharmony_ci	of_node_put(np);
22798c2ecf20Sopenharmony_ci	if (IS_ERR(nc->io)) {
22808c2ecf20Sopenharmony_ci		ret = PTR_ERR(nc->io);
22818c2ecf20Sopenharmony_ci		dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret);
22828c2ecf20Sopenharmony_ci		return ret;
22838c2ecf20Sopenharmony_ci	}
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci	nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node,
22868c2ecf20Sopenharmony_ci					 "atmel,nfc-sram", 0);
22878c2ecf20Sopenharmony_ci	if (!nc->sram.pool) {
22888c2ecf20Sopenharmony_ci		dev_err(nc->base.dev, "Missing SRAM\n");
22898c2ecf20Sopenharmony_ci		return -ENOMEM;
22908c2ecf20Sopenharmony_ci	}
22918c2ecf20Sopenharmony_ci
22928c2ecf20Sopenharmony_ci	nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool,
22938c2ecf20Sopenharmony_ci							   ATMEL_NFC_SRAM_SIZE,
22948c2ecf20Sopenharmony_ci							   &nc->sram.dma);
22958c2ecf20Sopenharmony_ci	if (!nc->sram.virt) {
22968c2ecf20Sopenharmony_ci		dev_err(nc->base.dev,
22978c2ecf20Sopenharmony_ci			"Could not allocate memory from the NFC SRAM pool\n");
22988c2ecf20Sopenharmony_ci		return -ENOMEM;
22998c2ecf20Sopenharmony_ci	}
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci	return 0;
23028c2ecf20Sopenharmony_ci}
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_cistatic int
23058c2ecf20Sopenharmony_ciatmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
23068c2ecf20Sopenharmony_ci{
23078c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *hsmc_nc;
23088c2ecf20Sopenharmony_ci	int ret;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	ret = atmel_nand_controller_remove_nands(nc);
23118c2ecf20Sopenharmony_ci	if (ret)
23128c2ecf20Sopenharmony_ci		return ret;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
23158c2ecf20Sopenharmony_ci	regmap_write(hsmc_nc->base.smc, ATMEL_HSMC_NFC_CTRL,
23168c2ecf20Sopenharmony_ci		     ATMEL_HSMC_NFC_CTRL_DIS);
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci	if (hsmc_nc->sram.pool)
23198c2ecf20Sopenharmony_ci		gen_pool_free(hsmc_nc->sram.pool,
23208c2ecf20Sopenharmony_ci			      (unsigned long)hsmc_nc->sram.virt,
23218c2ecf20Sopenharmony_ci			      ATMEL_NFC_SRAM_SIZE);
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci	if (hsmc_nc->clk) {
23248c2ecf20Sopenharmony_ci		clk_disable_unprepare(hsmc_nc->clk);
23258c2ecf20Sopenharmony_ci		clk_put(hsmc_nc->clk);
23268c2ecf20Sopenharmony_ci	}
23278c2ecf20Sopenharmony_ci
23288c2ecf20Sopenharmony_ci	atmel_nand_controller_cleanup(nc);
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	return 0;
23318c2ecf20Sopenharmony_ci}
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
23348c2ecf20Sopenharmony_ci				const struct atmel_nand_controller_caps *caps)
23358c2ecf20Sopenharmony_ci{
23368c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
23378c2ecf20Sopenharmony_ci	struct atmel_hsmc_nand_controller *nc;
23388c2ecf20Sopenharmony_ci	int ret;
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ci	nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
23418c2ecf20Sopenharmony_ci	if (!nc)
23428c2ecf20Sopenharmony_ci		return -ENOMEM;
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	ret = atmel_nand_controller_init(&nc->base, pdev, caps);
23458c2ecf20Sopenharmony_ci	if (ret)
23468c2ecf20Sopenharmony_ci		return ret;
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci	if (caps->legacy_of_bindings)
23498c2ecf20Sopenharmony_ci		ret = atmel_hsmc_nand_controller_legacy_init(nc);
23508c2ecf20Sopenharmony_ci	else
23518c2ecf20Sopenharmony_ci		ret = atmel_hsmc_nand_controller_init(nc);
23528c2ecf20Sopenharmony_ci
23538c2ecf20Sopenharmony_ci	if (ret)
23548c2ecf20Sopenharmony_ci		return ret;
23558c2ecf20Sopenharmony_ci
23568c2ecf20Sopenharmony_ci	/* Make sure all irqs are masked before registering our IRQ handler. */
23578c2ecf20Sopenharmony_ci	regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
23588c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt,
23598c2ecf20Sopenharmony_ci			       IRQF_SHARED, "nfc", nc);
23608c2ecf20Sopenharmony_ci	if (ret) {
23618c2ecf20Sopenharmony_ci		dev_err(dev,
23628c2ecf20Sopenharmony_ci			"Could not get register NFC interrupt handler (err = %d)\n",
23638c2ecf20Sopenharmony_ci			ret);
23648c2ecf20Sopenharmony_ci		goto err;
23658c2ecf20Sopenharmony_ci	}
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	/* Initial NFC configuration. */
23688c2ecf20Sopenharmony_ci	regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG,
23698c2ecf20Sopenharmony_ci		     ATMEL_HSMC_NFC_CFG_DTO_MAX);
23708c2ecf20Sopenharmony_ci	regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
23718c2ecf20Sopenharmony_ci		     ATMEL_HSMC_NFC_CTRL_EN);
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ci	ret = atmel_nand_controller_add_nands(&nc->base);
23748c2ecf20Sopenharmony_ci	if (ret)
23758c2ecf20Sopenharmony_ci		goto err;
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci	return 0;
23788c2ecf20Sopenharmony_ci
23798c2ecf20Sopenharmony_cierr:
23808c2ecf20Sopenharmony_ci	atmel_hsmc_nand_controller_remove(&nc->base);
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci	return ret;
23838c2ecf20Sopenharmony_ci}
23848c2ecf20Sopenharmony_ci
23858c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
23868c2ecf20Sopenharmony_ci	.probe = atmel_hsmc_nand_controller_probe,
23878c2ecf20Sopenharmony_ci	.remove = atmel_hsmc_nand_controller_remove,
23888c2ecf20Sopenharmony_ci	.ecc_init = atmel_hsmc_nand_ecc_init,
23898c2ecf20Sopenharmony_ci	.nand_init = atmel_nand_init,
23908c2ecf20Sopenharmony_ci	.setup_interface = atmel_hsmc_nand_setup_interface,
23918c2ecf20Sopenharmony_ci	.exec_op = atmel_hsmc_nand_exec_op,
23928c2ecf20Sopenharmony_ci};
23938c2ecf20Sopenharmony_ci
23948c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
23958c2ecf20Sopenharmony_ci	.has_dma = true,
23968c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
23978c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
23988c2ecf20Sopenharmony_ci	.ops = &atmel_hsmc_nc_ops,
23998c2ecf20Sopenharmony_ci};
24008c2ecf20Sopenharmony_ci
24018c2ecf20Sopenharmony_ci/* Only used to parse old bindings. */
24028c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sama5_nand_caps = {
24038c2ecf20Sopenharmony_ci	.has_dma = true,
24048c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
24058c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
24068c2ecf20Sopenharmony_ci	.ops = &atmel_hsmc_nc_ops,
24078c2ecf20Sopenharmony_ci	.legacy_of_bindings = true,
24088c2ecf20Sopenharmony_ci};
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_cistatic int atmel_smc_nand_controller_probe(struct platform_device *pdev,
24118c2ecf20Sopenharmony_ci				const struct atmel_nand_controller_caps *caps)
24128c2ecf20Sopenharmony_ci{
24138c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
24148c2ecf20Sopenharmony_ci	struct atmel_smc_nand_controller *nc;
24158c2ecf20Sopenharmony_ci	int ret;
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
24188c2ecf20Sopenharmony_ci	if (!nc)
24198c2ecf20Sopenharmony_ci		return -ENOMEM;
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	ret = atmel_nand_controller_init(&nc->base, pdev, caps);
24228c2ecf20Sopenharmony_ci	if (ret)
24238c2ecf20Sopenharmony_ci		return ret;
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	ret = atmel_smc_nand_controller_init(nc);
24268c2ecf20Sopenharmony_ci	if (ret)
24278c2ecf20Sopenharmony_ci		return ret;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	return atmel_nand_controller_add_nands(&nc->base);
24308c2ecf20Sopenharmony_ci}
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_cistatic int
24338c2ecf20Sopenharmony_ciatmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
24348c2ecf20Sopenharmony_ci{
24358c2ecf20Sopenharmony_ci	int ret;
24368c2ecf20Sopenharmony_ci
24378c2ecf20Sopenharmony_ci	ret = atmel_nand_controller_remove_nands(nc);
24388c2ecf20Sopenharmony_ci	if (ret)
24398c2ecf20Sopenharmony_ci		return ret;
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	atmel_nand_controller_cleanup(nc);
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	return 0;
24448c2ecf20Sopenharmony_ci}
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci/*
24478c2ecf20Sopenharmony_ci * The SMC reg layout of at91rm9200 is completely different which prevents us
24488c2ecf20Sopenharmony_ci * from re-using atmel_smc_nand_setup_interface() for the
24498c2ecf20Sopenharmony_ci * ->setup_interface() hook.
24508c2ecf20Sopenharmony_ci * At this point, there's no support for the at91rm9200 SMC IP, so we leave
24518c2ecf20Sopenharmony_ci * ->setup_interface() unassigned.
24528c2ecf20Sopenharmony_ci */
24538c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_ops at91rm9200_nc_ops = {
24548c2ecf20Sopenharmony_ci	.probe = atmel_smc_nand_controller_probe,
24558c2ecf20Sopenharmony_ci	.remove = atmel_smc_nand_controller_remove,
24568c2ecf20Sopenharmony_ci	.ecc_init = atmel_nand_ecc_init,
24578c2ecf20Sopenharmony_ci	.nand_init = atmel_smc_nand_init,
24588c2ecf20Sopenharmony_ci	.exec_op = atmel_smc_nand_exec_op,
24598c2ecf20Sopenharmony_ci};
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
24628c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
24638c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
24648c2ecf20Sopenharmony_ci	.ebi_csa_regmap_name = "atmel,matrix",
24658c2ecf20Sopenharmony_ci	.ops = &at91rm9200_nc_ops,
24668c2ecf20Sopenharmony_ci};
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
24698c2ecf20Sopenharmony_ci	.probe = atmel_smc_nand_controller_probe,
24708c2ecf20Sopenharmony_ci	.remove = atmel_smc_nand_controller_remove,
24718c2ecf20Sopenharmony_ci	.ecc_init = atmel_nand_ecc_init,
24728c2ecf20Sopenharmony_ci	.nand_init = atmel_smc_nand_init,
24738c2ecf20Sopenharmony_ci	.setup_interface = atmel_smc_nand_setup_interface,
24748c2ecf20Sopenharmony_ci	.exec_op = atmel_smc_nand_exec_op,
24758c2ecf20Sopenharmony_ci};
24768c2ecf20Sopenharmony_ci
24778c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = {
24788c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
24798c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
24808c2ecf20Sopenharmony_ci	.ebi_csa_regmap_name = "atmel,matrix",
24818c2ecf20Sopenharmony_ci	.ops = &atmel_smc_nc_ops,
24828c2ecf20Sopenharmony_ci};
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = {
24858c2ecf20Sopenharmony_ci	.ale_offs = BIT(22),
24868c2ecf20Sopenharmony_ci	.cle_offs = BIT(21),
24878c2ecf20Sopenharmony_ci	.ebi_csa_regmap_name = "atmel,matrix",
24888c2ecf20Sopenharmony_ci	.ops = &atmel_smc_nc_ops,
24898c2ecf20Sopenharmony_ci};
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = {
24928c2ecf20Sopenharmony_ci	.has_dma = true,
24938c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
24948c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
24958c2ecf20Sopenharmony_ci	.ebi_csa_regmap_name = "atmel,matrix",
24968c2ecf20Sopenharmony_ci	.ops = &atmel_smc_nc_ops,
24978c2ecf20Sopenharmony_ci};
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps microchip_sam9x60_nc_caps = {
25008c2ecf20Sopenharmony_ci	.has_dma = true,
25018c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
25028c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
25038c2ecf20Sopenharmony_ci	.ebi_csa_regmap_name = "microchip,sfr",
25048c2ecf20Sopenharmony_ci	.ops = &atmel_smc_nc_ops,
25058c2ecf20Sopenharmony_ci};
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci/* Only used to parse old bindings. */
25088c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = {
25098c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
25108c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
25118c2ecf20Sopenharmony_ci	.ops = &atmel_smc_nc_ops,
25128c2ecf20Sopenharmony_ci	.legacy_of_bindings = true,
25138c2ecf20Sopenharmony_ci};
25148c2ecf20Sopenharmony_ci
25158c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = {
25168c2ecf20Sopenharmony_ci	.ale_offs = BIT(22),
25178c2ecf20Sopenharmony_ci	.cle_offs = BIT(21),
25188c2ecf20Sopenharmony_ci	.ops = &atmel_smc_nc_ops,
25198c2ecf20Sopenharmony_ci	.legacy_of_bindings = true,
25208c2ecf20Sopenharmony_ci};
25218c2ecf20Sopenharmony_ci
25228c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = {
25238c2ecf20Sopenharmony_ci	.has_dma = true,
25248c2ecf20Sopenharmony_ci	.ale_offs = BIT(21),
25258c2ecf20Sopenharmony_ci	.cle_offs = BIT(22),
25268c2ecf20Sopenharmony_ci	.ops = &atmel_smc_nc_ops,
25278c2ecf20Sopenharmony_ci	.legacy_of_bindings = true,
25288c2ecf20Sopenharmony_ci};
25298c2ecf20Sopenharmony_ci
25308c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_nand_controller_of_ids[] = {
25318c2ecf20Sopenharmony_ci	{
25328c2ecf20Sopenharmony_ci		.compatible = "atmel,at91rm9200-nand-controller",
25338c2ecf20Sopenharmony_ci		.data = &atmel_rm9200_nc_caps,
25348c2ecf20Sopenharmony_ci	},
25358c2ecf20Sopenharmony_ci	{
25368c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9260-nand-controller",
25378c2ecf20Sopenharmony_ci		.data = &atmel_sam9260_nc_caps,
25388c2ecf20Sopenharmony_ci	},
25398c2ecf20Sopenharmony_ci	{
25408c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9261-nand-controller",
25418c2ecf20Sopenharmony_ci		.data = &atmel_sam9261_nc_caps,
25428c2ecf20Sopenharmony_ci	},
25438c2ecf20Sopenharmony_ci	{
25448c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9g45-nand-controller",
25458c2ecf20Sopenharmony_ci		.data = &atmel_sam9g45_nc_caps,
25468c2ecf20Sopenharmony_ci	},
25478c2ecf20Sopenharmony_ci	{
25488c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d3-nand-controller",
25498c2ecf20Sopenharmony_ci		.data = &atmel_sama5_nc_caps,
25508c2ecf20Sopenharmony_ci	},
25518c2ecf20Sopenharmony_ci	{
25528c2ecf20Sopenharmony_ci		.compatible = "microchip,sam9x60-nand-controller",
25538c2ecf20Sopenharmony_ci		.data = &microchip_sam9x60_nc_caps,
25548c2ecf20Sopenharmony_ci	},
25558c2ecf20Sopenharmony_ci	/* Support for old/deprecated bindings: */
25568c2ecf20Sopenharmony_ci	{
25578c2ecf20Sopenharmony_ci		.compatible = "atmel,at91rm9200-nand",
25588c2ecf20Sopenharmony_ci		.data = &atmel_rm9200_nand_caps,
25598c2ecf20Sopenharmony_ci	},
25608c2ecf20Sopenharmony_ci	{
25618c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d4-nand",
25628c2ecf20Sopenharmony_ci		.data = &atmel_rm9200_nand_caps,
25638c2ecf20Sopenharmony_ci	},
25648c2ecf20Sopenharmony_ci	{
25658c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d2-nand",
25668c2ecf20Sopenharmony_ci		.data = &atmel_rm9200_nand_caps,
25678c2ecf20Sopenharmony_ci	},
25688c2ecf20Sopenharmony_ci	{ /* sentinel */ },
25698c2ecf20Sopenharmony_ci};
25708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids);
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_cistatic int atmel_nand_controller_probe(struct platform_device *pdev)
25738c2ecf20Sopenharmony_ci{
25748c2ecf20Sopenharmony_ci	const struct atmel_nand_controller_caps *caps;
25758c2ecf20Sopenharmony_ci
25768c2ecf20Sopenharmony_ci	if (pdev->id_entry)
25778c2ecf20Sopenharmony_ci		caps = (void *)pdev->id_entry->driver_data;
25788c2ecf20Sopenharmony_ci	else
25798c2ecf20Sopenharmony_ci		caps = of_device_get_match_data(&pdev->dev);
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ci	if (!caps) {
25828c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Could not retrieve NFC caps\n");
25838c2ecf20Sopenharmony_ci		return -EINVAL;
25848c2ecf20Sopenharmony_ci	}
25858c2ecf20Sopenharmony_ci
25868c2ecf20Sopenharmony_ci	if (caps->legacy_of_bindings) {
25878c2ecf20Sopenharmony_ci		struct device_node *nfc_node;
25888c2ecf20Sopenharmony_ci		u32 ale_offs = 21;
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ci		/*
25918c2ecf20Sopenharmony_ci		 * If we are parsing legacy DT props and the DT contains a
25928c2ecf20Sopenharmony_ci		 * valid NFC node, forward the request to the sama5 logic.
25938c2ecf20Sopenharmony_ci		 */
25948c2ecf20Sopenharmony_ci		nfc_node = of_get_compatible_child(pdev->dev.of_node,
25958c2ecf20Sopenharmony_ci						   "atmel,sama5d3-nfc");
25968c2ecf20Sopenharmony_ci		if (nfc_node) {
25978c2ecf20Sopenharmony_ci			caps = &atmel_sama5_nand_caps;
25988c2ecf20Sopenharmony_ci			of_node_put(nfc_node);
25998c2ecf20Sopenharmony_ci		}
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci		/*
26028c2ecf20Sopenharmony_ci		 * Even if the compatible says we are dealing with an
26038c2ecf20Sopenharmony_ci		 * at91rm9200 controller, the atmel,nand-has-dma specify that
26048c2ecf20Sopenharmony_ci		 * this controller supports DMA, which means we are in fact
26058c2ecf20Sopenharmony_ci		 * dealing with an at91sam9g45+ controller.
26068c2ecf20Sopenharmony_ci		 */
26078c2ecf20Sopenharmony_ci		if (!caps->has_dma &&
26088c2ecf20Sopenharmony_ci		    of_property_read_bool(pdev->dev.of_node,
26098c2ecf20Sopenharmony_ci					  "atmel,nand-has-dma"))
26108c2ecf20Sopenharmony_ci			caps = &atmel_sam9g45_nand_caps;
26118c2ecf20Sopenharmony_ci
26128c2ecf20Sopenharmony_ci		/*
26138c2ecf20Sopenharmony_ci		 * All SoCs except the at91sam9261 are assigning ALE to A21 and
26148c2ecf20Sopenharmony_ci		 * CLE to A22. If atmel,nand-addr-offset != 21 this means we're
26158c2ecf20Sopenharmony_ci		 * actually dealing with an at91sam9261 controller.
26168c2ecf20Sopenharmony_ci		 */
26178c2ecf20Sopenharmony_ci		of_property_read_u32(pdev->dev.of_node,
26188c2ecf20Sopenharmony_ci				     "atmel,nand-addr-offset", &ale_offs);
26198c2ecf20Sopenharmony_ci		if (ale_offs != 21)
26208c2ecf20Sopenharmony_ci			caps = &atmel_sam9261_nand_caps;
26218c2ecf20Sopenharmony_ci	}
26228c2ecf20Sopenharmony_ci
26238c2ecf20Sopenharmony_ci	return caps->ops->probe(pdev, caps);
26248c2ecf20Sopenharmony_ci}
26258c2ecf20Sopenharmony_ci
26268c2ecf20Sopenharmony_cistatic int atmel_nand_controller_remove(struct platform_device *pdev)
26278c2ecf20Sopenharmony_ci{
26288c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc = platform_get_drvdata(pdev);
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_ci	return nc->caps->ops->remove(nc);
26318c2ecf20Sopenharmony_ci}
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_cistatic __maybe_unused int atmel_nand_controller_resume(struct device *dev)
26348c2ecf20Sopenharmony_ci{
26358c2ecf20Sopenharmony_ci	struct atmel_nand_controller *nc = dev_get_drvdata(dev);
26368c2ecf20Sopenharmony_ci	struct atmel_nand *nand;
26378c2ecf20Sopenharmony_ci
26388c2ecf20Sopenharmony_ci	if (nc->pmecc)
26398c2ecf20Sopenharmony_ci		atmel_pmecc_reset(nc->pmecc);
26408c2ecf20Sopenharmony_ci
26418c2ecf20Sopenharmony_ci	list_for_each_entry(nand, &nc->chips, node) {
26428c2ecf20Sopenharmony_ci		int i;
26438c2ecf20Sopenharmony_ci
26448c2ecf20Sopenharmony_ci		for (i = 0; i < nand->numcs; i++)
26458c2ecf20Sopenharmony_ci			nand_reset(&nand->base, i);
26468c2ecf20Sopenharmony_ci	}
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	return 0;
26498c2ecf20Sopenharmony_ci}
26508c2ecf20Sopenharmony_ci
26518c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL,
26528c2ecf20Sopenharmony_ci			 atmel_nand_controller_resume);
26538c2ecf20Sopenharmony_ci
26548c2ecf20Sopenharmony_cistatic struct platform_driver atmel_nand_controller_driver = {
26558c2ecf20Sopenharmony_ci	.driver = {
26568c2ecf20Sopenharmony_ci		.name = "atmel-nand-controller",
26578c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(atmel_nand_controller_of_ids),
26588c2ecf20Sopenharmony_ci		.pm = &atmel_nand_controller_pm_ops,
26598c2ecf20Sopenharmony_ci	},
26608c2ecf20Sopenharmony_ci	.probe = atmel_nand_controller_probe,
26618c2ecf20Sopenharmony_ci	.remove = atmel_nand_controller_remove,
26628c2ecf20Sopenharmony_ci};
26638c2ecf20Sopenharmony_cimodule_platform_driver(atmel_nand_controller_driver);
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
26668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
26678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs");
26688c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:atmel-nand-controller");
2669