xref: /linux/drivers/mtd/nand/raw/sunxi_nand.c (revision 353b7a55dcaf5fb8758e09ebe2ddf5f3adbac7c5)
1f5f88871SBoris Brezillon // SPDX-License-Identifier: GPL-2.0+
21fef62c1SBoris BREZILLON /*
31fef62c1SBoris BREZILLON  * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
41fef62c1SBoris BREZILLON  *
51fef62c1SBoris BREZILLON  * Derived from:
61fef62c1SBoris BREZILLON  *	https://github.com/yuq/sunxi-nfc-mtd
71fef62c1SBoris BREZILLON  *	Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
81fef62c1SBoris BREZILLON  *
91fef62c1SBoris BREZILLON  *	https://github.com/hno/Allwinner-Info
101fef62c1SBoris BREZILLON  *	Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
111fef62c1SBoris BREZILLON  *
121fef62c1SBoris BREZILLON  *	Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
131fef62c1SBoris BREZILLON  *	Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
141fef62c1SBoris BREZILLON  */
151fef62c1SBoris BREZILLON 
161fef62c1SBoris BREZILLON #include <linux/dma-mapping.h>
171fef62c1SBoris BREZILLON #include <linux/slab.h>
181fef62c1SBoris BREZILLON #include <linux/module.h>
191fef62c1SBoris BREZILLON #include <linux/moduleparam.h>
201fef62c1SBoris BREZILLON #include <linux/platform_device.h>
211fef62c1SBoris BREZILLON #include <linux/of.h>
221fef62c1SBoris BREZILLON #include <linux/of_device.h>
231fef62c1SBoris BREZILLON #include <linux/mtd/mtd.h>
24d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h>
251fef62c1SBoris BREZILLON #include <linux/mtd/partitions.h>
261fef62c1SBoris BREZILLON #include <linux/clk.h>
271fef62c1SBoris BREZILLON #include <linux/delay.h>
281fef62c1SBoris BREZILLON #include <linux/dmaengine.h>
291fef62c1SBoris BREZILLON #include <linux/interrupt.h>
30166f08c7SBoris Brezillon #include <linux/iopoll.h>
31ab9d6a78SIcenowy Zheng #include <linux/reset.h>
321fef62c1SBoris BREZILLON 
331fef62c1SBoris BREZILLON #define NFC_REG_CTL		0x0000
341fef62c1SBoris BREZILLON #define NFC_REG_ST		0x0004
351fef62c1SBoris BREZILLON #define NFC_REG_INT		0x0008
361fef62c1SBoris BREZILLON #define NFC_REG_TIMING_CTL	0x000C
371fef62c1SBoris BREZILLON #define NFC_REG_TIMING_CFG	0x0010
381fef62c1SBoris BREZILLON #define NFC_REG_ADDR_LOW	0x0014
391fef62c1SBoris BREZILLON #define NFC_REG_ADDR_HIGH	0x0018
401fef62c1SBoris BREZILLON #define NFC_REG_SECTOR_NUM	0x001C
411fef62c1SBoris BREZILLON #define NFC_REG_CNT		0x0020
421fef62c1SBoris BREZILLON #define NFC_REG_CMD		0x0024
431fef62c1SBoris BREZILLON #define NFC_REG_RCMD_SET	0x0028
441fef62c1SBoris BREZILLON #define NFC_REG_WCMD_SET	0x002C
45a760e77dSMiquel Raynal #define NFC_REG_A10_IO_DATA	0x0030
46c7a87cebSMiquel Raynal #define NFC_REG_A23_IO_DATA	0x0300
471fef62c1SBoris BREZILLON #define NFC_REG_ECC_CTL		0x0034
481fef62c1SBoris BREZILLON #define NFC_REG_ECC_ST		0x0038
491fef62c1SBoris BREZILLON #define NFC_REG_DEBUG		0x003C
50b6a02c08SBoris BREZILLON #define NFC_REG_ECC_ERR_CNT(x)	((0x0040 + (x)) & ~0x3)
51b6a02c08SBoris BREZILLON #define NFC_REG_USER_DATA(x)	(0x0050 + ((x) * 4))
521fef62c1SBoris BREZILLON #define NFC_REG_SPARE_AREA	0x00A0
534be4e03eSBoris BREZILLON #define NFC_REG_PAT_ID		0x00A4
54910ef7a4SManuel Dipolt #define NFC_REG_MDMA_ADDR	0x00C0
55c7a87cebSMiquel Raynal #define NFC_REG_MDMA_CNT	0x00C4
561fef62c1SBoris BREZILLON #define NFC_RAM0_BASE		0x0400
571fef62c1SBoris BREZILLON #define NFC_RAM1_BASE		0x0800
581fef62c1SBoris BREZILLON 
591fef62c1SBoris BREZILLON /* define bit use in NFC_CTL */
601fef62c1SBoris BREZILLON #define NFC_EN			BIT(0)
611fef62c1SBoris BREZILLON #define NFC_RESET		BIT(1)
62b6a02c08SBoris BREZILLON #define NFC_BUS_WIDTH_MSK	BIT(2)
63b6a02c08SBoris BREZILLON #define NFC_BUS_WIDTH_8		(0 << 2)
64b6a02c08SBoris BREZILLON #define NFC_BUS_WIDTH_16	(1 << 2)
65b6a02c08SBoris BREZILLON #define NFC_RB_SEL_MSK		BIT(3)
66b6a02c08SBoris BREZILLON #define NFC_RB_SEL(x)		((x) << 3)
67b6a02c08SBoris BREZILLON #define NFC_CE_SEL_MSK		GENMASK(26, 24)
68b6a02c08SBoris BREZILLON #define NFC_CE_SEL(x)		((x) << 24)
691fef62c1SBoris BREZILLON #define NFC_CE_CTL		BIT(6)
70b6a02c08SBoris BREZILLON #define NFC_PAGE_SHIFT_MSK	GENMASK(11, 8)
71b6a02c08SBoris BREZILLON #define NFC_PAGE_SHIFT(x)	(((x) < 10 ? 0 : (x) - 10) << 8)
721fef62c1SBoris BREZILLON #define NFC_SAM			BIT(12)
731fef62c1SBoris BREZILLON #define NFC_RAM_METHOD		BIT(14)
74c7a87cebSMiquel Raynal #define NFC_DMA_TYPE_NORMAL	BIT(15)
751fef62c1SBoris BREZILLON #define NFC_DEBUG_CTL		BIT(31)
761fef62c1SBoris BREZILLON 
771fef62c1SBoris BREZILLON /* define bit use in NFC_ST */
781fef62c1SBoris BREZILLON #define NFC_RB_B2R		BIT(0)
791fef62c1SBoris BREZILLON #define NFC_CMD_INT_FLAG	BIT(1)
801fef62c1SBoris BREZILLON #define NFC_DMA_INT_FLAG	BIT(2)
811fef62c1SBoris BREZILLON #define NFC_CMD_FIFO_STATUS	BIT(3)
821fef62c1SBoris BREZILLON #define NFC_STA			BIT(4)
831fef62c1SBoris BREZILLON #define NFC_NATCH_INT_FLAG	BIT(5)
84b6a02c08SBoris BREZILLON #define NFC_RB_STATE(x)		BIT(x + 8)
851fef62c1SBoris BREZILLON 
861fef62c1SBoris BREZILLON /* define bit use in NFC_INT */
871fef62c1SBoris BREZILLON #define NFC_B2R_INT_ENABLE	BIT(0)
881fef62c1SBoris BREZILLON #define NFC_CMD_INT_ENABLE	BIT(1)
891fef62c1SBoris BREZILLON #define NFC_DMA_INT_ENABLE	BIT(2)
901fef62c1SBoris BREZILLON #define NFC_INT_MASK		(NFC_B2R_INT_ENABLE | \
911fef62c1SBoris BREZILLON 				 NFC_CMD_INT_ENABLE | \
921fef62c1SBoris BREZILLON 				 NFC_DMA_INT_ENABLE)
931fef62c1SBoris BREZILLON 
94d052e508SRoy Spliet /* define bit use in NFC_TIMING_CTL */
95d052e508SRoy Spliet #define NFC_TIMING_CTL_EDO	BIT(8)
96d052e508SRoy Spliet 
979c618292SRoy Spliet /* define NFC_TIMING_CFG register layout */
989c618292SRoy Spliet #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD)		\
999c618292SRoy Spliet 	(((tWB) & 0x3) | (((tADL) & 0x3) << 2) |		\
1009c618292SRoy Spliet 	(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) |		\
1019c618292SRoy Spliet 	(((tCAD) & 0x7) << 8))
1029c618292SRoy Spliet 
1031fef62c1SBoris BREZILLON /* define bit use in NFC_CMD */
104b6a02c08SBoris BREZILLON #define NFC_CMD_LOW_BYTE_MSK	GENMASK(7, 0)
105b6a02c08SBoris BREZILLON #define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)
106b6a02c08SBoris BREZILLON #define NFC_CMD(x)		(x)
107b6a02c08SBoris BREZILLON #define NFC_ADR_NUM_MSK		GENMASK(18, 16)
108b6a02c08SBoris BREZILLON #define NFC_ADR_NUM(x)		(((x) - 1) << 16)
1091fef62c1SBoris BREZILLON #define NFC_SEND_ADR		BIT(19)
1101fef62c1SBoris BREZILLON #define NFC_ACCESS_DIR		BIT(20)
1111fef62c1SBoris BREZILLON #define NFC_DATA_TRANS		BIT(21)
1121fef62c1SBoris BREZILLON #define NFC_SEND_CMD1		BIT(22)
1131fef62c1SBoris BREZILLON #define NFC_WAIT_FLAG		BIT(23)
1141fef62c1SBoris BREZILLON #define NFC_SEND_CMD2		BIT(24)
1151fef62c1SBoris BREZILLON #define NFC_SEQ			BIT(25)
1161fef62c1SBoris BREZILLON #define NFC_DATA_SWAP_METHOD	BIT(26)
1171fef62c1SBoris BREZILLON #define NFC_ROW_AUTO_INC	BIT(27)
1181fef62c1SBoris BREZILLON #define NFC_SEND_CMD3		BIT(28)
1191fef62c1SBoris BREZILLON #define NFC_SEND_CMD4		BIT(29)
120b6a02c08SBoris BREZILLON #define NFC_CMD_TYPE_MSK	GENMASK(31, 30)
121b6a02c08SBoris BREZILLON #define NFC_NORMAL_OP		(0 << 30)
122b6a02c08SBoris BREZILLON #define NFC_ECC_OP		(1 << 30)
123cf3e3fd2SBoris Brezillon #define NFC_PAGE_OP		(2U << 30)
1241fef62c1SBoris BREZILLON 
1251fef62c1SBoris BREZILLON /* define bit use in NFC_RCMD_SET */
126b6a02c08SBoris BREZILLON #define NFC_READ_CMD_MSK	GENMASK(7, 0)
127b6a02c08SBoris BREZILLON #define NFC_RND_READ_CMD0_MSK	GENMASK(15, 8)
128b6a02c08SBoris BREZILLON #define NFC_RND_READ_CMD1_MSK	GENMASK(23, 16)
1291fef62c1SBoris BREZILLON 
1301fef62c1SBoris BREZILLON /* define bit use in NFC_WCMD_SET */
131b6a02c08SBoris BREZILLON #define NFC_PROGRAM_CMD_MSK	GENMASK(7, 0)
132b6a02c08SBoris BREZILLON #define NFC_RND_WRITE_CMD_MSK	GENMASK(15, 8)
133b6a02c08SBoris BREZILLON #define NFC_READ_CMD0_MSK	GENMASK(23, 16)
134b6a02c08SBoris BREZILLON #define NFC_READ_CMD1_MSK	GENMASK(31, 24)
1351fef62c1SBoris BREZILLON 
1361fef62c1SBoris BREZILLON /* define bit use in NFC_ECC_CTL */
1371fef62c1SBoris BREZILLON #define NFC_ECC_EN		BIT(0)
1381fef62c1SBoris BREZILLON #define NFC_ECC_PIPELINE	BIT(3)
1391fef62c1SBoris BREZILLON #define NFC_ECC_EXCEPTION	BIT(4)
140b6a02c08SBoris BREZILLON #define NFC_ECC_BLOCK_SIZE_MSK	BIT(5)
141f59dab8dSBoris Brezillon #define NFC_ECC_BLOCK_512	BIT(5)
1421fef62c1SBoris BREZILLON #define NFC_RANDOM_EN		BIT(9)
1431fef62c1SBoris BREZILLON #define NFC_RANDOM_DIRECTION	BIT(10)
144b6a02c08SBoris BREZILLON #define NFC_ECC_MODE_MSK	GENMASK(15, 12)
145b6a02c08SBoris BREZILLON #define NFC_ECC_MODE(x)		((x) << 12)
146b6a02c08SBoris BREZILLON #define NFC_RANDOM_SEED_MSK	GENMASK(30, 16)
147b6a02c08SBoris BREZILLON #define NFC_RANDOM_SEED(x)	((x) << 16)
148b6a02c08SBoris BREZILLON 
149b6a02c08SBoris BREZILLON /* define bit use in NFC_ECC_ST */
150b6a02c08SBoris BREZILLON #define NFC_ECC_ERR(x)		BIT(x)
151614049a8SBoris Brezillon #define NFC_ECC_ERR_MSK		GENMASK(15, 0)
152b6a02c08SBoris BREZILLON #define NFC_ECC_PAT_FOUND(x)	BIT(x + 16)
153f8b04746SBoris Brezillon #define NFC_ECC_ERR_CNT(b, x)	(((x) >> (((b) % 4) * 8)) & 0xff)
1541fef62c1SBoris BREZILLON 
1551fef62c1SBoris BREZILLON #define NFC_DEFAULT_TIMEOUT_MS	1000
1561fef62c1SBoris BREZILLON 
1571fef62c1SBoris BREZILLON #define NFC_SRAM_SIZE		1024
1581fef62c1SBoris BREZILLON 
1591fef62c1SBoris BREZILLON #define NFC_MAX_CS		7
1601fef62c1SBoris BREZILLON 
16167c88008SBoris Brezillon /**
16267c88008SBoris Brezillon  * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
1631fef62c1SBoris BREZILLON  *
1641fef62c1SBoris BREZILLON  * @cs: the NAND CS id used to communicate with a NAND Chip
16567c88008SBoris Brezillon  * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the NFC
1661fef62c1SBoris BREZILLON  */
1671fef62c1SBoris BREZILLON struct sunxi_nand_chip_sel {
1681fef62c1SBoris BREZILLON 	u8 cs;
169ddd5ed3aSBoris Brezillon 	s8 rb;
1701fef62c1SBoris BREZILLON };
1711fef62c1SBoris BREZILLON 
17267c88008SBoris Brezillon /**
17367c88008SBoris Brezillon  * struct sunxi_nand_hw_ecc - stores information related to HW ECC support
1741fef62c1SBoris BREZILLON  *
1751fef62c1SBoris BREZILLON  * @mode: the sunxi ECC mode field deduced from ECC requirements
1761fef62c1SBoris BREZILLON  */
1771fef62c1SBoris BREZILLON struct sunxi_nand_hw_ecc {
1781fef62c1SBoris BREZILLON 	int mode;
1791fef62c1SBoris BREZILLON };
1801fef62c1SBoris BREZILLON 
18167c88008SBoris Brezillon /**
18267c88008SBoris Brezillon  * struct sunxi_nand_chip - stores NAND chip device related information
1831fef62c1SBoris BREZILLON  *
1841fef62c1SBoris BREZILLON  * @node: used to store NAND chips into a list
1851fef62c1SBoris BREZILLON  * @nand: base NAND chip structure
186cbd87780SMiquel Raynal  * @ecc: ECC controller structure
1871fef62c1SBoris BREZILLON  * @clk_rate: clk_rate required for this NAND chip
18867c88008SBoris Brezillon  * @timing_cfg: TIMING_CFG register value for this NAND chip
18967c88008SBoris Brezillon  * @timing_ctl: TIMING_CTL register value for this NAND chip
1901fef62c1SBoris BREZILLON  * @nsels: number of CS lines required by the NAND chip
1911fef62c1SBoris BREZILLON  * @sels: array of CS lines descriptions
1921fef62c1SBoris BREZILLON  */
1931fef62c1SBoris BREZILLON struct sunxi_nand_chip {
1941fef62c1SBoris BREZILLON 	struct list_head node;
1951fef62c1SBoris BREZILLON 	struct nand_chip nand;
196cbd87780SMiquel Raynal 	struct sunxi_nand_hw_ecc *ecc;
1971fef62c1SBoris BREZILLON 	unsigned long clk_rate;
1989c618292SRoy Spliet 	u32 timing_cfg;
199d052e508SRoy Spliet 	u32 timing_ctl;
2001fef62c1SBoris BREZILLON 	int nsels;
20149f1c330SGustavo A. R. Silva 	struct sunxi_nand_chip_sel sels[];
2021fef62c1SBoris BREZILLON };
2031fef62c1SBoris BREZILLON 
2041fef62c1SBoris BREZILLON static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
2051fef62c1SBoris BREZILLON {
2061fef62c1SBoris BREZILLON 	return container_of(nand, struct sunxi_nand_chip, nand);
2071fef62c1SBoris BREZILLON }
2081fef62c1SBoris BREZILLON 
209a760e77dSMiquel Raynal /*
210a760e77dSMiquel Raynal  * NAND Controller capabilities structure: stores NAND controller capabilities
211a760e77dSMiquel Raynal  * for distinction between compatible strings.
212a760e77dSMiquel Raynal  *
213910ef7a4SManuel Dipolt  * @has_mdma:		Use mbus dma mode, otherwise general dma
214c7a87cebSMiquel Raynal  *			through MBUS on A23/A33 needs extra configuration.
215a760e77dSMiquel Raynal  * @reg_io_data:	I/O data register
216a760e77dSMiquel Raynal  * @dma_maxburst:	DMA maxburst
217a760e77dSMiquel Raynal  */
218a760e77dSMiquel Raynal struct sunxi_nfc_caps {
219910ef7a4SManuel Dipolt 	bool has_mdma;
220a760e77dSMiquel Raynal 	unsigned int reg_io_data;
221a760e77dSMiquel Raynal 	unsigned int dma_maxburst;
222a760e77dSMiquel Raynal };
223a760e77dSMiquel Raynal 
22467c88008SBoris Brezillon /**
22567c88008SBoris Brezillon  * struct sunxi_nfc - stores sunxi NAND controller information
2261fef62c1SBoris BREZILLON  *
2271fef62c1SBoris BREZILLON  * @controller: base controller structure
2281fef62c1SBoris BREZILLON  * @dev: parent device (used to print error messages)
2291fef62c1SBoris BREZILLON  * @regs: NAND controller registers
23067c88008SBoris Brezillon  * @ahb_clk: NAND controller AHB clock
23167c88008SBoris Brezillon  * @mod_clk: NAND controller mod clock
23267c88008SBoris Brezillon  * @reset: NAND controller reset line
2331fef62c1SBoris BREZILLON  * @assigned_cs: bitmask describing already assigned CS lines
2341fef62c1SBoris BREZILLON  * @clk_rate: NAND controller current clock rate
23567c88008SBoris Brezillon  * @chips: a list containing all the NAND chips attached to this NAND
23667c88008SBoris Brezillon  *	   controller
23767c88008SBoris Brezillon  * @complete: a completion object used to wait for NAND controller events
23867c88008SBoris Brezillon  * @dmac: the DMA channel attached to the NAND controller
2390d5c506dSLee Jones  * @caps: NAND Controller capabilities
2401fef62c1SBoris BREZILLON  */
2411fef62c1SBoris BREZILLON struct sunxi_nfc {
2427da45139SMiquel Raynal 	struct nand_controller controller;
2431fef62c1SBoris BREZILLON 	struct device *dev;
2441fef62c1SBoris BREZILLON 	void __iomem *regs;
2451fef62c1SBoris BREZILLON 	struct clk *ahb_clk;
2461fef62c1SBoris BREZILLON 	struct clk *mod_clk;
247ab9d6a78SIcenowy Zheng 	struct reset_control *reset;
2481fef62c1SBoris BREZILLON 	unsigned long assigned_cs;
2491fef62c1SBoris BREZILLON 	unsigned long clk_rate;
2501fef62c1SBoris BREZILLON 	struct list_head chips;
2511fef62c1SBoris BREZILLON 	struct completion complete;
252614049a8SBoris Brezillon 	struct dma_chan *dmac;
253a760e77dSMiquel Raynal 	const struct sunxi_nfc_caps *caps;
2541fef62c1SBoris BREZILLON };
2551fef62c1SBoris BREZILLON 
2567da45139SMiquel Raynal static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl)
2571fef62c1SBoris BREZILLON {
2581fef62c1SBoris BREZILLON 	return container_of(ctrl, struct sunxi_nfc, controller);
2591fef62c1SBoris BREZILLON }
2601fef62c1SBoris BREZILLON 
2611fef62c1SBoris BREZILLON static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
2621fef62c1SBoris BREZILLON {
2631fef62c1SBoris BREZILLON 	struct sunxi_nfc *nfc = dev_id;
2641fef62c1SBoris BREZILLON 	u32 st = readl(nfc->regs + NFC_REG_ST);
2651fef62c1SBoris BREZILLON 	u32 ien = readl(nfc->regs + NFC_REG_INT);
2661fef62c1SBoris BREZILLON 
2671fef62c1SBoris BREZILLON 	if (!(ien & st))
2681fef62c1SBoris BREZILLON 		return IRQ_NONE;
2691fef62c1SBoris BREZILLON 
2701fef62c1SBoris BREZILLON 	if ((ien & st) == ien)
2711fef62c1SBoris BREZILLON 		complete(&nfc->complete);
2721fef62c1SBoris BREZILLON 
2731fef62c1SBoris BREZILLON 	writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
2741fef62c1SBoris BREZILLON 	writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
2751fef62c1SBoris BREZILLON 
2761fef62c1SBoris BREZILLON 	return IRQ_HANDLED;
2771fef62c1SBoris BREZILLON }
2781fef62c1SBoris BREZILLON 
279c0c9dfa8SBoris Brezillon static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
280c0c9dfa8SBoris Brezillon 				 bool use_polling, unsigned int timeout_ms)
2811fef62c1SBoris BREZILLON {
282c0c9dfa8SBoris Brezillon 	int ret;
2831fef62c1SBoris BREZILLON 
284c0c9dfa8SBoris Brezillon 	if (events & ~NFC_INT_MASK)
285c0c9dfa8SBoris Brezillon 		return -EINVAL;
2861fef62c1SBoris BREZILLON 
2871fef62c1SBoris BREZILLON 	if (!timeout_ms)
2881fef62c1SBoris BREZILLON 		timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
2891fef62c1SBoris BREZILLON 
290c0c9dfa8SBoris Brezillon 	if (!use_polling) {
291c0c9dfa8SBoris Brezillon 		init_completion(&nfc->complete);
292c0c9dfa8SBoris Brezillon 
293c0c9dfa8SBoris Brezillon 		writel(events, nfc->regs + NFC_REG_INT);
294c0c9dfa8SBoris Brezillon 
295c0c9dfa8SBoris Brezillon 		ret = wait_for_completion_timeout(&nfc->complete,
296c0c9dfa8SBoris Brezillon 						msecs_to_jiffies(timeout_ms));
29719649e2cSBoris Brezillon 		if (!ret)
29819649e2cSBoris Brezillon 			ret = -ETIMEDOUT;
29919649e2cSBoris Brezillon 		else
30019649e2cSBoris Brezillon 			ret = 0;
301c0c9dfa8SBoris Brezillon 
302c0c9dfa8SBoris Brezillon 		writel(0, nfc->regs + NFC_REG_INT);
303c0c9dfa8SBoris Brezillon 	} else {
304c0c9dfa8SBoris Brezillon 		u32 status;
305c0c9dfa8SBoris Brezillon 
306c0c9dfa8SBoris Brezillon 		ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
307c0c9dfa8SBoris Brezillon 					 (status & events) == events, 1,
308c0c9dfa8SBoris Brezillon 					 timeout_ms * 1000);
3091fef62c1SBoris BREZILLON 	}
3101fef62c1SBoris BREZILLON 
311c0c9dfa8SBoris Brezillon 	writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
312c0c9dfa8SBoris Brezillon 
313c0c9dfa8SBoris Brezillon 	if (ret)
314c0c9dfa8SBoris Brezillon 		dev_err(nfc->dev, "wait interrupt timedout\n");
315c0c9dfa8SBoris Brezillon 
316c0c9dfa8SBoris Brezillon 	return ret;
3171fef62c1SBoris BREZILLON }
3181fef62c1SBoris BREZILLON 
3191fef62c1SBoris BREZILLON static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
3201fef62c1SBoris BREZILLON {
321166f08c7SBoris Brezillon 	u32 status;
322166f08c7SBoris Brezillon 	int ret;
3231fef62c1SBoris BREZILLON 
324166f08c7SBoris Brezillon 	ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
325166f08c7SBoris Brezillon 				 !(status & NFC_CMD_FIFO_STATUS), 1,
326166f08c7SBoris Brezillon 				 NFC_DEFAULT_TIMEOUT_MS * 1000);
327166f08c7SBoris Brezillon 	if (ret)
3281fef62c1SBoris BREZILLON 		dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
329166f08c7SBoris Brezillon 
330166f08c7SBoris Brezillon 	return ret;
3311fef62c1SBoris BREZILLON }
3321fef62c1SBoris BREZILLON 
3331fef62c1SBoris BREZILLON static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
3341fef62c1SBoris BREZILLON {
335166f08c7SBoris Brezillon 	u32 ctl;
336166f08c7SBoris Brezillon 	int ret;
3371fef62c1SBoris BREZILLON 
3381fef62c1SBoris BREZILLON 	writel(0, nfc->regs + NFC_REG_ECC_CTL);
3391fef62c1SBoris BREZILLON 	writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
3401fef62c1SBoris BREZILLON 
341166f08c7SBoris Brezillon 	ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl,
342166f08c7SBoris Brezillon 				 !(ctl & NFC_RESET), 1,
343166f08c7SBoris Brezillon 				 NFC_DEFAULT_TIMEOUT_MS * 1000);
344166f08c7SBoris Brezillon 	if (ret)
3451fef62c1SBoris BREZILLON 		dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
346166f08c7SBoris Brezillon 
347166f08c7SBoris Brezillon 	return ret;
3481fef62c1SBoris BREZILLON }
3491fef62c1SBoris BREZILLON 
350cde567e3SBoris Brezillon static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
351614049a8SBoris Brezillon 				    int chunksize, int nchunks,
352614049a8SBoris Brezillon 				    enum dma_data_direction ddir,
353614049a8SBoris Brezillon 				    struct scatterlist *sg)
354614049a8SBoris Brezillon {
355614049a8SBoris Brezillon 	struct dma_async_tx_descriptor *dmad;
356614049a8SBoris Brezillon 	enum dma_transfer_direction tdir;
357614049a8SBoris Brezillon 	dma_cookie_t dmat;
358614049a8SBoris Brezillon 	int ret;
359614049a8SBoris Brezillon 
360614049a8SBoris Brezillon 	if (ddir == DMA_FROM_DEVICE)
361614049a8SBoris Brezillon 		tdir = DMA_DEV_TO_MEM;
362614049a8SBoris Brezillon 	else
363614049a8SBoris Brezillon 		tdir = DMA_MEM_TO_DEV;
364614049a8SBoris Brezillon 
365614049a8SBoris Brezillon 	sg_init_one(sg, buf, nchunks * chunksize);
366614049a8SBoris Brezillon 	ret = dma_map_sg(nfc->dev, sg, 1, ddir);
367614049a8SBoris Brezillon 	if (!ret)
368614049a8SBoris Brezillon 		return -ENOMEM;
369614049a8SBoris Brezillon 
370910ef7a4SManuel Dipolt 	if (!nfc->caps->has_mdma) {
371614049a8SBoris Brezillon 		dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
37228f3d01eSWei Yongjun 		if (!dmad) {
37328f3d01eSWei Yongjun 			ret = -EINVAL;
374614049a8SBoris Brezillon 			goto err_unmap_buf;
375614049a8SBoris Brezillon 		}
376910ef7a4SManuel Dipolt 	}
377614049a8SBoris Brezillon 
378614049a8SBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
379614049a8SBoris Brezillon 	       nfc->regs + NFC_REG_CTL);
380614049a8SBoris Brezillon 	writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
381614049a8SBoris Brezillon 	writel(chunksize, nfc->regs + NFC_REG_CNT);
382c7a87cebSMiquel Raynal 
383910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma) {
384910ef7a4SManuel Dipolt 		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
385910ef7a4SManuel Dipolt 		       nfc->regs + NFC_REG_CTL);
386910ef7a4SManuel Dipolt 		writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
387910ef7a4SManuel Dipolt 		writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR);
388910ef7a4SManuel Dipolt 	} else {
389614049a8SBoris Brezillon 		dmat = dmaengine_submit(dmad);
390614049a8SBoris Brezillon 
391614049a8SBoris Brezillon 		ret = dma_submit_error(dmat);
392614049a8SBoris Brezillon 		if (ret)
393614049a8SBoris Brezillon 			goto err_clr_dma_flag;
394910ef7a4SManuel Dipolt 	}
395614049a8SBoris Brezillon 
396614049a8SBoris Brezillon 	return 0;
397614049a8SBoris Brezillon 
398614049a8SBoris Brezillon err_clr_dma_flag:
399614049a8SBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
400614049a8SBoris Brezillon 	       nfc->regs + NFC_REG_CTL);
401614049a8SBoris Brezillon 
402614049a8SBoris Brezillon err_unmap_buf:
403614049a8SBoris Brezillon 	dma_unmap_sg(nfc->dev, sg, 1, ddir);
404614049a8SBoris Brezillon 	return ret;
405614049a8SBoris Brezillon }
406614049a8SBoris Brezillon 
407cde567e3SBoris Brezillon static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
408614049a8SBoris Brezillon 				     enum dma_data_direction ddir,
409614049a8SBoris Brezillon 				     struct scatterlist *sg)
410614049a8SBoris Brezillon {
411614049a8SBoris Brezillon 	dma_unmap_sg(nfc->dev, sg, 1, ddir);
412614049a8SBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
413614049a8SBoris Brezillon 	       nfc->regs + NFC_REG_CTL);
414614049a8SBoris Brezillon }
415614049a8SBoris Brezillon 
416df505799SBoris Brezillon static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
4171fef62c1SBoris BREZILLON {
418758b56f5SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
4191fef62c1SBoris BREZILLON 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
4201fef62c1SBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
4211fef62c1SBoris BREZILLON 	struct sunxi_nand_chip_sel *sel;
4221fef62c1SBoris BREZILLON 	u32 ctl;
4231fef62c1SBoris BREZILLON 
424df505799SBoris Brezillon 	if (cs > 0 && cs >= sunxi_nand->nsels)
4251fef62c1SBoris BREZILLON 		return;
4261fef62c1SBoris BREZILLON 
4271fef62c1SBoris BREZILLON 	ctl = readl(nfc->regs + NFC_REG_CTL) &
428b6a02c08SBoris BREZILLON 	      ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
4291fef62c1SBoris BREZILLON 
430df505799SBoris Brezillon 	sel = &sunxi_nand->sels[cs];
431df505799SBoris Brezillon 	ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift);
432df505799SBoris Brezillon 	if (sel->rb >= 0)
433ddd5ed3aSBoris Brezillon 		ctl |= NFC_RB_SEL(sel->rb);
4341fef62c1SBoris BREZILLON 
4351fef62c1SBoris BREZILLON 	writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
4361fef62c1SBoris BREZILLON 
4371fef62c1SBoris BREZILLON 	if (nfc->clk_rate != sunxi_nand->clk_rate) {
4381fef62c1SBoris BREZILLON 		clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
4391fef62c1SBoris BREZILLON 		nfc->clk_rate = sunxi_nand->clk_rate;
4401fef62c1SBoris BREZILLON 	}
4411fef62c1SBoris BREZILLON 
442d052e508SRoy Spliet 	writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
4439c618292SRoy Spliet 	writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
4441fef62c1SBoris BREZILLON 	writel(ctl, nfc->regs + NFC_REG_CTL);
4451fef62c1SBoris BREZILLON }
4461fef62c1SBoris BREZILLON 
4477e534323SBoris Brezillon static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len)
4481fef62c1SBoris BREZILLON {
4491fef62c1SBoris BREZILLON 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
4501fef62c1SBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
4511fef62c1SBoris BREZILLON 	int ret;
4521fef62c1SBoris BREZILLON 	int cnt;
4531fef62c1SBoris BREZILLON 	int offs = 0;
4541fef62c1SBoris BREZILLON 	u32 tmp;
4551fef62c1SBoris BREZILLON 
4561fef62c1SBoris BREZILLON 	while (len > offs) {
4578de15e1fSBoris Brezillon 		bool poll = false;
4588de15e1fSBoris Brezillon 
4591fef62c1SBoris BREZILLON 		cnt = min(len - offs, NFC_SRAM_SIZE);
4601fef62c1SBoris BREZILLON 
4611fef62c1SBoris BREZILLON 		ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
4621fef62c1SBoris BREZILLON 		if (ret)
4631fef62c1SBoris BREZILLON 			break;
4641fef62c1SBoris BREZILLON 
4651fef62c1SBoris BREZILLON 		writel(cnt, nfc->regs + NFC_REG_CNT);
4661fef62c1SBoris BREZILLON 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
4671fef62c1SBoris BREZILLON 		writel(tmp, nfc->regs + NFC_REG_CMD);
4681fef62c1SBoris BREZILLON 
4698de15e1fSBoris Brezillon 		/* Arbitrary limit for polling mode */
4708de15e1fSBoris Brezillon 		if (cnt < 64)
4718de15e1fSBoris Brezillon 			poll = true;
4728de15e1fSBoris Brezillon 
4738de15e1fSBoris Brezillon 		ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
4741fef62c1SBoris BREZILLON 		if (ret)
4751fef62c1SBoris BREZILLON 			break;
4761fef62c1SBoris BREZILLON 
4771fef62c1SBoris BREZILLON 		if (buf)
4781fef62c1SBoris BREZILLON 			memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
4791fef62c1SBoris BREZILLON 				      cnt);
4801fef62c1SBoris BREZILLON 		offs += cnt;
4811fef62c1SBoris BREZILLON 	}
4821fef62c1SBoris BREZILLON }
4831fef62c1SBoris BREZILLON 
484c0739d85SBoris Brezillon static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf,
4851fef62c1SBoris BREZILLON 				int len)
4861fef62c1SBoris BREZILLON {
4871fef62c1SBoris BREZILLON 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
4881fef62c1SBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
4891fef62c1SBoris BREZILLON 	int ret;
4901fef62c1SBoris BREZILLON 	int cnt;
4911fef62c1SBoris BREZILLON 	int offs = 0;
4921fef62c1SBoris BREZILLON 	u32 tmp;
4931fef62c1SBoris BREZILLON 
4941fef62c1SBoris BREZILLON 	while (len > offs) {
4958de15e1fSBoris Brezillon 		bool poll = false;
4968de15e1fSBoris Brezillon 
4971fef62c1SBoris BREZILLON 		cnt = min(len - offs, NFC_SRAM_SIZE);
4981fef62c1SBoris BREZILLON 
4991fef62c1SBoris BREZILLON 		ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
5001fef62c1SBoris BREZILLON 		if (ret)
5011fef62c1SBoris BREZILLON 			break;
5021fef62c1SBoris BREZILLON 
5031fef62c1SBoris BREZILLON 		writel(cnt, nfc->regs + NFC_REG_CNT);
5041fef62c1SBoris BREZILLON 		memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
5051fef62c1SBoris BREZILLON 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
5061fef62c1SBoris BREZILLON 		      NFC_ACCESS_DIR;
5071fef62c1SBoris BREZILLON 		writel(tmp, nfc->regs + NFC_REG_CMD);
5081fef62c1SBoris BREZILLON 
5098de15e1fSBoris Brezillon 		/* Arbitrary limit for polling mode */
5108de15e1fSBoris Brezillon 		if (cnt < 64)
5118de15e1fSBoris Brezillon 			poll = true;
5128de15e1fSBoris Brezillon 
5138de15e1fSBoris Brezillon 		ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
5141fef62c1SBoris BREZILLON 		if (ret)
5151fef62c1SBoris BREZILLON 			break;
5161fef62c1SBoris BREZILLON 
5171fef62c1SBoris BREZILLON 		offs += cnt;
5181fef62c1SBoris BREZILLON 	}
5191fef62c1SBoris BREZILLON }
5201fef62c1SBoris BREZILLON 
5214be4e03eSBoris BREZILLON /* These seed values have been extracted from Allwinner's BSP */
5224be4e03eSBoris BREZILLON static const u16 sunxi_nfc_randomizer_page_seeds[] = {
5234be4e03eSBoris BREZILLON 	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
5244be4e03eSBoris BREZILLON 	0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
5254be4e03eSBoris BREZILLON 	0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
5264be4e03eSBoris BREZILLON 	0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
5274be4e03eSBoris BREZILLON 	0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
5284be4e03eSBoris BREZILLON 	0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
5294be4e03eSBoris BREZILLON 	0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
5304be4e03eSBoris BREZILLON 	0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
5314be4e03eSBoris BREZILLON 	0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
5324be4e03eSBoris BREZILLON 	0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
5334be4e03eSBoris BREZILLON 	0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
5344be4e03eSBoris BREZILLON 	0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
5354be4e03eSBoris BREZILLON 	0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
5364be4e03eSBoris BREZILLON 	0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
5374be4e03eSBoris BREZILLON 	0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
5384be4e03eSBoris BREZILLON 	0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
5394be4e03eSBoris BREZILLON };
5404be4e03eSBoris BREZILLON 
5414be4e03eSBoris BREZILLON /*
5424be4e03eSBoris BREZILLON  * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
5434be4e03eSBoris BREZILLON  * have been generated using
5444be4e03eSBoris BREZILLON  * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
5454be4e03eSBoris BREZILLON  * the randomizer engine does internally before de/scrambling OOB data.
5464be4e03eSBoris BREZILLON  *
5474be4e03eSBoris BREZILLON  * Those tables are statically defined to avoid calculating randomizer state
5484be4e03eSBoris BREZILLON  * at runtime.
5494be4e03eSBoris BREZILLON  */
5504be4e03eSBoris BREZILLON static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
5514be4e03eSBoris BREZILLON 	0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
5524be4e03eSBoris BREZILLON 	0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
5534be4e03eSBoris BREZILLON 	0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
5544be4e03eSBoris BREZILLON 	0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
5554be4e03eSBoris BREZILLON 	0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
5564be4e03eSBoris BREZILLON 	0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
5574be4e03eSBoris BREZILLON 	0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
5584be4e03eSBoris BREZILLON 	0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
5594be4e03eSBoris BREZILLON 	0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
5604be4e03eSBoris BREZILLON 	0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
5614be4e03eSBoris BREZILLON 	0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
5624be4e03eSBoris BREZILLON 	0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
5634be4e03eSBoris BREZILLON 	0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
5644be4e03eSBoris BREZILLON 	0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
5654be4e03eSBoris BREZILLON 	0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
5664be4e03eSBoris BREZILLON 	0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
5674be4e03eSBoris BREZILLON };
5684be4e03eSBoris BREZILLON 
5694be4e03eSBoris BREZILLON static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
5704be4e03eSBoris BREZILLON 	0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
5714be4e03eSBoris BREZILLON 	0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
5724be4e03eSBoris BREZILLON 	0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
5734be4e03eSBoris BREZILLON 	0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
5744be4e03eSBoris BREZILLON 	0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
5754be4e03eSBoris BREZILLON 	0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
5764be4e03eSBoris BREZILLON 	0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
5774be4e03eSBoris BREZILLON 	0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
5784be4e03eSBoris BREZILLON 	0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
5794be4e03eSBoris BREZILLON 	0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
5804be4e03eSBoris BREZILLON 	0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
5814be4e03eSBoris BREZILLON 	0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
5824be4e03eSBoris BREZILLON 	0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
5834be4e03eSBoris BREZILLON 	0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
5844be4e03eSBoris BREZILLON 	0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
5854be4e03eSBoris BREZILLON 	0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
5864be4e03eSBoris BREZILLON };
5874be4e03eSBoris BREZILLON 
5884be4e03eSBoris BREZILLON static u16 sunxi_nfc_randomizer_step(u16 state, int count)
5894be4e03eSBoris BREZILLON {
5904be4e03eSBoris BREZILLON 	state &= 0x7fff;
5914be4e03eSBoris BREZILLON 
5924be4e03eSBoris BREZILLON 	/*
5934be4e03eSBoris BREZILLON 	 * This loop is just a simple implementation of a Fibonacci LFSR using
5944be4e03eSBoris BREZILLON 	 * the x16 + x15 + 1 polynomial.
5954be4e03eSBoris BREZILLON 	 */
5964be4e03eSBoris BREZILLON 	while (count--)
5974be4e03eSBoris BREZILLON 		state = ((state >> 1) |
5984be4e03eSBoris BREZILLON 			 (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
5994be4e03eSBoris BREZILLON 
6004be4e03eSBoris BREZILLON 	return state;
6014be4e03eSBoris BREZILLON }
6024be4e03eSBoris BREZILLON 
603cde567e3SBoris Brezillon static u16 sunxi_nfc_randomizer_state(struct nand_chip *nand, int page,
604cde567e3SBoris Brezillon 				      bool ecc)
6054be4e03eSBoris BREZILLON {
606cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
6074be4e03eSBoris BREZILLON 	const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
60846c135c2SBrian Norris 	int mod = mtd_div_by_ws(mtd->erasesize, mtd);
6094be4e03eSBoris BREZILLON 
6104be4e03eSBoris BREZILLON 	if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
6114be4e03eSBoris BREZILLON 		mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
6124be4e03eSBoris BREZILLON 
6134be4e03eSBoris BREZILLON 	if (ecc) {
6144be4e03eSBoris BREZILLON 		if (mtd->ecc_step_size == 512)
6154be4e03eSBoris BREZILLON 			seeds = sunxi_nfc_randomizer_ecc512_seeds;
6164be4e03eSBoris BREZILLON 		else
6174be4e03eSBoris BREZILLON 			seeds = sunxi_nfc_randomizer_ecc1024_seeds;
6184be4e03eSBoris BREZILLON 	}
6194be4e03eSBoris BREZILLON 
6204be4e03eSBoris BREZILLON 	return seeds[page % mod];
6214be4e03eSBoris BREZILLON }
6224be4e03eSBoris BREZILLON 
623cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page,
624cde567e3SBoris Brezillon 					bool ecc)
6254be4e03eSBoris BREZILLON {
6264be4e03eSBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
6274be4e03eSBoris BREZILLON 	u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
6284be4e03eSBoris BREZILLON 	u16 state;
6294be4e03eSBoris BREZILLON 
6304be4e03eSBoris BREZILLON 	if (!(nand->options & NAND_NEED_SCRAMBLING))
6314be4e03eSBoris BREZILLON 		return;
6324be4e03eSBoris BREZILLON 
6334be4e03eSBoris BREZILLON 	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
634cde567e3SBoris Brezillon 	state = sunxi_nfc_randomizer_state(nand, page, ecc);
6354be4e03eSBoris BREZILLON 	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
6364be4e03eSBoris BREZILLON 	writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
6374be4e03eSBoris BREZILLON }
6384be4e03eSBoris BREZILLON 
639cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_enable(struct nand_chip *nand)
6404be4e03eSBoris BREZILLON {
6414be4e03eSBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
6424be4e03eSBoris BREZILLON 
6434be4e03eSBoris BREZILLON 	if (!(nand->options & NAND_NEED_SCRAMBLING))
6444be4e03eSBoris BREZILLON 		return;
6454be4e03eSBoris BREZILLON 
6464be4e03eSBoris BREZILLON 	writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
6474be4e03eSBoris BREZILLON 	       nfc->regs + NFC_REG_ECC_CTL);
6484be4e03eSBoris BREZILLON }
6494be4e03eSBoris BREZILLON 
650cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_disable(struct nand_chip *nand)
6514be4e03eSBoris BREZILLON {
6524be4e03eSBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
6534be4e03eSBoris BREZILLON 
6544be4e03eSBoris BREZILLON 	if (!(nand->options & NAND_NEED_SCRAMBLING))
6554be4e03eSBoris BREZILLON 		return;
6564be4e03eSBoris BREZILLON 
6574be4e03eSBoris BREZILLON 	writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
6584be4e03eSBoris BREZILLON 	       nfc->regs + NFC_REG_ECC_CTL);
6594be4e03eSBoris BREZILLON }
6604be4e03eSBoris BREZILLON 
661cde567e3SBoris Brezillon static void sunxi_nfc_randomize_bbm(struct nand_chip *nand, int page, u8 *bbm)
6624be4e03eSBoris BREZILLON {
663cde567e3SBoris Brezillon 	u16 state = sunxi_nfc_randomizer_state(nand, page, true);
6644be4e03eSBoris BREZILLON 
6654be4e03eSBoris BREZILLON 	bbm[0] ^= state;
6664be4e03eSBoris BREZILLON 	bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
6674be4e03eSBoris BREZILLON }
6684be4e03eSBoris BREZILLON 
669cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_write_buf(struct nand_chip *nand,
6704be4e03eSBoris BREZILLON 					   const uint8_t *buf, int len,
6714be4e03eSBoris BREZILLON 					   bool ecc, int page)
6724be4e03eSBoris BREZILLON {
673cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, ecc);
674cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
675cde567e3SBoris Brezillon 	sunxi_nfc_write_buf(nand, buf, len);
676cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
6774be4e03eSBoris BREZILLON }
6784be4e03eSBoris BREZILLON 
679cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf,
6804be4e03eSBoris BREZILLON 					  int len, bool ecc, int page)
6814be4e03eSBoris BREZILLON {
682cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, ecc);
683cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
684cde567e3SBoris Brezillon 	sunxi_nfc_read_buf(nand, buf, len);
685cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
6864be4e03eSBoris BREZILLON }
6874be4e03eSBoris BREZILLON 
688cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand)
689c9118eceSBoris BREZILLON {
690cbd87780SMiquel Raynal 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
691c9118eceSBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
692c9118eceSBoris BREZILLON 	u32 ecc_ctl;
693c9118eceSBoris BREZILLON 
694c9118eceSBoris BREZILLON 	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
695c9118eceSBoris BREZILLON 	ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE |
696c9118eceSBoris BREZILLON 		     NFC_ECC_BLOCK_SIZE_MSK);
697cbd87780SMiquel Raynal 	ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc->mode) |
698cbd87780SMiquel Raynal 		   NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE;
699c9118eceSBoris BREZILLON 
700f59dab8dSBoris Brezillon 	if (nand->ecc.size == 512)
701f59dab8dSBoris Brezillon 		ecc_ctl |= NFC_ECC_BLOCK_512;
702f59dab8dSBoris Brezillon 
703c9118eceSBoris BREZILLON 	writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
704c9118eceSBoris BREZILLON }
705c9118eceSBoris BREZILLON 
706cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand)
707c9118eceSBoris BREZILLON {
708c9118eceSBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
709c9118eceSBoris BREZILLON 
710c9118eceSBoris BREZILLON 	writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
711c9118eceSBoris BREZILLON 	       nfc->regs + NFC_REG_ECC_CTL);
712c9118eceSBoris BREZILLON }
713c9118eceSBoris BREZILLON 
714f363e0faSBoris BREZILLON static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
715f363e0faSBoris BREZILLON {
716f363e0faSBoris BREZILLON 	buf[0] = user_data;
717f363e0faSBoris BREZILLON 	buf[1] = user_data >> 8;
718f363e0faSBoris BREZILLON 	buf[2] = user_data >> 16;
719f363e0faSBoris BREZILLON 	buf[3] = user_data >> 24;
720f363e0faSBoris BREZILLON }
721f363e0faSBoris BREZILLON 
722cc6822fbSBoris Brezillon static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
723cc6822fbSBoris Brezillon {
724cc6822fbSBoris Brezillon 	return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
725cc6822fbSBoris Brezillon }
726cc6822fbSBoris Brezillon 
727cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
728cc6822fbSBoris Brezillon 						int step, bool bbm, int page)
729cc6822fbSBoris Brezillon {
730cc6822fbSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
731cc6822fbSBoris Brezillon 
732cc6822fbSBoris Brezillon 	sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
733cc6822fbSBoris Brezillon 				   oob);
734cc6822fbSBoris Brezillon 
735cc6822fbSBoris Brezillon 	/* De-randomize the Bad Block Marker. */
736cc6822fbSBoris Brezillon 	if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
737cde567e3SBoris Brezillon 		sunxi_nfc_randomize_bbm(nand, page, oob);
738cc6822fbSBoris Brezillon }
739cc6822fbSBoris Brezillon 
740cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
741cc6822fbSBoris Brezillon 						const u8 *oob, int step,
742cc6822fbSBoris Brezillon 						bool bbm, int page)
743cc6822fbSBoris Brezillon {
744cc6822fbSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
745cc6822fbSBoris Brezillon 	u8 user_data[4];
746cc6822fbSBoris Brezillon 
747cc6822fbSBoris Brezillon 	/* Randomize the Bad Block Marker. */
748cc6822fbSBoris Brezillon 	if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
749cc6822fbSBoris Brezillon 		memcpy(user_data, oob, sizeof(user_data));
750cde567e3SBoris Brezillon 		sunxi_nfc_randomize_bbm(nand, page, user_data);
751cc6822fbSBoris Brezillon 		oob = user_data;
752cc6822fbSBoris Brezillon 	}
753cc6822fbSBoris Brezillon 
754cc6822fbSBoris Brezillon 	writel(sunxi_nfc_buf_to_user_data(oob),
755cc6822fbSBoris Brezillon 	       nfc->regs + NFC_REG_USER_DATA(step));
756cc6822fbSBoris Brezillon }
757cc6822fbSBoris Brezillon 
758cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
759cc6822fbSBoris Brezillon 					  unsigned int *max_bitflips, int ret)
760cc6822fbSBoris Brezillon {
761cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
762cde567e3SBoris Brezillon 
763cc6822fbSBoris Brezillon 	if (ret < 0) {
764cc6822fbSBoris Brezillon 		mtd->ecc_stats.failed++;
765cc6822fbSBoris Brezillon 	} else {
766cc6822fbSBoris Brezillon 		mtd->ecc_stats.corrected += ret;
767cc6822fbSBoris Brezillon 		*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
768cc6822fbSBoris Brezillon 	}
769cc6822fbSBoris Brezillon }
770cc6822fbSBoris Brezillon 
771cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
772614049a8SBoris Brezillon 				    int step, u32 status, bool *erased)
773cc6822fbSBoris Brezillon {
774cc6822fbSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
775cc6822fbSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
776614049a8SBoris Brezillon 	u32 tmp;
777cc6822fbSBoris Brezillon 
778cc6822fbSBoris Brezillon 	*erased = false;
779cc6822fbSBoris Brezillon 
780cc6822fbSBoris Brezillon 	if (status & NFC_ECC_ERR(step))
781cc6822fbSBoris Brezillon 		return -EBADMSG;
782cc6822fbSBoris Brezillon 
783cc6822fbSBoris Brezillon 	if (status & NFC_ECC_PAT_FOUND(step)) {
784cc6822fbSBoris Brezillon 		u8 pattern;
785cc6822fbSBoris Brezillon 
786cc6822fbSBoris Brezillon 		if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
787cc6822fbSBoris Brezillon 			pattern = 0x0;
788cc6822fbSBoris Brezillon 		} else {
789cc6822fbSBoris Brezillon 			pattern = 0xff;
790cc6822fbSBoris Brezillon 			*erased = true;
791cc6822fbSBoris Brezillon 		}
792cc6822fbSBoris Brezillon 
793cc6822fbSBoris Brezillon 		if (data)
794cc6822fbSBoris Brezillon 			memset(data, pattern, ecc->size);
795cc6822fbSBoris Brezillon 
796cc6822fbSBoris Brezillon 		if (oob)
797cc6822fbSBoris Brezillon 			memset(oob, pattern, ecc->bytes + 4);
798cc6822fbSBoris Brezillon 
799cc6822fbSBoris Brezillon 		return 0;
800cc6822fbSBoris Brezillon 	}
801cc6822fbSBoris Brezillon 
802cc6822fbSBoris Brezillon 	tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
803cc6822fbSBoris Brezillon 
804cc6822fbSBoris Brezillon 	return NFC_ECC_ERR_CNT(step, tmp);
805cc6822fbSBoris Brezillon }
806cc6822fbSBoris Brezillon 
807cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
808913821bdSBoris BREZILLON 				       u8 *data, int data_off,
809913821bdSBoris BREZILLON 				       u8 *oob, int oob_off,
810913821bdSBoris BREZILLON 				       int *cur_off,
8114be4e03eSBoris BREZILLON 				       unsigned int *max_bitflips,
812828dec15SBoris Brezillon 				       bool bbm, bool oob_required, int page)
813913821bdSBoris BREZILLON {
814913821bdSBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
815913821bdSBoris BREZILLON 	struct nand_ecc_ctrl *ecc = &nand->ecc;
8164be4e03eSBoris BREZILLON 	int raw_mode = 0;
817cc6822fbSBoris Brezillon 	bool erased;
818913821bdSBoris BREZILLON 	int ret;
819913821bdSBoris BREZILLON 
820913821bdSBoris BREZILLON 	if (*cur_off != data_off)
82197d90da8SBoris Brezillon 		nand_change_read_column_op(nand, data_off, NULL, 0, false);
822913821bdSBoris BREZILLON 
823cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_read_buf(nand, NULL, ecc->size, false, page);
824913821bdSBoris BREZILLON 
82574eb9ff5SBoris BREZILLON 	if (data_off + ecc->size != oob_off)
82697d90da8SBoris Brezillon 		nand_change_read_column_op(nand, oob_off, NULL, 0, false);
827913821bdSBoris BREZILLON 
828913821bdSBoris BREZILLON 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
829913821bdSBoris BREZILLON 	if (ret)
830913821bdSBoris BREZILLON 		return ret;
831913821bdSBoris BREZILLON 
832cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
833913821bdSBoris BREZILLON 	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
834913821bdSBoris BREZILLON 	       nfc->regs + NFC_REG_CMD);
835913821bdSBoris BREZILLON 
8368de15e1fSBoris Brezillon 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
837cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
838913821bdSBoris BREZILLON 	if (ret)
839913821bdSBoris BREZILLON 		return ret;
840913821bdSBoris BREZILLON 
8414be4e03eSBoris BREZILLON 	*cur_off = oob_off + ecc->bytes + 4;
8424be4e03eSBoris BREZILLON 
843cde567e3SBoris Brezillon 	ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0,
844614049a8SBoris Brezillon 				       readl(nfc->regs + NFC_REG_ECC_ST),
845828dec15SBoris Brezillon 				       &erased);
846cc6822fbSBoris Brezillon 	if (erased)
8474be4e03eSBoris BREZILLON 		return 1;
8484be4e03eSBoris BREZILLON 
849cc6822fbSBoris Brezillon 	if (ret < 0) {
8504be4e03eSBoris BREZILLON 		/*
8514be4e03eSBoris BREZILLON 		 * Re-read the data with the randomizer disabled to identify
8524be4e03eSBoris BREZILLON 		 * bitflips in erased pages.
8534be4e03eSBoris BREZILLON 		 */
85497d90da8SBoris Brezillon 		if (nand->options & NAND_NEED_SCRAMBLING)
85597d90da8SBoris Brezillon 			nand_change_read_column_op(nand, data_off, data,
85697d90da8SBoris Brezillon 						   ecc->size, false);
85797d90da8SBoris Brezillon 		else
858cc6822fbSBoris Brezillon 			memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
859cc6822fbSBoris Brezillon 				      ecc->size);
860cc6822fbSBoris Brezillon 
86197d90da8SBoris Brezillon 		nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4,
86297d90da8SBoris Brezillon 					   false);
8634be4e03eSBoris BREZILLON 
864146b503eSBoris BREZILLON 		ret = nand_check_erased_ecc_chunk(data,	ecc->size,
865146b503eSBoris BREZILLON 						  oob, ecc->bytes + 4,
866146b503eSBoris BREZILLON 						  NULL, 0, ecc->strength);
8674be4e03eSBoris BREZILLON 		if (ret >= 0)
8684be4e03eSBoris BREZILLON 			raw_mode = 1;
869f363e0faSBoris BREZILLON 	} else {
870cc6822fbSBoris Brezillon 		memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
8714be4e03eSBoris BREZILLON 
872828dec15SBoris Brezillon 		if (oob_required) {
87397d90da8SBoris Brezillon 			nand_change_read_column_op(nand, oob_off, NULL, 0,
87497d90da8SBoris Brezillon 						   false);
875cde567e3SBoris Brezillon 			sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4,
876cc6822fbSBoris Brezillon 						      true, page);
877cc6822fbSBoris Brezillon 
878cde567e3SBoris Brezillon 			sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0,
879cc6822fbSBoris Brezillon 							    bbm, page);
880f363e0faSBoris BREZILLON 		}
881828dec15SBoris Brezillon 	}
882913821bdSBoris BREZILLON 
883cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_update_stats(nand, max_bitflips, ret);
884913821bdSBoris BREZILLON 
8854be4e03eSBoris BREZILLON 	return raw_mode;
886913821bdSBoris BREZILLON }
887913821bdSBoris BREZILLON 
888cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand,
8894be4e03eSBoris BREZILLON 					    u8 *oob, int *cur_off,
8904be4e03eSBoris BREZILLON 					    bool randomize, int page)
89135d0e24fSBoris BREZILLON {
892cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
89335d0e24fSBoris BREZILLON 	struct nand_ecc_ctrl *ecc = &nand->ecc;
89435d0e24fSBoris BREZILLON 	int offset = ((ecc->bytes + 4) * ecc->steps);
89535d0e24fSBoris BREZILLON 	int len = mtd->oobsize - offset;
89635d0e24fSBoris BREZILLON 
89735d0e24fSBoris BREZILLON 	if (len <= 0)
89835d0e24fSBoris BREZILLON 		return;
89935d0e24fSBoris BREZILLON 
900c4f3ef2cSBoris Brezillon 	if (!cur_off || *cur_off != offset)
90197d90da8SBoris Brezillon 		nand_change_read_column_op(nand, mtd->writesize, NULL, 0,
90297d90da8SBoris Brezillon 					   false);
90335d0e24fSBoris BREZILLON 
9044be4e03eSBoris BREZILLON 	if (!randomize)
9057e534323SBoris Brezillon 		sunxi_nfc_read_buf(nand, oob + offset, len);
9064be4e03eSBoris BREZILLON 	else
907cde567e3SBoris Brezillon 		sunxi_nfc_randomizer_read_buf(nand, oob + offset, len,
9084be4e03eSBoris BREZILLON 					      false, page);
90935d0e24fSBoris BREZILLON 
910c4f3ef2cSBoris Brezillon 	if (cur_off)
91135d0e24fSBoris BREZILLON 		*cur_off = mtd->oobsize + mtd->writesize;
91235d0e24fSBoris BREZILLON }
91335d0e24fSBoris BREZILLON 
914cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf,
915614049a8SBoris Brezillon 					    int oob_required, int page,
916614049a8SBoris Brezillon 					    int nchunks)
917614049a8SBoris Brezillon {
918614049a8SBoris Brezillon 	bool randomized = nand->options & NAND_NEED_SCRAMBLING;
919614049a8SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
920cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
921614049a8SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
922614049a8SBoris Brezillon 	unsigned int max_bitflips = 0;
923614049a8SBoris Brezillon 	int ret, i, raw_mode = 0;
924614049a8SBoris Brezillon 	struct scatterlist sg;
925910ef7a4SManuel Dipolt 	u32 status, wait;
926614049a8SBoris Brezillon 
927614049a8SBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
928614049a8SBoris Brezillon 	if (ret)
929614049a8SBoris Brezillon 		return ret;
930614049a8SBoris Brezillon 
931cde567e3SBoris Brezillon 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks,
932614049a8SBoris Brezillon 				       DMA_FROM_DEVICE, &sg);
933614049a8SBoris Brezillon 	if (ret)
934614049a8SBoris Brezillon 		return ret;
935614049a8SBoris Brezillon 
936cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
937cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, false);
938cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
939614049a8SBoris Brezillon 
940614049a8SBoris Brezillon 	writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
941614049a8SBoris Brezillon 	       NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
942614049a8SBoris Brezillon 
943910ef7a4SManuel Dipolt 	wait = NFC_CMD_INT_FLAG;
944910ef7a4SManuel Dipolt 
945910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma)
946910ef7a4SManuel Dipolt 		wait |= NFC_DMA_INT_FLAG;
947910ef7a4SManuel Dipolt 	else
948614049a8SBoris Brezillon 		dma_async_issue_pending(nfc->dmac);
949614049a8SBoris Brezillon 
950614049a8SBoris Brezillon 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
951614049a8SBoris Brezillon 	       nfc->regs + NFC_REG_CMD);
952614049a8SBoris Brezillon 
953910ef7a4SManuel Dipolt 	ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
954910ef7a4SManuel Dipolt 	if (ret && !nfc->caps->has_mdma)
955614049a8SBoris Brezillon 		dmaengine_terminate_all(nfc->dmac);
956614049a8SBoris Brezillon 
957cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
958cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
959614049a8SBoris Brezillon 
960cde567e3SBoris Brezillon 	sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg);
961614049a8SBoris Brezillon 
962614049a8SBoris Brezillon 	if (ret)
963614049a8SBoris Brezillon 		return ret;
964614049a8SBoris Brezillon 
965614049a8SBoris Brezillon 	status = readl(nfc->regs + NFC_REG_ECC_ST);
966614049a8SBoris Brezillon 
967614049a8SBoris Brezillon 	for (i = 0; i < nchunks; i++) {
968614049a8SBoris Brezillon 		int data_off = i * ecc->size;
969614049a8SBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
970614049a8SBoris Brezillon 		u8 *data = buf + data_off;
971614049a8SBoris Brezillon 		u8 *oob = nand->oob_poi + oob_off;
972614049a8SBoris Brezillon 		bool erased;
973614049a8SBoris Brezillon 
974cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL,
975614049a8SBoris Brezillon 					       oob_required ? oob : NULL,
976614049a8SBoris Brezillon 					       i, status, &erased);
977614049a8SBoris Brezillon 
978614049a8SBoris Brezillon 		/* ECC errors are handled in the second loop. */
979614049a8SBoris Brezillon 		if (ret < 0)
980614049a8SBoris Brezillon 			continue;
981614049a8SBoris Brezillon 
982614049a8SBoris Brezillon 		if (oob_required && !erased) {
983614049a8SBoris Brezillon 			/* TODO: use DMA to retrieve OOB */
98497d90da8SBoris Brezillon 			nand_change_read_column_op(nand,
98597d90da8SBoris Brezillon 						   mtd->writesize + oob_off,
98697d90da8SBoris Brezillon 						   oob, ecc->bytes + 4, false);
987614049a8SBoris Brezillon 
988cde567e3SBoris Brezillon 			sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i,
989614049a8SBoris Brezillon 							    !i, page);
990614049a8SBoris Brezillon 		}
991614049a8SBoris Brezillon 
992614049a8SBoris Brezillon 		if (erased)
993614049a8SBoris Brezillon 			raw_mode = 1;
994614049a8SBoris Brezillon 
995cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
996614049a8SBoris Brezillon 	}
997614049a8SBoris Brezillon 
998614049a8SBoris Brezillon 	if (status & NFC_ECC_ERR_MSK) {
999614049a8SBoris Brezillon 		for (i = 0; i < nchunks; i++) {
1000614049a8SBoris Brezillon 			int data_off = i * ecc->size;
1001614049a8SBoris Brezillon 			int oob_off = i * (ecc->bytes + 4);
1002614049a8SBoris Brezillon 			u8 *data = buf + data_off;
1003614049a8SBoris Brezillon 			u8 *oob = nand->oob_poi + oob_off;
1004614049a8SBoris Brezillon 
1005614049a8SBoris Brezillon 			if (!(status & NFC_ECC_ERR(i)))
1006614049a8SBoris Brezillon 				continue;
1007614049a8SBoris Brezillon 
1008614049a8SBoris Brezillon 			/*
1009614049a8SBoris Brezillon 			 * Re-read the data with the randomizer disabled to
1010614049a8SBoris Brezillon 			 * identify bitflips in erased pages.
101197d90da8SBoris Brezillon 			 * TODO: use DMA to read page in raw mode
1012614049a8SBoris Brezillon 			 */
101397d90da8SBoris Brezillon 			if (randomized)
101497d90da8SBoris Brezillon 				nand_change_read_column_op(nand, data_off,
101597d90da8SBoris Brezillon 							   data, ecc->size,
101697d90da8SBoris Brezillon 							   false);
1017614049a8SBoris Brezillon 
1018614049a8SBoris Brezillon 			/* TODO: use DMA to retrieve OOB */
101997d90da8SBoris Brezillon 			nand_change_read_column_op(nand,
102097d90da8SBoris Brezillon 						   mtd->writesize + oob_off,
102197d90da8SBoris Brezillon 						   oob, ecc->bytes + 4, false);
1022614049a8SBoris Brezillon 
1023614049a8SBoris Brezillon 			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
1024614049a8SBoris Brezillon 							  oob, ecc->bytes + 4,
1025614049a8SBoris Brezillon 							  NULL, 0,
1026614049a8SBoris Brezillon 							  ecc->strength);
1027614049a8SBoris Brezillon 			if (ret >= 0)
1028614049a8SBoris Brezillon 				raw_mode = 1;
1029614049a8SBoris Brezillon 
1030cde567e3SBoris Brezillon 			sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
1031614049a8SBoris Brezillon 		}
1032614049a8SBoris Brezillon 	}
1033614049a8SBoris Brezillon 
1034614049a8SBoris Brezillon 	if (oob_required)
1035cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi,
1036614049a8SBoris Brezillon 						NULL, !raw_mode,
1037614049a8SBoris Brezillon 						page);
1038614049a8SBoris Brezillon 
1039614049a8SBoris Brezillon 	return max_bitflips;
1040614049a8SBoris Brezillon }
1041614049a8SBoris Brezillon 
1042cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
1043913821bdSBoris BREZILLON 					const u8 *data, int data_off,
1044913821bdSBoris BREZILLON 					const u8 *oob, int oob_off,
10454be4e03eSBoris BREZILLON 					int *cur_off, bool bbm,
10464be4e03eSBoris BREZILLON 					int page)
1047913821bdSBoris BREZILLON {
1048913821bdSBoris BREZILLON 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1049913821bdSBoris BREZILLON 	struct nand_ecc_ctrl *ecc = &nand->ecc;
1050913821bdSBoris BREZILLON 	int ret;
1051913821bdSBoris BREZILLON 
1052913821bdSBoris BREZILLON 	if (data_off != *cur_off)
105397d90da8SBoris Brezillon 		nand_change_write_column_op(nand, data_off, NULL, 0, false);
1054913821bdSBoris BREZILLON 
1055cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_write_buf(nand, data, ecc->size, false, page);
1056913821bdSBoris BREZILLON 
105774eb9ff5SBoris BREZILLON 	if (data_off + ecc->size != oob_off)
105897d90da8SBoris Brezillon 		nand_change_write_column_op(nand, oob_off, NULL, 0, false);
1059913821bdSBoris BREZILLON 
1060913821bdSBoris BREZILLON 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
1061913821bdSBoris BREZILLON 	if (ret)
1062913821bdSBoris BREZILLON 		return ret;
1063913821bdSBoris BREZILLON 
1064cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
1065cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
1066cc6822fbSBoris Brezillon 
1067913821bdSBoris BREZILLON 	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
1068913821bdSBoris BREZILLON 	       NFC_ACCESS_DIR | NFC_ECC_OP,
1069913821bdSBoris BREZILLON 	       nfc->regs + NFC_REG_CMD);
1070913821bdSBoris BREZILLON 
10718de15e1fSBoris Brezillon 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
1072cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
1073913821bdSBoris BREZILLON 	if (ret)
1074913821bdSBoris BREZILLON 		return ret;
1075913821bdSBoris BREZILLON 
1076913821bdSBoris BREZILLON 	*cur_off = oob_off + ecc->bytes + 4;
1077913821bdSBoris BREZILLON 
1078913821bdSBoris BREZILLON 	return 0;
1079913821bdSBoris BREZILLON }
1080913821bdSBoris BREZILLON 
1081cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand,
10824be4e03eSBoris BREZILLON 					     u8 *oob, int *cur_off,
10834be4e03eSBoris BREZILLON 					     int page)
108435d0e24fSBoris BREZILLON {
1085cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
108635d0e24fSBoris BREZILLON 	struct nand_ecc_ctrl *ecc = &nand->ecc;
108735d0e24fSBoris BREZILLON 	int offset = ((ecc->bytes + 4) * ecc->steps);
108835d0e24fSBoris BREZILLON 	int len = mtd->oobsize - offset;
108935d0e24fSBoris BREZILLON 
109035d0e24fSBoris BREZILLON 	if (len <= 0)
109135d0e24fSBoris BREZILLON 		return;
109235d0e24fSBoris BREZILLON 
1093c4f3ef2cSBoris Brezillon 	if (!cur_off || *cur_off != offset)
109497d90da8SBoris Brezillon 		nand_change_write_column_op(nand, offset + mtd->writesize,
109597d90da8SBoris Brezillon 					    NULL, 0, false);
109635d0e24fSBoris BREZILLON 
1097cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_write_buf(nand, oob + offset, len, false, page);
109835d0e24fSBoris BREZILLON 
1099c4f3ef2cSBoris Brezillon 	if (cur_off)
110035d0e24fSBoris BREZILLON 		*cur_off = mtd->oobsize + mtd->writesize;
110135d0e24fSBoris BREZILLON }
110235d0e24fSBoris BREZILLON 
1103a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
11041fef62c1SBoris BREZILLON 				      int oob_required, int page)
11051fef62c1SBoris BREZILLON {
1106a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1107a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
11081fef62c1SBoris BREZILLON 	unsigned int max_bitflips = 0;
1109b462551cSBoris BREZILLON 	int ret, i, cur_off = 0;
11104be4e03eSBoris BREZILLON 	bool raw_mode = false;
11111fef62c1SBoris BREZILLON 
1112df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1113df505799SBoris Brezillon 
1114a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
111525f815f6SBoris Brezillon 
1116cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
11171fef62c1SBoris BREZILLON 
11181fef62c1SBoris BREZILLON 	for (i = 0; i < ecc->steps; i++) {
1119b462551cSBoris BREZILLON 		int data_off = i * ecc->size;
1120b462551cSBoris BREZILLON 		int oob_off = i * (ecc->bytes + 4);
1121b462551cSBoris BREZILLON 		u8 *data = buf + data_off;
1122a55abb36SBoris Brezillon 		u8 *oob = nand->oob_poi + oob_off;
11231fef62c1SBoris BREZILLON 
1124cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob,
1125b462551cSBoris BREZILLON 						  oob_off + mtd->writesize,
11264be4e03eSBoris BREZILLON 						  &cur_off, &max_bitflips,
1127828dec15SBoris Brezillon 						  !i, oob_required, page);
11284be4e03eSBoris BREZILLON 		if (ret < 0)
11291fef62c1SBoris BREZILLON 			return ret;
11304be4e03eSBoris BREZILLON 		else if (ret)
11314be4e03eSBoris BREZILLON 			raw_mode = true;
11321fef62c1SBoris BREZILLON 	}
11331fef62c1SBoris BREZILLON 
113435d0e24fSBoris BREZILLON 	if (oob_required)
1135cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, &cur_off,
11364be4e03eSBoris BREZILLON 						!raw_mode, page);
11371fef62c1SBoris BREZILLON 
1138cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
11391fef62c1SBoris BREZILLON 
11401fef62c1SBoris BREZILLON 	return max_bitflips;
11411fef62c1SBoris BREZILLON }
11421fef62c1SBoris BREZILLON 
1143a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf,
1144614049a8SBoris Brezillon 					  int oob_required, int page)
1145614049a8SBoris Brezillon {
1146614049a8SBoris Brezillon 	int ret;
1147614049a8SBoris Brezillon 
1148df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1149df505799SBoris Brezillon 
1150a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
115125f815f6SBoris Brezillon 
1152cde567e3SBoris Brezillon 	ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page,
1153a55abb36SBoris Brezillon 					       nand->ecc.steps);
1154614049a8SBoris Brezillon 	if (ret >= 0)
1155614049a8SBoris Brezillon 		return ret;
1156614049a8SBoris Brezillon 
1157614049a8SBoris Brezillon 	/* Fallback to PIO mode */
1158a55abb36SBoris Brezillon 	return sunxi_nfc_hw_ecc_read_page(nand, buf, oob_required, page);
1159614049a8SBoris Brezillon }
1160614049a8SBoris Brezillon 
1161a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
1162fe82ccefSBoris Brezillon 					 u32 data_offs, u32 readlen,
1163fe82ccefSBoris Brezillon 					 u8 *bufpoi, int page)
1164fe82ccefSBoris Brezillon {
1165a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1166a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
1167fe82ccefSBoris Brezillon 	int ret, i, cur_off = 0;
1168fe82ccefSBoris Brezillon 	unsigned int max_bitflips = 0;
1169fe82ccefSBoris Brezillon 
1170df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1171df505799SBoris Brezillon 
1172a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
117325f815f6SBoris Brezillon 
1174cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
1175fe82ccefSBoris Brezillon 
1176fe82ccefSBoris Brezillon 	for (i = data_offs / ecc->size;
1177fe82ccefSBoris Brezillon 	     i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
1178fe82ccefSBoris Brezillon 		int data_off = i * ecc->size;
1179fe82ccefSBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
1180fe82ccefSBoris Brezillon 		u8 *data = bufpoi + data_off;
1181a55abb36SBoris Brezillon 		u8 *oob = nand->oob_poi + oob_off;
1182fe82ccefSBoris Brezillon 
1183cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off,
1184fe82ccefSBoris Brezillon 						  oob,
1185fe82ccefSBoris Brezillon 						  oob_off + mtd->writesize,
1186828dec15SBoris Brezillon 						  &cur_off, &max_bitflips, !i,
1187828dec15SBoris Brezillon 						  false, page);
1188fe82ccefSBoris Brezillon 		if (ret < 0)
1189fe82ccefSBoris Brezillon 			return ret;
1190fe82ccefSBoris Brezillon 	}
1191fe82ccefSBoris Brezillon 
1192cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
1193fe82ccefSBoris Brezillon 
1194fe82ccefSBoris Brezillon 	return max_bitflips;
1195fe82ccefSBoris Brezillon }
1196fe82ccefSBoris Brezillon 
1197a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand,
1198614049a8SBoris Brezillon 					     u32 data_offs, u32 readlen,
1199614049a8SBoris Brezillon 					     u8 *buf, int page)
1200614049a8SBoris Brezillon {
1201a55abb36SBoris Brezillon 	int nchunks = DIV_ROUND_UP(data_offs + readlen, nand->ecc.size);
1202614049a8SBoris Brezillon 	int ret;
1203614049a8SBoris Brezillon 
1204df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1205df505799SBoris Brezillon 
1206a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
120725f815f6SBoris Brezillon 
1208cde567e3SBoris Brezillon 	ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks);
1209614049a8SBoris Brezillon 	if (ret >= 0)
1210614049a8SBoris Brezillon 		return ret;
1211614049a8SBoris Brezillon 
1212614049a8SBoris Brezillon 	/* Fallback to PIO mode */
1213a55abb36SBoris Brezillon 	return sunxi_nfc_hw_ecc_read_subpage(nand, data_offs, readlen,
1214614049a8SBoris Brezillon 					     buf, page);
1215614049a8SBoris Brezillon }
1216614049a8SBoris Brezillon 
1217a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
121845aaeff9SBoris BREZILLON 				       const uint8_t *buf, int oob_required,
121945aaeff9SBoris BREZILLON 				       int page)
12201fef62c1SBoris BREZILLON {
1221a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1222a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
1223b462551cSBoris BREZILLON 	int ret, i, cur_off = 0;
12241fef62c1SBoris BREZILLON 
1225df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1226df505799SBoris Brezillon 
1227a55abb36SBoris Brezillon 	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
122825f815f6SBoris Brezillon 
1229cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
12301fef62c1SBoris BREZILLON 
12311fef62c1SBoris BREZILLON 	for (i = 0; i < ecc->steps; i++) {
1232b462551cSBoris BREZILLON 		int data_off = i * ecc->size;
1233b462551cSBoris BREZILLON 		int oob_off = i * (ecc->bytes + 4);
1234b462551cSBoris BREZILLON 		const u8 *data = buf + data_off;
1235a55abb36SBoris Brezillon 		const u8 *oob = nand->oob_poi + oob_off;
12361fef62c1SBoris BREZILLON 
1237cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
1238b462551cSBoris BREZILLON 						   oob_off + mtd->writesize,
12394be4e03eSBoris BREZILLON 						   &cur_off, !i, page);
12401fef62c1SBoris BREZILLON 		if (ret)
12411fef62c1SBoris BREZILLON 			return ret;
12421fef62c1SBoris BREZILLON 	}
12431fef62c1SBoris BREZILLON 
1244a55abb36SBoris Brezillon 	if (oob_required || (nand->options & NAND_NEED_SCRAMBLING))
1245cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi,
12464be4e03eSBoris BREZILLON 						 &cur_off, page);
12471fef62c1SBoris BREZILLON 
1248cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
12491fef62c1SBoris BREZILLON 
1250a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
12511fef62c1SBoris BREZILLON }
12521fef62c1SBoris BREZILLON 
1253a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
125403b1d11aSBoris Brezillon 					  u32 data_offs, u32 data_len,
125503b1d11aSBoris Brezillon 					  const u8 *buf, int oob_required,
125603b1d11aSBoris Brezillon 					  int page)
125703b1d11aSBoris Brezillon {
1258a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1259a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
126003b1d11aSBoris Brezillon 	int ret, i, cur_off = 0;
126103b1d11aSBoris Brezillon 
1262df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1263df505799SBoris Brezillon 
1264a55abb36SBoris Brezillon 	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
126525f815f6SBoris Brezillon 
1266cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
126703b1d11aSBoris Brezillon 
126803b1d11aSBoris Brezillon 	for (i = data_offs / ecc->size;
126903b1d11aSBoris Brezillon 	     i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
127003b1d11aSBoris Brezillon 		int data_off = i * ecc->size;
127103b1d11aSBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
127203b1d11aSBoris Brezillon 		const u8 *data = buf + data_off;
1273a55abb36SBoris Brezillon 		const u8 *oob = nand->oob_poi + oob_off;
127403b1d11aSBoris Brezillon 
1275cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
127603b1d11aSBoris Brezillon 						   oob_off + mtd->writesize,
127703b1d11aSBoris Brezillon 						   &cur_off, !i, page);
127803b1d11aSBoris Brezillon 		if (ret)
127903b1d11aSBoris Brezillon 			return ret;
128003b1d11aSBoris Brezillon 	}
128103b1d11aSBoris Brezillon 
1282cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
128303b1d11aSBoris Brezillon 
1284a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
128503b1d11aSBoris Brezillon }
128603b1d11aSBoris Brezillon 
1287a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
1288614049a8SBoris Brezillon 					   const u8 *buf,
1289614049a8SBoris Brezillon 					   int oob_required,
1290614049a8SBoris Brezillon 					   int page)
1291614049a8SBoris Brezillon {
1292614049a8SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1293614049a8SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
1294614049a8SBoris Brezillon 	struct scatterlist sg;
1295910ef7a4SManuel Dipolt 	u32 wait;
1296614049a8SBoris Brezillon 	int ret, i;
1297614049a8SBoris Brezillon 
1298df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1299df505799SBoris Brezillon 
1300614049a8SBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
1301614049a8SBoris Brezillon 	if (ret)
1302614049a8SBoris Brezillon 		return ret;
1303614049a8SBoris Brezillon 
1304cde567e3SBoris Brezillon 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps,
1305614049a8SBoris Brezillon 				       DMA_TO_DEVICE, &sg);
1306614049a8SBoris Brezillon 	if (ret)
1307614049a8SBoris Brezillon 		goto pio_fallback;
1308614049a8SBoris Brezillon 
1309614049a8SBoris Brezillon 	for (i = 0; i < ecc->steps; i++) {
1310614049a8SBoris Brezillon 		const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
1311614049a8SBoris Brezillon 
1312cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
1313614049a8SBoris Brezillon 	}
1314614049a8SBoris Brezillon 
1315a55abb36SBoris Brezillon 	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
131625f815f6SBoris Brezillon 
1317cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
1318cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, false);
1319cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
1320614049a8SBoris Brezillon 
1321614049a8SBoris Brezillon 	writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
132273277443SBoris Brezillon 	       nfc->regs + NFC_REG_WCMD_SET);
1323614049a8SBoris Brezillon 
1324910ef7a4SManuel Dipolt 	wait = NFC_CMD_INT_FLAG;
1325910ef7a4SManuel Dipolt 
1326910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma)
1327910ef7a4SManuel Dipolt 		wait |= NFC_DMA_INT_FLAG;
1328910ef7a4SManuel Dipolt 	else
1329614049a8SBoris Brezillon 		dma_async_issue_pending(nfc->dmac);
1330614049a8SBoris Brezillon 
1331614049a8SBoris Brezillon 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
1332614049a8SBoris Brezillon 	       NFC_DATA_TRANS | NFC_ACCESS_DIR,
1333614049a8SBoris Brezillon 	       nfc->regs + NFC_REG_CMD);
1334614049a8SBoris Brezillon 
1335910ef7a4SManuel Dipolt 	ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
1336910ef7a4SManuel Dipolt 	if (ret && !nfc->caps->has_mdma)
1337614049a8SBoris Brezillon 		dmaengine_terminate_all(nfc->dmac);
1338614049a8SBoris Brezillon 
1339cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
1340cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
1341614049a8SBoris Brezillon 
1342cde567e3SBoris Brezillon 	sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg);
1343614049a8SBoris Brezillon 
1344614049a8SBoris Brezillon 	if (ret)
1345614049a8SBoris Brezillon 		return ret;
1346614049a8SBoris Brezillon 
1347a55abb36SBoris Brezillon 	if (oob_required || (nand->options & NAND_NEED_SCRAMBLING))
1348614049a8SBoris Brezillon 		/* TODO: use DMA to transfer extra OOB bytes ? */
1349cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi,
1350614049a8SBoris Brezillon 						 NULL, page);
1351614049a8SBoris Brezillon 
1352a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
1353614049a8SBoris Brezillon 
1354614049a8SBoris Brezillon pio_fallback:
1355a55abb36SBoris Brezillon 	return sunxi_nfc_hw_ecc_write_page(nand, buf, oob_required, page);
1356614049a8SBoris Brezillon }
1357614049a8SBoris Brezillon 
1358a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page)
13591c1bdd6fSBoris Brezillon {
1360eeab7174SBoris Brezillon 	u8 *buf = nand_get_data_buf(nand);
13611c1bdd6fSBoris Brezillon 
1362eeab7174SBoris Brezillon 	return nand->ecc.read_page(nand, buf, 1, page);
13631c1bdd6fSBoris Brezillon }
13641c1bdd6fSBoris Brezillon 
1365a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
13661c1bdd6fSBoris Brezillon {
1367a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1368eeab7174SBoris Brezillon 	u8 *buf = nand_get_data_buf(nand);
136997d90da8SBoris Brezillon 	int ret;
13701c1bdd6fSBoris Brezillon 
1371eeab7174SBoris Brezillon 	memset(buf, 0xff, mtd->writesize);
1372eeab7174SBoris Brezillon 	ret = nand->ecc.write_page(nand, buf, 1, page);
13731c1bdd6fSBoris Brezillon 	if (ret)
13741c1bdd6fSBoris Brezillon 		return ret;
13751c1bdd6fSBoris Brezillon 
13761c1bdd6fSBoris Brezillon 	/* Send command to program the OOB data */
1377a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
13781c1bdd6fSBoris Brezillon }
13791c1bdd6fSBoris Brezillon 
13809c618292SRoy Spliet static const s32 tWB_lut[] = {6, 12, 16, 20};
13819c618292SRoy Spliet static const s32 tRHW_lut[] = {4, 8, 12, 20};
13829c618292SRoy Spliet 
13839c618292SRoy Spliet static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
13849c618292SRoy Spliet 		u32 clk_period)
13859c618292SRoy Spliet {
13869c618292SRoy Spliet 	u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
13879c618292SRoy Spliet 	int i;
13889c618292SRoy Spliet 
13899c618292SRoy Spliet 	for (i = 0; i < lut_size; i++) {
13909c618292SRoy Spliet 		if (clk_cycles <= lut[i])
13919c618292SRoy Spliet 			return i;
13929c618292SRoy Spliet 	}
13939c618292SRoy Spliet 
13949c618292SRoy Spliet 	/* Doesn't fit */
13959c618292SRoy Spliet 	return -EINVAL;
13969c618292SRoy Spliet }
13979c618292SRoy Spliet 
13989c618292SRoy Spliet #define sunxi_nand_lookup_timing(l, p, c) \
13999c618292SRoy Spliet 			_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
14009c618292SRoy Spliet 
14014c46667bSMiquel Raynal static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
14024c46667bSMiquel Raynal 				     const struct nand_interface_config *conf)
14031fef62c1SBoris BREZILLON {
1404f385ebf0SBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1405f385ebf0SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
1406907f45fbSSascha Hauer 	const struct nand_sdr_timings *timings;
14071fef62c1SBoris BREZILLON 	u32 min_clk_period = 0;
14089c618292SRoy Spliet 	s32 tWB, tADL, tWHR, tRHW, tCAD;
14092d43457fSBoris Brezillon 	long real_clk_rate;
14101fef62c1SBoris BREZILLON 
1411907f45fbSSascha Hauer 	timings = nand_get_sdr_timings(conf);
1412907f45fbSSascha Hauer 	if (IS_ERR(timings))
1413907f45fbSSascha Hauer 		return -ENOTSUPP;
1414907f45fbSSascha Hauer 
14151fef62c1SBoris BREZILLON 	/* T1 <=> tCLS */
14161fef62c1SBoris BREZILLON 	if (timings->tCLS_min > min_clk_period)
14171fef62c1SBoris BREZILLON 		min_clk_period = timings->tCLS_min;
14181fef62c1SBoris BREZILLON 
14191fef62c1SBoris BREZILLON 	/* T2 <=> tCLH */
14201fef62c1SBoris BREZILLON 	if (timings->tCLH_min > min_clk_period)
14211fef62c1SBoris BREZILLON 		min_clk_period = timings->tCLH_min;
14221fef62c1SBoris BREZILLON 
14231fef62c1SBoris BREZILLON 	/* T3 <=> tCS */
14241fef62c1SBoris BREZILLON 	if (timings->tCS_min > min_clk_period)
14251fef62c1SBoris BREZILLON 		min_clk_period = timings->tCS_min;
14261fef62c1SBoris BREZILLON 
14271fef62c1SBoris BREZILLON 	/* T4 <=> tCH */
14281fef62c1SBoris BREZILLON 	if (timings->tCH_min > min_clk_period)
14291fef62c1SBoris BREZILLON 		min_clk_period = timings->tCH_min;
14301fef62c1SBoris BREZILLON 
14311fef62c1SBoris BREZILLON 	/* T5 <=> tWP */
14321fef62c1SBoris BREZILLON 	if (timings->tWP_min > min_clk_period)
14331fef62c1SBoris BREZILLON 		min_clk_period = timings->tWP_min;
14341fef62c1SBoris BREZILLON 
14351fef62c1SBoris BREZILLON 	/* T6 <=> tWH */
14361fef62c1SBoris BREZILLON 	if (timings->tWH_min > min_clk_period)
14371fef62c1SBoris BREZILLON 		min_clk_period = timings->tWH_min;
14381fef62c1SBoris BREZILLON 
14391fef62c1SBoris BREZILLON 	/* T7 <=> tALS */
14401fef62c1SBoris BREZILLON 	if (timings->tALS_min > min_clk_period)
14411fef62c1SBoris BREZILLON 		min_clk_period = timings->tALS_min;
14421fef62c1SBoris BREZILLON 
14431fef62c1SBoris BREZILLON 	/* T8 <=> tDS */
14441fef62c1SBoris BREZILLON 	if (timings->tDS_min > min_clk_period)
14451fef62c1SBoris BREZILLON 		min_clk_period = timings->tDS_min;
14461fef62c1SBoris BREZILLON 
14471fef62c1SBoris BREZILLON 	/* T9 <=> tDH */
14481fef62c1SBoris BREZILLON 	if (timings->tDH_min > min_clk_period)
14491fef62c1SBoris BREZILLON 		min_clk_period = timings->tDH_min;
14501fef62c1SBoris BREZILLON 
14511fef62c1SBoris BREZILLON 	/* T10 <=> tRR */
14521fef62c1SBoris BREZILLON 	if (timings->tRR_min > (min_clk_period * 3))
14531fef62c1SBoris BREZILLON 		min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
14541fef62c1SBoris BREZILLON 
14551fef62c1SBoris BREZILLON 	/* T11 <=> tALH */
14561fef62c1SBoris BREZILLON 	if (timings->tALH_min > min_clk_period)
14571fef62c1SBoris BREZILLON 		min_clk_period = timings->tALH_min;
14581fef62c1SBoris BREZILLON 
14591fef62c1SBoris BREZILLON 	/* T12 <=> tRP */
14601fef62c1SBoris BREZILLON 	if (timings->tRP_min > min_clk_period)
14611fef62c1SBoris BREZILLON 		min_clk_period = timings->tRP_min;
14621fef62c1SBoris BREZILLON 
14631fef62c1SBoris BREZILLON 	/* T13 <=> tREH */
14641fef62c1SBoris BREZILLON 	if (timings->tREH_min > min_clk_period)
14651fef62c1SBoris BREZILLON 		min_clk_period = timings->tREH_min;
14661fef62c1SBoris BREZILLON 
14671fef62c1SBoris BREZILLON 	/* T14 <=> tRC */
14681fef62c1SBoris BREZILLON 	if (timings->tRC_min > (min_clk_period * 2))
14691fef62c1SBoris BREZILLON 		min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
14701fef62c1SBoris BREZILLON 
14711fef62c1SBoris BREZILLON 	/* T15 <=> tWC */
14721fef62c1SBoris BREZILLON 	if (timings->tWC_min > (min_clk_period * 2))
14731fef62c1SBoris BREZILLON 		min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
14741fef62c1SBoris BREZILLON 
14759c618292SRoy Spliet 	/* T16 - T19 + tCAD */
14765abcd95dSBoris Brezillon 	if (timings->tWB_max > (min_clk_period * 20))
14775abcd95dSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
14785abcd95dSBoris Brezillon 
14795abcd95dSBoris Brezillon 	if (timings->tADL_min > (min_clk_period * 32))
14805abcd95dSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
14815abcd95dSBoris Brezillon 
14825abcd95dSBoris Brezillon 	if (timings->tWHR_min > (min_clk_period * 32))
14835abcd95dSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
14845abcd95dSBoris Brezillon 
14855abcd95dSBoris Brezillon 	if (timings->tRHW_min > (min_clk_period * 20))
14865abcd95dSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
14875abcd95dSBoris Brezillon 
1488511d05e0SBoris Brezillon 	/*
1489511d05e0SBoris Brezillon 	 * In non-EDO, tREA should be less than tRP to guarantee that the
1490511d05e0SBoris Brezillon 	 * controller does not sample the IO lines too early. Unfortunately,
1491511d05e0SBoris Brezillon 	 * the sunxi NAND controller does not allow us to have different
1492511d05e0SBoris Brezillon 	 * values for tRP and tREH (tRP = tREH = tRW / 2).
1493511d05e0SBoris Brezillon 	 *
1494511d05e0SBoris Brezillon 	 * We have 2 options to overcome this limitation:
1495511d05e0SBoris Brezillon 	 *
1496511d05e0SBoris Brezillon 	 * 1/ Extend tRC to fulfil the tREA <= tRC / 2 constraint
1497511d05e0SBoris Brezillon 	 * 2/ Use EDO mode (only works if timings->tRLOH > 0)
1498511d05e0SBoris Brezillon 	 */
1499511d05e0SBoris Brezillon 	if (timings->tREA_max > min_clk_period && !timings->tRLOH_min)
1500511d05e0SBoris Brezillon 		min_clk_period = timings->tREA_max;
1501511d05e0SBoris Brezillon 
15029c618292SRoy Spliet 	tWB  = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
15039c618292SRoy Spliet 					min_clk_period);
15049c618292SRoy Spliet 	if (tWB < 0) {
15059c618292SRoy Spliet 		dev_err(nfc->dev, "unsupported tWB\n");
15069c618292SRoy Spliet 		return tWB;
15079c618292SRoy Spliet 	}
15089c618292SRoy Spliet 
15099c618292SRoy Spliet 	tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
15109c618292SRoy Spliet 	if (tADL > 3) {
15119c618292SRoy Spliet 		dev_err(nfc->dev, "unsupported tADL\n");
15129c618292SRoy Spliet 		return -EINVAL;
15139c618292SRoy Spliet 	}
15149c618292SRoy Spliet 
15159c618292SRoy Spliet 	tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
15169c618292SRoy Spliet 	if (tWHR > 3) {
15179c618292SRoy Spliet 		dev_err(nfc->dev, "unsupported tWHR\n");
15189c618292SRoy Spliet 		return -EINVAL;
15199c618292SRoy Spliet 	}
15209c618292SRoy Spliet 
15219c618292SRoy Spliet 	tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
15229c618292SRoy Spliet 					min_clk_period);
15239c618292SRoy Spliet 	if (tRHW < 0) {
15249c618292SRoy Spliet 		dev_err(nfc->dev, "unsupported tRHW\n");
15259c618292SRoy Spliet 		return tRHW;
15269c618292SRoy Spliet 	}
15279c618292SRoy Spliet 
1528104e442aSBoris Brezillon 	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
1529907f45fbSSascha Hauer 		return 0;
1530907f45fbSSascha Hauer 
15319c618292SRoy Spliet 	/*
15329c618292SRoy Spliet 	 * TODO: according to ONFI specs this value only applies for DDR NAND,
15339c618292SRoy Spliet 	 * but Allwinner seems to set this to 0x7. Mimic them for now.
15349c618292SRoy Spliet 	 */
15359c618292SRoy Spliet 	tCAD = 0x7;
15369c618292SRoy Spliet 
15379c618292SRoy Spliet 	/* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
1538f385ebf0SBoris Brezillon 	sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
15391fef62c1SBoris BREZILLON 
15401fef62c1SBoris BREZILLON 	/* Convert min_clk_period from picoseconds to nanoseconds */
15411fef62c1SBoris BREZILLON 	min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
15421fef62c1SBoris BREZILLON 
15431fef62c1SBoris BREZILLON 	/*
15442f9992e0SBoris Brezillon 	 * Unlike what is stated in Allwinner datasheet, the clk_rate should
15452f9992e0SBoris Brezillon 	 * be set to (1 / min_clk_period), and not (2 / min_clk_period).
15462f9992e0SBoris Brezillon 	 * This new formula was verified with a scope and validated by
15472f9992e0SBoris Brezillon 	 * Allwinner engineers.
15481fef62c1SBoris BREZILLON 	 */
1549f385ebf0SBoris Brezillon 	sunxi_nand->clk_rate = NSEC_PER_SEC / min_clk_period;
1550f385ebf0SBoris Brezillon 	real_clk_rate = clk_round_rate(nfc->mod_clk, sunxi_nand->clk_rate);
1551791eccd9SBryan O'Donoghue 	if (real_clk_rate <= 0) {
1552f385ebf0SBoris Brezillon 		dev_err(nfc->dev, "Unable to round clk %lu\n",
1553f385ebf0SBoris Brezillon 			sunxi_nand->clk_rate);
1554791eccd9SBryan O'Donoghue 		return -EINVAL;
1555791eccd9SBryan O'Donoghue 	}
15562d43457fSBoris Brezillon 
1557511d05e0SBoris Brezillon 	sunxi_nand->timing_ctl = 0;
1558511d05e0SBoris Brezillon 
15592d43457fSBoris Brezillon 	/*
15602d43457fSBoris Brezillon 	 * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
15612d43457fSBoris Brezillon 	 * output cycle timings shall be used if the host drives tRC less than
1562511d05e0SBoris Brezillon 	 * 30 ns. We should also use EDO mode if tREA is bigger than tRP.
15632d43457fSBoris Brezillon 	 */
15642d43457fSBoris Brezillon 	min_clk_period = NSEC_PER_SEC / real_clk_rate;
1565511d05e0SBoris Brezillon 	if (min_clk_period * 2 < 30 || min_clk_period * 1000 < timings->tREA_max)
1566511d05e0SBoris Brezillon 		sunxi_nand->timing_ctl = NFC_TIMING_CTL_EDO;
15671fef62c1SBoris BREZILLON 
15681fef62c1SBoris BREZILLON 	return 0;
15691fef62c1SBoris BREZILLON }
15701fef62c1SBoris BREZILLON 
1571c66811e6SBoris Brezillon static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
1572c66811e6SBoris Brezillon 				    struct mtd_oob_region *oobregion)
1573c66811e6SBoris Brezillon {
1574c66811e6SBoris Brezillon 	struct nand_chip *nand = mtd_to_nand(mtd);
1575c66811e6SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
1576c66811e6SBoris Brezillon 
1577c66811e6SBoris Brezillon 	if (section >= ecc->steps)
1578c66811e6SBoris Brezillon 		return -ERANGE;
1579c66811e6SBoris Brezillon 
1580c66811e6SBoris Brezillon 	oobregion->offset = section * (ecc->bytes + 4) + 4;
1581c66811e6SBoris Brezillon 	oobregion->length = ecc->bytes;
1582c66811e6SBoris Brezillon 
1583c66811e6SBoris Brezillon 	return 0;
1584c66811e6SBoris Brezillon }
1585c66811e6SBoris Brezillon 
1586c66811e6SBoris Brezillon static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
1587c66811e6SBoris Brezillon 				     struct mtd_oob_region *oobregion)
1588c66811e6SBoris Brezillon {
1589c66811e6SBoris Brezillon 	struct nand_chip *nand = mtd_to_nand(mtd);
1590c66811e6SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
1591c66811e6SBoris Brezillon 
1592c66811e6SBoris Brezillon 	if (section > ecc->steps)
1593c66811e6SBoris Brezillon 		return -ERANGE;
1594c66811e6SBoris Brezillon 
1595c66811e6SBoris Brezillon 	/*
1596c66811e6SBoris Brezillon 	 * The first 2 bytes are used for BB markers, hence we
1597c66811e6SBoris Brezillon 	 * only have 2 bytes available in the first user data
1598c66811e6SBoris Brezillon 	 * section.
1599c66811e6SBoris Brezillon 	 */
1600bace41f8SMiquel Raynal 	if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
1601c66811e6SBoris Brezillon 		oobregion->offset = 2;
1602c66811e6SBoris Brezillon 		oobregion->length = 2;
1603c66811e6SBoris Brezillon 
1604c66811e6SBoris Brezillon 		return 0;
1605c66811e6SBoris Brezillon 	}
1606c66811e6SBoris Brezillon 
1607c66811e6SBoris Brezillon 	oobregion->offset = section * (ecc->bytes + 4);
1608c66811e6SBoris Brezillon 
1609c66811e6SBoris Brezillon 	if (section < ecc->steps)
1610c66811e6SBoris Brezillon 		oobregion->length = 4;
1611c66811e6SBoris Brezillon 	else
1612c66811e6SBoris Brezillon 		oobregion->offset = mtd->oobsize - oobregion->offset;
1613c66811e6SBoris Brezillon 
1614c66811e6SBoris Brezillon 	return 0;
1615c66811e6SBoris Brezillon }
1616c66811e6SBoris Brezillon 
1617c66811e6SBoris Brezillon static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
1618c66811e6SBoris Brezillon 	.ecc = sunxi_nand_ooblayout_ecc,
1619c66811e6SBoris Brezillon 	.free = sunxi_nand_ooblayout_free,
1620c66811e6SBoris Brezillon };
1621c66811e6SBoris Brezillon 
1622cbd87780SMiquel Raynal static void sunxi_nand_hw_ecc_ctrl_cleanup(struct sunxi_nand_chip *sunxi_nand)
162315d6f118SBoris Brezillon {
1624cbd87780SMiquel Raynal 	kfree(sunxi_nand->ecc);
162515d6f118SBoris Brezillon }
162615d6f118SBoris Brezillon 
1627cde567e3SBoris Brezillon static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
16281fef62c1SBoris BREZILLON 				       struct nand_ecc_ctrl *ecc,
16291fef62c1SBoris BREZILLON 				       struct device_node *np)
16301fef62c1SBoris BREZILLON {
16311fef62c1SBoris BREZILLON 	static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
1632cbd87780SMiquel Raynal 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1633cde567e3SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1634cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1635b5156335SMiquel Raynal 	struct nand_device *nanddev = mtd_to_nanddev(mtd);
16361fef62c1SBoris BREZILLON 	int nsectors;
16371fef62c1SBoris BREZILLON 	int ret;
16381fef62c1SBoris BREZILLON 	int i;
16391fef62c1SBoris BREZILLON 
1640b5156335SMiquel Raynal 	if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
16414796d865SBoris Brezillon 		int bytes;
16424796d865SBoris Brezillon 
16434796d865SBoris Brezillon 		ecc->size = 1024;
16444796d865SBoris Brezillon 		nsectors = mtd->writesize / ecc->size;
16454796d865SBoris Brezillon 
16464796d865SBoris Brezillon 		/* Reserve 2 bytes for the BBM */
16474796d865SBoris Brezillon 		bytes = (mtd->oobsize - 2) / nsectors;
16484796d865SBoris Brezillon 
16494796d865SBoris Brezillon 		/* 4 non-ECC bytes are added before each ECC bytes section */
16504796d865SBoris Brezillon 		bytes -= 4;
16514796d865SBoris Brezillon 
16524796d865SBoris Brezillon 		/* and bytes has to be even. */
16534796d865SBoris Brezillon 		if (bytes % 2)
16544796d865SBoris Brezillon 			bytes--;
16554796d865SBoris Brezillon 
16564796d865SBoris Brezillon 		ecc->strength = bytes * 8 / fls(8 * ecc->size);
16574796d865SBoris Brezillon 
16584796d865SBoris Brezillon 		for (i = 0; i < ARRAY_SIZE(strengths); i++) {
16594796d865SBoris Brezillon 			if (strengths[i] > ecc->strength)
16604796d865SBoris Brezillon 				break;
16614796d865SBoris Brezillon 		}
16624796d865SBoris Brezillon 
16634796d865SBoris Brezillon 		if (!i)
16644796d865SBoris Brezillon 			ecc->strength = 0;
16654796d865SBoris Brezillon 		else
16664796d865SBoris Brezillon 			ecc->strength = strengths[i - 1];
16674796d865SBoris Brezillon 	}
16684796d865SBoris Brezillon 
166940297e7fSDan Carpenter 	if (ecc->size != 512 && ecc->size != 1024)
167040297e7fSDan Carpenter 		return -EINVAL;
167140297e7fSDan Carpenter 
1672cbd87780SMiquel Raynal 	sunxi_nand->ecc = kzalloc(sizeof(*sunxi_nand->ecc), GFP_KERNEL);
1673cbd87780SMiquel Raynal 	if (!sunxi_nand->ecc)
16741fef62c1SBoris BREZILLON 		return -ENOMEM;
16751fef62c1SBoris BREZILLON 
1676872164e4SBoris Brezillon 	/* Prefer 1k ECC chunk over 512 ones */
1677872164e4SBoris Brezillon 	if (ecc->size == 512 && mtd->writesize > 512) {
1678872164e4SBoris Brezillon 		ecc->size = 1024;
1679872164e4SBoris Brezillon 		ecc->strength *= 2;
1680872164e4SBoris Brezillon 	}
1681872164e4SBoris Brezillon 
16821fef62c1SBoris BREZILLON 	/* Add ECC info retrieval from DT */
16831fef62c1SBoris BREZILLON 	for (i = 0; i < ARRAY_SIZE(strengths); i++) {
1684f4c6cd1aSMiquel Raynal 		if (ecc->strength <= strengths[i]) {
1685f4c6cd1aSMiquel Raynal 			/*
1686f4c6cd1aSMiquel Raynal 			 * Update ecc->strength value with the actual strength
1687f4c6cd1aSMiquel Raynal 			 * that will be used by the ECC engine.
1688f4c6cd1aSMiquel Raynal 			 */
1689f4c6cd1aSMiquel Raynal 			ecc->strength = strengths[i];
16901fef62c1SBoris BREZILLON 			break;
16911fef62c1SBoris BREZILLON 		}
1692f4c6cd1aSMiquel Raynal 	}
16931fef62c1SBoris BREZILLON 
16941fef62c1SBoris BREZILLON 	if (i >= ARRAY_SIZE(strengths)) {
16951fef62c1SBoris BREZILLON 		dev_err(nfc->dev, "unsupported strength\n");
16961fef62c1SBoris BREZILLON 		ret = -ENOTSUPP;
16971fef62c1SBoris BREZILLON 		goto err;
16981fef62c1SBoris BREZILLON 	}
16991fef62c1SBoris BREZILLON 
1700cbd87780SMiquel Raynal 	sunxi_nand->ecc->mode = i;
17011fef62c1SBoris BREZILLON 
17021fef62c1SBoris BREZILLON 	/* HW ECC always request ECC bytes for 1024 bytes blocks */
17031fef62c1SBoris BREZILLON 	ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
17041fef62c1SBoris BREZILLON 
17051fef62c1SBoris BREZILLON 	/* HW ECC always work with even numbers of ECC bytes */
17061fef62c1SBoris BREZILLON 	ecc->bytes = ALIGN(ecc->bytes, 2);
17071fef62c1SBoris BREZILLON 
17081fef62c1SBoris BREZILLON 	nsectors = mtd->writesize / ecc->size;
17091fef62c1SBoris BREZILLON 
17101fef62c1SBoris BREZILLON 	if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
17111fef62c1SBoris BREZILLON 		ret = -EINVAL;
17121fef62c1SBoris BREZILLON 		goto err;
17131fef62c1SBoris BREZILLON 	}
17141fef62c1SBoris BREZILLON 
171515d6f118SBoris Brezillon 	ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
171615d6f118SBoris Brezillon 	ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
1717c66811e6SBoris Brezillon 	mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
17181fef62c1SBoris BREZILLON 
1719910ef7a4SManuel Dipolt 	if (nfc->dmac || nfc->caps->has_mdma) {
1720614049a8SBoris Brezillon 		ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
1721614049a8SBoris Brezillon 		ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
1722614049a8SBoris Brezillon 		ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
1723ce8148d7SMiquel Raynal 		nand->options |= NAND_USES_DMA;
1724614049a8SBoris Brezillon 	} else {
17251fef62c1SBoris BREZILLON 		ecc->read_page = sunxi_nfc_hw_ecc_read_page;
1726614049a8SBoris Brezillon 		ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
17271fef62c1SBoris BREZILLON 		ecc->write_page = sunxi_nfc_hw_ecc_write_page;
1728614049a8SBoris Brezillon 	}
1729614049a8SBoris Brezillon 
173003b1d11aSBoris Brezillon 	/* TODO: support DMA for raw accesses and subpage write */
173103b1d11aSBoris Brezillon 	ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
17321c1bdd6fSBoris Brezillon 	ecc->read_oob_raw = nand_read_oob_std;
17331c1bdd6fSBoris Brezillon 	ecc->write_oob_raw = nand_write_oob_std;
17341fef62c1SBoris BREZILLON 
17351fef62c1SBoris BREZILLON 	return 0;
17361fef62c1SBoris BREZILLON 
173715d6f118SBoris Brezillon err:
1738cbd87780SMiquel Raynal 	kfree(sunxi_nand->ecc);
17391fef62c1SBoris BREZILLON 
17401fef62c1SBoris BREZILLON 	return ret;
17411fef62c1SBoris BREZILLON }
17421fef62c1SBoris BREZILLON 
1743cbd87780SMiquel Raynal static void sunxi_nand_ecc_cleanup(struct sunxi_nand_chip *sunxi_nand)
17441fef62c1SBoris BREZILLON {
1745cbd87780SMiquel Raynal 	struct nand_ecc_ctrl *ecc = &sunxi_nand->nand.ecc;
1746cbd87780SMiquel Raynal 
1747bace41f8SMiquel Raynal 	switch (ecc->engine_type) {
1748bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_ON_HOST:
1749cbd87780SMiquel Raynal 		sunxi_nand_hw_ecc_ctrl_cleanup(sunxi_nand);
17501fef62c1SBoris BREZILLON 		break;
1751bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_NONE:
17521fef62c1SBoris BREZILLON 	default:
17531fef62c1SBoris BREZILLON 		break;
17541fef62c1SBoris BREZILLON 	}
17551fef62c1SBoris BREZILLON }
17561fef62c1SBoris BREZILLON 
17572a4d9c16SMiquel Raynal static int sunxi_nand_attach_chip(struct nand_chip *nand)
17581fef62c1SBoris BREZILLON {
175953576c7bSMiquel Raynal 	const struct nand_ecc_props *requirements =
176053576c7bSMiquel Raynal 		nanddev_get_ecc_requirements(&nand->base);
17612a4d9c16SMiquel Raynal 	struct nand_ecc_ctrl *ecc = &nand->ecc;
17622a4d9c16SMiquel Raynal 	struct device_node *np = nand_get_flash_node(nand);
17631fef62c1SBoris BREZILLON 	int ret;
17641fef62c1SBoris BREZILLON 
17652a4d9c16SMiquel Raynal 	if (nand->bbt_options & NAND_BBT_USE_FLASH)
17662a4d9c16SMiquel Raynal 		nand->bbt_options |= NAND_BBT_NO_OOB;
17672a4d9c16SMiquel Raynal 
17682a4d9c16SMiquel Raynal 	if (nand->options & NAND_NEED_SCRAMBLING)
17692a4d9c16SMiquel Raynal 		nand->options |= NAND_NO_SUBPAGE_WRITE;
17702a4d9c16SMiquel Raynal 
17712a4d9c16SMiquel Raynal 	nand->options |= NAND_SUBPAGE_READ;
17722a4d9c16SMiquel Raynal 
1773a3d22a55SBoris BREZILLON 	if (!ecc->size) {
177453576c7bSMiquel Raynal 		ecc->size = requirements->step_size;
177553576c7bSMiquel Raynal 		ecc->strength = requirements->strength;
17761fef62c1SBoris BREZILLON 	}
17771fef62c1SBoris BREZILLON 
17781fef62c1SBoris BREZILLON 	if (!ecc->size || !ecc->strength)
17791fef62c1SBoris BREZILLON 		return -EINVAL;
17801fef62c1SBoris BREZILLON 
1781bace41f8SMiquel Raynal 	switch (ecc->engine_type) {
1782bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_ON_HOST:
1783cde567e3SBoris Brezillon 		ret = sunxi_nand_hw_ecc_ctrl_init(nand, ecc, np);
17841fef62c1SBoris BREZILLON 		if (ret)
17851fef62c1SBoris BREZILLON 			return ret;
17861fef62c1SBoris BREZILLON 		break;
1787bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_NONE:
1788bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_SOFT:
17891fef62c1SBoris BREZILLON 		break;
17901fef62c1SBoris BREZILLON 	default:
17911fef62c1SBoris BREZILLON 		return -EINVAL;
17921fef62c1SBoris BREZILLON 	}
17931fef62c1SBoris BREZILLON 
17941fef62c1SBoris BREZILLON 	return 0;
17951fef62c1SBoris BREZILLON }
17961fef62c1SBoris BREZILLON 
1797df505799SBoris Brezillon static int sunxi_nfc_exec_subop(struct nand_chip *nand,
1798df505799SBoris Brezillon 				const struct nand_subop *subop)
1799df505799SBoris Brezillon {
1800df505799SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1801df505799SBoris Brezillon 	u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = { };
1802df505799SBoris Brezillon 	unsigned int i, j, remaining, start;
1803df505799SBoris Brezillon 	void *inbuf = NULL;
1804df505799SBoris Brezillon 	int ret;
1805df505799SBoris Brezillon 
1806df505799SBoris Brezillon 	for (i = 0; i < subop->ninstrs; i++) {
1807df505799SBoris Brezillon 		const struct nand_op_instr *instr = &subop->instrs[i];
1808df505799SBoris Brezillon 
1809df505799SBoris Brezillon 		switch (instr->type) {
1810df505799SBoris Brezillon 		case NAND_OP_CMD_INSTR:
1811df505799SBoris Brezillon 			if (cmd & NFC_SEND_CMD1) {
1812df505799SBoris Brezillon 				if (WARN_ON(cmd & NFC_SEND_CMD2))
1813df505799SBoris Brezillon 					return -EINVAL;
1814df505799SBoris Brezillon 
1815df505799SBoris Brezillon 				cmd |= NFC_SEND_CMD2;
1816df505799SBoris Brezillon 				extcmd |= instr->ctx.cmd.opcode;
1817df505799SBoris Brezillon 			} else {
1818df505799SBoris Brezillon 				cmd |= NFC_SEND_CMD1 |
1819df505799SBoris Brezillon 				       NFC_CMD(instr->ctx.cmd.opcode);
1820df505799SBoris Brezillon 			}
1821df505799SBoris Brezillon 			break;
1822df505799SBoris Brezillon 
1823df505799SBoris Brezillon 		case NAND_OP_ADDR_INSTR:
1824df505799SBoris Brezillon 			remaining = nand_subop_get_num_addr_cyc(subop, i);
1825df505799SBoris Brezillon 			start = nand_subop_get_addr_start_off(subop, i);
1826df505799SBoris Brezillon 			for (j = 0; j < 8 && j + start < remaining; j++) {
1827df505799SBoris Brezillon 				u32 addr = instr->ctx.addr.addrs[j + start];
1828df505799SBoris Brezillon 
1829df505799SBoris Brezillon 				addrs[j / 4] |= addr << (j % 4) * 8;
1830df505799SBoris Brezillon 			}
1831df505799SBoris Brezillon 
1832df505799SBoris Brezillon 			if (j)
1833df505799SBoris Brezillon 				cmd |= NFC_SEND_ADR | NFC_ADR_NUM(j);
1834df505799SBoris Brezillon 
1835df505799SBoris Brezillon 			break;
1836df505799SBoris Brezillon 
1837df505799SBoris Brezillon 		case NAND_OP_DATA_IN_INSTR:
1838df505799SBoris Brezillon 		case NAND_OP_DATA_OUT_INSTR:
1839df505799SBoris Brezillon 			start = nand_subop_get_data_start_off(subop, i);
1840df505799SBoris Brezillon 			remaining = nand_subop_get_data_len(subop, i);
1841df505799SBoris Brezillon 			cnt = min_t(u32, remaining, NFC_SRAM_SIZE);
1842df505799SBoris Brezillon 			cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
1843df505799SBoris Brezillon 
1844df505799SBoris Brezillon 			if (instr->type == NAND_OP_DATA_OUT_INSTR) {
1845df505799SBoris Brezillon 				cmd |= NFC_ACCESS_DIR;
1846df505799SBoris Brezillon 				memcpy_toio(nfc->regs + NFC_RAM0_BASE,
1847df505799SBoris Brezillon 					    instr->ctx.data.buf.out + start,
1848df505799SBoris Brezillon 					    cnt);
1849df505799SBoris Brezillon 			} else {
1850df505799SBoris Brezillon 				inbuf = instr->ctx.data.buf.in + start;
1851df505799SBoris Brezillon 			}
1852df505799SBoris Brezillon 
1853df505799SBoris Brezillon 			break;
1854df505799SBoris Brezillon 
1855df505799SBoris Brezillon 		case NAND_OP_WAITRDY_INSTR:
1856df505799SBoris Brezillon 			cmd |= NFC_WAIT_FLAG;
1857df505799SBoris Brezillon 			break;
1858df505799SBoris Brezillon 		}
1859df505799SBoris Brezillon 	}
1860df505799SBoris Brezillon 
1861df505799SBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
1862df505799SBoris Brezillon 	if (ret)
1863df505799SBoris Brezillon 		return ret;
1864df505799SBoris Brezillon 
1865df505799SBoris Brezillon 	if (cmd & NFC_SEND_ADR) {
1866df505799SBoris Brezillon 		writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW);
1867df505799SBoris Brezillon 		writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH);
1868df505799SBoris Brezillon 	}
1869df505799SBoris Brezillon 
1870df505799SBoris Brezillon 	if (cmd & NFC_SEND_CMD2)
1871df505799SBoris Brezillon 		writel(extcmd,
1872df505799SBoris Brezillon 		       nfc->regs +
1873df505799SBoris Brezillon 		       (cmd & NFC_ACCESS_DIR ?
1874df505799SBoris Brezillon 			NFC_REG_WCMD_SET : NFC_REG_RCMD_SET));
1875df505799SBoris Brezillon 
1876df505799SBoris Brezillon 	if (cmd & NFC_DATA_TRANS)
1877df505799SBoris Brezillon 		writel(cnt, nfc->regs + NFC_REG_CNT);
1878df505799SBoris Brezillon 
1879df505799SBoris Brezillon 	writel(cmd, nfc->regs + NFC_REG_CMD);
1880df505799SBoris Brezillon 
1881df505799SBoris Brezillon 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG,
1882df505799SBoris Brezillon 				    !(cmd & NFC_WAIT_FLAG) && cnt < 64,
1883df505799SBoris Brezillon 				    0);
1884df505799SBoris Brezillon 	if (ret)
1885df505799SBoris Brezillon 		return ret;
1886df505799SBoris Brezillon 
1887df505799SBoris Brezillon 	if (inbuf)
1888df505799SBoris Brezillon 		memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt);
1889df505799SBoris Brezillon 
1890df505799SBoris Brezillon 	return 0;
1891df505799SBoris Brezillon }
1892df505799SBoris Brezillon 
1893df505799SBoris Brezillon static int sunxi_nfc_soft_waitrdy(struct nand_chip *nand,
1894df505799SBoris Brezillon 				  const struct nand_subop *subop)
1895df505799SBoris Brezillon {
1896df505799SBoris Brezillon 	return nand_soft_waitrdy(nand,
1897df505799SBoris Brezillon 				 subop->instrs[0].ctx.waitrdy.timeout_ms);
1898df505799SBoris Brezillon }
1899df505799SBoris Brezillon 
1900df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_op_parser = NAND_OP_PARSER(
1901df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1902df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1903df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1904df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1905df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
1906df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
1907df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1908df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1909df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1910df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
1911df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1912df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
1913df505799SBoris Brezillon );
1914df505799SBoris Brezillon 
1915df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_norb_op_parser = NAND_OP_PARSER(
1916df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1917df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1918df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1919df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1920df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
1921df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1922df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1923df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1924df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
1925df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true)),
1926df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_soft_waitrdy,
1927df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
1928df505799SBoris Brezillon );
1929df505799SBoris Brezillon 
1930df505799SBoris Brezillon static int sunxi_nfc_exec_op(struct nand_chip *nand,
1931df505799SBoris Brezillon 			     const struct nand_operation *op, bool check_only)
1932df505799SBoris Brezillon {
1933df505799SBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1934df505799SBoris Brezillon 	const struct nand_op_parser *parser;
1935df505799SBoris Brezillon 
1936ce446b4bSBoris Brezillon 	if (!check_only)
1937df505799SBoris Brezillon 		sunxi_nfc_select_chip(nand, op->cs);
1938df505799SBoris Brezillon 
1939df505799SBoris Brezillon 	if (sunxi_nand->sels[op->cs].rb >= 0)
1940df505799SBoris Brezillon 		parser = &sunxi_nfc_op_parser;
1941df505799SBoris Brezillon 	else
1942df505799SBoris Brezillon 		parser = &sunxi_nfc_norb_op_parser;
1943df505799SBoris Brezillon 
1944df505799SBoris Brezillon 	return nand_op_parser_exec_op(nand, parser, op, check_only);
1945df505799SBoris Brezillon }
1946df505799SBoris Brezillon 
19472a4d9c16SMiquel Raynal static const struct nand_controller_ops sunxi_nand_controller_ops = {
19482a4d9c16SMiquel Raynal 	.attach_chip = sunxi_nand_attach_chip,
19494c46667bSMiquel Raynal 	.setup_interface = sunxi_nfc_setup_interface,
1950df505799SBoris Brezillon 	.exec_op = sunxi_nfc_exec_op,
19512a4d9c16SMiquel Raynal };
19522a4d9c16SMiquel Raynal 
19531fef62c1SBoris BREZILLON static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
19541fef62c1SBoris BREZILLON 				struct device_node *np)
19551fef62c1SBoris BREZILLON {
1956f385ebf0SBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand;
19571fef62c1SBoris BREZILLON 	struct mtd_info *mtd;
19581fef62c1SBoris BREZILLON 	struct nand_chip *nand;
19591fef62c1SBoris BREZILLON 	int nsels;
19601fef62c1SBoris BREZILLON 	int ret;
19611fef62c1SBoris BREZILLON 	int i;
19621fef62c1SBoris BREZILLON 	u32 tmp;
19631fef62c1SBoris BREZILLON 
19641fef62c1SBoris BREZILLON 	if (!of_get_property(np, "reg", &nsels))
19651fef62c1SBoris BREZILLON 		return -EINVAL;
19661fef62c1SBoris BREZILLON 
19671fef62c1SBoris BREZILLON 	nsels /= sizeof(u32);
19681fef62c1SBoris BREZILLON 	if (!nsels) {
19691fef62c1SBoris BREZILLON 		dev_err(dev, "invalid reg property size\n");
19701fef62c1SBoris BREZILLON 		return -EINVAL;
19711fef62c1SBoris BREZILLON 	}
19721fef62c1SBoris BREZILLON 
19736c721acdSBoris Brezillon 	sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels),
19741fef62c1SBoris BREZILLON 				  GFP_KERNEL);
1975*b0821cc5SZhen Lei 	if (!sunxi_nand)
19761fef62c1SBoris BREZILLON 		return -ENOMEM;
19771fef62c1SBoris BREZILLON 
1978f385ebf0SBoris Brezillon 	sunxi_nand->nsels = nsels;
19791fef62c1SBoris BREZILLON 
19801fef62c1SBoris BREZILLON 	for (i = 0; i < nsels; i++) {
19811fef62c1SBoris BREZILLON 		ret = of_property_read_u32_index(np, "reg", i, &tmp);
19821fef62c1SBoris BREZILLON 		if (ret) {
19831fef62c1SBoris BREZILLON 			dev_err(dev, "could not retrieve reg property: %d\n",
19841fef62c1SBoris BREZILLON 				ret);
19851fef62c1SBoris BREZILLON 			return ret;
19861fef62c1SBoris BREZILLON 		}
19871fef62c1SBoris BREZILLON 
19881fef62c1SBoris BREZILLON 		if (tmp > NFC_MAX_CS) {
19891fef62c1SBoris BREZILLON 			dev_err(dev,
19901fef62c1SBoris BREZILLON 				"invalid reg value: %u (max CS = 7)\n",
19911fef62c1SBoris BREZILLON 				tmp);
19921fef62c1SBoris BREZILLON 			return -EINVAL;
19931fef62c1SBoris BREZILLON 		}
19941fef62c1SBoris BREZILLON 
19951fef62c1SBoris BREZILLON 		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
19961fef62c1SBoris BREZILLON 			dev_err(dev, "CS %d already assigned\n", tmp);
19971fef62c1SBoris BREZILLON 			return -EINVAL;
19981fef62c1SBoris BREZILLON 		}
19991fef62c1SBoris BREZILLON 
2000f385ebf0SBoris Brezillon 		sunxi_nand->sels[i].cs = tmp;
20011fef62c1SBoris BREZILLON 
20021fef62c1SBoris BREZILLON 		if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
2003ddd5ed3aSBoris Brezillon 		    tmp < 2)
2004f385ebf0SBoris Brezillon 			sunxi_nand->sels[i].rb = tmp;
2005ddd5ed3aSBoris Brezillon 		else
2006f385ebf0SBoris Brezillon 			sunxi_nand->sels[i].rb = -1;
20071fef62c1SBoris BREZILLON 	}
20081fef62c1SBoris BREZILLON 
2009f385ebf0SBoris Brezillon 	nand = &sunxi_nand->nand;
20101fef62c1SBoris BREZILLON 	/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
20111fef62c1SBoris BREZILLON 	nand->controller = &nfc->controller;
20122a4d9c16SMiquel Raynal 	nand->controller->ops = &sunxi_nand_controller_ops;
20132a4d9c16SMiquel Raynal 
2014a3d22a55SBoris BREZILLON 	/*
2015a3d22a55SBoris BREZILLON 	 * Set the ECC mode to the default value in case nothing is specified
2016a3d22a55SBoris BREZILLON 	 * in the DT.
2017a3d22a55SBoris BREZILLON 	 */
2018bace41f8SMiquel Raynal 	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
201963752199SBrian Norris 	nand_set_flash_node(nand, np);
20201fef62c1SBoris BREZILLON 
202132e9f2d8SBoris BREZILLON 	mtd = nand_to_mtd(nand);
20221fef62c1SBoris BREZILLON 	mtd->dev.parent = dev;
20231fef62c1SBoris BREZILLON 
202400ad378fSBoris Brezillon 	ret = nand_scan(nand, nsels);
20251fef62c1SBoris BREZILLON 	if (ret)
20261fef62c1SBoris BREZILLON 		return ret;
20271fef62c1SBoris BREZILLON 
2028a61ae81aSBrian Norris 	ret = mtd_device_register(mtd, NULL, 0);
20291fef62c1SBoris BREZILLON 	if (ret) {
20301fef62c1SBoris BREZILLON 		dev_err(dev, "failed to register mtd device: %d\n", ret);
20313d84515fSMiquel Raynal 		nand_cleanup(nand);
20321fef62c1SBoris BREZILLON 		return ret;
20331fef62c1SBoris BREZILLON 	}
20341fef62c1SBoris BREZILLON 
2035f385ebf0SBoris Brezillon 	list_add_tail(&sunxi_nand->node, &nfc->chips);
20361fef62c1SBoris BREZILLON 
20371fef62c1SBoris BREZILLON 	return 0;
20381fef62c1SBoris BREZILLON }
20391fef62c1SBoris BREZILLON 
20401fef62c1SBoris BREZILLON static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
20411fef62c1SBoris BREZILLON {
20421fef62c1SBoris BREZILLON 	struct device_node *np = dev->of_node;
20431fef62c1SBoris BREZILLON 	struct device_node *nand_np;
20441fef62c1SBoris BREZILLON 	int nchips = of_get_child_count(np);
20451fef62c1SBoris BREZILLON 	int ret;
20461fef62c1SBoris BREZILLON 
20471fef62c1SBoris BREZILLON 	if (nchips > 8) {
20481fef62c1SBoris BREZILLON 		dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips);
20491fef62c1SBoris BREZILLON 		return -EINVAL;
20501fef62c1SBoris BREZILLON 	}
20511fef62c1SBoris BREZILLON 
20521fef62c1SBoris BREZILLON 	for_each_child_of_node(np, nand_np) {
20531fef62c1SBoris BREZILLON 		ret = sunxi_nand_chip_init(dev, nfc, nand_np);
2054a81c0f07SJulia Lawall 		if (ret) {
2055a81c0f07SJulia Lawall 			of_node_put(nand_np);
20561fef62c1SBoris BREZILLON 			return ret;
20571fef62c1SBoris BREZILLON 		}
2058a81c0f07SJulia Lawall 	}
20591fef62c1SBoris BREZILLON 
20601fef62c1SBoris BREZILLON 	return 0;
20611fef62c1SBoris BREZILLON }
20621fef62c1SBoris BREZILLON 
20631fef62c1SBoris BREZILLON static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
20641fef62c1SBoris BREZILLON {
2065f385ebf0SBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand;
2066068d86ecSMiquel Raynal 	struct nand_chip *chip;
2067068d86ecSMiquel Raynal 	int ret;
20681fef62c1SBoris BREZILLON 
20691fef62c1SBoris BREZILLON 	while (!list_empty(&nfc->chips)) {
2070f385ebf0SBoris Brezillon 		sunxi_nand = list_first_entry(&nfc->chips,
2071f385ebf0SBoris Brezillon 					      struct sunxi_nand_chip,
20721fef62c1SBoris BREZILLON 					      node);
2073068d86ecSMiquel Raynal 		chip = &sunxi_nand->nand;
2074068d86ecSMiquel Raynal 		ret = mtd_device_unregister(nand_to_mtd(chip));
2075068d86ecSMiquel Raynal 		WARN_ON(ret);
2076068d86ecSMiquel Raynal 		nand_cleanup(chip);
2077cbd87780SMiquel Raynal 		sunxi_nand_ecc_cleanup(sunxi_nand);
2078f385ebf0SBoris Brezillon 		list_del(&sunxi_nand->node);
20791fef62c1SBoris BREZILLON 	}
20801fef62c1SBoris BREZILLON }
20811fef62c1SBoris BREZILLON 
2082910ef7a4SManuel Dipolt static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
2083910ef7a4SManuel Dipolt {
2084910ef7a4SManuel Dipolt 	int ret;
2085910ef7a4SManuel Dipolt 
2086910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma)
2087910ef7a4SManuel Dipolt 		return 0;
2088910ef7a4SManuel Dipolt 
2089910ef7a4SManuel Dipolt 	nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
2090910ef7a4SManuel Dipolt 	if (IS_ERR(nfc->dmac)) {
2091910ef7a4SManuel Dipolt 		ret = PTR_ERR(nfc->dmac);
2092910ef7a4SManuel Dipolt 		if (ret == -EPROBE_DEFER)
2093910ef7a4SManuel Dipolt 			return ret;
2094910ef7a4SManuel Dipolt 
2095910ef7a4SManuel Dipolt 		/* Ignore errors to fall back to PIO mode */
2096910ef7a4SManuel Dipolt 		dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret);
2097910ef7a4SManuel Dipolt 		nfc->dmac = NULL;
2098910ef7a4SManuel Dipolt 	} else {
2099910ef7a4SManuel Dipolt 		struct dma_slave_config dmac_cfg = { };
2100910ef7a4SManuel Dipolt 
2101910ef7a4SManuel Dipolt 		dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
2102910ef7a4SManuel Dipolt 		dmac_cfg.dst_addr = dmac_cfg.src_addr;
2103910ef7a4SManuel Dipolt 		dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
2104910ef7a4SManuel Dipolt 		dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
2105910ef7a4SManuel Dipolt 		dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
2106910ef7a4SManuel Dipolt 		dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
2107910ef7a4SManuel Dipolt 		dmaengine_slave_config(nfc->dmac, &dmac_cfg);
2108910ef7a4SManuel Dipolt 	}
2109910ef7a4SManuel Dipolt 	return 0;
2110910ef7a4SManuel Dipolt }
2111910ef7a4SManuel Dipolt 
21121fef62c1SBoris BREZILLON static int sunxi_nfc_probe(struct platform_device *pdev)
21131fef62c1SBoris BREZILLON {
21141fef62c1SBoris BREZILLON 	struct device *dev = &pdev->dev;
21151fef62c1SBoris BREZILLON 	struct resource *r;
21161fef62c1SBoris BREZILLON 	struct sunxi_nfc *nfc;
21171fef62c1SBoris BREZILLON 	int irq;
21181fef62c1SBoris BREZILLON 	int ret;
21191fef62c1SBoris BREZILLON 
21201fef62c1SBoris BREZILLON 	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
21211fef62c1SBoris BREZILLON 	if (!nfc)
21221fef62c1SBoris BREZILLON 		return -ENOMEM;
21231fef62c1SBoris BREZILLON 
21241fef62c1SBoris BREZILLON 	nfc->dev = dev;
21257da45139SMiquel Raynal 	nand_controller_init(&nfc->controller);
21261fef62c1SBoris BREZILLON 	INIT_LIST_HEAD(&nfc->chips);
21271fef62c1SBoris BREZILLON 
21281fef62c1SBoris BREZILLON 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
21291fef62c1SBoris BREZILLON 	nfc->regs = devm_ioremap_resource(dev, r);
21301fef62c1SBoris BREZILLON 	if (IS_ERR(nfc->regs))
21311fef62c1SBoris BREZILLON 		return PTR_ERR(nfc->regs);
21321fef62c1SBoris BREZILLON 
21331fef62c1SBoris BREZILLON 	irq = platform_get_irq(pdev, 0);
2134aab478caSStephen Boyd 	if (irq < 0)
21351fef62c1SBoris BREZILLON 		return irq;
21361fef62c1SBoris BREZILLON 
21371fef62c1SBoris BREZILLON 	nfc->ahb_clk = devm_clk_get(dev, "ahb");
21381fef62c1SBoris BREZILLON 	if (IS_ERR(nfc->ahb_clk)) {
21391fef62c1SBoris BREZILLON 		dev_err(dev, "failed to retrieve ahb clk\n");
21401fef62c1SBoris BREZILLON 		return PTR_ERR(nfc->ahb_clk);
21411fef62c1SBoris BREZILLON 	}
21421fef62c1SBoris BREZILLON 
21431fef62c1SBoris BREZILLON 	ret = clk_prepare_enable(nfc->ahb_clk);
21441fef62c1SBoris BREZILLON 	if (ret)
21451fef62c1SBoris BREZILLON 		return ret;
21461fef62c1SBoris BREZILLON 
21471fef62c1SBoris BREZILLON 	nfc->mod_clk = devm_clk_get(dev, "mod");
21481fef62c1SBoris BREZILLON 	if (IS_ERR(nfc->mod_clk)) {
21491fef62c1SBoris BREZILLON 		dev_err(dev, "failed to retrieve mod clk\n");
21501fef62c1SBoris BREZILLON 		ret = PTR_ERR(nfc->mod_clk);
21511fef62c1SBoris BREZILLON 		goto out_ahb_clk_unprepare;
21521fef62c1SBoris BREZILLON 	}
21531fef62c1SBoris BREZILLON 
21541fef62c1SBoris BREZILLON 	ret = clk_prepare_enable(nfc->mod_clk);
21551fef62c1SBoris BREZILLON 	if (ret)
21561fef62c1SBoris BREZILLON 		goto out_ahb_clk_unprepare;
21571fef62c1SBoris BREZILLON 
2158fcf59f1fSPhilipp Zabel 	nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
21596b244bbfSPhilipp Zabel 	if (IS_ERR(nfc->reset)) {
21606b244bbfSPhilipp Zabel 		ret = PTR_ERR(nfc->reset);
21616b244bbfSPhilipp Zabel 		goto out_mod_clk_unprepare;
21626b244bbfSPhilipp Zabel 	}
21636b244bbfSPhilipp Zabel 
2164ab9d6a78SIcenowy Zheng 	ret = reset_control_deassert(nfc->reset);
2165ab9d6a78SIcenowy Zheng 	if (ret) {
2166ab9d6a78SIcenowy Zheng 		dev_err(dev, "reset err %d\n", ret);
2167ab9d6a78SIcenowy Zheng 		goto out_mod_clk_unprepare;
2168ab9d6a78SIcenowy Zheng 	}
2169ab9d6a78SIcenowy Zheng 
2170a760e77dSMiquel Raynal 	nfc->caps = of_device_get_match_data(&pdev->dev);
2171a760e77dSMiquel Raynal 	if (!nfc->caps) {
2172a760e77dSMiquel Raynal 		ret = -EINVAL;
2173a760e77dSMiquel Raynal 		goto out_ahb_reset_reassert;
2174a760e77dSMiquel Raynal 	}
2175a760e77dSMiquel Raynal 
21761fef62c1SBoris BREZILLON 	ret = sunxi_nfc_rst(nfc);
21771fef62c1SBoris BREZILLON 	if (ret)
2178ab9d6a78SIcenowy Zheng 		goto out_ahb_reset_reassert;
21791fef62c1SBoris BREZILLON 
21801fef62c1SBoris BREZILLON 	writel(0, nfc->regs + NFC_REG_INT);
21811fef62c1SBoris BREZILLON 	ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
21821fef62c1SBoris BREZILLON 			       0, "sunxi-nand", nfc);
21831fef62c1SBoris BREZILLON 	if (ret)
2184ab9d6a78SIcenowy Zheng 		goto out_ahb_reset_reassert;
21851fef62c1SBoris BREZILLON 
2186910ef7a4SManuel Dipolt 	ret = sunxi_nfc_dma_init(nfc, r);
2187910ef7a4SManuel Dipolt 
2188910ef7a4SManuel Dipolt 	if (ret)
2189ac80c55bSPeter Ujfalusi 		goto out_ahb_reset_reassert;
2190ac80c55bSPeter Ujfalusi 
21911fef62c1SBoris BREZILLON 	platform_set_drvdata(pdev, nfc);
21921fef62c1SBoris BREZILLON 
21931fef62c1SBoris BREZILLON 	ret = sunxi_nand_chips_init(dev, nfc);
21941fef62c1SBoris BREZILLON 	if (ret) {
21951fef62c1SBoris BREZILLON 		dev_err(dev, "failed to init nand chips\n");
2196614049a8SBoris Brezillon 		goto out_release_dmac;
21971fef62c1SBoris BREZILLON 	}
21981fef62c1SBoris BREZILLON 
21991fef62c1SBoris BREZILLON 	return 0;
22001fef62c1SBoris BREZILLON 
2201614049a8SBoris Brezillon out_release_dmac:
2202614049a8SBoris Brezillon 	if (nfc->dmac)
2203614049a8SBoris Brezillon 		dma_release_channel(nfc->dmac);
2204ab9d6a78SIcenowy Zheng out_ahb_reset_reassert:
2205ab9d6a78SIcenowy Zheng 	reset_control_assert(nfc->reset);
22061fef62c1SBoris BREZILLON out_mod_clk_unprepare:
22071fef62c1SBoris BREZILLON 	clk_disable_unprepare(nfc->mod_clk);
22081fef62c1SBoris BREZILLON out_ahb_clk_unprepare:
22091fef62c1SBoris BREZILLON 	clk_disable_unprepare(nfc->ahb_clk);
22101fef62c1SBoris BREZILLON 
22111fef62c1SBoris BREZILLON 	return ret;
22121fef62c1SBoris BREZILLON }
22131fef62c1SBoris BREZILLON 
22141fef62c1SBoris BREZILLON static int sunxi_nfc_remove(struct platform_device *pdev)
22151fef62c1SBoris BREZILLON {
22161fef62c1SBoris BREZILLON 	struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
22171fef62c1SBoris BREZILLON 
22181fef62c1SBoris BREZILLON 	sunxi_nand_chips_cleanup(nfc);
2219ab9d6a78SIcenowy Zheng 
2220ab9d6a78SIcenowy Zheng 	reset_control_assert(nfc->reset);
2221ab9d6a78SIcenowy Zheng 
2222614049a8SBoris Brezillon 	if (nfc->dmac)
2223614049a8SBoris Brezillon 		dma_release_channel(nfc->dmac);
2224dd26a458SBoris Brezillon 	clk_disable_unprepare(nfc->mod_clk);
2225dd26a458SBoris Brezillon 	clk_disable_unprepare(nfc->ahb_clk);
22261fef62c1SBoris BREZILLON 
22271fef62c1SBoris BREZILLON 	return 0;
22281fef62c1SBoris BREZILLON }
22291fef62c1SBoris BREZILLON 
2230a760e77dSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
2231a760e77dSMiquel Raynal 	.reg_io_data = NFC_REG_A10_IO_DATA,
2232a760e77dSMiquel Raynal 	.dma_maxburst = 4,
2233a760e77dSMiquel Raynal };
2234a760e77dSMiquel Raynal 
2235c7a87cebSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
2236910ef7a4SManuel Dipolt 	.has_mdma = true,
2237c7a87cebSMiquel Raynal 	.reg_io_data = NFC_REG_A23_IO_DATA,
2238c7a87cebSMiquel Raynal 	.dma_maxburst = 8,
2239c7a87cebSMiquel Raynal };
2240c7a87cebSMiquel Raynal 
22411fef62c1SBoris BREZILLON static const struct of_device_id sunxi_nfc_ids[] = {
2242a760e77dSMiquel Raynal 	{
2243a760e77dSMiquel Raynal 		.compatible = "allwinner,sun4i-a10-nand",
2244a760e77dSMiquel Raynal 		.data = &sunxi_nfc_a10_caps,
2245a760e77dSMiquel Raynal 	},
2246c7a87cebSMiquel Raynal 	{
2247c7a87cebSMiquel Raynal 		.compatible = "allwinner,sun8i-a23-nand-controller",
2248c7a87cebSMiquel Raynal 		.data = &sunxi_nfc_a23_caps,
2249c7a87cebSMiquel Raynal 	},
22501fef62c1SBoris BREZILLON 	{ /* sentinel */ }
22511fef62c1SBoris BREZILLON };
22521fef62c1SBoris BREZILLON MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
22531fef62c1SBoris BREZILLON 
22541fef62c1SBoris BREZILLON static struct platform_driver sunxi_nfc_driver = {
22551fef62c1SBoris BREZILLON 	.driver = {
22561fef62c1SBoris BREZILLON 		.name = "sunxi_nand",
22571fef62c1SBoris BREZILLON 		.of_match_table = sunxi_nfc_ids,
22581fef62c1SBoris BREZILLON 	},
22591fef62c1SBoris BREZILLON 	.probe = sunxi_nfc_probe,
22601fef62c1SBoris BREZILLON 	.remove = sunxi_nfc_remove,
22611fef62c1SBoris BREZILLON };
22621fef62c1SBoris BREZILLON module_platform_driver(sunxi_nfc_driver);
22631fef62c1SBoris BREZILLON 
2264f5f88871SBoris Brezillon MODULE_LICENSE("GPL");
22651fef62c1SBoris BREZILLON MODULE_AUTHOR("Boris BREZILLON");
22661fef62c1SBoris BREZILLON MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
22671fef62c1SBoris BREZILLON MODULE_ALIAS("platform:sunxi_nand");
2268