18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
48c2ecf20Sopenharmony_ci * Copyright 2008 Sascha Hauer, kernel@pengutronix.de
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
128c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h>
138c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/device.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/clk.h>
188c2ecf20Sopenharmony_ci#include <linux/err.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/irq.h>
218c2ecf20Sopenharmony_ci#include <linux/completion.h>
228c2ecf20Sopenharmony_ci#include <linux/of.h>
238c2ecf20Sopenharmony_ci#include <linux/of_device.h>
248c2ecf20Sopenharmony_ci#include <linux/platform_data/mtd-mxc_nand.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define DRIVER_NAME "mxc_nand"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Addresses for NFC registers */
298c2ecf20Sopenharmony_ci#define NFC_V1_V2_BUF_SIZE		(host->regs + 0x00)
308c2ecf20Sopenharmony_ci#define NFC_V1_V2_BUF_ADDR		(host->regs + 0x04)
318c2ecf20Sopenharmony_ci#define NFC_V1_V2_FLASH_ADDR		(host->regs + 0x06)
328c2ecf20Sopenharmony_ci#define NFC_V1_V2_FLASH_CMD		(host->regs + 0x08)
338c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG		(host->regs + 0x0a)
348c2ecf20Sopenharmony_ci#define NFC_V1_V2_ECC_STATUS_RESULT	(host->regs + 0x0c)
358c2ecf20Sopenharmony_ci#define NFC_V1_V2_RSLTMAIN_AREA		(host->regs + 0x0e)
368c2ecf20Sopenharmony_ci#define NFC_V21_RSLTSPARE_AREA		(host->regs + 0x10)
378c2ecf20Sopenharmony_ci#define NFC_V1_V2_WRPROT		(host->regs + 0x12)
388c2ecf20Sopenharmony_ci#define NFC_V1_UNLOCKSTART_BLKADDR	(host->regs + 0x14)
398c2ecf20Sopenharmony_ci#define NFC_V1_UNLOCKEND_BLKADDR	(host->regs + 0x16)
408c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR0	(host->regs + 0x20)
418c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR1	(host->regs + 0x24)
428c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR2	(host->regs + 0x28)
438c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR3	(host->regs + 0x2c)
448c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR0	(host->regs + 0x22)
458c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR1	(host->regs + 0x26)
468c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR2	(host->regs + 0x2a)
478c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR3	(host->regs + 0x2e)
488c2ecf20Sopenharmony_ci#define NFC_V1_V2_NF_WRPRST		(host->regs + 0x18)
498c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1		(host->regs + 0x1a)
508c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG2		(host->regs + 0x1c)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_ECC_MODE_4	(1 << 0)
538c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_SP_EN		(1 << 2)
548c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_ECC_EN	(1 << 3)
558c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_INT_MSK	(1 << 4)
568c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_BIG		(1 << 5)
578c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_RST		(1 << 6)
588c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_CE		(1 << 7)
598c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_ONE_CYCLE	(1 << 8)
608c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_PPB(x)		(((x) & 0x3) << 9)
618c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_FP_INT		(1 << 11)
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG2_INT		(1 << 15)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * Operation modes for the NFC. Valid for v1, v2 and v3
678c2ecf20Sopenharmony_ci * type controllers.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_ci#define NFC_CMD				(1 << 0)
708c2ecf20Sopenharmony_ci#define NFC_ADDR			(1 << 1)
718c2ecf20Sopenharmony_ci#define NFC_INPUT			(1 << 2)
728c2ecf20Sopenharmony_ci#define NFC_OUTPUT			(1 << 3)
738c2ecf20Sopenharmony_ci#define NFC_ID				(1 << 4)
748c2ecf20Sopenharmony_ci#define NFC_STATUS			(1 << 5)
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define NFC_V3_FLASH_CMD		(host->regs_axi + 0x00)
778c2ecf20Sopenharmony_ci#define NFC_V3_FLASH_ADDR0		(host->regs_axi + 0x04)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG1			(host->regs_axi + 0x34)
808c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG1_SP_EN		(1 << 0)
818c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG1_RBA(x)		(((x) & 0x7 ) << 4)
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define NFC_V3_ECC_STATUS_RESULT	(host->regs_axi + 0x38)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#define NFC_V3_LAUNCH			(host->regs_axi + 0x40)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT			(host->regs_ip + 0x0)
888c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_LOCK_TIGHT	(1 << 0)
898c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_LOCK		(1 << 1)
908c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_UNLOCK		(1 << 2)
918c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_BLS_UNLOCK	(2 << 6)
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0   (host->regs_ip + 0x04)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2			(host->regs_ip + 0x24)
968c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PS_512			(0 << 0)
978c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PS_2048			(1 << 0)
988c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PS_4096			(2 << 0)
998c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ONE_CYCLE		(1 << 2)
1008c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ECC_EN			(1 << 3)
1018c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_2CMD_PHASES		(1 << 4)
1028c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0		(1 << 5)
1038c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ECC_MODE_8		(1 << 6)
1048c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PPB(x, shift)		(((x) & 0x3) << shift)
1058c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x)	(((x) & 0x3) << 12)
1068c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_INT_MSK			(1 << 15)
1078c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ST_CMD(x)		(((x) & 0xff) << 24)
1088c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_SPAS(x)			(((x) & 0xff) << 16)
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3				(host->regs_ip + 0x28)
1118c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_ADD_OP(x)		(((x) & 0x3) << 0)
1128c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_FW8			(1 << 3)
1138c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_SBB(x)			(((x) & 0x7) << 8)
1148c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x)	(((x) & 0x7) << 12)
1158c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_RBB_MODE			(1 << 15)
1168c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_NO_SDMA			(1 << 20)
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define NFC_V3_IPC			(host->regs_ip + 0x2C)
1198c2ecf20Sopenharmony_ci#define NFC_V3_IPC_CREQ			(1 << 0)
1208c2ecf20Sopenharmony_ci#define NFC_V3_IPC_INT			(1 << 31)
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci#define NFC_V3_DELAY_LINE		(host->regs_ip + 0x34)
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistruct mxc_nand_host;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistruct mxc_nand_devtype_data {
1278c2ecf20Sopenharmony_ci	void (*preset)(struct mtd_info *);
1288c2ecf20Sopenharmony_ci	int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc,
1298c2ecf20Sopenharmony_ci			 int page);
1308c2ecf20Sopenharmony_ci	void (*send_cmd)(struct mxc_nand_host *, uint16_t, int);
1318c2ecf20Sopenharmony_ci	void (*send_addr)(struct mxc_nand_host *, uint16_t, int);
1328c2ecf20Sopenharmony_ci	void (*send_page)(struct mtd_info *, unsigned int);
1338c2ecf20Sopenharmony_ci	void (*send_read_id)(struct mxc_nand_host *);
1348c2ecf20Sopenharmony_ci	uint16_t (*get_dev_status)(struct mxc_nand_host *);
1358c2ecf20Sopenharmony_ci	int (*check_int)(struct mxc_nand_host *);
1368c2ecf20Sopenharmony_ci	void (*irq_control)(struct mxc_nand_host *, int);
1378c2ecf20Sopenharmony_ci	u32 (*get_ecc_status)(struct mxc_nand_host *);
1388c2ecf20Sopenharmony_ci	const struct mtd_ooblayout_ops *ooblayout;
1398c2ecf20Sopenharmony_ci	void (*select_chip)(struct nand_chip *chip, int cs);
1408c2ecf20Sopenharmony_ci	int (*setup_interface)(struct nand_chip *chip, int csline,
1418c2ecf20Sopenharmony_ci			       const struct nand_interface_config *conf);
1428c2ecf20Sopenharmony_ci	void (*enable_hwecc)(struct nand_chip *chip, bool enable);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/*
1458c2ecf20Sopenharmony_ci	 * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked
1468c2ecf20Sopenharmony_ci	 * (CONFIG1:INT_MSK is set). To handle this the driver uses
1478c2ecf20Sopenharmony_ci	 * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK
1488c2ecf20Sopenharmony_ci	 */
1498c2ecf20Sopenharmony_ci	int irqpending_quirk;
1508c2ecf20Sopenharmony_ci	int needs_ip;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	size_t regs_offset;
1538c2ecf20Sopenharmony_ci	size_t spare0_offset;
1548c2ecf20Sopenharmony_ci	size_t axi_offset;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	int spare_len;
1578c2ecf20Sopenharmony_ci	int eccbytes;
1588c2ecf20Sopenharmony_ci	int eccsize;
1598c2ecf20Sopenharmony_ci	int ppb_shift;
1608c2ecf20Sopenharmony_ci};
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistruct mxc_nand_host {
1638c2ecf20Sopenharmony_ci	struct nand_chip	nand;
1648c2ecf20Sopenharmony_ci	struct device		*dev;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	void __iomem		*spare0;
1678c2ecf20Sopenharmony_ci	void __iomem		*main_area0;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	void __iomem		*base;
1708c2ecf20Sopenharmony_ci	void __iomem		*regs;
1718c2ecf20Sopenharmony_ci	void __iomem		*regs_axi;
1728c2ecf20Sopenharmony_ci	void __iomem		*regs_ip;
1738c2ecf20Sopenharmony_ci	int			status_request;
1748c2ecf20Sopenharmony_ci	struct clk		*clk;
1758c2ecf20Sopenharmony_ci	int			clk_act;
1768c2ecf20Sopenharmony_ci	int			irq;
1778c2ecf20Sopenharmony_ci	int			eccsize;
1788c2ecf20Sopenharmony_ci	int			used_oobsize;
1798c2ecf20Sopenharmony_ci	int			active_cs;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	struct completion	op_completion;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	uint8_t			*data_buf;
1848c2ecf20Sopenharmony_ci	unsigned int		buf_start;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	const struct mxc_nand_devtype_data *devtype_data;
1878c2ecf20Sopenharmony_ci	struct mxc_nand_platform_data pdata;
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic const char * const part_probes[] = {
1918c2ecf20Sopenharmony_ci	"cmdlinepart", "RedBoot", "ofpart", NULL };
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic void memcpy32_fromio(void *trg, const void __iomem  *src, size_t size)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int i;
1968c2ecf20Sopenharmony_ci	u32 *t = trg;
1978c2ecf20Sopenharmony_ci	const __iomem u32 *s = src;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	for (i = 0; i < (size >> 2); i++)
2008c2ecf20Sopenharmony_ci		*t++ = __raw_readl(s++);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void memcpy16_fromio(void *trg, const void __iomem  *src, size_t size)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int i;
2068c2ecf20Sopenharmony_ci	u16 *t = trg;
2078c2ecf20Sopenharmony_ci	const __iomem u16 *s = src;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* We assume that src (IO) is always 32bit aligned */
2108c2ecf20Sopenharmony_ci	if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) {
2118c2ecf20Sopenharmony_ci		memcpy32_fromio(trg, src, size);
2128c2ecf20Sopenharmony_ci		return;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	for (i = 0; i < (size >> 1); i++)
2168c2ecf20Sopenharmony_ci		*t++ = __raw_readw(s++);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	/* __iowrite32_copy use 32bit size values so divide by 4 */
2228c2ecf20Sopenharmony_ci	__iowrite32_copy(trg, src, size / 4);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void memcpy16_toio(void __iomem *trg, const void *src, int size)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	int i;
2288c2ecf20Sopenharmony_ci	__iomem u16 *t = trg;
2298c2ecf20Sopenharmony_ci	const u16 *s = src;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* We assume that trg (IO) is always 32bit aligned */
2328c2ecf20Sopenharmony_ci	if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) {
2338c2ecf20Sopenharmony_ci		memcpy32_toio(trg, src, size);
2348c2ecf20Sopenharmony_ci		return;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	for (i = 0; i < (size >> 1); i++)
2388c2ecf20Sopenharmony_ci		__raw_writew(*s++, t++);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/*
2428c2ecf20Sopenharmony_ci * The controller splits a page into data chunks of 512 bytes + partial oob.
2438c2ecf20Sopenharmony_ci * There are writesize / 512 such chunks, the size of the partial oob parts is
2448c2ecf20Sopenharmony_ci * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then
2458c2ecf20Sopenharmony_ci * contains additionally the byte lost by rounding (if any).
2468c2ecf20Sopenharmony_ci * This function handles the needed shuffling between host->data_buf (which
2478c2ecf20Sopenharmony_ci * holds a page in natural order, i.e. writesize bytes data + oobsize bytes
2488c2ecf20Sopenharmony_ci * spare) and the NFC buffer.
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_cistatic void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct nand_chip *this = mtd_to_nand(mtd);
2538c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(this);
2548c2ecf20Sopenharmony_ci	u16 i, oob_chunk_size;
2558c2ecf20Sopenharmony_ci	u16 num_chunks = mtd->writesize / 512;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	u8 *d = buf;
2588c2ecf20Sopenharmony_ci	u8 __iomem *s = host->spare0;
2598c2ecf20Sopenharmony_ci	u16 sparebuf_size = host->devtype_data->spare_len;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* size of oob chunk for all but possibly the last one */
2628c2ecf20Sopenharmony_ci	oob_chunk_size = (host->used_oobsize / num_chunks) & ~1;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (bfrom) {
2658c2ecf20Sopenharmony_ci		for (i = 0; i < num_chunks - 1; i++)
2668c2ecf20Sopenharmony_ci			memcpy16_fromio(d + i * oob_chunk_size,
2678c2ecf20Sopenharmony_ci					s + i * sparebuf_size,
2688c2ecf20Sopenharmony_ci					oob_chunk_size);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		/* the last chunk */
2718c2ecf20Sopenharmony_ci		memcpy16_fromio(d + i * oob_chunk_size,
2728c2ecf20Sopenharmony_ci				s + i * sparebuf_size,
2738c2ecf20Sopenharmony_ci				host->used_oobsize - i * oob_chunk_size);
2748c2ecf20Sopenharmony_ci	} else {
2758c2ecf20Sopenharmony_ci		for (i = 0; i < num_chunks - 1; i++)
2768c2ecf20Sopenharmony_ci			memcpy16_toio(&s[i * sparebuf_size],
2778c2ecf20Sopenharmony_ci				      &d[i * oob_chunk_size],
2788c2ecf20Sopenharmony_ci				      oob_chunk_size);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		/* the last chunk */
2818c2ecf20Sopenharmony_ci		memcpy16_toio(&s[i * sparebuf_size],
2828c2ecf20Sopenharmony_ci			      &d[i * oob_chunk_size],
2838c2ecf20Sopenharmony_ci			      host->used_oobsize - i * oob_chunk_size);
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * MXC NANDFC can only perform full page+spare or spare-only read/write.  When
2898c2ecf20Sopenharmony_ci * the upper layers perform a read/write buf operation, the saved column address
2908c2ecf20Sopenharmony_ci * is used to index into the full page. So usually this function is called with
2918c2ecf20Sopenharmony_ci * column == 0 (unless no column cycle is needed indicated by column == -1)
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_cistatic void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
2968c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Write out column address, if necessary */
2998c2ecf20Sopenharmony_ci	if (column != -1) {
3008c2ecf20Sopenharmony_ci		host->devtype_data->send_addr(host, column & 0xff,
3018c2ecf20Sopenharmony_ci					      page_addr == -1);
3028c2ecf20Sopenharmony_ci		if (mtd->writesize > 512)
3038c2ecf20Sopenharmony_ci			/* another col addr cycle for 2k page */
3048c2ecf20Sopenharmony_ci			host->devtype_data->send_addr(host,
3058c2ecf20Sopenharmony_ci						      (column >> 8) & 0xff,
3068c2ecf20Sopenharmony_ci						      false);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Write out page address, if necessary */
3108c2ecf20Sopenharmony_ci	if (page_addr != -1) {
3118c2ecf20Sopenharmony_ci		/* paddr_0 - p_addr_7 */
3128c2ecf20Sopenharmony_ci		host->devtype_data->send_addr(host, (page_addr & 0xff), false);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		if (mtd->writesize > 512) {
3158c2ecf20Sopenharmony_ci			if (mtd->size >= 0x10000000) {
3168c2ecf20Sopenharmony_ci				/* paddr_8 - paddr_15 */
3178c2ecf20Sopenharmony_ci				host->devtype_data->send_addr(host,
3188c2ecf20Sopenharmony_ci						(page_addr >> 8) & 0xff,
3198c2ecf20Sopenharmony_ci						false);
3208c2ecf20Sopenharmony_ci				host->devtype_data->send_addr(host,
3218c2ecf20Sopenharmony_ci						(page_addr >> 16) & 0xff,
3228c2ecf20Sopenharmony_ci						true);
3238c2ecf20Sopenharmony_ci			} else
3248c2ecf20Sopenharmony_ci				/* paddr_8 - paddr_15 */
3258c2ecf20Sopenharmony_ci				host->devtype_data->send_addr(host,
3268c2ecf20Sopenharmony_ci						(page_addr >> 8) & 0xff, true);
3278c2ecf20Sopenharmony_ci		} else {
3288c2ecf20Sopenharmony_ci			if (nand_chip->options & NAND_ROW_ADDR_3) {
3298c2ecf20Sopenharmony_ci				/* paddr_8 - paddr_15 */
3308c2ecf20Sopenharmony_ci				host->devtype_data->send_addr(host,
3318c2ecf20Sopenharmony_ci						(page_addr >> 8) & 0xff,
3328c2ecf20Sopenharmony_ci						false);
3338c2ecf20Sopenharmony_ci				host->devtype_data->send_addr(host,
3348c2ecf20Sopenharmony_ci						(page_addr >> 16) & 0xff,
3358c2ecf20Sopenharmony_ci						true);
3368c2ecf20Sopenharmony_ci			} else
3378c2ecf20Sopenharmony_ci				/* paddr_8 - paddr_15 */
3388c2ecf20Sopenharmony_ci				host->devtype_data->send_addr(host,
3398c2ecf20Sopenharmony_ci						(page_addr >> 8) & 0xff, true);
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int check_int_v3(struct mxc_nand_host *host)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	uint32_t tmp;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	tmp = readl(NFC_V3_IPC);
3498c2ecf20Sopenharmony_ci	if (!(tmp & NFC_V3_IPC_INT))
3508c2ecf20Sopenharmony_ci		return 0;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	tmp &= ~NFC_V3_IPC_INT;
3538c2ecf20Sopenharmony_ci	writel(tmp, NFC_V3_IPC);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	return 1;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic int check_int_v1_v2(struct mxc_nand_host *host)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	uint32_t tmp;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	tmp = readw(NFC_V1_V2_CONFIG2);
3638c2ecf20Sopenharmony_ci	if (!(tmp & NFC_V1_V2_CONFIG2_INT))
3648c2ecf20Sopenharmony_ci		return 0;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (!host->devtype_data->irqpending_quirk)
3678c2ecf20Sopenharmony_ci		writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return 1;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void irq_control_v1_v2(struct mxc_nand_host *host, int activate)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	uint16_t tmp;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	tmp = readw(NFC_V1_V2_CONFIG1);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (activate)
3798c2ecf20Sopenharmony_ci		tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK;
3808c2ecf20Sopenharmony_ci	else
3818c2ecf20Sopenharmony_ci		tmp |= NFC_V1_V2_CONFIG1_INT_MSK;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	writew(tmp, NFC_V1_V2_CONFIG1);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic void irq_control_v3(struct mxc_nand_host *host, int activate)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	uint32_t tmp;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	tmp = readl(NFC_V3_CONFIG2);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	if (activate)
3938c2ecf20Sopenharmony_ci		tmp &= ~NFC_V3_CONFIG2_INT_MSK;
3948c2ecf20Sopenharmony_ci	else
3958c2ecf20Sopenharmony_ci		tmp |= NFC_V3_CONFIG2_INT_MSK;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	writel(tmp, NFC_V3_CONFIG2);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic void irq_control(struct mxc_nand_host *host, int activate)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	if (host->devtype_data->irqpending_quirk) {
4038c2ecf20Sopenharmony_ci		if (activate)
4048c2ecf20Sopenharmony_ci			enable_irq(host->irq);
4058c2ecf20Sopenharmony_ci		else
4068c2ecf20Sopenharmony_ci			disable_irq_nosync(host->irq);
4078c2ecf20Sopenharmony_ci	} else {
4088c2ecf20Sopenharmony_ci		host->devtype_data->irq_control(host, activate);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic u32 get_ecc_status_v1(struct mxc_nand_host *host)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	return readw(NFC_V1_V2_ECC_STATUS_RESULT);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic u32 get_ecc_status_v2(struct mxc_nand_host *host)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	return readl(NFC_V1_V2_ECC_STATUS_RESULT);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic u32 get_ecc_status_v3(struct mxc_nand_host *host)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	return readl(NFC_V3_ECC_STATUS_RESULT);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic irqreturn_t mxc_nfc_irq(int irq, void *dev_id)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = dev_id;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (!host->devtype_data->check_int(host))
4328c2ecf20Sopenharmony_ci		return IRQ_NONE;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	irq_control(host, 0);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	complete(&host->op_completion);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci/* This function polls the NANDFC to wait for the basic operation to
4428c2ecf20Sopenharmony_ci * complete by checking the INT bit of config2 register.
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_cistatic int wait_op_done(struct mxc_nand_host *host, int useirq)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	int ret = 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/*
4498c2ecf20Sopenharmony_ci	 * If operation is already complete, don't bother to setup an irq or a
4508c2ecf20Sopenharmony_ci	 * loop.
4518c2ecf20Sopenharmony_ci	 */
4528c2ecf20Sopenharmony_ci	if (host->devtype_data->check_int(host))
4538c2ecf20Sopenharmony_ci		return 0;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (useirq) {
4568c2ecf20Sopenharmony_ci		unsigned long timeout;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		reinit_completion(&host->op_completion);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		irq_control(host, 1);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		timeout = wait_for_completion_timeout(&host->op_completion, HZ);
4638c2ecf20Sopenharmony_ci		if (!timeout && !host->devtype_data->check_int(host)) {
4648c2ecf20Sopenharmony_ci			dev_dbg(host->dev, "timeout waiting for irq\n");
4658c2ecf20Sopenharmony_ci			ret = -ETIMEDOUT;
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci	} else {
4688c2ecf20Sopenharmony_ci		int max_retries = 8000;
4698c2ecf20Sopenharmony_ci		int done;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		do {
4728c2ecf20Sopenharmony_ci			udelay(1);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci			done = host->devtype_data->check_int(host);
4758c2ecf20Sopenharmony_ci			if (done)
4768c2ecf20Sopenharmony_ci				break;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		} while (--max_retries);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		if (!done) {
4818c2ecf20Sopenharmony_ci			dev_dbg(host->dev, "timeout polling for completion\n");
4828c2ecf20Sopenharmony_ci			ret = -ETIMEDOUT;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return ret;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	/* fill command */
4948c2ecf20Sopenharmony_ci	writel(cmd, NFC_V3_FLASH_CMD);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* send out command */
4978c2ecf20Sopenharmony_ci	writel(NFC_CMD, NFC_V3_LAUNCH);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* Wait for operation to complete */
5008c2ecf20Sopenharmony_ci	wait_op_done(host, useirq);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci/* This function issues the specified command to the NAND device and
5048c2ecf20Sopenharmony_ci * waits for completion. */
5058c2ecf20Sopenharmony_cistatic void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	writew(cmd, NFC_V1_V2_FLASH_CMD);
5108c2ecf20Sopenharmony_ci	writew(NFC_CMD, NFC_V1_V2_CONFIG2);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) {
5138c2ecf20Sopenharmony_ci		int max_retries = 100;
5148c2ecf20Sopenharmony_ci		/* Reset completion is indicated by NFC_CONFIG2 */
5158c2ecf20Sopenharmony_ci		/* being set to 0 */
5168c2ecf20Sopenharmony_ci		while (max_retries-- > 0) {
5178c2ecf20Sopenharmony_ci			if (readw(NFC_V1_V2_CONFIG2) == 0) {
5188c2ecf20Sopenharmony_ci				break;
5198c2ecf20Sopenharmony_ci			}
5208c2ecf20Sopenharmony_ci			udelay(1);
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci		if (max_retries < 0)
5238c2ecf20Sopenharmony_ci			dev_dbg(host->dev, "%s: RESET failed\n", __func__);
5248c2ecf20Sopenharmony_ci	} else {
5258c2ecf20Sopenharmony_ci		/* Wait for operation to complete */
5268c2ecf20Sopenharmony_ci		wait_op_done(host, useirq);
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	/* fill address */
5338c2ecf20Sopenharmony_ci	writel(addr, NFC_V3_FLASH_ADDR0);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* send out address */
5368c2ecf20Sopenharmony_ci	writel(NFC_ADDR, NFC_V3_LAUNCH);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	wait_op_done(host, 0);
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/* This function sends an address (or partial address) to the
5428c2ecf20Sopenharmony_ci * NAND device. The address is used to select the source/destination for
5438c2ecf20Sopenharmony_ci * a NAND command. */
5448c2ecf20Sopenharmony_cistatic void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	writew(addr, NFC_V1_V2_FLASH_ADDR);
5498c2ecf20Sopenharmony_ci	writew(NFC_ADDR, NFC_V1_V2_CONFIG2);
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/* Wait for operation to complete */
5528c2ecf20Sopenharmony_ci	wait_op_done(host, islast);
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic void send_page_v3(struct mtd_info *mtd, unsigned int ops)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
5588c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
5598c2ecf20Sopenharmony_ci	uint32_t tmp;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	tmp = readl(NFC_V3_CONFIG1);
5628c2ecf20Sopenharmony_ci	tmp &= ~(7 << 4);
5638c2ecf20Sopenharmony_ci	writel(tmp, NFC_V3_CONFIG1);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/* transfer data from NFC ram to nand */
5668c2ecf20Sopenharmony_ci	writel(ops, NFC_V3_LAUNCH);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	wait_op_done(host, false);
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void send_page_v2(struct mtd_info *mtd, unsigned int ops)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
5748c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* NANDFC buffer 0 is used for page read/write */
5778c2ecf20Sopenharmony_ci	writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	writew(ops, NFC_V1_V2_CONFIG2);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* Wait for operation to complete */
5828c2ecf20Sopenharmony_ci	wait_op_done(host, true);
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic void send_page_v1(struct mtd_info *mtd, unsigned int ops)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
5888c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
5898c2ecf20Sopenharmony_ci	int bufs, i;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (mtd->writesize > 512)
5928c2ecf20Sopenharmony_ci		bufs = 4;
5938c2ecf20Sopenharmony_ci	else
5948c2ecf20Sopenharmony_ci		bufs = 1;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	for (i = 0; i < bufs; i++) {
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci		/* NANDFC buffer 0 is used for page read/write */
5998c2ecf20Sopenharmony_ci		writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		writew(ops, NFC_V1_V2_CONFIG2);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci		/* Wait for operation to complete */
6048c2ecf20Sopenharmony_ci		wait_op_done(host, true);
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic void send_read_id_v3(struct mxc_nand_host *host)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	/* Read ID into main buffer */
6118c2ecf20Sopenharmony_ci	writel(NFC_ID, NFC_V3_LAUNCH);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	wait_op_done(host, true);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	memcpy32_fromio(host->data_buf, host->main_area0, 16);
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/* Request the NANDFC to perform a read of the NAND device ID. */
6198c2ecf20Sopenharmony_cistatic void send_read_id_v1_v2(struct mxc_nand_host *host)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	/* NANDFC buffer 0 is used for device ID output */
6228c2ecf20Sopenharmony_ci	writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	writew(NFC_ID, NFC_V1_V2_CONFIG2);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	/* Wait for operation to complete */
6278c2ecf20Sopenharmony_ci	wait_op_done(host, true);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	memcpy32_fromio(host->data_buf, host->main_area0, 16);
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic uint16_t get_dev_status_v3(struct mxc_nand_host *host)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	writew(NFC_STATUS, NFC_V3_LAUNCH);
6358c2ecf20Sopenharmony_ci	wait_op_done(host, true);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	return readl(NFC_V3_CONFIG1) >> 16;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci/* This function requests the NANDFC to perform a read of the
6418c2ecf20Sopenharmony_ci * NAND device status and returns the current status. */
6428c2ecf20Sopenharmony_cistatic uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	void __iomem *main_buf = host->main_area0;
6458c2ecf20Sopenharmony_ci	uint32_t store;
6468c2ecf20Sopenharmony_ci	uint16_t ret;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/*
6518c2ecf20Sopenharmony_ci	 * The device status is stored in main_area0. To
6528c2ecf20Sopenharmony_ci	 * prevent corruption of the buffer save the value
6538c2ecf20Sopenharmony_ci	 * and restore it afterwards.
6548c2ecf20Sopenharmony_ci	 */
6558c2ecf20Sopenharmony_ci	store = readl(main_buf);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	writew(NFC_STATUS, NFC_V1_V2_CONFIG2);
6588c2ecf20Sopenharmony_ci	wait_op_done(host, true);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	ret = readw(main_buf);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	writel(store, main_buf);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	return ret;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
6708c2ecf20Sopenharmony_ci	uint16_t config1;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
6738c2ecf20Sopenharmony_ci		return;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	config1 = readw(NFC_V1_V2_CONFIG1);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	if (enable)
6788c2ecf20Sopenharmony_ci		config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
6798c2ecf20Sopenharmony_ci	else
6808c2ecf20Sopenharmony_ci		config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	writew(config1, NFC_V1_V2_CONFIG1);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
6888c2ecf20Sopenharmony_ci	uint32_t config2;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
6918c2ecf20Sopenharmony_ci		return;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	config2 = readl(NFC_V3_CONFIG2);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (enable)
6968c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_ECC_EN;
6978c2ecf20Sopenharmony_ci	else
6988c2ecf20Sopenharmony_ci		config2 &= ~NFC_V3_CONFIG2_ECC_EN;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	writel(config2, NFC_V3_CONFIG2);
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci/* This functions is used by upper layer to checks if device is ready */
7048c2ecf20Sopenharmony_cistatic int mxc_nand_dev_ready(struct nand_chip *chip)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	/*
7078c2ecf20Sopenharmony_ci	 * NFC handles R/B internally. Therefore, this function
7088c2ecf20Sopenharmony_ci	 * always returns status as ready.
7098c2ecf20Sopenharmony_ci	 */
7108c2ecf20Sopenharmony_ci	return 1;
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob,
7148c2ecf20Sopenharmony_ci				 bool ecc, int page)
7158c2ecf20Sopenharmony_ci{
7168c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
7178c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
7188c2ecf20Sopenharmony_ci	unsigned int bitflips_corrected = 0;
7198c2ecf20Sopenharmony_ci	int no_subpages;
7208c2ecf20Sopenharmony_ci	int i;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	host->devtype_data->enable_hwecc(chip, ecc);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	host->devtype_data->send_cmd(host, NAND_CMD_READ0, false);
7258c2ecf20Sopenharmony_ci	mxc_do_addr_cycle(mtd, 0, page);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (mtd->writesize > 512)
7288c2ecf20Sopenharmony_ci		host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	no_subpages = mtd->writesize >> 9;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	for (i = 0; i < no_subpages; i++) {
7338c2ecf20Sopenharmony_ci		uint16_t ecc_stats;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		/* NANDFC buffer 0 is used for page read/write */
7368c2ecf20Sopenharmony_ci		writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci		writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		/* Wait for operation to complete */
7418c2ecf20Sopenharmony_ci		wait_op_done(host, true);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci		ecc_stats = get_ecc_status_v1(host);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci		ecc_stats >>= 2;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci		if (buf && ecc) {
7488c2ecf20Sopenharmony_ci			switch (ecc_stats & 0x3) {
7498c2ecf20Sopenharmony_ci			case 0:
7508c2ecf20Sopenharmony_ci			default:
7518c2ecf20Sopenharmony_ci				break;
7528c2ecf20Sopenharmony_ci			case 1:
7538c2ecf20Sopenharmony_ci				mtd->ecc_stats.corrected++;
7548c2ecf20Sopenharmony_ci				bitflips_corrected = 1;
7558c2ecf20Sopenharmony_ci				break;
7568c2ecf20Sopenharmony_ci			case 2:
7578c2ecf20Sopenharmony_ci				mtd->ecc_stats.failed++;
7588c2ecf20Sopenharmony_ci				break;
7598c2ecf20Sopenharmony_ci			}
7608c2ecf20Sopenharmony_ci		}
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (buf)
7648c2ecf20Sopenharmony_ci		memcpy32_fromio(buf, host->main_area0, mtd->writesize);
7658c2ecf20Sopenharmony_ci	if (oob)
7668c2ecf20Sopenharmony_ci		copy_spare(mtd, true, oob);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	return bitflips_corrected;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf,
7728c2ecf20Sopenharmony_ci				    void *oob, bool ecc, int page)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
7758c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
7768c2ecf20Sopenharmony_ci	unsigned int max_bitflips = 0;
7778c2ecf20Sopenharmony_ci	u32 ecc_stat, err;
7788c2ecf20Sopenharmony_ci	int no_subpages;
7798c2ecf20Sopenharmony_ci	u8 ecc_bit_mask, err_limit;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	host->devtype_data->enable_hwecc(chip, ecc);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	host->devtype_data->send_cmd(host, NAND_CMD_READ0, false);
7848c2ecf20Sopenharmony_ci	mxc_do_addr_cycle(mtd, 0, page);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (mtd->writesize > 512)
7878c2ecf20Sopenharmony_ci		host->devtype_data->send_cmd(host,
7888c2ecf20Sopenharmony_ci				NAND_CMD_READSTART, true);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	host->devtype_data->send_page(mtd, NFC_OUTPUT);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (buf)
7938c2ecf20Sopenharmony_ci		memcpy32_fromio(buf, host->main_area0, mtd->writesize);
7948c2ecf20Sopenharmony_ci	if (oob)
7958c2ecf20Sopenharmony_ci		copy_spare(mtd, true, oob);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf;
7988c2ecf20Sopenharmony_ci	err_limit = (host->eccsize == 4) ? 0x4 : 0x8;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	no_subpages = mtd->writesize >> 9;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	ecc_stat = host->devtype_data->get_ecc_status(host);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	do {
8058c2ecf20Sopenharmony_ci		err = ecc_stat & ecc_bit_mask;
8068c2ecf20Sopenharmony_ci		if (err > err_limit) {
8078c2ecf20Sopenharmony_ci			mtd->ecc_stats.failed++;
8088c2ecf20Sopenharmony_ci		} else {
8098c2ecf20Sopenharmony_ci			mtd->ecc_stats.corrected += err;
8108c2ecf20Sopenharmony_ci			max_bitflips = max_t(unsigned int, max_bitflips, err);
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci		ecc_stat >>= 4;
8148c2ecf20Sopenharmony_ci	} while (--no_subpages);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return max_bitflips;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic int mxc_nand_read_page(struct nand_chip *chip, uint8_t *buf,
8208c2ecf20Sopenharmony_ci			      int oob_required, int page)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
8238c2ecf20Sopenharmony_ci	void *oob_buf;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	if (oob_required)
8268c2ecf20Sopenharmony_ci		oob_buf = chip->oob_poi;
8278c2ecf20Sopenharmony_ci	else
8288c2ecf20Sopenharmony_ci		oob_buf = NULL;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	return host->devtype_data->read_page(chip, buf, oob_buf, 1, page);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic int mxc_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
8348c2ecf20Sopenharmony_ci				  int oob_required, int page)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
8378c2ecf20Sopenharmony_ci	void *oob_buf;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (oob_required)
8408c2ecf20Sopenharmony_ci		oob_buf = chip->oob_poi;
8418c2ecf20Sopenharmony_ci	else
8428c2ecf20Sopenharmony_ci		oob_buf = NULL;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	return host->devtype_data->read_page(chip, buf, oob_buf, 0, page);
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cistatic int mxc_nand_read_oob(struct nand_chip *chip, int page)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0,
8528c2ecf20Sopenharmony_ci					     page);
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistatic int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf,
8568c2ecf20Sopenharmony_ci			       bool ecc, int page)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
8598c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	host->devtype_data->enable_hwecc(chip, ecc);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false);
8648c2ecf20Sopenharmony_ci	mxc_do_addr_cycle(mtd, 0, page);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	memcpy32_toio(host->main_area0, buf, mtd->writesize);
8678c2ecf20Sopenharmony_ci	copy_spare(mtd, false, chip->oob_poi);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	host->devtype_data->send_page(mtd, NFC_INPUT);
8708c2ecf20Sopenharmony_ci	host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true);
8718c2ecf20Sopenharmony_ci	mxc_do_addr_cycle(mtd, 0, page);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	return 0;
8748c2ecf20Sopenharmony_ci}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_cistatic int mxc_nand_write_page_ecc(struct nand_chip *chip, const uint8_t *buf,
8778c2ecf20Sopenharmony_ci				   int oob_required, int page)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	return mxc_nand_write_page(chip, buf, true, page);
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic int mxc_nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
8838c2ecf20Sopenharmony_ci				   int oob_required, int page)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	return mxc_nand_write_page(chip, buf, false, page);
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_cistatic int mxc_nand_write_oob(struct nand_chip *chip, int page)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
8918c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	memset(host->data_buf, 0xff, mtd->writesize);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return mxc_nand_write_page(chip, host->data_buf, false, page);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic u_char mxc_nand_read_byte(struct nand_chip *nand_chip)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
9018c2ecf20Sopenharmony_ci	uint8_t ret;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* Check for status request */
9048c2ecf20Sopenharmony_ci	if (host->status_request)
9058c2ecf20Sopenharmony_ci		return host->devtype_data->get_dev_status(host) & 0xFF;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	if (nand_chip->options & NAND_BUSWIDTH_16) {
9088c2ecf20Sopenharmony_ci		/* only take the lower byte of each word */
9098c2ecf20Sopenharmony_ci		ret = *(uint16_t *)(host->data_buf + host->buf_start);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci		host->buf_start += 2;
9128c2ecf20Sopenharmony_ci	} else {
9138c2ecf20Sopenharmony_ci		ret = *(uint8_t *)(host->data_buf + host->buf_start);
9148c2ecf20Sopenharmony_ci		host->buf_start++;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	dev_dbg(host->dev, "%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start);
9188c2ecf20Sopenharmony_ci	return ret;
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci/* Write data of length len to buffer buf. The data to be
9228c2ecf20Sopenharmony_ci * written on NAND Flash is first copied to RAMbuffer. After the Data Input
9238c2ecf20Sopenharmony_ci * Operation by the NFC, the data is written to NAND Flash */
9248c2ecf20Sopenharmony_cistatic void mxc_nand_write_buf(struct nand_chip *nand_chip, const u_char *buf,
9258c2ecf20Sopenharmony_ci			       int len)
9268c2ecf20Sopenharmony_ci{
9278c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand_chip);
9288c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
9298c2ecf20Sopenharmony_ci	u16 col = host->buf_start;
9308c2ecf20Sopenharmony_ci	int n = mtd->oobsize + mtd->writesize - col;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	n = min(n, len);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	memcpy(host->data_buf + col, buf, n);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	host->buf_start += n;
9378c2ecf20Sopenharmony_ci}
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci/* Read the data buffer from the NAND Flash. To read the data from NAND
9408c2ecf20Sopenharmony_ci * Flash first the data output cycle is initiated by the NFC, which copies
9418c2ecf20Sopenharmony_ci * the data to RAMbuffer. This data of length len is then copied to buffer buf.
9428c2ecf20Sopenharmony_ci */
9438c2ecf20Sopenharmony_cistatic void mxc_nand_read_buf(struct nand_chip *nand_chip, u_char *buf,
9448c2ecf20Sopenharmony_ci			      int len)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand_chip);
9478c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
9488c2ecf20Sopenharmony_ci	u16 col = host->buf_start;
9498c2ecf20Sopenharmony_ci	int n = mtd->oobsize + mtd->writesize - col;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	n = min(n, len);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	memcpy(buf, host->data_buf + col, n);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	host->buf_start += n;
9568c2ecf20Sopenharmony_ci}
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci/* This function is used by upper layer for select and
9598c2ecf20Sopenharmony_ci * deselect of the NAND chip */
9608c2ecf20Sopenharmony_cistatic void mxc_nand_select_chip_v1_v3(struct nand_chip *nand_chip, int chip)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	if (chip == -1) {
9658c2ecf20Sopenharmony_ci		/* Disable the NFC clock */
9668c2ecf20Sopenharmony_ci		if (host->clk_act) {
9678c2ecf20Sopenharmony_ci			clk_disable_unprepare(host->clk);
9688c2ecf20Sopenharmony_ci			host->clk_act = 0;
9698c2ecf20Sopenharmony_ci		}
9708c2ecf20Sopenharmony_ci		return;
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	if (!host->clk_act) {
9748c2ecf20Sopenharmony_ci		/* Enable the NFC clock */
9758c2ecf20Sopenharmony_ci		clk_prepare_enable(host->clk);
9768c2ecf20Sopenharmony_ci		host->clk_act = 1;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_cistatic void mxc_nand_select_chip_v2(struct nand_chip *nand_chip, int chip)
9818c2ecf20Sopenharmony_ci{
9828c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	if (chip == -1) {
9858c2ecf20Sopenharmony_ci		/* Disable the NFC clock */
9868c2ecf20Sopenharmony_ci		if (host->clk_act) {
9878c2ecf20Sopenharmony_ci			clk_disable_unprepare(host->clk);
9888c2ecf20Sopenharmony_ci			host->clk_act = 0;
9898c2ecf20Sopenharmony_ci		}
9908c2ecf20Sopenharmony_ci		return;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (!host->clk_act) {
9948c2ecf20Sopenharmony_ci		/* Enable the NFC clock */
9958c2ecf20Sopenharmony_ci		clk_prepare_enable(host->clk);
9968c2ecf20Sopenharmony_ci		host->clk_act = 1;
9978c2ecf20Sopenharmony_ci	}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	host->active_cs = chip;
10008c2ecf20Sopenharmony_ci	writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR);
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci#define MXC_V1_ECCBYTES		5
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
10068c2ecf20Sopenharmony_ci				struct mtd_oob_region *oobregion)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (section >= nand_chip->ecc.steps)
10118c2ecf20Sopenharmony_ci		return -ERANGE;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	oobregion->offset = (section * 16) + 6;
10148c2ecf20Sopenharmony_ci	oobregion->length = MXC_V1_ECCBYTES;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	return 0;
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section,
10208c2ecf20Sopenharmony_ci				 struct mtd_oob_region *oobregion)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (section > nand_chip->ecc.steps)
10258c2ecf20Sopenharmony_ci		return -ERANGE;
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (!section) {
10288c2ecf20Sopenharmony_ci		if (mtd->writesize <= 512) {
10298c2ecf20Sopenharmony_ci			oobregion->offset = 0;
10308c2ecf20Sopenharmony_ci			oobregion->length = 5;
10318c2ecf20Sopenharmony_ci		} else {
10328c2ecf20Sopenharmony_ci			oobregion->offset = 2;
10338c2ecf20Sopenharmony_ci			oobregion->length = 4;
10348c2ecf20Sopenharmony_ci		}
10358c2ecf20Sopenharmony_ci	} else {
10368c2ecf20Sopenharmony_ci		oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6;
10378c2ecf20Sopenharmony_ci		if (section < nand_chip->ecc.steps)
10388c2ecf20Sopenharmony_ci			oobregion->length = (section * 16) + 6 -
10398c2ecf20Sopenharmony_ci					    oobregion->offset;
10408c2ecf20Sopenharmony_ci		else
10418c2ecf20Sopenharmony_ci			oobregion->length = mtd->oobsize - oobregion->offset;
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	return 0;
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = {
10488c2ecf20Sopenharmony_ci	.ecc = mxc_v1_ooblayout_ecc,
10498c2ecf20Sopenharmony_ci	.free = mxc_v1_ooblayout_free,
10508c2ecf20Sopenharmony_ci};
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cistatic int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section,
10538c2ecf20Sopenharmony_ci				struct mtd_oob_region *oobregion)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
10568c2ecf20Sopenharmony_ci	int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	if (section >= nand_chip->ecc.steps)
10598c2ecf20Sopenharmony_ci		return -ERANGE;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	oobregion->offset = (section * stepsize) + 7;
10628c2ecf20Sopenharmony_ci	oobregion->length = nand_chip->ecc.bytes;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	return 0;
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section,
10688c2ecf20Sopenharmony_ci				 struct mtd_oob_region *oobregion)
10698c2ecf20Sopenharmony_ci{
10708c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
10718c2ecf20Sopenharmony_ci	int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	if (section >= nand_chip->ecc.steps)
10748c2ecf20Sopenharmony_ci		return -ERANGE;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (!section) {
10778c2ecf20Sopenharmony_ci		if (mtd->writesize <= 512) {
10788c2ecf20Sopenharmony_ci			oobregion->offset = 0;
10798c2ecf20Sopenharmony_ci			oobregion->length = 5;
10808c2ecf20Sopenharmony_ci		} else {
10818c2ecf20Sopenharmony_ci			oobregion->offset = 2;
10828c2ecf20Sopenharmony_ci			oobregion->length = 4;
10838c2ecf20Sopenharmony_ci		}
10848c2ecf20Sopenharmony_ci	} else {
10858c2ecf20Sopenharmony_ci		oobregion->offset = section * stepsize;
10868c2ecf20Sopenharmony_ci		oobregion->length = 7;
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	return 0;
10908c2ecf20Sopenharmony_ci}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = {
10938c2ecf20Sopenharmony_ci	.ecc = mxc_v2_ooblayout_ecc,
10948c2ecf20Sopenharmony_ci	.free = mxc_v2_ooblayout_free,
10958c2ecf20Sopenharmony_ci};
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci/*
10988c2ecf20Sopenharmony_ci * v2 and v3 type controllers can do 4bit or 8bit ecc depending
10998c2ecf20Sopenharmony_ci * on how much oob the nand chip has. For 8bit ecc we need at least
11008c2ecf20Sopenharmony_ci * 26 bytes of oob data per 512 byte block.
11018c2ecf20Sopenharmony_ci */
11028c2ecf20Sopenharmony_cistatic int get_eccsize(struct mtd_info *mtd)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	int oobbytes_per_512 = 0;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	if (oobbytes_per_512 < 26)
11098c2ecf20Sopenharmony_ci		return 4;
11108c2ecf20Sopenharmony_ci	else
11118c2ecf20Sopenharmony_ci		return 8;
11128c2ecf20Sopenharmony_ci}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic void preset_v1(struct mtd_info *mtd)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
11178c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
11188c2ecf20Sopenharmony_ci	uint16_t config1 = 0;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST &&
11218c2ecf20Sopenharmony_ci	    mtd->writesize)
11228c2ecf20Sopenharmony_ci		config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	if (!host->devtype_data->irqpending_quirk)
11258c2ecf20Sopenharmony_ci		config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	host->eccsize = 1;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	writew(config1, NFC_V1_V2_CONFIG1);
11308c2ecf20Sopenharmony_ci	/* preset operation */
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	/* Unlock the internal RAM Buffer */
11338c2ecf20Sopenharmony_ci	writew(0x2, NFC_V1_V2_CONFIG);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	/* Blocks to be unlocked */
11368c2ecf20Sopenharmony_ci	writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR);
11378c2ecf20Sopenharmony_ci	writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* Unlock Block Command for given address range */
11408c2ecf20Sopenharmony_ci	writew(0x4, NFC_V1_V2_WRPROT);
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic int mxc_nand_v2_setup_interface(struct nand_chip *chip, int csline,
11448c2ecf20Sopenharmony_ci				       const struct nand_interface_config *conf)
11458c2ecf20Sopenharmony_ci{
11468c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
11478c2ecf20Sopenharmony_ci	int tRC_min_ns, tRC_ps, ret;
11488c2ecf20Sopenharmony_ci	unsigned long rate, rate_round;
11498c2ecf20Sopenharmony_ci	const struct nand_sdr_timings *timings;
11508c2ecf20Sopenharmony_ci	u16 config1;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	timings = nand_get_sdr_timings(conf);
11538c2ecf20Sopenharmony_ci	if (IS_ERR(timings))
11548c2ecf20Sopenharmony_ci		return -ENOTSUPP;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	config1 = readw(NFC_V1_V2_CONFIG1);
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	tRC_min_ns = timings->tRC_min / 1000;
11598c2ecf20Sopenharmony_ci	rate = 1000000000 / tRC_min_ns;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	/*
11628c2ecf20Sopenharmony_ci	 * For tRC < 30ns we have to use EDO mode. In this case the controller
11638c2ecf20Sopenharmony_ci	 * does one access per clock cycle. Otherwise the controller does one
11648c2ecf20Sopenharmony_ci	 * access in two clock cycles, thus we have to double the rate to the
11658c2ecf20Sopenharmony_ci	 * controller.
11668c2ecf20Sopenharmony_ci	 */
11678c2ecf20Sopenharmony_ci	if (tRC_min_ns < 30) {
11688c2ecf20Sopenharmony_ci		rate_round = clk_round_rate(host->clk, rate);
11698c2ecf20Sopenharmony_ci		config1 |= NFC_V2_CONFIG1_ONE_CYCLE;
11708c2ecf20Sopenharmony_ci		tRC_ps = 1000000000 / (rate_round / 1000);
11718c2ecf20Sopenharmony_ci	} else {
11728c2ecf20Sopenharmony_ci		rate *= 2;
11738c2ecf20Sopenharmony_ci		rate_round = clk_round_rate(host->clk, rate);
11748c2ecf20Sopenharmony_ci		config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE;
11758c2ecf20Sopenharmony_ci		tRC_ps = 1000000000 / (rate_round / 1000 / 2);
11768c2ecf20Sopenharmony_ci	}
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	/*
11798c2ecf20Sopenharmony_ci	 * The timing values compared against are from the i.MX25 Automotive
11808c2ecf20Sopenharmony_ci	 * datasheet, Table 50. NFC Timing Parameters
11818c2ecf20Sopenharmony_ci	 */
11828c2ecf20Sopenharmony_ci	if (timings->tCLS_min > tRC_ps - 1000 ||
11838c2ecf20Sopenharmony_ci	    timings->tCLH_min > tRC_ps - 2000 ||
11848c2ecf20Sopenharmony_ci	    timings->tCS_min > tRC_ps - 1000 ||
11858c2ecf20Sopenharmony_ci	    timings->tCH_min > tRC_ps - 2000 ||
11868c2ecf20Sopenharmony_ci	    timings->tWP_min > tRC_ps - 1500 ||
11878c2ecf20Sopenharmony_ci	    timings->tALS_min > tRC_ps ||
11888c2ecf20Sopenharmony_ci	    timings->tALH_min > tRC_ps - 3000 ||
11898c2ecf20Sopenharmony_ci	    timings->tDS_min > tRC_ps ||
11908c2ecf20Sopenharmony_ci	    timings->tDH_min > tRC_ps - 5000 ||
11918c2ecf20Sopenharmony_ci	    timings->tWC_min > 2 * tRC_ps ||
11928c2ecf20Sopenharmony_ci	    timings->tWH_min > tRC_ps - 2500 ||
11938c2ecf20Sopenharmony_ci	    timings->tRR_min > 6 * tRC_ps ||
11948c2ecf20Sopenharmony_ci	    timings->tRP_min > 3 * tRC_ps / 2 ||
11958c2ecf20Sopenharmony_ci	    timings->tRC_min > 2 * tRC_ps ||
11968c2ecf20Sopenharmony_ci	    timings->tREH_min > (tRC_ps / 2) - 2500) {
11978c2ecf20Sopenharmony_ci		dev_dbg(host->dev, "Timing out of bounds\n");
11988c2ecf20Sopenharmony_ci		return -EINVAL;
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
12028c2ecf20Sopenharmony_ci		return 0;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	ret = clk_set_rate(host->clk, rate);
12058c2ecf20Sopenharmony_ci	if (ret)
12068c2ecf20Sopenharmony_ci		return ret;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	writew(config1, NFC_V1_V2_CONFIG1);
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round,
12118c2ecf20Sopenharmony_ci		config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" :
12128c2ecf20Sopenharmony_ci		"normal");
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	return 0;
12158c2ecf20Sopenharmony_ci}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_cistatic void preset_v2(struct mtd_info *mtd)
12188c2ecf20Sopenharmony_ci{
12198c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
12208c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
12218c2ecf20Sopenharmony_ci	uint16_t config1 = 0;
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	config1 |= NFC_V2_CONFIG1_FP_INT;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	if (!host->devtype_data->irqpending_quirk)
12268c2ecf20Sopenharmony_ci		config1 |= NFC_V1_V2_CONFIG1_INT_MSK;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	if (mtd->writesize) {
12298c2ecf20Sopenharmony_ci		uint16_t pages_per_block = mtd->erasesize / mtd->writesize;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci		if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
12328c2ecf20Sopenharmony_ci			config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci		host->eccsize = get_eccsize(mtd);
12358c2ecf20Sopenharmony_ci		if (host->eccsize == 4)
12368c2ecf20Sopenharmony_ci			config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6);
12398c2ecf20Sopenharmony_ci	} else {
12408c2ecf20Sopenharmony_ci		host->eccsize = 1;
12418c2ecf20Sopenharmony_ci	}
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	writew(config1, NFC_V1_V2_CONFIG1);
12448c2ecf20Sopenharmony_ci	/* preset operation */
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	/* spare area size in 16-bit half-words */
12478c2ecf20Sopenharmony_ci	writew(mtd->oobsize / 2, NFC_V21_RSLTSPARE_AREA);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	/* Unlock the internal RAM Buffer */
12508c2ecf20Sopenharmony_ci	writew(0x2, NFC_V1_V2_CONFIG);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	/* Blocks to be unlocked */
12538c2ecf20Sopenharmony_ci	writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0);
12548c2ecf20Sopenharmony_ci	writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1);
12558c2ecf20Sopenharmony_ci	writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2);
12568c2ecf20Sopenharmony_ci	writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3);
12578c2ecf20Sopenharmony_ci	writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0);
12588c2ecf20Sopenharmony_ci	writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1);
12598c2ecf20Sopenharmony_ci	writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2);
12608c2ecf20Sopenharmony_ci	writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	/* Unlock Block Command for given address range */
12638c2ecf20Sopenharmony_ci	writew(0x4, NFC_V1_V2_WRPROT);
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_cistatic void preset_v3(struct mtd_info *mtd)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
12698c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
12708c2ecf20Sopenharmony_ci	uint32_t config2, config3;
12718c2ecf20Sopenharmony_ci	int i, addr_phases;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1);
12748c2ecf20Sopenharmony_ci	writel(NFC_V3_IPC_CREQ, NFC_V3_IPC);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* Unlock the internal RAM Buffer */
12778c2ecf20Sopenharmony_ci	writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
12788c2ecf20Sopenharmony_ci			NFC_V3_WRPROT);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	/* Blocks to be unlocked */
12818c2ecf20Sopenharmony_ci	for (i = 0; i < NAND_MAX_CHIPS; i++)
12828c2ecf20Sopenharmony_ci		writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2));
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	writel(0, NFC_V3_IPC);
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	config2 = NFC_V3_CONFIG2_ONE_CYCLE |
12878c2ecf20Sopenharmony_ci		NFC_V3_CONFIG2_2CMD_PHASES |
12888c2ecf20Sopenharmony_ci		NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) |
12898c2ecf20Sopenharmony_ci		NFC_V3_CONFIG2_ST_CMD(0x70) |
12908c2ecf20Sopenharmony_ci		NFC_V3_CONFIG2_INT_MSK |
12918c2ecf20Sopenharmony_ci		NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	addr_phases = fls(chip->pagemask) >> 3;
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	if (mtd->writesize == 2048) {
12968c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_PS_2048;
12978c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
12988c2ecf20Sopenharmony_ci	} else if (mtd->writesize == 4096) {
12998c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_PS_4096;
13008c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases);
13018c2ecf20Sopenharmony_ci	} else {
13028c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_PS_512;
13038c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1);
13048c2ecf20Sopenharmony_ci	}
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	if (mtd->writesize) {
13078c2ecf20Sopenharmony_ci		if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
13088c2ecf20Sopenharmony_ci			config2 |= NFC_V3_CONFIG2_ECC_EN;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci		config2 |= NFC_V3_CONFIG2_PPB(
13118c2ecf20Sopenharmony_ci				ffs(mtd->erasesize / mtd->writesize) - 6,
13128c2ecf20Sopenharmony_ci				host->devtype_data->ppb_shift);
13138c2ecf20Sopenharmony_ci		host->eccsize = get_eccsize(mtd);
13148c2ecf20Sopenharmony_ci		if (host->eccsize == 8)
13158c2ecf20Sopenharmony_ci			config2 |= NFC_V3_CONFIG2_ECC_MODE_8;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	writel(config2, NFC_V3_CONFIG2);
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) |
13218c2ecf20Sopenharmony_ci			NFC_V3_CONFIG3_NO_SDMA |
13228c2ecf20Sopenharmony_ci			NFC_V3_CONFIG3_RBB_MODE |
13238c2ecf20Sopenharmony_ci			NFC_V3_CONFIG3_SBB(6) | /* Reset default */
13248c2ecf20Sopenharmony_ci			NFC_V3_CONFIG3_ADD_OP(0);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	if (!(chip->options & NAND_BUSWIDTH_16))
13278c2ecf20Sopenharmony_ci		config3 |= NFC_V3_CONFIG3_FW8;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	writel(config3, NFC_V3_CONFIG3);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	writel(0, NFC_V3_DELAY_LINE);
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci/* Used by the upper layer to write command to NAND Flash for
13358c2ecf20Sopenharmony_ci * different operations to be carried out on NAND Flash */
13368c2ecf20Sopenharmony_cistatic void mxc_nand_command(struct nand_chip *nand_chip, unsigned command,
13378c2ecf20Sopenharmony_ci			     int column, int page_addr)
13388c2ecf20Sopenharmony_ci{
13398c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand_chip);
13408c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(nand_chip);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	dev_dbg(host->dev, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
13438c2ecf20Sopenharmony_ci	      command, column, page_addr);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	/* Reset command state information */
13468c2ecf20Sopenharmony_ci	host->status_request = false;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	/* Command pre-processing step */
13498c2ecf20Sopenharmony_ci	switch (command) {
13508c2ecf20Sopenharmony_ci	case NAND_CMD_RESET:
13518c2ecf20Sopenharmony_ci		host->devtype_data->preset(mtd);
13528c2ecf20Sopenharmony_ci		host->devtype_data->send_cmd(host, command, false);
13538c2ecf20Sopenharmony_ci		break;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	case NAND_CMD_STATUS:
13568c2ecf20Sopenharmony_ci		host->buf_start = 0;
13578c2ecf20Sopenharmony_ci		host->status_request = true;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci		host->devtype_data->send_cmd(host, command, true);
13608c2ecf20Sopenharmony_ci		WARN_ONCE(column != -1 || page_addr != -1,
13618c2ecf20Sopenharmony_ci			  "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n",
13628c2ecf20Sopenharmony_ci			  command, column, page_addr);
13638c2ecf20Sopenharmony_ci		mxc_do_addr_cycle(mtd, column, page_addr);
13648c2ecf20Sopenharmony_ci		break;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	case NAND_CMD_READID:
13678c2ecf20Sopenharmony_ci		host->devtype_data->send_cmd(host, command, true);
13688c2ecf20Sopenharmony_ci		mxc_do_addr_cycle(mtd, column, page_addr);
13698c2ecf20Sopenharmony_ci		host->devtype_data->send_read_id(host);
13708c2ecf20Sopenharmony_ci		host->buf_start = 0;
13718c2ecf20Sopenharmony_ci		break;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE1:
13748c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE2:
13758c2ecf20Sopenharmony_ci		host->devtype_data->send_cmd(host, command, false);
13768c2ecf20Sopenharmony_ci		WARN_ONCE(column != -1,
13778c2ecf20Sopenharmony_ci			  "Unexpected column value (cmd=%u, col=%d)\n",
13788c2ecf20Sopenharmony_ci			  command, column);
13798c2ecf20Sopenharmony_ci		mxc_do_addr_cycle(mtd, column, page_addr);
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci		break;
13828c2ecf20Sopenharmony_ci	case NAND_CMD_PARAM:
13838c2ecf20Sopenharmony_ci		host->devtype_data->send_cmd(host, command, false);
13848c2ecf20Sopenharmony_ci		mxc_do_addr_cycle(mtd, column, page_addr);
13858c2ecf20Sopenharmony_ci		host->devtype_data->send_page(mtd, NFC_OUTPUT);
13868c2ecf20Sopenharmony_ci		memcpy32_fromio(host->data_buf, host->main_area0, 512);
13878c2ecf20Sopenharmony_ci		host->buf_start = 0;
13888c2ecf20Sopenharmony_ci		break;
13898c2ecf20Sopenharmony_ci	default:
13908c2ecf20Sopenharmony_ci		WARN_ONCE(1, "Unimplemented command (cmd=%u)\n",
13918c2ecf20Sopenharmony_ci			  command);
13928c2ecf20Sopenharmony_ci		break;
13938c2ecf20Sopenharmony_ci	}
13948c2ecf20Sopenharmony_ci}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_cistatic int mxc_nand_set_features(struct nand_chip *chip, int addr,
13978c2ecf20Sopenharmony_ci				 u8 *subfeature_param)
13988c2ecf20Sopenharmony_ci{
13998c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
14008c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
14018c2ecf20Sopenharmony_ci	int i;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	host->buf_start = 0;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
14068c2ecf20Sopenharmony_ci		chip->legacy.write_byte(chip, subfeature_param[i]);
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize);
14098c2ecf20Sopenharmony_ci	host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false);
14108c2ecf20Sopenharmony_ci	mxc_do_addr_cycle(mtd, addr, -1);
14118c2ecf20Sopenharmony_ci	host->devtype_data->send_page(mtd, NFC_INPUT);
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	return 0;
14148c2ecf20Sopenharmony_ci}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_cistatic int mxc_nand_get_features(struct nand_chip *chip, int addr,
14178c2ecf20Sopenharmony_ci				 u8 *subfeature_param)
14188c2ecf20Sopenharmony_ci{
14198c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
14208c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
14218c2ecf20Sopenharmony_ci	int i;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false);
14248c2ecf20Sopenharmony_ci	mxc_do_addr_cycle(mtd, addr, -1);
14258c2ecf20Sopenharmony_ci	host->devtype_data->send_page(mtd, NFC_OUTPUT);
14268c2ecf20Sopenharmony_ci	memcpy32_fromio(host->data_buf, host->main_area0, 512);
14278c2ecf20Sopenharmony_ci	host->buf_start = 0;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
14308c2ecf20Sopenharmony_ci		*subfeature_param++ = chip->legacy.read_byte(chip);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	return 0;
14338c2ecf20Sopenharmony_ci}
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci/*
14368c2ecf20Sopenharmony_ci * The generic flash bbt descriptors overlap with our ecc
14378c2ecf20Sopenharmony_ci * hardware, so define some i.MX specific ones.
14388c2ecf20Sopenharmony_ci */
14398c2ecf20Sopenharmony_cistatic uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
14408c2ecf20Sopenharmony_cistatic uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = {
14438c2ecf20Sopenharmony_ci	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
14448c2ecf20Sopenharmony_ci	    | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
14458c2ecf20Sopenharmony_ci	.offs = 0,
14468c2ecf20Sopenharmony_ci	.len = 4,
14478c2ecf20Sopenharmony_ci	.veroffs = 4,
14488c2ecf20Sopenharmony_ci	.maxblocks = 4,
14498c2ecf20Sopenharmony_ci	.pattern = bbt_pattern,
14508c2ecf20Sopenharmony_ci};
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = {
14538c2ecf20Sopenharmony_ci	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
14548c2ecf20Sopenharmony_ci	    | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
14558c2ecf20Sopenharmony_ci	.offs = 0,
14568c2ecf20Sopenharmony_ci	.len = 4,
14578c2ecf20Sopenharmony_ci	.veroffs = 4,
14588c2ecf20Sopenharmony_ci	.maxblocks = 4,
14598c2ecf20Sopenharmony_ci	.pattern = mirror_pattern,
14608c2ecf20Sopenharmony_ci};
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci/* v1 + irqpending_quirk: i.MX21 */
14638c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx21_nand_devtype_data = {
14648c2ecf20Sopenharmony_ci	.preset = preset_v1,
14658c2ecf20Sopenharmony_ci	.read_page = mxc_nand_read_page_v1,
14668c2ecf20Sopenharmony_ci	.send_cmd = send_cmd_v1_v2,
14678c2ecf20Sopenharmony_ci	.send_addr = send_addr_v1_v2,
14688c2ecf20Sopenharmony_ci	.send_page = send_page_v1,
14698c2ecf20Sopenharmony_ci	.send_read_id = send_read_id_v1_v2,
14708c2ecf20Sopenharmony_ci	.get_dev_status = get_dev_status_v1_v2,
14718c2ecf20Sopenharmony_ci	.check_int = check_int_v1_v2,
14728c2ecf20Sopenharmony_ci	.irq_control = irq_control_v1_v2,
14738c2ecf20Sopenharmony_ci	.get_ecc_status = get_ecc_status_v1,
14748c2ecf20Sopenharmony_ci	.ooblayout = &mxc_v1_ooblayout_ops,
14758c2ecf20Sopenharmony_ci	.select_chip = mxc_nand_select_chip_v1_v3,
14768c2ecf20Sopenharmony_ci	.enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
14778c2ecf20Sopenharmony_ci	.irqpending_quirk = 1,
14788c2ecf20Sopenharmony_ci	.needs_ip = 0,
14798c2ecf20Sopenharmony_ci	.regs_offset = 0xe00,
14808c2ecf20Sopenharmony_ci	.spare0_offset = 0x800,
14818c2ecf20Sopenharmony_ci	.spare_len = 16,
14828c2ecf20Sopenharmony_ci	.eccbytes = 3,
14838c2ecf20Sopenharmony_ci	.eccsize = 1,
14848c2ecf20Sopenharmony_ci};
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci/* v1 + !irqpending_quirk: i.MX27, i.MX31 */
14878c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx27_nand_devtype_data = {
14888c2ecf20Sopenharmony_ci	.preset = preset_v1,
14898c2ecf20Sopenharmony_ci	.read_page = mxc_nand_read_page_v1,
14908c2ecf20Sopenharmony_ci	.send_cmd = send_cmd_v1_v2,
14918c2ecf20Sopenharmony_ci	.send_addr = send_addr_v1_v2,
14928c2ecf20Sopenharmony_ci	.send_page = send_page_v1,
14938c2ecf20Sopenharmony_ci	.send_read_id = send_read_id_v1_v2,
14948c2ecf20Sopenharmony_ci	.get_dev_status = get_dev_status_v1_v2,
14958c2ecf20Sopenharmony_ci	.check_int = check_int_v1_v2,
14968c2ecf20Sopenharmony_ci	.irq_control = irq_control_v1_v2,
14978c2ecf20Sopenharmony_ci	.get_ecc_status = get_ecc_status_v1,
14988c2ecf20Sopenharmony_ci	.ooblayout = &mxc_v1_ooblayout_ops,
14998c2ecf20Sopenharmony_ci	.select_chip = mxc_nand_select_chip_v1_v3,
15008c2ecf20Sopenharmony_ci	.enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
15018c2ecf20Sopenharmony_ci	.irqpending_quirk = 0,
15028c2ecf20Sopenharmony_ci	.needs_ip = 0,
15038c2ecf20Sopenharmony_ci	.regs_offset = 0xe00,
15048c2ecf20Sopenharmony_ci	.spare0_offset = 0x800,
15058c2ecf20Sopenharmony_ci	.axi_offset = 0,
15068c2ecf20Sopenharmony_ci	.spare_len = 16,
15078c2ecf20Sopenharmony_ci	.eccbytes = 3,
15088c2ecf20Sopenharmony_ci	.eccsize = 1,
15098c2ecf20Sopenharmony_ci};
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci/* v21: i.MX25, i.MX35 */
15128c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx25_nand_devtype_data = {
15138c2ecf20Sopenharmony_ci	.preset = preset_v2,
15148c2ecf20Sopenharmony_ci	.read_page = mxc_nand_read_page_v2_v3,
15158c2ecf20Sopenharmony_ci	.send_cmd = send_cmd_v1_v2,
15168c2ecf20Sopenharmony_ci	.send_addr = send_addr_v1_v2,
15178c2ecf20Sopenharmony_ci	.send_page = send_page_v2,
15188c2ecf20Sopenharmony_ci	.send_read_id = send_read_id_v1_v2,
15198c2ecf20Sopenharmony_ci	.get_dev_status = get_dev_status_v1_v2,
15208c2ecf20Sopenharmony_ci	.check_int = check_int_v1_v2,
15218c2ecf20Sopenharmony_ci	.irq_control = irq_control_v1_v2,
15228c2ecf20Sopenharmony_ci	.get_ecc_status = get_ecc_status_v2,
15238c2ecf20Sopenharmony_ci	.ooblayout = &mxc_v2_ooblayout_ops,
15248c2ecf20Sopenharmony_ci	.select_chip = mxc_nand_select_chip_v2,
15258c2ecf20Sopenharmony_ci	.setup_interface = mxc_nand_v2_setup_interface,
15268c2ecf20Sopenharmony_ci	.enable_hwecc = mxc_nand_enable_hwecc_v1_v2,
15278c2ecf20Sopenharmony_ci	.irqpending_quirk = 0,
15288c2ecf20Sopenharmony_ci	.needs_ip = 0,
15298c2ecf20Sopenharmony_ci	.regs_offset = 0x1e00,
15308c2ecf20Sopenharmony_ci	.spare0_offset = 0x1000,
15318c2ecf20Sopenharmony_ci	.axi_offset = 0,
15328c2ecf20Sopenharmony_ci	.spare_len = 64,
15338c2ecf20Sopenharmony_ci	.eccbytes = 9,
15348c2ecf20Sopenharmony_ci	.eccsize = 0,
15358c2ecf20Sopenharmony_ci};
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci/* v3.2a: i.MX51 */
15388c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx51_nand_devtype_data = {
15398c2ecf20Sopenharmony_ci	.preset = preset_v3,
15408c2ecf20Sopenharmony_ci	.read_page = mxc_nand_read_page_v2_v3,
15418c2ecf20Sopenharmony_ci	.send_cmd = send_cmd_v3,
15428c2ecf20Sopenharmony_ci	.send_addr = send_addr_v3,
15438c2ecf20Sopenharmony_ci	.send_page = send_page_v3,
15448c2ecf20Sopenharmony_ci	.send_read_id = send_read_id_v3,
15458c2ecf20Sopenharmony_ci	.get_dev_status = get_dev_status_v3,
15468c2ecf20Sopenharmony_ci	.check_int = check_int_v3,
15478c2ecf20Sopenharmony_ci	.irq_control = irq_control_v3,
15488c2ecf20Sopenharmony_ci	.get_ecc_status = get_ecc_status_v3,
15498c2ecf20Sopenharmony_ci	.ooblayout = &mxc_v2_ooblayout_ops,
15508c2ecf20Sopenharmony_ci	.select_chip = mxc_nand_select_chip_v1_v3,
15518c2ecf20Sopenharmony_ci	.enable_hwecc = mxc_nand_enable_hwecc_v3,
15528c2ecf20Sopenharmony_ci	.irqpending_quirk = 0,
15538c2ecf20Sopenharmony_ci	.needs_ip = 1,
15548c2ecf20Sopenharmony_ci	.regs_offset = 0,
15558c2ecf20Sopenharmony_ci	.spare0_offset = 0x1000,
15568c2ecf20Sopenharmony_ci	.axi_offset = 0x1e00,
15578c2ecf20Sopenharmony_ci	.spare_len = 64,
15588c2ecf20Sopenharmony_ci	.eccbytes = 0,
15598c2ecf20Sopenharmony_ci	.eccsize = 0,
15608c2ecf20Sopenharmony_ci	.ppb_shift = 7,
15618c2ecf20Sopenharmony_ci};
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci/* v3.2b: i.MX53 */
15648c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx53_nand_devtype_data = {
15658c2ecf20Sopenharmony_ci	.preset = preset_v3,
15668c2ecf20Sopenharmony_ci	.read_page = mxc_nand_read_page_v2_v3,
15678c2ecf20Sopenharmony_ci	.send_cmd = send_cmd_v3,
15688c2ecf20Sopenharmony_ci	.send_addr = send_addr_v3,
15698c2ecf20Sopenharmony_ci	.send_page = send_page_v3,
15708c2ecf20Sopenharmony_ci	.send_read_id = send_read_id_v3,
15718c2ecf20Sopenharmony_ci	.get_dev_status = get_dev_status_v3,
15728c2ecf20Sopenharmony_ci	.check_int = check_int_v3,
15738c2ecf20Sopenharmony_ci	.irq_control = irq_control_v3,
15748c2ecf20Sopenharmony_ci	.get_ecc_status = get_ecc_status_v3,
15758c2ecf20Sopenharmony_ci	.ooblayout = &mxc_v2_ooblayout_ops,
15768c2ecf20Sopenharmony_ci	.select_chip = mxc_nand_select_chip_v1_v3,
15778c2ecf20Sopenharmony_ci	.enable_hwecc = mxc_nand_enable_hwecc_v3,
15788c2ecf20Sopenharmony_ci	.irqpending_quirk = 0,
15798c2ecf20Sopenharmony_ci	.needs_ip = 1,
15808c2ecf20Sopenharmony_ci	.regs_offset = 0,
15818c2ecf20Sopenharmony_ci	.spare0_offset = 0x1000,
15828c2ecf20Sopenharmony_ci	.axi_offset = 0x1e00,
15838c2ecf20Sopenharmony_ci	.spare_len = 64,
15848c2ecf20Sopenharmony_ci	.eccbytes = 0,
15858c2ecf20Sopenharmony_ci	.eccsize = 0,
15868c2ecf20Sopenharmony_ci	.ppb_shift = 8,
15878c2ecf20Sopenharmony_ci};
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_cistatic inline int is_imx21_nfc(struct mxc_nand_host *host)
15908c2ecf20Sopenharmony_ci{
15918c2ecf20Sopenharmony_ci	return host->devtype_data == &imx21_nand_devtype_data;
15928c2ecf20Sopenharmony_ci}
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_cistatic inline int is_imx27_nfc(struct mxc_nand_host *host)
15958c2ecf20Sopenharmony_ci{
15968c2ecf20Sopenharmony_ci	return host->devtype_data == &imx27_nand_devtype_data;
15978c2ecf20Sopenharmony_ci}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_cistatic inline int is_imx25_nfc(struct mxc_nand_host *host)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	return host->devtype_data == &imx25_nand_devtype_data;
16028c2ecf20Sopenharmony_ci}
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_cistatic inline int is_imx51_nfc(struct mxc_nand_host *host)
16058c2ecf20Sopenharmony_ci{
16068c2ecf20Sopenharmony_ci	return host->devtype_data == &imx51_nand_devtype_data;
16078c2ecf20Sopenharmony_ci}
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_cistatic inline int is_imx53_nfc(struct mxc_nand_host *host)
16108c2ecf20Sopenharmony_ci{
16118c2ecf20Sopenharmony_ci	return host->devtype_data == &imx53_nand_devtype_data;
16128c2ecf20Sopenharmony_ci}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_cistatic const struct platform_device_id mxcnd_devtype[] = {
16158c2ecf20Sopenharmony_ci	{
16168c2ecf20Sopenharmony_ci		.name = "imx21-nand",
16178c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t) &imx21_nand_devtype_data,
16188c2ecf20Sopenharmony_ci	}, {
16198c2ecf20Sopenharmony_ci		.name = "imx27-nand",
16208c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t) &imx27_nand_devtype_data,
16218c2ecf20Sopenharmony_ci	}, {
16228c2ecf20Sopenharmony_ci		.name = "imx25-nand",
16238c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t) &imx25_nand_devtype_data,
16248c2ecf20Sopenharmony_ci	}, {
16258c2ecf20Sopenharmony_ci		.name = "imx51-nand",
16268c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t) &imx51_nand_devtype_data,
16278c2ecf20Sopenharmony_ci	}, {
16288c2ecf20Sopenharmony_ci		.name = "imx53-nand",
16298c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t) &imx53_nand_devtype_data,
16308c2ecf20Sopenharmony_ci	}, {
16318c2ecf20Sopenharmony_ci		/* sentinel */
16328c2ecf20Sopenharmony_ci	}
16338c2ecf20Sopenharmony_ci};
16348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, mxcnd_devtype);
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
16378c2ecf20Sopenharmony_cistatic const struct of_device_id mxcnd_dt_ids[] = {
16388c2ecf20Sopenharmony_ci	{
16398c2ecf20Sopenharmony_ci		.compatible = "fsl,imx21-nand",
16408c2ecf20Sopenharmony_ci		.data = &imx21_nand_devtype_data,
16418c2ecf20Sopenharmony_ci	}, {
16428c2ecf20Sopenharmony_ci		.compatible = "fsl,imx27-nand",
16438c2ecf20Sopenharmony_ci		.data = &imx27_nand_devtype_data,
16448c2ecf20Sopenharmony_ci	}, {
16458c2ecf20Sopenharmony_ci		.compatible = "fsl,imx25-nand",
16468c2ecf20Sopenharmony_ci		.data = &imx25_nand_devtype_data,
16478c2ecf20Sopenharmony_ci	}, {
16488c2ecf20Sopenharmony_ci		.compatible = "fsl,imx51-nand",
16498c2ecf20Sopenharmony_ci		.data = &imx51_nand_devtype_data,
16508c2ecf20Sopenharmony_ci	}, {
16518c2ecf20Sopenharmony_ci		.compatible = "fsl,imx53-nand",
16528c2ecf20Sopenharmony_ci		.data = &imx53_nand_devtype_data,
16538c2ecf20Sopenharmony_ci	},
16548c2ecf20Sopenharmony_ci	{ /* sentinel */ }
16558c2ecf20Sopenharmony_ci};
16568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxcnd_dt_ids);
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_cistatic int mxcnd_probe_dt(struct mxc_nand_host *host)
16598c2ecf20Sopenharmony_ci{
16608c2ecf20Sopenharmony_ci	struct device_node *np = host->dev->of_node;
16618c2ecf20Sopenharmony_ci	const struct of_device_id *of_id =
16628c2ecf20Sopenharmony_ci		of_match_device(mxcnd_dt_ids, host->dev);
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	if (!np)
16658c2ecf20Sopenharmony_ci		return 1;
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_ci	host->devtype_data = of_id->data;
16688c2ecf20Sopenharmony_ci
16698c2ecf20Sopenharmony_ci	return 0;
16708c2ecf20Sopenharmony_ci}
16718c2ecf20Sopenharmony_ci#else
16728c2ecf20Sopenharmony_cistatic int mxcnd_probe_dt(struct mxc_nand_host *host)
16738c2ecf20Sopenharmony_ci{
16748c2ecf20Sopenharmony_ci	return 1;
16758c2ecf20Sopenharmony_ci}
16768c2ecf20Sopenharmony_ci#endif
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_cistatic int mxcnd_attach_chip(struct nand_chip *chip)
16798c2ecf20Sopenharmony_ci{
16808c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
16818c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
16828c2ecf20Sopenharmony_ci	struct device *dev = mtd->dev.parent;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	chip->ecc.bytes = host->devtype_data->eccbytes;
16858c2ecf20Sopenharmony_ci	host->eccsize = host->devtype_data->eccsize;
16868c2ecf20Sopenharmony_ci	chip->ecc.size = 512;
16878c2ecf20Sopenharmony_ci	mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	switch (chip->ecc.engine_type) {
16908c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_HOST:
16918c2ecf20Sopenharmony_ci		chip->ecc.read_page = mxc_nand_read_page;
16928c2ecf20Sopenharmony_ci		chip->ecc.read_page_raw = mxc_nand_read_page_raw;
16938c2ecf20Sopenharmony_ci		chip->ecc.read_oob = mxc_nand_read_oob;
16948c2ecf20Sopenharmony_ci		chip->ecc.write_page = mxc_nand_write_page_ecc;
16958c2ecf20Sopenharmony_ci		chip->ecc.write_page_raw = mxc_nand_write_page_raw;
16968c2ecf20Sopenharmony_ci		chip->ecc.write_oob = mxc_nand_write_oob;
16978c2ecf20Sopenharmony_ci		break;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_SOFT:
17008c2ecf20Sopenharmony_ci		break;
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	default:
17038c2ecf20Sopenharmony_ci		return -EINVAL;
17048c2ecf20Sopenharmony_ci	}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci	if (chip->bbt_options & NAND_BBT_USE_FLASH) {
17078c2ecf20Sopenharmony_ci		chip->bbt_td = &bbt_main_descr;
17088c2ecf20Sopenharmony_ci		chip->bbt_md = &bbt_mirror_descr;
17098c2ecf20Sopenharmony_ci	}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	/* Allocate the right size buffer now */
17128c2ecf20Sopenharmony_ci	devm_kfree(dev, (void *)host->data_buf);
17138c2ecf20Sopenharmony_ci	host->data_buf = devm_kzalloc(dev, mtd->writesize + mtd->oobsize,
17148c2ecf20Sopenharmony_ci				      GFP_KERNEL);
17158c2ecf20Sopenharmony_ci	if (!host->data_buf)
17168c2ecf20Sopenharmony_ci		return -ENOMEM;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	/* Call preset again, with correct writesize chip time */
17198c2ecf20Sopenharmony_ci	host->devtype_data->preset(mtd);
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	if (!chip->ecc.bytes) {
17228c2ecf20Sopenharmony_ci		if (host->eccsize == 8)
17238c2ecf20Sopenharmony_ci			chip->ecc.bytes = 18;
17248c2ecf20Sopenharmony_ci		else if (host->eccsize == 4)
17258c2ecf20Sopenharmony_ci			chip->ecc.bytes = 9;
17268c2ecf20Sopenharmony_ci	}
17278c2ecf20Sopenharmony_ci
17288c2ecf20Sopenharmony_ci	/*
17298c2ecf20Sopenharmony_ci	 * Experimentation shows that i.MX NFC can only handle up to 218 oob
17308c2ecf20Sopenharmony_ci	 * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare()
17318c2ecf20Sopenharmony_ci	 * into copying invalid data to/from the spare IO buffer, as this
17328c2ecf20Sopenharmony_ci	 * might cause ECC data corruption when doing sub-page write to a
17338c2ecf20Sopenharmony_ci	 * partially written page.
17348c2ecf20Sopenharmony_ci	 */
17358c2ecf20Sopenharmony_ci	host->used_oobsize = min(mtd->oobsize, 218U);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
17388c2ecf20Sopenharmony_ci		if (is_imx21_nfc(host) || is_imx27_nfc(host))
17398c2ecf20Sopenharmony_ci			chip->ecc.strength = 1;
17408c2ecf20Sopenharmony_ci		else
17418c2ecf20Sopenharmony_ci			chip->ecc.strength = (host->eccsize == 4) ? 4 : 8;
17428c2ecf20Sopenharmony_ci	}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	return 0;
17458c2ecf20Sopenharmony_ci}
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_cistatic int mxcnd_setup_interface(struct nand_chip *chip, int chipnr,
17488c2ecf20Sopenharmony_ci				 const struct nand_interface_config *conf)
17498c2ecf20Sopenharmony_ci{
17508c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = nand_get_controller_data(chip);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	return host->devtype_data->setup_interface(chip, chipnr, conf);
17538c2ecf20Sopenharmony_ci}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_cistatic const struct nand_controller_ops mxcnd_controller_ops = {
17568c2ecf20Sopenharmony_ci	.attach_chip = mxcnd_attach_chip,
17578c2ecf20Sopenharmony_ci	.setup_interface = mxcnd_setup_interface,
17588c2ecf20Sopenharmony_ci};
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_cistatic int mxcnd_probe(struct platform_device *pdev)
17618c2ecf20Sopenharmony_ci{
17628c2ecf20Sopenharmony_ci	struct nand_chip *this;
17638c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
17648c2ecf20Sopenharmony_ci	struct mxc_nand_host *host;
17658c2ecf20Sopenharmony_ci	struct resource *res;
17668c2ecf20Sopenharmony_ci	int err = 0;
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	/* Allocate memory for MTD device structure and private data */
17698c2ecf20Sopenharmony_ci	host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host),
17708c2ecf20Sopenharmony_ci			GFP_KERNEL);
17718c2ecf20Sopenharmony_ci	if (!host)
17728c2ecf20Sopenharmony_ci		return -ENOMEM;
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	/* allocate a temporary buffer for the nand_scan_ident() */
17758c2ecf20Sopenharmony_ci	host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL);
17768c2ecf20Sopenharmony_ci	if (!host->data_buf)
17778c2ecf20Sopenharmony_ci		return -ENOMEM;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	host->dev = &pdev->dev;
17808c2ecf20Sopenharmony_ci	/* structures must be linked */
17818c2ecf20Sopenharmony_ci	this = &host->nand;
17828c2ecf20Sopenharmony_ci	mtd = nand_to_mtd(this);
17838c2ecf20Sopenharmony_ci	mtd->dev.parent = &pdev->dev;
17848c2ecf20Sopenharmony_ci	mtd->name = DRIVER_NAME;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	/* 50 us command delay time */
17878c2ecf20Sopenharmony_ci	this->legacy.chip_delay = 5;
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	nand_set_controller_data(this, host);
17908c2ecf20Sopenharmony_ci	nand_set_flash_node(this, pdev->dev.of_node),
17918c2ecf20Sopenharmony_ci	this->legacy.dev_ready = mxc_nand_dev_ready;
17928c2ecf20Sopenharmony_ci	this->legacy.cmdfunc = mxc_nand_command;
17938c2ecf20Sopenharmony_ci	this->legacy.read_byte = mxc_nand_read_byte;
17948c2ecf20Sopenharmony_ci	this->legacy.write_buf = mxc_nand_write_buf;
17958c2ecf20Sopenharmony_ci	this->legacy.read_buf = mxc_nand_read_buf;
17968c2ecf20Sopenharmony_ci	this->legacy.set_features = mxc_nand_set_features;
17978c2ecf20Sopenharmony_ci	this->legacy.get_features = mxc_nand_get_features;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	host->clk = devm_clk_get(&pdev->dev, NULL);
18008c2ecf20Sopenharmony_ci	if (IS_ERR(host->clk))
18018c2ecf20Sopenharmony_ci		return PTR_ERR(host->clk);
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	err = mxcnd_probe_dt(host);
18048c2ecf20Sopenharmony_ci	if (err > 0) {
18058c2ecf20Sopenharmony_ci		struct mxc_nand_platform_data *pdata =
18068c2ecf20Sopenharmony_ci					dev_get_platdata(&pdev->dev);
18078c2ecf20Sopenharmony_ci		if (pdata) {
18088c2ecf20Sopenharmony_ci			host->pdata = *pdata;
18098c2ecf20Sopenharmony_ci			host->devtype_data = (struct mxc_nand_devtype_data *)
18108c2ecf20Sopenharmony_ci						pdev->id_entry->driver_data;
18118c2ecf20Sopenharmony_ci		} else {
18128c2ecf20Sopenharmony_ci			err = -ENODEV;
18138c2ecf20Sopenharmony_ci		}
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci	if (err < 0)
18168c2ecf20Sopenharmony_ci		return err;
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci	if (!host->devtype_data->setup_interface)
18198c2ecf20Sopenharmony_ci		this->options |= NAND_KEEP_TIMINGS;
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	if (host->devtype_data->needs_ip) {
18228c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
18238c2ecf20Sopenharmony_ci		host->regs_ip = devm_ioremap_resource(&pdev->dev, res);
18248c2ecf20Sopenharmony_ci		if (IS_ERR(host->regs_ip))
18258c2ecf20Sopenharmony_ci			return PTR_ERR(host->regs_ip);
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
18288c2ecf20Sopenharmony_ci	} else {
18298c2ecf20Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
18308c2ecf20Sopenharmony_ci	}
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	host->base = devm_ioremap_resource(&pdev->dev, res);
18338c2ecf20Sopenharmony_ci	if (IS_ERR(host->base))
18348c2ecf20Sopenharmony_ci		return PTR_ERR(host->base);
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	host->main_area0 = host->base;
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	if (host->devtype_data->regs_offset)
18398c2ecf20Sopenharmony_ci		host->regs = host->base + host->devtype_data->regs_offset;
18408c2ecf20Sopenharmony_ci	host->spare0 = host->base + host->devtype_data->spare0_offset;
18418c2ecf20Sopenharmony_ci	if (host->devtype_data->axi_offset)
18428c2ecf20Sopenharmony_ci		host->regs_axi = host->base + host->devtype_data->axi_offset;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	this->legacy.select_chip = host->devtype_data->select_chip;
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	/* NAND bus width determines access functions used by upper layer */
18478c2ecf20Sopenharmony_ci	if (host->pdata.width == 2)
18488c2ecf20Sopenharmony_ci		this->options |= NAND_BUSWIDTH_16;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	/* update flash based bbt */
18518c2ecf20Sopenharmony_ci	if (host->pdata.flash_bbt)
18528c2ecf20Sopenharmony_ci		this->bbt_options |= NAND_BBT_USE_FLASH;
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_ci	init_completion(&host->op_completion);
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	host->irq = platform_get_irq(pdev, 0);
18578c2ecf20Sopenharmony_ci	if (host->irq < 0)
18588c2ecf20Sopenharmony_ci		return host->irq;
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	/*
18618c2ecf20Sopenharmony_ci	 * Use host->devtype_data->irq_control() here instead of irq_control()
18628c2ecf20Sopenharmony_ci	 * because we must not disable_irq_nosync without having requested the
18638c2ecf20Sopenharmony_ci	 * irq.
18648c2ecf20Sopenharmony_ci	 */
18658c2ecf20Sopenharmony_ci	host->devtype_data->irq_control(host, 0);
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq,
18688c2ecf20Sopenharmony_ci			0, DRIVER_NAME, host);
18698c2ecf20Sopenharmony_ci	if (err)
18708c2ecf20Sopenharmony_ci		return err;
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	err = clk_prepare_enable(host->clk);
18738c2ecf20Sopenharmony_ci	if (err)
18748c2ecf20Sopenharmony_ci		return err;
18758c2ecf20Sopenharmony_ci	host->clk_act = 1;
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	/*
18788c2ecf20Sopenharmony_ci	 * Now that we "own" the interrupt make sure the interrupt mask bit is
18798c2ecf20Sopenharmony_ci	 * cleared on i.MX21. Otherwise we can't read the interrupt status bit
18808c2ecf20Sopenharmony_ci	 * on this machine.
18818c2ecf20Sopenharmony_ci	 */
18828c2ecf20Sopenharmony_ci	if (host->devtype_data->irqpending_quirk) {
18838c2ecf20Sopenharmony_ci		disable_irq_nosync(host->irq);
18848c2ecf20Sopenharmony_ci		host->devtype_data->irq_control(host, 1);
18858c2ecf20Sopenharmony_ci	}
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	/* Scan the NAND device */
18888c2ecf20Sopenharmony_ci	this->legacy.dummy_controller.ops = &mxcnd_controller_ops;
18898c2ecf20Sopenharmony_ci	err = nand_scan(this, is_imx25_nfc(host) ? 4 : 1);
18908c2ecf20Sopenharmony_ci	if (err)
18918c2ecf20Sopenharmony_ci		goto escan;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	/* Register the partitions */
18948c2ecf20Sopenharmony_ci	err = mtd_device_parse_register(mtd, part_probes, NULL,
18958c2ecf20Sopenharmony_ci					host->pdata.parts,
18968c2ecf20Sopenharmony_ci					host->pdata.nr_parts);
18978c2ecf20Sopenharmony_ci	if (err)
18988c2ecf20Sopenharmony_ci		goto cleanup_nand;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, host);
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	return 0;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_cicleanup_nand:
19058c2ecf20Sopenharmony_ci	nand_cleanup(this);
19068c2ecf20Sopenharmony_ciescan:
19078c2ecf20Sopenharmony_ci	if (host->clk_act)
19088c2ecf20Sopenharmony_ci		clk_disable_unprepare(host->clk);
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	return err;
19118c2ecf20Sopenharmony_ci}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_cistatic int mxcnd_remove(struct platform_device *pdev)
19148c2ecf20Sopenharmony_ci{
19158c2ecf20Sopenharmony_ci	struct mxc_nand_host *host = platform_get_drvdata(pdev);
19168c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->nand;
19178c2ecf20Sopenharmony_ci	int ret;
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
19208c2ecf20Sopenharmony_ci	WARN_ON(ret);
19218c2ecf20Sopenharmony_ci	nand_cleanup(chip);
19228c2ecf20Sopenharmony_ci	if (host->clk_act)
19238c2ecf20Sopenharmony_ci		clk_disable_unprepare(host->clk);
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	return 0;
19268c2ecf20Sopenharmony_ci}
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_cistatic struct platform_driver mxcnd_driver = {
19298c2ecf20Sopenharmony_ci	.driver = {
19308c2ecf20Sopenharmony_ci		   .name = DRIVER_NAME,
19318c2ecf20Sopenharmony_ci		   .of_match_table = of_match_ptr(mxcnd_dt_ids),
19328c2ecf20Sopenharmony_ci	},
19338c2ecf20Sopenharmony_ci	.id_table = mxcnd_devtype,
19348c2ecf20Sopenharmony_ci	.probe = mxcnd_probe,
19358c2ecf20Sopenharmony_ci	.remove = mxcnd_remove,
19368c2ecf20Sopenharmony_ci};
19378c2ecf20Sopenharmony_cimodule_platform_driver(mxcnd_driver);
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc.");
19408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MXC NAND MTD driver");
19418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1942