xref: /openbmc/linux/drivers/mtd/nand/raw/sunxi_nand.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1f5f88871SBoris Brezillon // SPDX-License-Identifier: GPL-2.0+
293db446aSBoris Brezillon /*
393db446aSBoris Brezillon  * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
493db446aSBoris Brezillon  *
593db446aSBoris Brezillon  * Derived from:
693db446aSBoris Brezillon  *	https://github.com/yuq/sunxi-nfc-mtd
793db446aSBoris Brezillon  *	Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
893db446aSBoris Brezillon  *
993db446aSBoris Brezillon  *	https://github.com/hno/Allwinner-Info
1093db446aSBoris Brezillon  *	Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
1193db446aSBoris Brezillon  *
1293db446aSBoris Brezillon  *	Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
1393db446aSBoris Brezillon  *	Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
1493db446aSBoris Brezillon  */
1593db446aSBoris Brezillon 
1693db446aSBoris Brezillon #include <linux/dma-mapping.h>
1793db446aSBoris Brezillon #include <linux/slab.h>
1893db446aSBoris Brezillon #include <linux/module.h>
1993db446aSBoris Brezillon #include <linux/moduleparam.h>
2093db446aSBoris Brezillon #include <linux/platform_device.h>
2193db446aSBoris Brezillon #include <linux/of.h>
2293db446aSBoris Brezillon #include <linux/mtd/mtd.h>
2393db446aSBoris Brezillon #include <linux/mtd/rawnand.h>
2493db446aSBoris Brezillon #include <linux/mtd/partitions.h>
2593db446aSBoris Brezillon #include <linux/clk.h>
2693db446aSBoris Brezillon #include <linux/delay.h>
2793db446aSBoris Brezillon #include <linux/dmaengine.h>
2893db446aSBoris Brezillon #include <linux/interrupt.h>
2993db446aSBoris Brezillon #include <linux/iopoll.h>
3093db446aSBoris Brezillon #include <linux/reset.h>
3193db446aSBoris Brezillon 
3293db446aSBoris Brezillon #define NFC_REG_CTL		0x0000
3393db446aSBoris Brezillon #define NFC_REG_ST		0x0004
3493db446aSBoris Brezillon #define NFC_REG_INT		0x0008
3593db446aSBoris Brezillon #define NFC_REG_TIMING_CTL	0x000C
3693db446aSBoris Brezillon #define NFC_REG_TIMING_CFG	0x0010
3793db446aSBoris Brezillon #define NFC_REG_ADDR_LOW	0x0014
3893db446aSBoris Brezillon #define NFC_REG_ADDR_HIGH	0x0018
3993db446aSBoris Brezillon #define NFC_REG_SECTOR_NUM	0x001C
4093db446aSBoris Brezillon #define NFC_REG_CNT		0x0020
4193db446aSBoris Brezillon #define NFC_REG_CMD		0x0024
4293db446aSBoris Brezillon #define NFC_REG_RCMD_SET	0x0028
4393db446aSBoris Brezillon #define NFC_REG_WCMD_SET	0x002C
44a760e77dSMiquel Raynal #define NFC_REG_A10_IO_DATA	0x0030
45c7a87cebSMiquel Raynal #define NFC_REG_A23_IO_DATA	0x0300
4693db446aSBoris Brezillon #define NFC_REG_ECC_CTL		0x0034
4793db446aSBoris Brezillon #define NFC_REG_ECC_ST		0x0038
4893db446aSBoris Brezillon #define NFC_REG_DEBUG		0x003C
4993db446aSBoris Brezillon #define NFC_REG_ECC_ERR_CNT(x)	((0x0040 + (x)) & ~0x3)
5093db446aSBoris Brezillon #define NFC_REG_USER_DATA(x)	(0x0050 + ((x) * 4))
5193db446aSBoris Brezillon #define NFC_REG_SPARE_AREA	0x00A0
5293db446aSBoris Brezillon #define NFC_REG_PAT_ID		0x00A4
53910ef7a4SManuel Dipolt #define NFC_REG_MDMA_ADDR	0x00C0
54c7a87cebSMiquel Raynal #define NFC_REG_MDMA_CNT	0x00C4
5593db446aSBoris Brezillon #define NFC_RAM0_BASE		0x0400
5693db446aSBoris Brezillon #define NFC_RAM1_BASE		0x0800
5793db446aSBoris Brezillon 
5893db446aSBoris Brezillon /* define bit use in NFC_CTL */
5993db446aSBoris Brezillon #define NFC_EN			BIT(0)
6093db446aSBoris Brezillon #define NFC_RESET		BIT(1)
6193db446aSBoris Brezillon #define NFC_BUS_WIDTH_MSK	BIT(2)
6293db446aSBoris Brezillon #define NFC_BUS_WIDTH_8		(0 << 2)
6393db446aSBoris Brezillon #define NFC_BUS_WIDTH_16	(1 << 2)
6493db446aSBoris Brezillon #define NFC_RB_SEL_MSK		BIT(3)
6593db446aSBoris Brezillon #define NFC_RB_SEL(x)		((x) << 3)
6693db446aSBoris Brezillon #define NFC_CE_SEL_MSK		GENMASK(26, 24)
6793db446aSBoris Brezillon #define NFC_CE_SEL(x)		((x) << 24)
6893db446aSBoris Brezillon #define NFC_CE_CTL		BIT(6)
6993db446aSBoris Brezillon #define NFC_PAGE_SHIFT_MSK	GENMASK(11, 8)
7093db446aSBoris Brezillon #define NFC_PAGE_SHIFT(x)	(((x) < 10 ? 0 : (x) - 10) << 8)
7193db446aSBoris Brezillon #define NFC_SAM			BIT(12)
7293db446aSBoris Brezillon #define NFC_RAM_METHOD		BIT(14)
73c7a87cebSMiquel Raynal #define NFC_DMA_TYPE_NORMAL	BIT(15)
7493db446aSBoris Brezillon #define NFC_DEBUG_CTL		BIT(31)
7593db446aSBoris Brezillon 
7693db446aSBoris Brezillon /* define bit use in NFC_ST */
7793db446aSBoris Brezillon #define NFC_RB_B2R		BIT(0)
7893db446aSBoris Brezillon #define NFC_CMD_INT_FLAG	BIT(1)
7993db446aSBoris Brezillon #define NFC_DMA_INT_FLAG	BIT(2)
8093db446aSBoris Brezillon #define NFC_CMD_FIFO_STATUS	BIT(3)
8193db446aSBoris Brezillon #define NFC_STA			BIT(4)
8293db446aSBoris Brezillon #define NFC_NATCH_INT_FLAG	BIT(5)
8393db446aSBoris Brezillon #define NFC_RB_STATE(x)		BIT(x + 8)
8493db446aSBoris Brezillon 
8593db446aSBoris Brezillon /* define bit use in NFC_INT */
8693db446aSBoris Brezillon #define NFC_B2R_INT_ENABLE	BIT(0)
8793db446aSBoris Brezillon #define NFC_CMD_INT_ENABLE	BIT(1)
8893db446aSBoris Brezillon #define NFC_DMA_INT_ENABLE	BIT(2)
8993db446aSBoris Brezillon #define NFC_INT_MASK		(NFC_B2R_INT_ENABLE | \
9093db446aSBoris Brezillon 				 NFC_CMD_INT_ENABLE | \
9193db446aSBoris Brezillon 				 NFC_DMA_INT_ENABLE)
9293db446aSBoris Brezillon 
9393db446aSBoris Brezillon /* define bit use in NFC_TIMING_CTL */
9493db446aSBoris Brezillon #define NFC_TIMING_CTL_EDO	BIT(8)
9593db446aSBoris Brezillon 
9693db446aSBoris Brezillon /* define NFC_TIMING_CFG register layout */
9793db446aSBoris Brezillon #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD)		\
9893db446aSBoris Brezillon 	(((tWB) & 0x3) | (((tADL) & 0x3) << 2) |		\
9993db446aSBoris Brezillon 	(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) |		\
10093db446aSBoris Brezillon 	(((tCAD) & 0x7) << 8))
10193db446aSBoris Brezillon 
10293db446aSBoris Brezillon /* define bit use in NFC_CMD */
10393db446aSBoris Brezillon #define NFC_CMD_LOW_BYTE_MSK	GENMASK(7, 0)
10493db446aSBoris Brezillon #define NFC_CMD_HIGH_BYTE_MSK	GENMASK(15, 8)
10593db446aSBoris Brezillon #define NFC_CMD(x)		(x)
10693db446aSBoris Brezillon #define NFC_ADR_NUM_MSK		GENMASK(18, 16)
10793db446aSBoris Brezillon #define NFC_ADR_NUM(x)		(((x) - 1) << 16)
10893db446aSBoris Brezillon #define NFC_SEND_ADR		BIT(19)
10993db446aSBoris Brezillon #define NFC_ACCESS_DIR		BIT(20)
11093db446aSBoris Brezillon #define NFC_DATA_TRANS		BIT(21)
11193db446aSBoris Brezillon #define NFC_SEND_CMD1		BIT(22)
11293db446aSBoris Brezillon #define NFC_WAIT_FLAG		BIT(23)
11393db446aSBoris Brezillon #define NFC_SEND_CMD2		BIT(24)
11493db446aSBoris Brezillon #define NFC_SEQ			BIT(25)
11593db446aSBoris Brezillon #define NFC_DATA_SWAP_METHOD	BIT(26)
11693db446aSBoris Brezillon #define NFC_ROW_AUTO_INC	BIT(27)
11793db446aSBoris Brezillon #define NFC_SEND_CMD3		BIT(28)
11893db446aSBoris Brezillon #define NFC_SEND_CMD4		BIT(29)
11993db446aSBoris Brezillon #define NFC_CMD_TYPE_MSK	GENMASK(31, 30)
12093db446aSBoris Brezillon #define NFC_NORMAL_OP		(0 << 30)
12193db446aSBoris Brezillon #define NFC_ECC_OP		(1 << 30)
122cf3e3fd2SBoris Brezillon #define NFC_PAGE_OP		(2U << 30)
12393db446aSBoris Brezillon 
12493db446aSBoris Brezillon /* define bit use in NFC_RCMD_SET */
12593db446aSBoris Brezillon #define NFC_READ_CMD_MSK	GENMASK(7, 0)
12693db446aSBoris Brezillon #define NFC_RND_READ_CMD0_MSK	GENMASK(15, 8)
12793db446aSBoris Brezillon #define NFC_RND_READ_CMD1_MSK	GENMASK(23, 16)
12893db446aSBoris Brezillon 
12993db446aSBoris Brezillon /* define bit use in NFC_WCMD_SET */
13093db446aSBoris Brezillon #define NFC_PROGRAM_CMD_MSK	GENMASK(7, 0)
13193db446aSBoris Brezillon #define NFC_RND_WRITE_CMD_MSK	GENMASK(15, 8)
13293db446aSBoris Brezillon #define NFC_READ_CMD0_MSK	GENMASK(23, 16)
13393db446aSBoris Brezillon #define NFC_READ_CMD1_MSK	GENMASK(31, 24)
13493db446aSBoris Brezillon 
13593db446aSBoris Brezillon /* define bit use in NFC_ECC_CTL */
13693db446aSBoris Brezillon #define NFC_ECC_EN		BIT(0)
13793db446aSBoris Brezillon #define NFC_ECC_PIPELINE	BIT(3)
13893db446aSBoris Brezillon #define NFC_ECC_EXCEPTION	BIT(4)
13993db446aSBoris Brezillon #define NFC_ECC_BLOCK_SIZE_MSK	BIT(5)
14093db446aSBoris Brezillon #define NFC_ECC_BLOCK_512	BIT(5)
14193db446aSBoris Brezillon #define NFC_RANDOM_EN		BIT(9)
14293db446aSBoris Brezillon #define NFC_RANDOM_DIRECTION	BIT(10)
14393db446aSBoris Brezillon #define NFC_ECC_MODE_MSK	GENMASK(15, 12)
14493db446aSBoris Brezillon #define NFC_ECC_MODE(x)		((x) << 12)
14593db446aSBoris Brezillon #define NFC_RANDOM_SEED_MSK	GENMASK(30, 16)
14693db446aSBoris Brezillon #define NFC_RANDOM_SEED(x)	((x) << 16)
14793db446aSBoris Brezillon 
14893db446aSBoris Brezillon /* define bit use in NFC_ECC_ST */
14993db446aSBoris Brezillon #define NFC_ECC_ERR(x)		BIT(x)
15093db446aSBoris Brezillon #define NFC_ECC_ERR_MSK		GENMASK(15, 0)
15193db446aSBoris Brezillon #define NFC_ECC_PAT_FOUND(x)	BIT(x + 16)
15293db446aSBoris Brezillon #define NFC_ECC_ERR_CNT(b, x)	(((x) >> (((b) % 4) * 8)) & 0xff)
15393db446aSBoris Brezillon 
15493db446aSBoris Brezillon #define NFC_DEFAULT_TIMEOUT_MS	1000
15593db446aSBoris Brezillon 
15693db446aSBoris Brezillon #define NFC_SRAM_SIZE		1024
15793db446aSBoris Brezillon 
15893db446aSBoris Brezillon #define NFC_MAX_CS		7
15993db446aSBoris Brezillon 
16067c88008SBoris Brezillon /**
16167c88008SBoris Brezillon  * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
16293db446aSBoris Brezillon  *
16393db446aSBoris Brezillon  * @cs: the NAND CS id used to communicate with a NAND Chip
16467c88008SBoris Brezillon  * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the NFC
16593db446aSBoris Brezillon  */
16693db446aSBoris Brezillon struct sunxi_nand_chip_sel {
16793db446aSBoris Brezillon 	u8 cs;
168ddd5ed3aSBoris Brezillon 	s8 rb;
16993db446aSBoris Brezillon };
17093db446aSBoris Brezillon 
17167c88008SBoris Brezillon /**
17267c88008SBoris Brezillon  * struct sunxi_nand_hw_ecc - stores information related to HW ECC support
17393db446aSBoris Brezillon  *
174ef3e6327SSamuel Holland  * @ecc_ctl: ECC_CTL register value for this NAND chip
17593db446aSBoris Brezillon  */
17693db446aSBoris Brezillon struct sunxi_nand_hw_ecc {
177ef3e6327SSamuel Holland 	u32 ecc_ctl;
17893db446aSBoris Brezillon };
17993db446aSBoris Brezillon 
18067c88008SBoris Brezillon /**
18167c88008SBoris Brezillon  * struct sunxi_nand_chip - stores NAND chip device related information
18293db446aSBoris Brezillon  *
18393db446aSBoris Brezillon  * @node: used to store NAND chips into a list
18493db446aSBoris Brezillon  * @nand: base NAND chip structure
185cbd87780SMiquel Raynal  * @ecc: ECC controller structure
18693db446aSBoris Brezillon  * @clk_rate: clk_rate required for this NAND chip
18767c88008SBoris Brezillon  * @timing_cfg: TIMING_CFG register value for this NAND chip
18867c88008SBoris Brezillon  * @timing_ctl: TIMING_CTL register value for this NAND chip
18993db446aSBoris Brezillon  * @nsels: number of CS lines required by the NAND chip
19093db446aSBoris Brezillon  * @sels: array of CS lines descriptions
19193db446aSBoris Brezillon  */
19293db446aSBoris Brezillon struct sunxi_nand_chip {
19393db446aSBoris Brezillon 	struct list_head node;
19493db446aSBoris Brezillon 	struct nand_chip nand;
195ac1c7072SSamuel Holland 	struct sunxi_nand_hw_ecc ecc;
19693db446aSBoris Brezillon 	unsigned long clk_rate;
19793db446aSBoris Brezillon 	u32 timing_cfg;
19893db446aSBoris Brezillon 	u32 timing_ctl;
19993db446aSBoris Brezillon 	int nsels;
20049f1c330SGustavo A. R. Silva 	struct sunxi_nand_chip_sel sels[];
20193db446aSBoris Brezillon };
20293db446aSBoris Brezillon 
to_sunxi_nand(struct nand_chip * nand)20393db446aSBoris Brezillon static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
20493db446aSBoris Brezillon {
20593db446aSBoris Brezillon 	return container_of(nand, struct sunxi_nand_chip, nand);
20693db446aSBoris Brezillon }
20793db446aSBoris Brezillon 
208a760e77dSMiquel Raynal /*
209a760e77dSMiquel Raynal  * NAND Controller capabilities structure: stores NAND controller capabilities
210a760e77dSMiquel Raynal  * for distinction between compatible strings.
211a760e77dSMiquel Raynal  *
212910ef7a4SManuel Dipolt  * @has_mdma:		Use mbus dma mode, otherwise general dma
213c7a87cebSMiquel Raynal  *			through MBUS on A23/A33 needs extra configuration.
214a760e77dSMiquel Raynal  * @reg_io_data:	I/O data register
215a760e77dSMiquel Raynal  * @dma_maxburst:	DMA maxburst
216a760e77dSMiquel Raynal  */
217a760e77dSMiquel Raynal struct sunxi_nfc_caps {
218910ef7a4SManuel Dipolt 	bool has_mdma;
219a760e77dSMiquel Raynal 	unsigned int reg_io_data;
220a760e77dSMiquel Raynal 	unsigned int dma_maxburst;
221a760e77dSMiquel Raynal };
222a760e77dSMiquel Raynal 
22367c88008SBoris Brezillon /**
22467c88008SBoris Brezillon  * struct sunxi_nfc - stores sunxi NAND controller information
22593db446aSBoris Brezillon  *
22693db446aSBoris Brezillon  * @controller: base controller structure
22793db446aSBoris Brezillon  * @dev: parent device (used to print error messages)
22893db446aSBoris Brezillon  * @regs: NAND controller registers
22967c88008SBoris Brezillon  * @ahb_clk: NAND controller AHB clock
23067c88008SBoris Brezillon  * @mod_clk: NAND controller mod clock
23167c88008SBoris Brezillon  * @reset: NAND controller reset line
23293db446aSBoris Brezillon  * @assigned_cs: bitmask describing already assigned CS lines
23393db446aSBoris Brezillon  * @clk_rate: NAND controller current clock rate
23467c88008SBoris Brezillon  * @chips: a list containing all the NAND chips attached to this NAND
23567c88008SBoris Brezillon  *	   controller
23667c88008SBoris Brezillon  * @complete: a completion object used to wait for NAND controller events
23767c88008SBoris Brezillon  * @dmac: the DMA channel attached to the NAND controller
2380d5c506dSLee Jones  * @caps: NAND Controller capabilities
23993db446aSBoris Brezillon  */
24093db446aSBoris Brezillon struct sunxi_nfc {
2417da45139SMiquel Raynal 	struct nand_controller controller;
24293db446aSBoris Brezillon 	struct device *dev;
24393db446aSBoris Brezillon 	void __iomem *regs;
24493db446aSBoris Brezillon 	struct clk *ahb_clk;
24593db446aSBoris Brezillon 	struct clk *mod_clk;
24693db446aSBoris Brezillon 	struct reset_control *reset;
24793db446aSBoris Brezillon 	unsigned long assigned_cs;
24893db446aSBoris Brezillon 	unsigned long clk_rate;
24993db446aSBoris Brezillon 	struct list_head chips;
25093db446aSBoris Brezillon 	struct completion complete;
25193db446aSBoris Brezillon 	struct dma_chan *dmac;
252a760e77dSMiquel Raynal 	const struct sunxi_nfc_caps *caps;
25393db446aSBoris Brezillon };
25493db446aSBoris Brezillon 
to_sunxi_nfc(struct nand_controller * ctrl)2557da45139SMiquel Raynal static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl)
25693db446aSBoris Brezillon {
25793db446aSBoris Brezillon 	return container_of(ctrl, struct sunxi_nfc, controller);
25893db446aSBoris Brezillon }
25993db446aSBoris Brezillon 
sunxi_nfc_interrupt(int irq,void * dev_id)26093db446aSBoris Brezillon static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
26193db446aSBoris Brezillon {
26293db446aSBoris Brezillon 	struct sunxi_nfc *nfc = dev_id;
26393db446aSBoris Brezillon 	u32 st = readl(nfc->regs + NFC_REG_ST);
26493db446aSBoris Brezillon 	u32 ien = readl(nfc->regs + NFC_REG_INT);
26593db446aSBoris Brezillon 
26693db446aSBoris Brezillon 	if (!(ien & st))
26793db446aSBoris Brezillon 		return IRQ_NONE;
26893db446aSBoris Brezillon 
26993db446aSBoris Brezillon 	if ((ien & st) == ien)
27093db446aSBoris Brezillon 		complete(&nfc->complete);
27193db446aSBoris Brezillon 
27293db446aSBoris Brezillon 	writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
27393db446aSBoris Brezillon 	writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
27493db446aSBoris Brezillon 
27593db446aSBoris Brezillon 	return IRQ_HANDLED;
27693db446aSBoris Brezillon }
27793db446aSBoris Brezillon 
sunxi_nfc_wait_events(struct sunxi_nfc * nfc,u32 events,bool use_polling,unsigned int timeout_ms)27893db446aSBoris Brezillon static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
27993db446aSBoris Brezillon 				 bool use_polling, unsigned int timeout_ms)
28093db446aSBoris Brezillon {
28193db446aSBoris Brezillon 	int ret;
28293db446aSBoris Brezillon 
28393db446aSBoris Brezillon 	if (events & ~NFC_INT_MASK)
28493db446aSBoris Brezillon 		return -EINVAL;
28593db446aSBoris Brezillon 
28693db446aSBoris Brezillon 	if (!timeout_ms)
28793db446aSBoris Brezillon 		timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
28893db446aSBoris Brezillon 
28993db446aSBoris Brezillon 	if (!use_polling) {
29093db446aSBoris Brezillon 		init_completion(&nfc->complete);
29193db446aSBoris Brezillon 
29293db446aSBoris Brezillon 		writel(events, nfc->regs + NFC_REG_INT);
29393db446aSBoris Brezillon 
29493db446aSBoris Brezillon 		ret = wait_for_completion_timeout(&nfc->complete,
29593db446aSBoris Brezillon 						msecs_to_jiffies(timeout_ms));
29693db446aSBoris Brezillon 		if (!ret)
29793db446aSBoris Brezillon 			ret = -ETIMEDOUT;
29893db446aSBoris Brezillon 		else
29993db446aSBoris Brezillon 			ret = 0;
30093db446aSBoris Brezillon 
30193db446aSBoris Brezillon 		writel(0, nfc->regs + NFC_REG_INT);
30293db446aSBoris Brezillon 	} else {
30393db446aSBoris Brezillon 		u32 status;
30493db446aSBoris Brezillon 
30593db446aSBoris Brezillon 		ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
30693db446aSBoris Brezillon 					 (status & events) == events, 1,
30793db446aSBoris Brezillon 					 timeout_ms * 1000);
30893db446aSBoris Brezillon 	}
30993db446aSBoris Brezillon 
31093db446aSBoris Brezillon 	writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
31193db446aSBoris Brezillon 
31293db446aSBoris Brezillon 	if (ret)
31393db446aSBoris Brezillon 		dev_err(nfc->dev, "wait interrupt timedout\n");
31493db446aSBoris Brezillon 
31593db446aSBoris Brezillon 	return ret;
31693db446aSBoris Brezillon }
31793db446aSBoris Brezillon 
sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc * nfc)31893db446aSBoris Brezillon static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
31993db446aSBoris Brezillon {
32093db446aSBoris Brezillon 	u32 status;
32193db446aSBoris Brezillon 	int ret;
32293db446aSBoris Brezillon 
32393db446aSBoris Brezillon 	ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
32493db446aSBoris Brezillon 				 !(status & NFC_CMD_FIFO_STATUS), 1,
32593db446aSBoris Brezillon 				 NFC_DEFAULT_TIMEOUT_MS * 1000);
32693db446aSBoris Brezillon 	if (ret)
32793db446aSBoris Brezillon 		dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
32893db446aSBoris Brezillon 
32993db446aSBoris Brezillon 	return ret;
33093db446aSBoris Brezillon }
33193db446aSBoris Brezillon 
sunxi_nfc_rst(struct sunxi_nfc * nfc)33293db446aSBoris Brezillon static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
33393db446aSBoris Brezillon {
33493db446aSBoris Brezillon 	u32 ctl;
33593db446aSBoris Brezillon 	int ret;
33693db446aSBoris Brezillon 
33793db446aSBoris Brezillon 	writel(0, nfc->regs + NFC_REG_ECC_CTL);
33893db446aSBoris Brezillon 	writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
33993db446aSBoris Brezillon 
34093db446aSBoris Brezillon 	ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl,
34193db446aSBoris Brezillon 				 !(ctl & NFC_RESET), 1,
34293db446aSBoris Brezillon 				 NFC_DEFAULT_TIMEOUT_MS * 1000);
34393db446aSBoris Brezillon 	if (ret)
34493db446aSBoris Brezillon 		dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
34593db446aSBoris Brezillon 
34693db446aSBoris Brezillon 	return ret;
34793db446aSBoris Brezillon }
34893db446aSBoris Brezillon 
sunxi_nfc_dma_op_prepare(struct sunxi_nfc * nfc,const void * buf,int chunksize,int nchunks,enum dma_data_direction ddir,struct scatterlist * sg)349cde567e3SBoris Brezillon static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
35093db446aSBoris Brezillon 				    int chunksize, int nchunks,
35193db446aSBoris Brezillon 				    enum dma_data_direction ddir,
35293db446aSBoris Brezillon 				    struct scatterlist *sg)
35393db446aSBoris Brezillon {
35493db446aSBoris Brezillon 	struct dma_async_tx_descriptor *dmad;
35593db446aSBoris Brezillon 	enum dma_transfer_direction tdir;
35693db446aSBoris Brezillon 	dma_cookie_t dmat;
35793db446aSBoris Brezillon 	int ret;
35893db446aSBoris Brezillon 
35993db446aSBoris Brezillon 	if (ddir == DMA_FROM_DEVICE)
36093db446aSBoris Brezillon 		tdir = DMA_DEV_TO_MEM;
36193db446aSBoris Brezillon 	else
36293db446aSBoris Brezillon 		tdir = DMA_MEM_TO_DEV;
36393db446aSBoris Brezillon 
36493db446aSBoris Brezillon 	sg_init_one(sg, buf, nchunks * chunksize);
36593db446aSBoris Brezillon 	ret = dma_map_sg(nfc->dev, sg, 1, ddir);
36693db446aSBoris Brezillon 	if (!ret)
36793db446aSBoris Brezillon 		return -ENOMEM;
36893db446aSBoris Brezillon 
369910ef7a4SManuel Dipolt 	if (!nfc->caps->has_mdma) {
37093db446aSBoris Brezillon 		dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
37193db446aSBoris Brezillon 		if (!dmad) {
37293db446aSBoris Brezillon 			ret = -EINVAL;
37393db446aSBoris Brezillon 			goto err_unmap_buf;
37493db446aSBoris Brezillon 		}
375910ef7a4SManuel Dipolt 	}
37693db446aSBoris Brezillon 
37793db446aSBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
37893db446aSBoris Brezillon 	       nfc->regs + NFC_REG_CTL);
37993db446aSBoris Brezillon 	writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
38093db446aSBoris Brezillon 	writel(chunksize, nfc->regs + NFC_REG_CNT);
381c7a87cebSMiquel Raynal 
382910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma) {
383910ef7a4SManuel Dipolt 		writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
384910ef7a4SManuel Dipolt 		       nfc->regs + NFC_REG_CTL);
385910ef7a4SManuel Dipolt 		writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
386910ef7a4SManuel Dipolt 		writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR);
387910ef7a4SManuel Dipolt 	} else {
38893db446aSBoris Brezillon 		dmat = dmaengine_submit(dmad);
38993db446aSBoris Brezillon 
39093db446aSBoris Brezillon 		ret = dma_submit_error(dmat);
39193db446aSBoris Brezillon 		if (ret)
39293db446aSBoris Brezillon 			goto err_clr_dma_flag;
393910ef7a4SManuel Dipolt 	}
39493db446aSBoris Brezillon 
39593db446aSBoris Brezillon 	return 0;
39693db446aSBoris Brezillon 
39793db446aSBoris Brezillon err_clr_dma_flag:
39893db446aSBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
39993db446aSBoris Brezillon 	       nfc->regs + NFC_REG_CTL);
40093db446aSBoris Brezillon 
40193db446aSBoris Brezillon err_unmap_buf:
40293db446aSBoris Brezillon 	dma_unmap_sg(nfc->dev, sg, 1, ddir);
40393db446aSBoris Brezillon 	return ret;
40493db446aSBoris Brezillon }
40593db446aSBoris Brezillon 
sunxi_nfc_dma_op_cleanup(struct sunxi_nfc * nfc,enum dma_data_direction ddir,struct scatterlist * sg)406cde567e3SBoris Brezillon static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
40793db446aSBoris Brezillon 				     enum dma_data_direction ddir,
40893db446aSBoris Brezillon 				     struct scatterlist *sg)
40993db446aSBoris Brezillon {
41093db446aSBoris Brezillon 	dma_unmap_sg(nfc->dev, sg, 1, ddir);
41193db446aSBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
41293db446aSBoris Brezillon 	       nfc->regs + NFC_REG_CTL);
41393db446aSBoris Brezillon }
41493db446aSBoris Brezillon 
sunxi_nfc_select_chip(struct nand_chip * nand,unsigned int cs)415df505799SBoris Brezillon static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
41693db446aSBoris Brezillon {
417758b56f5SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
41893db446aSBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
41993db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
42093db446aSBoris Brezillon 	struct sunxi_nand_chip_sel *sel;
42193db446aSBoris Brezillon 	u32 ctl;
42293db446aSBoris Brezillon 
42359186a40SSamuel Holland 	if (cs >= sunxi_nand->nsels)
42493db446aSBoris Brezillon 		return;
42593db446aSBoris Brezillon 
42693db446aSBoris Brezillon 	ctl = readl(nfc->regs + NFC_REG_CTL) &
42793db446aSBoris Brezillon 	      ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
42893db446aSBoris Brezillon 
429df505799SBoris Brezillon 	sel = &sunxi_nand->sels[cs];
430df505799SBoris Brezillon 	ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift);
431df505799SBoris Brezillon 	if (sel->rb >= 0)
432ddd5ed3aSBoris Brezillon 		ctl |= NFC_RB_SEL(sel->rb);
43393db446aSBoris Brezillon 
43493db446aSBoris Brezillon 	writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
43593db446aSBoris Brezillon 
43693db446aSBoris Brezillon 	if (nfc->clk_rate != sunxi_nand->clk_rate) {
43793db446aSBoris Brezillon 		clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
43893db446aSBoris Brezillon 		nfc->clk_rate = sunxi_nand->clk_rate;
43993db446aSBoris Brezillon 	}
44093db446aSBoris Brezillon 
44193db446aSBoris Brezillon 	writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
44293db446aSBoris Brezillon 	writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
44393db446aSBoris Brezillon 	writel(ctl, nfc->regs + NFC_REG_CTL);
44493db446aSBoris Brezillon }
44593db446aSBoris Brezillon 
sunxi_nfc_read_buf(struct nand_chip * nand,uint8_t * buf,int len)4467e534323SBoris Brezillon static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len)
44793db446aSBoris Brezillon {
44893db446aSBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
44993db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
45093db446aSBoris Brezillon 	int ret;
45193db446aSBoris Brezillon 	int cnt;
45293db446aSBoris Brezillon 	int offs = 0;
45393db446aSBoris Brezillon 	u32 tmp;
45493db446aSBoris Brezillon 
45593db446aSBoris Brezillon 	while (len > offs) {
45693db446aSBoris Brezillon 		bool poll = false;
45793db446aSBoris Brezillon 
45893db446aSBoris Brezillon 		cnt = min(len - offs, NFC_SRAM_SIZE);
45993db446aSBoris Brezillon 
46093db446aSBoris Brezillon 		ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
46193db446aSBoris Brezillon 		if (ret)
46293db446aSBoris Brezillon 			break;
46393db446aSBoris Brezillon 
46493db446aSBoris Brezillon 		writel(cnt, nfc->regs + NFC_REG_CNT);
46593db446aSBoris Brezillon 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
46693db446aSBoris Brezillon 		writel(tmp, nfc->regs + NFC_REG_CMD);
46793db446aSBoris Brezillon 
46893db446aSBoris Brezillon 		/* Arbitrary limit for polling mode */
46993db446aSBoris Brezillon 		if (cnt < 64)
47093db446aSBoris Brezillon 			poll = true;
47193db446aSBoris Brezillon 
47293db446aSBoris Brezillon 		ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
47393db446aSBoris Brezillon 		if (ret)
47493db446aSBoris Brezillon 			break;
47593db446aSBoris Brezillon 
47693db446aSBoris Brezillon 		if (buf)
47793db446aSBoris Brezillon 			memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
47893db446aSBoris Brezillon 				      cnt);
47993db446aSBoris Brezillon 		offs += cnt;
48093db446aSBoris Brezillon 	}
48193db446aSBoris Brezillon }
48293db446aSBoris Brezillon 
sunxi_nfc_write_buf(struct nand_chip * nand,const uint8_t * buf,int len)483c0739d85SBoris Brezillon static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf,
48493db446aSBoris Brezillon 				int len)
48593db446aSBoris Brezillon {
48693db446aSBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
48793db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
48893db446aSBoris Brezillon 	int ret;
48993db446aSBoris Brezillon 	int cnt;
49093db446aSBoris Brezillon 	int offs = 0;
49193db446aSBoris Brezillon 	u32 tmp;
49293db446aSBoris Brezillon 
49393db446aSBoris Brezillon 	while (len > offs) {
49493db446aSBoris Brezillon 		bool poll = false;
49593db446aSBoris Brezillon 
49693db446aSBoris Brezillon 		cnt = min(len - offs, NFC_SRAM_SIZE);
49793db446aSBoris Brezillon 
49893db446aSBoris Brezillon 		ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
49993db446aSBoris Brezillon 		if (ret)
50093db446aSBoris Brezillon 			break;
50193db446aSBoris Brezillon 
50293db446aSBoris Brezillon 		writel(cnt, nfc->regs + NFC_REG_CNT);
50393db446aSBoris Brezillon 		memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
50493db446aSBoris Brezillon 		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
50593db446aSBoris Brezillon 		      NFC_ACCESS_DIR;
50693db446aSBoris Brezillon 		writel(tmp, nfc->regs + NFC_REG_CMD);
50793db446aSBoris Brezillon 
50893db446aSBoris Brezillon 		/* Arbitrary limit for polling mode */
50993db446aSBoris Brezillon 		if (cnt < 64)
51093db446aSBoris Brezillon 			poll = true;
51193db446aSBoris Brezillon 
51293db446aSBoris Brezillon 		ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
51393db446aSBoris Brezillon 		if (ret)
51493db446aSBoris Brezillon 			break;
51593db446aSBoris Brezillon 
51693db446aSBoris Brezillon 		offs += cnt;
51793db446aSBoris Brezillon 	}
51893db446aSBoris Brezillon }
51993db446aSBoris Brezillon 
52093db446aSBoris Brezillon /* These seed values have been extracted from Allwinner's BSP */
52193db446aSBoris Brezillon static const u16 sunxi_nfc_randomizer_page_seeds[] = {
52293db446aSBoris Brezillon 	0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
52393db446aSBoris Brezillon 	0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
52493db446aSBoris Brezillon 	0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
52593db446aSBoris Brezillon 	0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
52693db446aSBoris Brezillon 	0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
52793db446aSBoris Brezillon 	0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
52893db446aSBoris Brezillon 	0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
52993db446aSBoris Brezillon 	0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
53093db446aSBoris Brezillon 	0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
53193db446aSBoris Brezillon 	0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
53293db446aSBoris Brezillon 	0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
53393db446aSBoris Brezillon 	0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
53493db446aSBoris Brezillon 	0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
53593db446aSBoris Brezillon 	0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
53693db446aSBoris Brezillon 	0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
53793db446aSBoris Brezillon 	0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
53893db446aSBoris Brezillon };
53993db446aSBoris Brezillon 
54093db446aSBoris Brezillon /*
54193db446aSBoris Brezillon  * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
54293db446aSBoris Brezillon  * have been generated using
54393db446aSBoris Brezillon  * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
54493db446aSBoris Brezillon  * the randomizer engine does internally before de/scrambling OOB data.
54593db446aSBoris Brezillon  *
54693db446aSBoris Brezillon  * Those tables are statically defined to avoid calculating randomizer state
54793db446aSBoris Brezillon  * at runtime.
54893db446aSBoris Brezillon  */
54993db446aSBoris Brezillon static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
55093db446aSBoris Brezillon 	0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
55193db446aSBoris Brezillon 	0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
55293db446aSBoris Brezillon 	0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
55393db446aSBoris Brezillon 	0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
55493db446aSBoris Brezillon 	0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
55593db446aSBoris Brezillon 	0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
55693db446aSBoris Brezillon 	0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
55793db446aSBoris Brezillon 	0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
55893db446aSBoris Brezillon 	0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
55993db446aSBoris Brezillon 	0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
56093db446aSBoris Brezillon 	0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
56193db446aSBoris Brezillon 	0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
56293db446aSBoris Brezillon 	0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
56393db446aSBoris Brezillon 	0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
56493db446aSBoris Brezillon 	0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
56593db446aSBoris Brezillon 	0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
56693db446aSBoris Brezillon };
56793db446aSBoris Brezillon 
56893db446aSBoris Brezillon static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
56993db446aSBoris Brezillon 	0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
57093db446aSBoris Brezillon 	0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
57193db446aSBoris Brezillon 	0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
57293db446aSBoris Brezillon 	0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
57393db446aSBoris Brezillon 	0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
57493db446aSBoris Brezillon 	0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
57593db446aSBoris Brezillon 	0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
57693db446aSBoris Brezillon 	0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
57793db446aSBoris Brezillon 	0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
57893db446aSBoris Brezillon 	0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
57993db446aSBoris Brezillon 	0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
58093db446aSBoris Brezillon 	0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
58193db446aSBoris Brezillon 	0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
58293db446aSBoris Brezillon 	0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
58393db446aSBoris Brezillon 	0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
58493db446aSBoris Brezillon 	0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
58593db446aSBoris Brezillon };
58693db446aSBoris Brezillon 
sunxi_nfc_randomizer_step(u16 state,int count)58793db446aSBoris Brezillon static u16 sunxi_nfc_randomizer_step(u16 state, int count)
58893db446aSBoris Brezillon {
58993db446aSBoris Brezillon 	state &= 0x7fff;
59093db446aSBoris Brezillon 
59193db446aSBoris Brezillon 	/*
59293db446aSBoris Brezillon 	 * This loop is just a simple implementation of a Fibonacci LFSR using
59393db446aSBoris Brezillon 	 * the x16 + x15 + 1 polynomial.
59493db446aSBoris Brezillon 	 */
59593db446aSBoris Brezillon 	while (count--)
59693db446aSBoris Brezillon 		state = ((state >> 1) |
59793db446aSBoris Brezillon 			 (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
59893db446aSBoris Brezillon 
59993db446aSBoris Brezillon 	return state;
60093db446aSBoris Brezillon }
60193db446aSBoris Brezillon 
sunxi_nfc_randomizer_state(struct nand_chip * nand,int page,bool ecc)602cde567e3SBoris Brezillon static u16 sunxi_nfc_randomizer_state(struct nand_chip *nand, int page,
603cde567e3SBoris Brezillon 				      bool ecc)
60493db446aSBoris Brezillon {
605cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
60693db446aSBoris Brezillon 	const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
60793db446aSBoris Brezillon 	int mod = mtd_div_by_ws(mtd->erasesize, mtd);
60893db446aSBoris Brezillon 
60993db446aSBoris Brezillon 	if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
61093db446aSBoris Brezillon 		mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
61193db446aSBoris Brezillon 
61293db446aSBoris Brezillon 	if (ecc) {
61393db446aSBoris Brezillon 		if (mtd->ecc_step_size == 512)
61493db446aSBoris Brezillon 			seeds = sunxi_nfc_randomizer_ecc512_seeds;
61593db446aSBoris Brezillon 		else
61693db446aSBoris Brezillon 			seeds = sunxi_nfc_randomizer_ecc1024_seeds;
61793db446aSBoris Brezillon 	}
61893db446aSBoris Brezillon 
61993db446aSBoris Brezillon 	return seeds[page % mod];
62093db446aSBoris Brezillon }
62193db446aSBoris Brezillon 
sunxi_nfc_randomizer_config(struct nand_chip * nand,int page,bool ecc)622cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page,
623cde567e3SBoris Brezillon 					bool ecc)
62493db446aSBoris Brezillon {
62593db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
62693db446aSBoris Brezillon 	u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
62793db446aSBoris Brezillon 	u16 state;
62893db446aSBoris Brezillon 
62993db446aSBoris Brezillon 	if (!(nand->options & NAND_NEED_SCRAMBLING))
63093db446aSBoris Brezillon 		return;
63193db446aSBoris Brezillon 
63293db446aSBoris Brezillon 	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
633cde567e3SBoris Brezillon 	state = sunxi_nfc_randomizer_state(nand, page, ecc);
63493db446aSBoris Brezillon 	ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
63593db446aSBoris Brezillon 	writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
63693db446aSBoris Brezillon }
63793db446aSBoris Brezillon 
sunxi_nfc_randomizer_enable(struct nand_chip * nand)638cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_enable(struct nand_chip *nand)
63993db446aSBoris Brezillon {
64093db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
64193db446aSBoris Brezillon 
64293db446aSBoris Brezillon 	if (!(nand->options & NAND_NEED_SCRAMBLING))
64393db446aSBoris Brezillon 		return;
64493db446aSBoris Brezillon 
64593db446aSBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
64693db446aSBoris Brezillon 	       nfc->regs + NFC_REG_ECC_CTL);
64793db446aSBoris Brezillon }
64893db446aSBoris Brezillon 
sunxi_nfc_randomizer_disable(struct nand_chip * nand)649cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_disable(struct nand_chip *nand)
65093db446aSBoris Brezillon {
65193db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
65293db446aSBoris Brezillon 
65393db446aSBoris Brezillon 	if (!(nand->options & NAND_NEED_SCRAMBLING))
65493db446aSBoris Brezillon 		return;
65593db446aSBoris Brezillon 
65693db446aSBoris Brezillon 	writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
65793db446aSBoris Brezillon 	       nfc->regs + NFC_REG_ECC_CTL);
65893db446aSBoris Brezillon }
65993db446aSBoris Brezillon 
sunxi_nfc_randomize_bbm(struct nand_chip * nand,int page,u8 * bbm)660cde567e3SBoris Brezillon static void sunxi_nfc_randomize_bbm(struct nand_chip *nand, int page, u8 *bbm)
66193db446aSBoris Brezillon {
662cde567e3SBoris Brezillon 	u16 state = sunxi_nfc_randomizer_state(nand, page, true);
66393db446aSBoris Brezillon 
66493db446aSBoris Brezillon 	bbm[0] ^= state;
66593db446aSBoris Brezillon 	bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
66693db446aSBoris Brezillon }
66793db446aSBoris Brezillon 
sunxi_nfc_randomizer_write_buf(struct nand_chip * nand,const uint8_t * buf,int len,bool ecc,int page)668cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_write_buf(struct nand_chip *nand,
66993db446aSBoris Brezillon 					   const uint8_t *buf, int len,
67093db446aSBoris Brezillon 					   bool ecc, int page)
67193db446aSBoris Brezillon {
672cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, ecc);
673cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
674cde567e3SBoris Brezillon 	sunxi_nfc_write_buf(nand, buf, len);
675cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
67693db446aSBoris Brezillon }
67793db446aSBoris Brezillon 
sunxi_nfc_randomizer_read_buf(struct nand_chip * nand,uint8_t * buf,int len,bool ecc,int page)678cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf,
67993db446aSBoris Brezillon 					  int len, bool ecc, int page)
68093db446aSBoris Brezillon {
681cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, ecc);
682cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
683cde567e3SBoris Brezillon 	sunxi_nfc_read_buf(nand, buf, len);
684cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
68593db446aSBoris Brezillon }
68693db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_enable(struct nand_chip * nand)687cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand)
68893db446aSBoris Brezillon {
689cbd87780SMiquel Raynal 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
69093db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
69193db446aSBoris Brezillon 
692ef3e6327SSamuel Holland 	writel(sunxi_nand->ecc.ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
69393db446aSBoris Brezillon }
69493db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_disable(struct nand_chip * nand)695cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand)
69693db446aSBoris Brezillon {
69793db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
69893db446aSBoris Brezillon 
699ef3e6327SSamuel Holland 	writel(0, nfc->regs + NFC_REG_ECC_CTL);
70093db446aSBoris Brezillon }
70193db446aSBoris Brezillon 
sunxi_nfc_user_data_to_buf(u32 user_data,u8 * buf)70293db446aSBoris Brezillon static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
70393db446aSBoris Brezillon {
70493db446aSBoris Brezillon 	buf[0] = user_data;
70593db446aSBoris Brezillon 	buf[1] = user_data >> 8;
70693db446aSBoris Brezillon 	buf[2] = user_data >> 16;
70793db446aSBoris Brezillon 	buf[3] = user_data >> 24;
70893db446aSBoris Brezillon }
70993db446aSBoris Brezillon 
sunxi_nfc_buf_to_user_data(const u8 * buf)71093db446aSBoris Brezillon static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
71193db446aSBoris Brezillon {
71293db446aSBoris Brezillon 	return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
71393db446aSBoris Brezillon }
71493db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip * nand,u8 * oob,int step,bool bbm,int page)715cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
71693db446aSBoris Brezillon 						int step, bool bbm, int page)
71793db446aSBoris Brezillon {
71893db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
71993db446aSBoris Brezillon 
72093db446aSBoris Brezillon 	sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
72193db446aSBoris Brezillon 				   oob);
72293db446aSBoris Brezillon 
72393db446aSBoris Brezillon 	/* De-randomize the Bad Block Marker. */
72493db446aSBoris Brezillon 	if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
725cde567e3SBoris Brezillon 		sunxi_nfc_randomize_bbm(nand, page, oob);
72693db446aSBoris Brezillon }
72793db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip * nand,const u8 * oob,int step,bool bbm,int page)728cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
72993db446aSBoris Brezillon 						const u8 *oob, int step,
73093db446aSBoris Brezillon 						bool bbm, int page)
73193db446aSBoris Brezillon {
73293db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
73393db446aSBoris Brezillon 	u8 user_data[4];
73493db446aSBoris Brezillon 
73593db446aSBoris Brezillon 	/* Randomize the Bad Block Marker. */
73693db446aSBoris Brezillon 	if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
73793db446aSBoris Brezillon 		memcpy(user_data, oob, sizeof(user_data));
738cde567e3SBoris Brezillon 		sunxi_nfc_randomize_bbm(nand, page, user_data);
73993db446aSBoris Brezillon 		oob = user_data;
74093db446aSBoris Brezillon 	}
74193db446aSBoris Brezillon 
74293db446aSBoris Brezillon 	writel(sunxi_nfc_buf_to_user_data(oob),
74393db446aSBoris Brezillon 	       nfc->regs + NFC_REG_USER_DATA(step));
74493db446aSBoris Brezillon }
74593db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_update_stats(struct nand_chip * nand,unsigned int * max_bitflips,int ret)746cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
74793db446aSBoris Brezillon 					  unsigned int *max_bitflips, int ret)
74893db446aSBoris Brezillon {
749cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
750cde567e3SBoris Brezillon 
75193db446aSBoris Brezillon 	if (ret < 0) {
75293db446aSBoris Brezillon 		mtd->ecc_stats.failed++;
75393db446aSBoris Brezillon 	} else {
75493db446aSBoris Brezillon 		mtd->ecc_stats.corrected += ret;
75593db446aSBoris Brezillon 		*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
75693db446aSBoris Brezillon 	}
75793db446aSBoris Brezillon }
75893db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_correct(struct nand_chip * nand,u8 * data,u8 * oob,int step,u32 status,bool * erased)759cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
76093db446aSBoris Brezillon 				    int step, u32 status, bool *erased)
76193db446aSBoris Brezillon {
76293db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
76393db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
76493db446aSBoris Brezillon 	u32 tmp;
76593db446aSBoris Brezillon 
76693db446aSBoris Brezillon 	*erased = false;
76793db446aSBoris Brezillon 
76893db446aSBoris Brezillon 	if (status & NFC_ECC_ERR(step))
76993db446aSBoris Brezillon 		return -EBADMSG;
77093db446aSBoris Brezillon 
77193db446aSBoris Brezillon 	if (status & NFC_ECC_PAT_FOUND(step)) {
77293db446aSBoris Brezillon 		u8 pattern;
77393db446aSBoris Brezillon 
77493db446aSBoris Brezillon 		if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
77593db446aSBoris Brezillon 			pattern = 0x0;
77693db446aSBoris Brezillon 		} else {
77793db446aSBoris Brezillon 			pattern = 0xff;
77893db446aSBoris Brezillon 			*erased = true;
77993db446aSBoris Brezillon 		}
78093db446aSBoris Brezillon 
78193db446aSBoris Brezillon 		if (data)
78293db446aSBoris Brezillon 			memset(data, pattern, ecc->size);
78393db446aSBoris Brezillon 
78493db446aSBoris Brezillon 		if (oob)
78593db446aSBoris Brezillon 			memset(oob, pattern, ecc->bytes + 4);
78693db446aSBoris Brezillon 
78793db446aSBoris Brezillon 		return 0;
78893db446aSBoris Brezillon 	}
78993db446aSBoris Brezillon 
79093db446aSBoris Brezillon 	tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
79193db446aSBoris Brezillon 
79293db446aSBoris Brezillon 	return NFC_ECC_ERR_CNT(step, tmp);
79393db446aSBoris Brezillon }
79493db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_chunk(struct nand_chip * nand,u8 * data,int data_off,u8 * oob,int oob_off,int * cur_off,unsigned int * max_bitflips,bool bbm,bool oob_required,int page)795cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
79693db446aSBoris Brezillon 				       u8 *data, int data_off,
79793db446aSBoris Brezillon 				       u8 *oob, int oob_off,
79893db446aSBoris Brezillon 				       int *cur_off,
79993db446aSBoris Brezillon 				       unsigned int *max_bitflips,
80093db446aSBoris Brezillon 				       bool bbm, bool oob_required, int page)
80193db446aSBoris Brezillon {
80293db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
80393db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
80493db446aSBoris Brezillon 	int raw_mode = 0;
80593db446aSBoris Brezillon 	bool erased;
80693db446aSBoris Brezillon 	int ret;
80793db446aSBoris Brezillon 
80893db446aSBoris Brezillon 	if (*cur_off != data_off)
80993db446aSBoris Brezillon 		nand_change_read_column_op(nand, data_off, NULL, 0, false);
81093db446aSBoris Brezillon 
811cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_read_buf(nand, NULL, ecc->size, false, page);
81293db446aSBoris Brezillon 
81393db446aSBoris Brezillon 	if (data_off + ecc->size != oob_off)
81493db446aSBoris Brezillon 		nand_change_read_column_op(nand, oob_off, NULL, 0, false);
81593db446aSBoris Brezillon 
81693db446aSBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
81793db446aSBoris Brezillon 	if (ret)
81893db446aSBoris Brezillon 		return ret;
81993db446aSBoris Brezillon 
820cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
82193db446aSBoris Brezillon 	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
82293db446aSBoris Brezillon 	       nfc->regs + NFC_REG_CMD);
82393db446aSBoris Brezillon 
82493db446aSBoris Brezillon 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
825cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
82693db446aSBoris Brezillon 	if (ret)
82793db446aSBoris Brezillon 		return ret;
82893db446aSBoris Brezillon 
82993db446aSBoris Brezillon 	*cur_off = oob_off + ecc->bytes + 4;
83093db446aSBoris Brezillon 
831cde567e3SBoris Brezillon 	ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0,
83293db446aSBoris Brezillon 				       readl(nfc->regs + NFC_REG_ECC_ST),
83393db446aSBoris Brezillon 				       &erased);
83493db446aSBoris Brezillon 	if (erased)
83593db446aSBoris Brezillon 		return 1;
83693db446aSBoris Brezillon 
83793db446aSBoris Brezillon 	if (ret < 0) {
83893db446aSBoris Brezillon 		/*
83993db446aSBoris Brezillon 		 * Re-read the data with the randomizer disabled to identify
84093db446aSBoris Brezillon 		 * bitflips in erased pages.
84193db446aSBoris Brezillon 		 */
84293db446aSBoris Brezillon 		if (nand->options & NAND_NEED_SCRAMBLING)
84393db446aSBoris Brezillon 			nand_change_read_column_op(nand, data_off, data,
84493db446aSBoris Brezillon 						   ecc->size, false);
84593db446aSBoris Brezillon 		else
84693db446aSBoris Brezillon 			memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
84793db446aSBoris Brezillon 				      ecc->size);
84893db446aSBoris Brezillon 
84993db446aSBoris Brezillon 		nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4,
85093db446aSBoris Brezillon 					   false);
85193db446aSBoris Brezillon 
85293db446aSBoris Brezillon 		ret = nand_check_erased_ecc_chunk(data,	ecc->size,
85393db446aSBoris Brezillon 						  oob, ecc->bytes + 4,
85493db446aSBoris Brezillon 						  NULL, 0, ecc->strength);
85593db446aSBoris Brezillon 		if (ret >= 0)
85693db446aSBoris Brezillon 			raw_mode = 1;
85793db446aSBoris Brezillon 	} else {
85893db446aSBoris Brezillon 		memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
85993db446aSBoris Brezillon 
86093db446aSBoris Brezillon 		if (oob_required) {
86193db446aSBoris Brezillon 			nand_change_read_column_op(nand, oob_off, NULL, 0,
86293db446aSBoris Brezillon 						   false);
863cde567e3SBoris Brezillon 			sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4,
86493db446aSBoris Brezillon 						      true, page);
86593db446aSBoris Brezillon 
866cde567e3SBoris Brezillon 			sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0,
86793db446aSBoris Brezillon 							    bbm, page);
86893db446aSBoris Brezillon 		}
86993db446aSBoris Brezillon 	}
87093db446aSBoris Brezillon 
871cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_update_stats(nand, max_bitflips, ret);
87293db446aSBoris Brezillon 
87393db446aSBoris Brezillon 	return raw_mode;
87493db446aSBoris Brezillon }
87593db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip * nand,u8 * oob,int * cur_off,bool randomize,int page)876cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand,
87793db446aSBoris Brezillon 					    u8 *oob, int *cur_off,
87893db446aSBoris Brezillon 					    bool randomize, int page)
87993db446aSBoris Brezillon {
880cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
88193db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
88293db446aSBoris Brezillon 	int offset = ((ecc->bytes + 4) * ecc->steps);
88393db446aSBoris Brezillon 	int len = mtd->oobsize - offset;
88493db446aSBoris Brezillon 
88593db446aSBoris Brezillon 	if (len <= 0)
88693db446aSBoris Brezillon 		return;
88793db446aSBoris Brezillon 
88893db446aSBoris Brezillon 	if (!cur_off || *cur_off != offset)
88993db446aSBoris Brezillon 		nand_change_read_column_op(nand, mtd->writesize, NULL, 0,
89093db446aSBoris Brezillon 					   false);
89193db446aSBoris Brezillon 
89293db446aSBoris Brezillon 	if (!randomize)
8937e534323SBoris Brezillon 		sunxi_nfc_read_buf(nand, oob + offset, len);
89493db446aSBoris Brezillon 	else
895cde567e3SBoris Brezillon 		sunxi_nfc_randomizer_read_buf(nand, oob + offset, len,
89693db446aSBoris Brezillon 					      false, page);
89793db446aSBoris Brezillon 
89893db446aSBoris Brezillon 	if (cur_off)
89993db446aSBoris Brezillon 		*cur_off = mtd->oobsize + mtd->writesize;
90093db446aSBoris Brezillon }
90193db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip * nand,uint8_t * buf,int oob_required,int page,int nchunks)902cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf,
90393db446aSBoris Brezillon 					    int oob_required, int page,
90493db446aSBoris Brezillon 					    int nchunks)
90593db446aSBoris Brezillon {
90693db446aSBoris Brezillon 	bool randomized = nand->options & NAND_NEED_SCRAMBLING;
90793db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
908cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
90993db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
91093db446aSBoris Brezillon 	unsigned int max_bitflips = 0;
91193db446aSBoris Brezillon 	int ret, i, raw_mode = 0;
91293db446aSBoris Brezillon 	struct scatterlist sg;
913910ef7a4SManuel Dipolt 	u32 status, wait;
91493db446aSBoris Brezillon 
91593db446aSBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
91693db446aSBoris Brezillon 	if (ret)
91793db446aSBoris Brezillon 		return ret;
91893db446aSBoris Brezillon 
919cde567e3SBoris Brezillon 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks,
92093db446aSBoris Brezillon 				       DMA_FROM_DEVICE, &sg);
92193db446aSBoris Brezillon 	if (ret)
92293db446aSBoris Brezillon 		return ret;
92393db446aSBoris Brezillon 
924cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
925cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, false);
926cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
92793db446aSBoris Brezillon 
92893db446aSBoris Brezillon 	writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
92993db446aSBoris Brezillon 	       NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
93093db446aSBoris Brezillon 
931910ef7a4SManuel Dipolt 	wait = NFC_CMD_INT_FLAG;
932910ef7a4SManuel Dipolt 
933910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma)
934910ef7a4SManuel Dipolt 		wait |= NFC_DMA_INT_FLAG;
935910ef7a4SManuel Dipolt 	else
93693db446aSBoris Brezillon 		dma_async_issue_pending(nfc->dmac);
93793db446aSBoris Brezillon 
93893db446aSBoris Brezillon 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
93993db446aSBoris Brezillon 	       nfc->regs + NFC_REG_CMD);
94093db446aSBoris Brezillon 
941910ef7a4SManuel Dipolt 	ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
942910ef7a4SManuel Dipolt 	if (ret && !nfc->caps->has_mdma)
94393db446aSBoris Brezillon 		dmaengine_terminate_all(nfc->dmac);
94493db446aSBoris Brezillon 
945cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
946cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
94793db446aSBoris Brezillon 
948cde567e3SBoris Brezillon 	sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg);
94993db446aSBoris Brezillon 
95093db446aSBoris Brezillon 	if (ret)
95193db446aSBoris Brezillon 		return ret;
95293db446aSBoris Brezillon 
95393db446aSBoris Brezillon 	status = readl(nfc->regs + NFC_REG_ECC_ST);
95493db446aSBoris Brezillon 
95593db446aSBoris Brezillon 	for (i = 0; i < nchunks; i++) {
95693db446aSBoris Brezillon 		int data_off = i * ecc->size;
95793db446aSBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
95893db446aSBoris Brezillon 		u8 *data = buf + data_off;
95993db446aSBoris Brezillon 		u8 *oob = nand->oob_poi + oob_off;
96093db446aSBoris Brezillon 		bool erased;
96193db446aSBoris Brezillon 
962cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL,
96393db446aSBoris Brezillon 					       oob_required ? oob : NULL,
96493db446aSBoris Brezillon 					       i, status, &erased);
96593db446aSBoris Brezillon 
96693db446aSBoris Brezillon 		/* ECC errors are handled in the second loop. */
96793db446aSBoris Brezillon 		if (ret < 0)
96893db446aSBoris Brezillon 			continue;
96993db446aSBoris Brezillon 
97093db446aSBoris Brezillon 		if (oob_required && !erased) {
97193db446aSBoris Brezillon 			/* TODO: use DMA to retrieve OOB */
97293db446aSBoris Brezillon 			nand_change_read_column_op(nand,
97393db446aSBoris Brezillon 						   mtd->writesize + oob_off,
97493db446aSBoris Brezillon 						   oob, ecc->bytes + 4, false);
97593db446aSBoris Brezillon 
976cde567e3SBoris Brezillon 			sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i,
97793db446aSBoris Brezillon 							    !i, page);
97893db446aSBoris Brezillon 		}
97993db446aSBoris Brezillon 
98093db446aSBoris Brezillon 		if (erased)
98193db446aSBoris Brezillon 			raw_mode = 1;
98293db446aSBoris Brezillon 
983cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
98493db446aSBoris Brezillon 	}
98593db446aSBoris Brezillon 
98693db446aSBoris Brezillon 	if (status & NFC_ECC_ERR_MSK) {
98793db446aSBoris Brezillon 		for (i = 0; i < nchunks; i++) {
98893db446aSBoris Brezillon 			int data_off = i * ecc->size;
98993db446aSBoris Brezillon 			int oob_off = i * (ecc->bytes + 4);
99093db446aSBoris Brezillon 			u8 *data = buf + data_off;
99193db446aSBoris Brezillon 			u8 *oob = nand->oob_poi + oob_off;
99293db446aSBoris Brezillon 
99393db446aSBoris Brezillon 			if (!(status & NFC_ECC_ERR(i)))
99493db446aSBoris Brezillon 				continue;
99593db446aSBoris Brezillon 
99693db446aSBoris Brezillon 			/*
99793db446aSBoris Brezillon 			 * Re-read the data with the randomizer disabled to
99893db446aSBoris Brezillon 			 * identify bitflips in erased pages.
99993db446aSBoris Brezillon 			 * TODO: use DMA to read page in raw mode
100093db446aSBoris Brezillon 			 */
100193db446aSBoris Brezillon 			if (randomized)
100293db446aSBoris Brezillon 				nand_change_read_column_op(nand, data_off,
100393db446aSBoris Brezillon 							   data, ecc->size,
100493db446aSBoris Brezillon 							   false);
100593db446aSBoris Brezillon 
100693db446aSBoris Brezillon 			/* TODO: use DMA to retrieve OOB */
100793db446aSBoris Brezillon 			nand_change_read_column_op(nand,
100893db446aSBoris Brezillon 						   mtd->writesize + oob_off,
100993db446aSBoris Brezillon 						   oob, ecc->bytes + 4, false);
101093db446aSBoris Brezillon 
101193db446aSBoris Brezillon 			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
101293db446aSBoris Brezillon 							  oob, ecc->bytes + 4,
101393db446aSBoris Brezillon 							  NULL, 0,
101493db446aSBoris Brezillon 							  ecc->strength);
101593db446aSBoris Brezillon 			if (ret >= 0)
101693db446aSBoris Brezillon 				raw_mode = 1;
101793db446aSBoris Brezillon 
1018cde567e3SBoris Brezillon 			sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
101993db446aSBoris Brezillon 		}
102093db446aSBoris Brezillon 	}
102193db446aSBoris Brezillon 
102293db446aSBoris Brezillon 	if (oob_required)
1023cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi,
102493db446aSBoris Brezillon 						NULL, !raw_mode,
102593db446aSBoris Brezillon 						page);
102693db446aSBoris Brezillon 
102793db446aSBoris Brezillon 	return max_bitflips;
102893db446aSBoris Brezillon }
102993db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_write_chunk(struct nand_chip * nand,const u8 * data,int data_off,const u8 * oob,int oob_off,int * cur_off,bool bbm,int page)1030cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
103193db446aSBoris Brezillon 					const u8 *data, int data_off,
103293db446aSBoris Brezillon 					const u8 *oob, int oob_off,
103393db446aSBoris Brezillon 					int *cur_off, bool bbm,
103493db446aSBoris Brezillon 					int page)
103593db446aSBoris Brezillon {
103693db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
103793db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
103893db446aSBoris Brezillon 	int ret;
103993db446aSBoris Brezillon 
104093db446aSBoris Brezillon 	if (data_off != *cur_off)
104193db446aSBoris Brezillon 		nand_change_write_column_op(nand, data_off, NULL, 0, false);
104293db446aSBoris Brezillon 
1043cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_write_buf(nand, data, ecc->size, false, page);
104493db446aSBoris Brezillon 
104593db446aSBoris Brezillon 	if (data_off + ecc->size != oob_off)
104693db446aSBoris Brezillon 		nand_change_write_column_op(nand, oob_off, NULL, 0, false);
104793db446aSBoris Brezillon 
104893db446aSBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
104993db446aSBoris Brezillon 	if (ret)
105093db446aSBoris Brezillon 		return ret;
105193db446aSBoris Brezillon 
1052cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
1053cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
105493db446aSBoris Brezillon 
105593db446aSBoris Brezillon 	writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
105693db446aSBoris Brezillon 	       NFC_ACCESS_DIR | NFC_ECC_OP,
105793db446aSBoris Brezillon 	       nfc->regs + NFC_REG_CMD);
105893db446aSBoris Brezillon 
105993db446aSBoris Brezillon 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
1060cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
106193db446aSBoris Brezillon 	if (ret)
106293db446aSBoris Brezillon 		return ret;
106393db446aSBoris Brezillon 
106493db446aSBoris Brezillon 	*cur_off = oob_off + ecc->bytes + 4;
106593db446aSBoris Brezillon 
106693db446aSBoris Brezillon 	return 0;
106793db446aSBoris Brezillon }
106893db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip * nand,u8 * oob,int * cur_off,int page)1069cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand,
107093db446aSBoris Brezillon 					     u8 *oob, int *cur_off,
107193db446aSBoris Brezillon 					     int page)
107293db446aSBoris Brezillon {
1073cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
107493db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
107593db446aSBoris Brezillon 	int offset = ((ecc->bytes + 4) * ecc->steps);
107693db446aSBoris Brezillon 	int len = mtd->oobsize - offset;
107793db446aSBoris Brezillon 
107893db446aSBoris Brezillon 	if (len <= 0)
107993db446aSBoris Brezillon 		return;
108093db446aSBoris Brezillon 
108193db446aSBoris Brezillon 	if (!cur_off || *cur_off != offset)
108293db446aSBoris Brezillon 		nand_change_write_column_op(nand, offset + mtd->writesize,
108393db446aSBoris Brezillon 					    NULL, 0, false);
108493db446aSBoris Brezillon 
1085cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_write_buf(nand, oob + offset, len, false, page);
108693db446aSBoris Brezillon 
108793db446aSBoris Brezillon 	if (cur_off)
108893db446aSBoris Brezillon 		*cur_off = mtd->oobsize + mtd->writesize;
108993db446aSBoris Brezillon }
109093db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_page(struct nand_chip * nand,uint8_t * buf,int oob_required,int page)1091a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
109293db446aSBoris Brezillon 				      int oob_required, int page)
109393db446aSBoris Brezillon {
1094a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1095a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
109693db446aSBoris Brezillon 	unsigned int max_bitflips = 0;
109793db446aSBoris Brezillon 	int ret, i, cur_off = 0;
109893db446aSBoris Brezillon 	bool raw_mode = false;
109993db446aSBoris Brezillon 
1100df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1101df505799SBoris Brezillon 
1102a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
110393db446aSBoris Brezillon 
1104cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
110593db446aSBoris Brezillon 
110693db446aSBoris Brezillon 	for (i = 0; i < ecc->steps; i++) {
110793db446aSBoris Brezillon 		int data_off = i * ecc->size;
110893db446aSBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
110993db446aSBoris Brezillon 		u8 *data = buf + data_off;
1110a55abb36SBoris Brezillon 		u8 *oob = nand->oob_poi + oob_off;
111193db446aSBoris Brezillon 
1112cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob,
111393db446aSBoris Brezillon 						  oob_off + mtd->writesize,
111493db446aSBoris Brezillon 						  &cur_off, &max_bitflips,
111593db446aSBoris Brezillon 						  !i, oob_required, page);
111693db446aSBoris Brezillon 		if (ret < 0)
111793db446aSBoris Brezillon 			return ret;
111893db446aSBoris Brezillon 		else if (ret)
111993db446aSBoris Brezillon 			raw_mode = true;
112093db446aSBoris Brezillon 	}
112193db446aSBoris Brezillon 
112293db446aSBoris Brezillon 	if (oob_required)
1123cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, &cur_off,
112493db446aSBoris Brezillon 						!raw_mode, page);
112593db446aSBoris Brezillon 
1126cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
112793db446aSBoris Brezillon 
112893db446aSBoris Brezillon 	return max_bitflips;
112993db446aSBoris Brezillon }
113093db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip * nand,u8 * buf,int oob_required,int page)1131a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf,
113293db446aSBoris Brezillon 					  int oob_required, int page)
113393db446aSBoris Brezillon {
113493db446aSBoris Brezillon 	int ret;
113593db446aSBoris Brezillon 
1136df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1137df505799SBoris Brezillon 
1138a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
113993db446aSBoris Brezillon 
1140cde567e3SBoris Brezillon 	ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page,
1141a55abb36SBoris Brezillon 					       nand->ecc.steps);
114293db446aSBoris Brezillon 	if (ret >= 0)
114393db446aSBoris Brezillon 		return ret;
114493db446aSBoris Brezillon 
114593db446aSBoris Brezillon 	/* Fallback to PIO mode */
1146a55abb36SBoris Brezillon 	return sunxi_nfc_hw_ecc_read_page(nand, buf, oob_required, page);
114793db446aSBoris Brezillon }
114893db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_subpage(struct nand_chip * nand,u32 data_offs,u32 readlen,u8 * bufpoi,int page)1149a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
115093db446aSBoris Brezillon 					 u32 data_offs, u32 readlen,
115193db446aSBoris Brezillon 					 u8 *bufpoi, int page)
115293db446aSBoris Brezillon {
1153a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1154a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
115593db446aSBoris Brezillon 	int ret, i, cur_off = 0;
115693db446aSBoris Brezillon 	unsigned int max_bitflips = 0;
115793db446aSBoris Brezillon 
1158df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1159df505799SBoris Brezillon 
1160a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
116193db446aSBoris Brezillon 
1162cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
116393db446aSBoris Brezillon 
116493db446aSBoris Brezillon 	for (i = data_offs / ecc->size;
116593db446aSBoris Brezillon 	     i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
116693db446aSBoris Brezillon 		int data_off = i * ecc->size;
116793db446aSBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
116893db446aSBoris Brezillon 		u8 *data = bufpoi + data_off;
1169a55abb36SBoris Brezillon 		u8 *oob = nand->oob_poi + oob_off;
117093db446aSBoris Brezillon 
1171cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off,
117293db446aSBoris Brezillon 						  oob,
117393db446aSBoris Brezillon 						  oob_off + mtd->writesize,
117493db446aSBoris Brezillon 						  &cur_off, &max_bitflips, !i,
117593db446aSBoris Brezillon 						  false, page);
117693db446aSBoris Brezillon 		if (ret < 0)
117793db446aSBoris Brezillon 			return ret;
117893db446aSBoris Brezillon 	}
117993db446aSBoris Brezillon 
1180cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
118193db446aSBoris Brezillon 
118293db446aSBoris Brezillon 	return max_bitflips;
118393db446aSBoris Brezillon }
118493db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip * nand,u32 data_offs,u32 readlen,u8 * buf,int page)1185a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand,
118693db446aSBoris Brezillon 					     u32 data_offs, u32 readlen,
118793db446aSBoris Brezillon 					     u8 *buf, int page)
118893db446aSBoris Brezillon {
1189a55abb36SBoris Brezillon 	int nchunks = DIV_ROUND_UP(data_offs + readlen, nand->ecc.size);
119093db446aSBoris Brezillon 	int ret;
119193db446aSBoris Brezillon 
1192df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1193df505799SBoris Brezillon 
1194a55abb36SBoris Brezillon 	nand_read_page_op(nand, page, 0, NULL, 0);
119593db446aSBoris Brezillon 
1196cde567e3SBoris Brezillon 	ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks);
119793db446aSBoris Brezillon 	if (ret >= 0)
119893db446aSBoris Brezillon 		return ret;
119993db446aSBoris Brezillon 
120093db446aSBoris Brezillon 	/* Fallback to PIO mode */
1201a55abb36SBoris Brezillon 	return sunxi_nfc_hw_ecc_read_subpage(nand, data_offs, readlen,
120293db446aSBoris Brezillon 					     buf, page);
120393db446aSBoris Brezillon }
120493db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_write_page(struct nand_chip * nand,const uint8_t * buf,int oob_required,int page)1205a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
120693db446aSBoris Brezillon 				       const uint8_t *buf, int oob_required,
120793db446aSBoris Brezillon 				       int page)
120893db446aSBoris Brezillon {
1209a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1210a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
121193db446aSBoris Brezillon 	int ret, i, cur_off = 0;
121293db446aSBoris Brezillon 
1213df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1214df505799SBoris Brezillon 
1215a55abb36SBoris Brezillon 	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
121693db446aSBoris Brezillon 
1217cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
121893db446aSBoris Brezillon 
121993db446aSBoris Brezillon 	for (i = 0; i < ecc->steps; i++) {
122093db446aSBoris Brezillon 		int data_off = i * ecc->size;
122193db446aSBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
122293db446aSBoris Brezillon 		const u8 *data = buf + data_off;
1223a55abb36SBoris Brezillon 		const u8 *oob = nand->oob_poi + oob_off;
122493db446aSBoris Brezillon 
1225cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
122693db446aSBoris Brezillon 						   oob_off + mtd->writesize,
122793db446aSBoris Brezillon 						   &cur_off, !i, page);
122893db446aSBoris Brezillon 		if (ret)
122993db446aSBoris Brezillon 			return ret;
123093db446aSBoris Brezillon 	}
123193db446aSBoris Brezillon 
1232a55abb36SBoris Brezillon 	if (oob_required || (nand->options & NAND_NEED_SCRAMBLING))
1233cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi,
123493db446aSBoris Brezillon 						 &cur_off, page);
123593db446aSBoris Brezillon 
1236cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
123793db446aSBoris Brezillon 
1238a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
123993db446aSBoris Brezillon }
124093db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_write_subpage(struct nand_chip * nand,u32 data_offs,u32 data_len,const u8 * buf,int oob_required,int page)1241a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
124293db446aSBoris Brezillon 					  u32 data_offs, u32 data_len,
124393db446aSBoris Brezillon 					  const u8 *buf, int oob_required,
124493db446aSBoris Brezillon 					  int page)
124593db446aSBoris Brezillon {
1246a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1247a55abb36SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
124893db446aSBoris Brezillon 	int ret, i, cur_off = 0;
124993db446aSBoris Brezillon 
1250df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1251df505799SBoris Brezillon 
1252a55abb36SBoris Brezillon 	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
125393db446aSBoris Brezillon 
1254cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
125593db446aSBoris Brezillon 
125693db446aSBoris Brezillon 	for (i = data_offs / ecc->size;
125793db446aSBoris Brezillon 	     i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
125893db446aSBoris Brezillon 		int data_off = i * ecc->size;
125993db446aSBoris Brezillon 		int oob_off = i * (ecc->bytes + 4);
126093db446aSBoris Brezillon 		const u8 *data = buf + data_off;
1261a55abb36SBoris Brezillon 		const u8 *oob = nand->oob_poi + oob_off;
126293db446aSBoris Brezillon 
1263cde567e3SBoris Brezillon 		ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
126493db446aSBoris Brezillon 						   oob_off + mtd->writesize,
126593db446aSBoris Brezillon 						   &cur_off, !i, page);
126693db446aSBoris Brezillon 		if (ret)
126793db446aSBoris Brezillon 			return ret;
126893db446aSBoris Brezillon 	}
126993db446aSBoris Brezillon 
1270cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
127193db446aSBoris Brezillon 
1272a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
127393db446aSBoris Brezillon }
127493db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip * nand,const u8 * buf,int oob_required,int page)1275a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
127693db446aSBoris Brezillon 					   const u8 *buf,
127793db446aSBoris Brezillon 					   int oob_required,
127893db446aSBoris Brezillon 					   int page)
127993db446aSBoris Brezillon {
128093db446aSBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
128193db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
128293db446aSBoris Brezillon 	struct scatterlist sg;
1283910ef7a4SManuel Dipolt 	u32 wait;
128493db446aSBoris Brezillon 	int ret, i;
128593db446aSBoris Brezillon 
1286df505799SBoris Brezillon 	sunxi_nfc_select_chip(nand, nand->cur_cs);
1287df505799SBoris Brezillon 
128893db446aSBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
128993db446aSBoris Brezillon 	if (ret)
129093db446aSBoris Brezillon 		return ret;
129193db446aSBoris Brezillon 
1292cde567e3SBoris Brezillon 	ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps,
129393db446aSBoris Brezillon 				       DMA_TO_DEVICE, &sg);
129493db446aSBoris Brezillon 	if (ret)
129593db446aSBoris Brezillon 		goto pio_fallback;
129693db446aSBoris Brezillon 
129793db446aSBoris Brezillon 	for (i = 0; i < ecc->steps; i++) {
129893db446aSBoris Brezillon 		const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
129993db446aSBoris Brezillon 
1300cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
130193db446aSBoris Brezillon 	}
130293db446aSBoris Brezillon 
1303a55abb36SBoris Brezillon 	nand_prog_page_begin_op(nand, page, 0, NULL, 0);
130493db446aSBoris Brezillon 
1305cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_enable(nand);
1306cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_config(nand, page, false);
1307cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_enable(nand);
130893db446aSBoris Brezillon 
130993db446aSBoris Brezillon 	writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
131073277443SBoris Brezillon 	       nfc->regs + NFC_REG_WCMD_SET);
131193db446aSBoris Brezillon 
1312910ef7a4SManuel Dipolt 	wait = NFC_CMD_INT_FLAG;
1313910ef7a4SManuel Dipolt 
1314910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma)
1315910ef7a4SManuel Dipolt 		wait |= NFC_DMA_INT_FLAG;
1316910ef7a4SManuel Dipolt 	else
131793db446aSBoris Brezillon 		dma_async_issue_pending(nfc->dmac);
131893db446aSBoris Brezillon 
131993db446aSBoris Brezillon 	writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
132093db446aSBoris Brezillon 	       NFC_DATA_TRANS | NFC_ACCESS_DIR,
132193db446aSBoris Brezillon 	       nfc->regs + NFC_REG_CMD);
132293db446aSBoris Brezillon 
1323910ef7a4SManuel Dipolt 	ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
1324910ef7a4SManuel Dipolt 	if (ret && !nfc->caps->has_mdma)
132593db446aSBoris Brezillon 		dmaengine_terminate_all(nfc->dmac);
132693db446aSBoris Brezillon 
1327cde567e3SBoris Brezillon 	sunxi_nfc_randomizer_disable(nand);
1328cde567e3SBoris Brezillon 	sunxi_nfc_hw_ecc_disable(nand);
132993db446aSBoris Brezillon 
1330cde567e3SBoris Brezillon 	sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg);
133193db446aSBoris Brezillon 
133293db446aSBoris Brezillon 	if (ret)
133393db446aSBoris Brezillon 		return ret;
133493db446aSBoris Brezillon 
1335a55abb36SBoris Brezillon 	if (oob_required || (nand->options & NAND_NEED_SCRAMBLING))
133693db446aSBoris Brezillon 		/* TODO: use DMA to transfer extra OOB bytes ? */
1337cde567e3SBoris Brezillon 		sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi,
133893db446aSBoris Brezillon 						 NULL, page);
133993db446aSBoris Brezillon 
1340a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
134193db446aSBoris Brezillon 
134293db446aSBoris Brezillon pio_fallback:
1343a55abb36SBoris Brezillon 	return sunxi_nfc_hw_ecc_write_page(nand, buf, oob_required, page);
134493db446aSBoris Brezillon }
134593db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_read_oob(struct nand_chip * nand,int page)1346a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page)
134793db446aSBoris Brezillon {
1348eeab7174SBoris Brezillon 	u8 *buf = nand_get_data_buf(nand);
134993db446aSBoris Brezillon 
1350eeab7174SBoris Brezillon 	return nand->ecc.read_page(nand, buf, 1, page);
135193db446aSBoris Brezillon }
135293db446aSBoris Brezillon 
sunxi_nfc_hw_ecc_write_oob(struct nand_chip * nand,int page)1353a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
135493db446aSBoris Brezillon {
1355a55abb36SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1356eeab7174SBoris Brezillon 	u8 *buf = nand_get_data_buf(nand);
135793db446aSBoris Brezillon 	int ret;
135893db446aSBoris Brezillon 
1359eeab7174SBoris Brezillon 	memset(buf, 0xff, mtd->writesize);
1360eeab7174SBoris Brezillon 	ret = nand->ecc.write_page(nand, buf, 1, page);
136193db446aSBoris Brezillon 	if (ret)
136293db446aSBoris Brezillon 		return ret;
136393db446aSBoris Brezillon 
136493db446aSBoris Brezillon 	/* Send command to program the OOB data */
1365a55abb36SBoris Brezillon 	return nand_prog_page_end_op(nand);
136693db446aSBoris Brezillon }
136793db446aSBoris Brezillon 
136893db446aSBoris Brezillon static const s32 tWB_lut[] = {6, 12, 16, 20};
136993db446aSBoris Brezillon static const s32 tRHW_lut[] = {4, 8, 12, 20};
137093db446aSBoris Brezillon 
_sunxi_nand_lookup_timing(const s32 * lut,int lut_size,u32 duration,u32 clk_period)137193db446aSBoris Brezillon static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
137293db446aSBoris Brezillon 		u32 clk_period)
137393db446aSBoris Brezillon {
137493db446aSBoris Brezillon 	u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
137593db446aSBoris Brezillon 	int i;
137693db446aSBoris Brezillon 
137793db446aSBoris Brezillon 	for (i = 0; i < lut_size; i++) {
137893db446aSBoris Brezillon 		if (clk_cycles <= lut[i])
137993db446aSBoris Brezillon 			return i;
138093db446aSBoris Brezillon 	}
138193db446aSBoris Brezillon 
138293db446aSBoris Brezillon 	/* Doesn't fit */
138393db446aSBoris Brezillon 	return -EINVAL;
138493db446aSBoris Brezillon }
138593db446aSBoris Brezillon 
138693db446aSBoris Brezillon #define sunxi_nand_lookup_timing(l, p, c) \
138793db446aSBoris Brezillon 			_sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
138893db446aSBoris Brezillon 
sunxi_nfc_setup_interface(struct nand_chip * nand,int csline,const struct nand_interface_config * conf)13894c46667bSMiquel Raynal static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
13904c46667bSMiquel Raynal 				     const struct nand_interface_config *conf)
139193db446aSBoris Brezillon {
1392f385ebf0SBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1393f385ebf0SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
139493db446aSBoris Brezillon 	const struct nand_sdr_timings *timings;
139593db446aSBoris Brezillon 	u32 min_clk_period = 0;
139693db446aSBoris Brezillon 	s32 tWB, tADL, tWHR, tRHW, tCAD;
139793db446aSBoris Brezillon 	long real_clk_rate;
139893db446aSBoris Brezillon 
139993db446aSBoris Brezillon 	timings = nand_get_sdr_timings(conf);
140093db446aSBoris Brezillon 	if (IS_ERR(timings))
140193db446aSBoris Brezillon 		return -ENOTSUPP;
140293db446aSBoris Brezillon 
140393db446aSBoris Brezillon 	/* T1 <=> tCLS */
140493db446aSBoris Brezillon 	if (timings->tCLS_min > min_clk_period)
140593db446aSBoris Brezillon 		min_clk_period = timings->tCLS_min;
140693db446aSBoris Brezillon 
140793db446aSBoris Brezillon 	/* T2 <=> tCLH */
140893db446aSBoris Brezillon 	if (timings->tCLH_min > min_clk_period)
140993db446aSBoris Brezillon 		min_clk_period = timings->tCLH_min;
141093db446aSBoris Brezillon 
141193db446aSBoris Brezillon 	/* T3 <=> tCS */
141293db446aSBoris Brezillon 	if (timings->tCS_min > min_clk_period)
141393db446aSBoris Brezillon 		min_clk_period = timings->tCS_min;
141493db446aSBoris Brezillon 
141593db446aSBoris Brezillon 	/* T4 <=> tCH */
141693db446aSBoris Brezillon 	if (timings->tCH_min > min_clk_period)
141793db446aSBoris Brezillon 		min_clk_period = timings->tCH_min;
141893db446aSBoris Brezillon 
141993db446aSBoris Brezillon 	/* T5 <=> tWP */
142093db446aSBoris Brezillon 	if (timings->tWP_min > min_clk_period)
142193db446aSBoris Brezillon 		min_clk_period = timings->tWP_min;
142293db446aSBoris Brezillon 
142393db446aSBoris Brezillon 	/* T6 <=> tWH */
142493db446aSBoris Brezillon 	if (timings->tWH_min > min_clk_period)
142593db446aSBoris Brezillon 		min_clk_period = timings->tWH_min;
142693db446aSBoris Brezillon 
142793db446aSBoris Brezillon 	/* T7 <=> tALS */
142893db446aSBoris Brezillon 	if (timings->tALS_min > min_clk_period)
142993db446aSBoris Brezillon 		min_clk_period = timings->tALS_min;
143093db446aSBoris Brezillon 
143193db446aSBoris Brezillon 	/* T8 <=> tDS */
143293db446aSBoris Brezillon 	if (timings->tDS_min > min_clk_period)
143393db446aSBoris Brezillon 		min_clk_period = timings->tDS_min;
143493db446aSBoris Brezillon 
143593db446aSBoris Brezillon 	/* T9 <=> tDH */
143693db446aSBoris Brezillon 	if (timings->tDH_min > min_clk_period)
143793db446aSBoris Brezillon 		min_clk_period = timings->tDH_min;
143893db446aSBoris Brezillon 
143993db446aSBoris Brezillon 	/* T10 <=> tRR */
144093db446aSBoris Brezillon 	if (timings->tRR_min > (min_clk_period * 3))
144193db446aSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
144293db446aSBoris Brezillon 
144393db446aSBoris Brezillon 	/* T11 <=> tALH */
144493db446aSBoris Brezillon 	if (timings->tALH_min > min_clk_period)
144593db446aSBoris Brezillon 		min_clk_period = timings->tALH_min;
144693db446aSBoris Brezillon 
144793db446aSBoris Brezillon 	/* T12 <=> tRP */
144893db446aSBoris Brezillon 	if (timings->tRP_min > min_clk_period)
144993db446aSBoris Brezillon 		min_clk_period = timings->tRP_min;
145093db446aSBoris Brezillon 
145193db446aSBoris Brezillon 	/* T13 <=> tREH */
145293db446aSBoris Brezillon 	if (timings->tREH_min > min_clk_period)
145393db446aSBoris Brezillon 		min_clk_period = timings->tREH_min;
145493db446aSBoris Brezillon 
145593db446aSBoris Brezillon 	/* T14 <=> tRC */
145693db446aSBoris Brezillon 	if (timings->tRC_min > (min_clk_period * 2))
145793db446aSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
145893db446aSBoris Brezillon 
145993db446aSBoris Brezillon 	/* T15 <=> tWC */
146093db446aSBoris Brezillon 	if (timings->tWC_min > (min_clk_period * 2))
146193db446aSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
146293db446aSBoris Brezillon 
146393db446aSBoris Brezillon 	/* T16 - T19 + tCAD */
146493db446aSBoris Brezillon 	if (timings->tWB_max > (min_clk_period * 20))
146593db446aSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
146693db446aSBoris Brezillon 
146793db446aSBoris Brezillon 	if (timings->tADL_min > (min_clk_period * 32))
146893db446aSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
146993db446aSBoris Brezillon 
147093db446aSBoris Brezillon 	if (timings->tWHR_min > (min_clk_period * 32))
147193db446aSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
147293db446aSBoris Brezillon 
147393db446aSBoris Brezillon 	if (timings->tRHW_min > (min_clk_period * 20))
147493db446aSBoris Brezillon 		min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
147593db446aSBoris Brezillon 
1476511d05e0SBoris Brezillon 	/*
1477511d05e0SBoris Brezillon 	 * In non-EDO, tREA should be less than tRP to guarantee that the
1478511d05e0SBoris Brezillon 	 * controller does not sample the IO lines too early. Unfortunately,
1479511d05e0SBoris Brezillon 	 * the sunxi NAND controller does not allow us to have different
1480511d05e0SBoris Brezillon 	 * values for tRP and tREH (tRP = tREH = tRW / 2).
1481511d05e0SBoris Brezillon 	 *
1482511d05e0SBoris Brezillon 	 * We have 2 options to overcome this limitation:
1483511d05e0SBoris Brezillon 	 *
1484511d05e0SBoris Brezillon 	 * 1/ Extend tRC to fulfil the tREA <= tRC / 2 constraint
1485511d05e0SBoris Brezillon 	 * 2/ Use EDO mode (only works if timings->tRLOH > 0)
1486511d05e0SBoris Brezillon 	 */
1487511d05e0SBoris Brezillon 	if (timings->tREA_max > min_clk_period && !timings->tRLOH_min)
1488511d05e0SBoris Brezillon 		min_clk_period = timings->tREA_max;
1489511d05e0SBoris Brezillon 
149093db446aSBoris Brezillon 	tWB  = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
149193db446aSBoris Brezillon 					min_clk_period);
149293db446aSBoris Brezillon 	if (tWB < 0) {
149393db446aSBoris Brezillon 		dev_err(nfc->dev, "unsupported tWB\n");
149493db446aSBoris Brezillon 		return tWB;
149593db446aSBoris Brezillon 	}
149693db446aSBoris Brezillon 
149793db446aSBoris Brezillon 	tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
149893db446aSBoris Brezillon 	if (tADL > 3) {
149993db446aSBoris Brezillon 		dev_err(nfc->dev, "unsupported tADL\n");
150093db446aSBoris Brezillon 		return -EINVAL;
150193db446aSBoris Brezillon 	}
150293db446aSBoris Brezillon 
150393db446aSBoris Brezillon 	tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
150493db446aSBoris Brezillon 	if (tWHR > 3) {
150593db446aSBoris Brezillon 		dev_err(nfc->dev, "unsupported tWHR\n");
150693db446aSBoris Brezillon 		return -EINVAL;
150793db446aSBoris Brezillon 	}
150893db446aSBoris Brezillon 
150993db446aSBoris Brezillon 	tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
151093db446aSBoris Brezillon 					min_clk_period);
151193db446aSBoris Brezillon 	if (tRHW < 0) {
151293db446aSBoris Brezillon 		dev_err(nfc->dev, "unsupported tRHW\n");
151393db446aSBoris Brezillon 		return tRHW;
151493db446aSBoris Brezillon 	}
151593db446aSBoris Brezillon 
151693db446aSBoris Brezillon 	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
151793db446aSBoris Brezillon 		return 0;
151893db446aSBoris Brezillon 
151993db446aSBoris Brezillon 	/*
152093db446aSBoris Brezillon 	 * TODO: according to ONFI specs this value only applies for DDR NAND,
152193db446aSBoris Brezillon 	 * but Allwinner seems to set this to 0x7. Mimic them for now.
152293db446aSBoris Brezillon 	 */
152393db446aSBoris Brezillon 	tCAD = 0x7;
152493db446aSBoris Brezillon 
152593db446aSBoris Brezillon 	/* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
1526f385ebf0SBoris Brezillon 	sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
152793db446aSBoris Brezillon 
152893db446aSBoris Brezillon 	/* Convert min_clk_period from picoseconds to nanoseconds */
152993db446aSBoris Brezillon 	min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
153093db446aSBoris Brezillon 
153193db446aSBoris Brezillon 	/*
153293db446aSBoris Brezillon 	 * Unlike what is stated in Allwinner datasheet, the clk_rate should
153393db446aSBoris Brezillon 	 * be set to (1 / min_clk_period), and not (2 / min_clk_period).
153493db446aSBoris Brezillon 	 * This new formula was verified with a scope and validated by
153593db446aSBoris Brezillon 	 * Allwinner engineers.
153693db446aSBoris Brezillon 	 */
1537f385ebf0SBoris Brezillon 	sunxi_nand->clk_rate = NSEC_PER_SEC / min_clk_period;
1538f385ebf0SBoris Brezillon 	real_clk_rate = clk_round_rate(nfc->mod_clk, sunxi_nand->clk_rate);
153993db446aSBoris Brezillon 	if (real_clk_rate <= 0) {
1540f385ebf0SBoris Brezillon 		dev_err(nfc->dev, "Unable to round clk %lu\n",
1541f385ebf0SBoris Brezillon 			sunxi_nand->clk_rate);
154293db446aSBoris Brezillon 		return -EINVAL;
154393db446aSBoris Brezillon 	}
154493db446aSBoris Brezillon 
1545511d05e0SBoris Brezillon 	sunxi_nand->timing_ctl = 0;
1546511d05e0SBoris Brezillon 
154793db446aSBoris Brezillon 	/*
154893db446aSBoris Brezillon 	 * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
154993db446aSBoris Brezillon 	 * output cycle timings shall be used if the host drives tRC less than
1550511d05e0SBoris Brezillon 	 * 30 ns. We should also use EDO mode if tREA is bigger than tRP.
155193db446aSBoris Brezillon 	 */
155293db446aSBoris Brezillon 	min_clk_period = NSEC_PER_SEC / real_clk_rate;
1553511d05e0SBoris Brezillon 	if (min_clk_period * 2 < 30 || min_clk_period * 1000 < timings->tREA_max)
1554511d05e0SBoris Brezillon 		sunxi_nand->timing_ctl = NFC_TIMING_CTL_EDO;
155593db446aSBoris Brezillon 
155693db446aSBoris Brezillon 	return 0;
155793db446aSBoris Brezillon }
155893db446aSBoris Brezillon 
sunxi_nand_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)155993db446aSBoris Brezillon static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
156093db446aSBoris Brezillon 				    struct mtd_oob_region *oobregion)
156193db446aSBoris Brezillon {
156293db446aSBoris Brezillon 	struct nand_chip *nand = mtd_to_nand(mtd);
156393db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
156493db446aSBoris Brezillon 
156593db446aSBoris Brezillon 	if (section >= ecc->steps)
156693db446aSBoris Brezillon 		return -ERANGE;
156793db446aSBoris Brezillon 
156893db446aSBoris Brezillon 	oobregion->offset = section * (ecc->bytes + 4) + 4;
156993db446aSBoris Brezillon 	oobregion->length = ecc->bytes;
157093db446aSBoris Brezillon 
157193db446aSBoris Brezillon 	return 0;
157293db446aSBoris Brezillon }
157393db446aSBoris Brezillon 
sunxi_nand_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)157493db446aSBoris Brezillon static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
157593db446aSBoris Brezillon 				     struct mtd_oob_region *oobregion)
157693db446aSBoris Brezillon {
157793db446aSBoris Brezillon 	struct nand_chip *nand = mtd_to_nand(mtd);
157893db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &nand->ecc;
157993db446aSBoris Brezillon 
158093db446aSBoris Brezillon 	if (section > ecc->steps)
158193db446aSBoris Brezillon 		return -ERANGE;
158293db446aSBoris Brezillon 
158393db446aSBoris Brezillon 	/*
158493db446aSBoris Brezillon 	 * The first 2 bytes are used for BB markers, hence we
158593db446aSBoris Brezillon 	 * only have 2 bytes available in the first user data
158693db446aSBoris Brezillon 	 * section.
158793db446aSBoris Brezillon 	 */
1588bace41f8SMiquel Raynal 	if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
158993db446aSBoris Brezillon 		oobregion->offset = 2;
159093db446aSBoris Brezillon 		oobregion->length = 2;
159193db446aSBoris Brezillon 
159293db446aSBoris Brezillon 		return 0;
159393db446aSBoris Brezillon 	}
159493db446aSBoris Brezillon 
15953998a461SSamuel Holland 	/*
15963998a461SSamuel Holland 	 * The controller does not provide access to OOB bytes
15973998a461SSamuel Holland 	 * past the end of the ECC data.
15983998a461SSamuel Holland 	 */
15993998a461SSamuel Holland 	if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
16003998a461SSamuel Holland 		return -ERANGE;
16013998a461SSamuel Holland 
160293db446aSBoris Brezillon 	oobregion->offset = section * (ecc->bytes + 4);
160393db446aSBoris Brezillon 
160493db446aSBoris Brezillon 	if (section < ecc->steps)
160593db446aSBoris Brezillon 		oobregion->length = 4;
160693db446aSBoris Brezillon 	else
160734569d86SSamuel Holland 		oobregion->length = mtd->oobsize - oobregion->offset;
160893db446aSBoris Brezillon 
160993db446aSBoris Brezillon 	return 0;
161093db446aSBoris Brezillon }
161193db446aSBoris Brezillon 
161293db446aSBoris Brezillon static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
161393db446aSBoris Brezillon 	.ecc = sunxi_nand_ooblayout_ecc,
161493db446aSBoris Brezillon 	.free = sunxi_nand_ooblayout_free,
161593db446aSBoris Brezillon };
161693db446aSBoris Brezillon 
sunxi_nand_hw_ecc_ctrl_init(struct nand_chip * nand,struct nand_ecc_ctrl * ecc,struct device_node * np)1617cde567e3SBoris Brezillon static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
161893db446aSBoris Brezillon 				       struct nand_ecc_ctrl *ecc,
161993db446aSBoris Brezillon 				       struct device_node *np)
162093db446aSBoris Brezillon {
162193db446aSBoris Brezillon 	static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
1622cbd87780SMiquel Raynal 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1623cde567e3SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1624cde567e3SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(nand);
1625b5156335SMiquel Raynal 	struct nand_device *nanddev = mtd_to_nanddev(mtd);
162693db446aSBoris Brezillon 	int nsectors;
162793db446aSBoris Brezillon 	int i;
162893db446aSBoris Brezillon 
1629b5156335SMiquel Raynal 	if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
163093db446aSBoris Brezillon 		int bytes;
163193db446aSBoris Brezillon 
163293db446aSBoris Brezillon 		ecc->size = 1024;
163393db446aSBoris Brezillon 		nsectors = mtd->writesize / ecc->size;
163493db446aSBoris Brezillon 
163593db446aSBoris Brezillon 		/* Reserve 2 bytes for the BBM */
163693db446aSBoris Brezillon 		bytes = (mtd->oobsize - 2) / nsectors;
163793db446aSBoris Brezillon 
163893db446aSBoris Brezillon 		/* 4 non-ECC bytes are added before each ECC bytes section */
163993db446aSBoris Brezillon 		bytes -= 4;
164093db446aSBoris Brezillon 
164193db446aSBoris Brezillon 		/* and bytes has to be even. */
164293db446aSBoris Brezillon 		if (bytes % 2)
164393db446aSBoris Brezillon 			bytes--;
164493db446aSBoris Brezillon 
164593db446aSBoris Brezillon 		ecc->strength = bytes * 8 / fls(8 * ecc->size);
164693db446aSBoris Brezillon 
164793db446aSBoris Brezillon 		for (i = 0; i < ARRAY_SIZE(strengths); i++) {
164893db446aSBoris Brezillon 			if (strengths[i] > ecc->strength)
164993db446aSBoris Brezillon 				break;
165093db446aSBoris Brezillon 		}
165193db446aSBoris Brezillon 
165293db446aSBoris Brezillon 		if (!i)
165393db446aSBoris Brezillon 			ecc->strength = 0;
165493db446aSBoris Brezillon 		else
165593db446aSBoris Brezillon 			ecc->strength = strengths[i - 1];
165693db446aSBoris Brezillon 	}
165793db446aSBoris Brezillon 
165893db446aSBoris Brezillon 	if (ecc->size != 512 && ecc->size != 1024)
165993db446aSBoris Brezillon 		return -EINVAL;
166093db446aSBoris Brezillon 
166193db446aSBoris Brezillon 	/* Prefer 1k ECC chunk over 512 ones */
166293db446aSBoris Brezillon 	if (ecc->size == 512 && mtd->writesize > 512) {
166393db446aSBoris Brezillon 		ecc->size = 1024;
166493db446aSBoris Brezillon 		ecc->strength *= 2;
166593db446aSBoris Brezillon 	}
166693db446aSBoris Brezillon 
166793db446aSBoris Brezillon 	/* Add ECC info retrieval from DT */
166893db446aSBoris Brezillon 	for (i = 0; i < ARRAY_SIZE(strengths); i++) {
166993db446aSBoris Brezillon 		if (ecc->strength <= strengths[i]) {
167093db446aSBoris Brezillon 			/*
167193db446aSBoris Brezillon 			 * Update ecc->strength value with the actual strength
167293db446aSBoris Brezillon 			 * that will be used by the ECC engine.
167393db446aSBoris Brezillon 			 */
167493db446aSBoris Brezillon 			ecc->strength = strengths[i];
167593db446aSBoris Brezillon 			break;
167693db446aSBoris Brezillon 		}
167793db446aSBoris Brezillon 	}
167893db446aSBoris Brezillon 
167993db446aSBoris Brezillon 	if (i >= ARRAY_SIZE(strengths)) {
168093db446aSBoris Brezillon 		dev_err(nfc->dev, "unsupported strength\n");
1681ac1c7072SSamuel Holland 		return -ENOTSUPP;
168293db446aSBoris Brezillon 	}
168393db446aSBoris Brezillon 
168493db446aSBoris Brezillon 	/* HW ECC always request ECC bytes for 1024 bytes blocks */
168593db446aSBoris Brezillon 	ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
168693db446aSBoris Brezillon 
168793db446aSBoris Brezillon 	/* HW ECC always work with even numbers of ECC bytes */
168893db446aSBoris Brezillon 	ecc->bytes = ALIGN(ecc->bytes, 2);
168993db446aSBoris Brezillon 
169093db446aSBoris Brezillon 	nsectors = mtd->writesize / ecc->size;
169193db446aSBoris Brezillon 
1692ac1c7072SSamuel Holland 	if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
1693ac1c7072SSamuel Holland 		return -EINVAL;
169493db446aSBoris Brezillon 
169515d6f118SBoris Brezillon 	ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
169615d6f118SBoris Brezillon 	ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
169793db446aSBoris Brezillon 	mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
169893db446aSBoris Brezillon 
1699910ef7a4SManuel Dipolt 	if (nfc->dmac || nfc->caps->has_mdma) {
170093db446aSBoris Brezillon 		ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
170193db446aSBoris Brezillon 		ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
170293db446aSBoris Brezillon 		ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
1703ce8148d7SMiquel Raynal 		nand->options |= NAND_USES_DMA;
170493db446aSBoris Brezillon 	} else {
170593db446aSBoris Brezillon 		ecc->read_page = sunxi_nfc_hw_ecc_read_page;
170693db446aSBoris Brezillon 		ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
170793db446aSBoris Brezillon 		ecc->write_page = sunxi_nfc_hw_ecc_write_page;
170893db446aSBoris Brezillon 	}
170993db446aSBoris Brezillon 
171093db446aSBoris Brezillon 	/* TODO: support DMA for raw accesses and subpage write */
171193db446aSBoris Brezillon 	ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
171293db446aSBoris Brezillon 	ecc->read_oob_raw = nand_read_oob_std;
171393db446aSBoris Brezillon 	ecc->write_oob_raw = nand_write_oob_std;
171493db446aSBoris Brezillon 
1715ef3e6327SSamuel Holland 	sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION |
1716ef3e6327SSamuel Holland 				  NFC_ECC_PIPELINE | NFC_ECC_EN;
1717ef3e6327SSamuel Holland 
1718ef3e6327SSamuel Holland 	if (ecc->size == 512)
1719ef3e6327SSamuel Holland 		sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512;
1720ef3e6327SSamuel Holland 
172193db446aSBoris Brezillon 	return 0;
172293db446aSBoris Brezillon }
172393db446aSBoris Brezillon 
sunxi_nand_attach_chip(struct nand_chip * nand)17242a4d9c16SMiquel Raynal static int sunxi_nand_attach_chip(struct nand_chip *nand)
172593db446aSBoris Brezillon {
172653576c7bSMiquel Raynal 	const struct nand_ecc_props *requirements =
172753576c7bSMiquel Raynal 		nanddev_get_ecc_requirements(&nand->base);
17282a4d9c16SMiquel Raynal 	struct nand_ecc_ctrl *ecc = &nand->ecc;
17292a4d9c16SMiquel Raynal 	struct device_node *np = nand_get_flash_node(nand);
173093db446aSBoris Brezillon 	int ret;
173193db446aSBoris Brezillon 
17322a4d9c16SMiquel Raynal 	if (nand->bbt_options & NAND_BBT_USE_FLASH)
17332a4d9c16SMiquel Raynal 		nand->bbt_options |= NAND_BBT_NO_OOB;
17342a4d9c16SMiquel Raynal 
17352a4d9c16SMiquel Raynal 	if (nand->options & NAND_NEED_SCRAMBLING)
17362a4d9c16SMiquel Raynal 		nand->options |= NAND_NO_SUBPAGE_WRITE;
17372a4d9c16SMiquel Raynal 
17382a4d9c16SMiquel Raynal 	nand->options |= NAND_SUBPAGE_READ;
17392a4d9c16SMiquel Raynal 
174093db446aSBoris Brezillon 	if (!ecc->size) {
174153576c7bSMiquel Raynal 		ecc->size = requirements->step_size;
174253576c7bSMiquel Raynal 		ecc->strength = requirements->strength;
174393db446aSBoris Brezillon 	}
174493db446aSBoris Brezillon 
174593db446aSBoris Brezillon 	if (!ecc->size || !ecc->strength)
174693db446aSBoris Brezillon 		return -EINVAL;
174793db446aSBoris Brezillon 
1748bace41f8SMiquel Raynal 	switch (ecc->engine_type) {
1749bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_ON_HOST:
1750cde567e3SBoris Brezillon 		ret = sunxi_nand_hw_ecc_ctrl_init(nand, ecc, np);
175193db446aSBoris Brezillon 		if (ret)
175293db446aSBoris Brezillon 			return ret;
175393db446aSBoris Brezillon 		break;
1754bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_NONE:
1755bace41f8SMiquel Raynal 	case NAND_ECC_ENGINE_TYPE_SOFT:
175693db446aSBoris Brezillon 		break;
175793db446aSBoris Brezillon 	default:
175893db446aSBoris Brezillon 		return -EINVAL;
175993db446aSBoris Brezillon 	}
176093db446aSBoris Brezillon 
176193db446aSBoris Brezillon 	return 0;
176293db446aSBoris Brezillon }
176393db446aSBoris Brezillon 
sunxi_nfc_exec_subop(struct nand_chip * nand,const struct nand_subop * subop)1764df505799SBoris Brezillon static int sunxi_nfc_exec_subop(struct nand_chip *nand,
1765df505799SBoris Brezillon 				const struct nand_subop *subop)
1766df505799SBoris Brezillon {
1767df505799SBoris Brezillon 	struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1768df505799SBoris Brezillon 	u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = { };
1769df505799SBoris Brezillon 	unsigned int i, j, remaining, start;
1770df505799SBoris Brezillon 	void *inbuf = NULL;
1771df505799SBoris Brezillon 	int ret;
1772df505799SBoris Brezillon 
1773df505799SBoris Brezillon 	for (i = 0; i < subop->ninstrs; i++) {
1774df505799SBoris Brezillon 		const struct nand_op_instr *instr = &subop->instrs[i];
1775df505799SBoris Brezillon 
1776df505799SBoris Brezillon 		switch (instr->type) {
1777df505799SBoris Brezillon 		case NAND_OP_CMD_INSTR:
1778df505799SBoris Brezillon 			if (cmd & NFC_SEND_CMD1) {
1779df505799SBoris Brezillon 				if (WARN_ON(cmd & NFC_SEND_CMD2))
1780df505799SBoris Brezillon 					return -EINVAL;
1781df505799SBoris Brezillon 
1782df505799SBoris Brezillon 				cmd |= NFC_SEND_CMD2;
1783df505799SBoris Brezillon 				extcmd |= instr->ctx.cmd.opcode;
1784df505799SBoris Brezillon 			} else {
1785df505799SBoris Brezillon 				cmd |= NFC_SEND_CMD1 |
1786df505799SBoris Brezillon 				       NFC_CMD(instr->ctx.cmd.opcode);
1787df505799SBoris Brezillon 			}
1788df505799SBoris Brezillon 			break;
1789df505799SBoris Brezillon 
1790df505799SBoris Brezillon 		case NAND_OP_ADDR_INSTR:
1791df505799SBoris Brezillon 			remaining = nand_subop_get_num_addr_cyc(subop, i);
1792df505799SBoris Brezillon 			start = nand_subop_get_addr_start_off(subop, i);
1793df505799SBoris Brezillon 			for (j = 0; j < 8 && j + start < remaining; j++) {
1794df505799SBoris Brezillon 				u32 addr = instr->ctx.addr.addrs[j + start];
1795df505799SBoris Brezillon 
1796df505799SBoris Brezillon 				addrs[j / 4] |= addr << (j % 4) * 8;
1797df505799SBoris Brezillon 			}
1798df505799SBoris Brezillon 
1799df505799SBoris Brezillon 			if (j)
1800df505799SBoris Brezillon 				cmd |= NFC_SEND_ADR | NFC_ADR_NUM(j);
1801df505799SBoris Brezillon 
1802df505799SBoris Brezillon 			break;
1803df505799SBoris Brezillon 
1804df505799SBoris Brezillon 		case NAND_OP_DATA_IN_INSTR:
1805df505799SBoris Brezillon 		case NAND_OP_DATA_OUT_INSTR:
1806df505799SBoris Brezillon 			start = nand_subop_get_data_start_off(subop, i);
1807df505799SBoris Brezillon 			remaining = nand_subop_get_data_len(subop, i);
1808df505799SBoris Brezillon 			cnt = min_t(u32, remaining, NFC_SRAM_SIZE);
1809df505799SBoris Brezillon 			cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
1810df505799SBoris Brezillon 
1811df505799SBoris Brezillon 			if (instr->type == NAND_OP_DATA_OUT_INSTR) {
1812df505799SBoris Brezillon 				cmd |= NFC_ACCESS_DIR;
1813df505799SBoris Brezillon 				memcpy_toio(nfc->regs + NFC_RAM0_BASE,
1814df505799SBoris Brezillon 					    instr->ctx.data.buf.out + start,
1815df505799SBoris Brezillon 					    cnt);
1816df505799SBoris Brezillon 			} else {
1817df505799SBoris Brezillon 				inbuf = instr->ctx.data.buf.in + start;
1818df505799SBoris Brezillon 			}
1819df505799SBoris Brezillon 
1820df505799SBoris Brezillon 			break;
1821df505799SBoris Brezillon 
1822df505799SBoris Brezillon 		case NAND_OP_WAITRDY_INSTR:
1823df505799SBoris Brezillon 			cmd |= NFC_WAIT_FLAG;
1824df505799SBoris Brezillon 			break;
1825df505799SBoris Brezillon 		}
1826df505799SBoris Brezillon 	}
1827df505799SBoris Brezillon 
1828df505799SBoris Brezillon 	ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
1829df505799SBoris Brezillon 	if (ret)
1830df505799SBoris Brezillon 		return ret;
1831df505799SBoris Brezillon 
1832df505799SBoris Brezillon 	if (cmd & NFC_SEND_ADR) {
1833df505799SBoris Brezillon 		writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW);
1834df505799SBoris Brezillon 		writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH);
1835df505799SBoris Brezillon 	}
1836df505799SBoris Brezillon 
1837df505799SBoris Brezillon 	if (cmd & NFC_SEND_CMD2)
1838df505799SBoris Brezillon 		writel(extcmd,
1839df505799SBoris Brezillon 		       nfc->regs +
1840df505799SBoris Brezillon 		       (cmd & NFC_ACCESS_DIR ?
1841df505799SBoris Brezillon 			NFC_REG_WCMD_SET : NFC_REG_RCMD_SET));
1842df505799SBoris Brezillon 
1843df505799SBoris Brezillon 	if (cmd & NFC_DATA_TRANS)
1844df505799SBoris Brezillon 		writel(cnt, nfc->regs + NFC_REG_CNT);
1845df505799SBoris Brezillon 
1846df505799SBoris Brezillon 	writel(cmd, nfc->regs + NFC_REG_CMD);
1847df505799SBoris Brezillon 
1848df505799SBoris Brezillon 	ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG,
1849df505799SBoris Brezillon 				    !(cmd & NFC_WAIT_FLAG) && cnt < 64,
1850df505799SBoris Brezillon 				    0);
1851df505799SBoris Brezillon 	if (ret)
1852df505799SBoris Brezillon 		return ret;
1853df505799SBoris Brezillon 
1854df505799SBoris Brezillon 	if (inbuf)
1855df505799SBoris Brezillon 		memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt);
1856df505799SBoris Brezillon 
1857df505799SBoris Brezillon 	return 0;
1858df505799SBoris Brezillon }
1859df505799SBoris Brezillon 
sunxi_nfc_soft_waitrdy(struct nand_chip * nand,const struct nand_subop * subop)1860df505799SBoris Brezillon static int sunxi_nfc_soft_waitrdy(struct nand_chip *nand,
1861df505799SBoris Brezillon 				  const struct nand_subop *subop)
1862df505799SBoris Brezillon {
1863df505799SBoris Brezillon 	return nand_soft_waitrdy(nand,
1864df505799SBoris Brezillon 				 subop->instrs[0].ctx.waitrdy.timeout_ms);
1865df505799SBoris Brezillon }
1866df505799SBoris Brezillon 
1867df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_op_parser = NAND_OP_PARSER(
1868df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1869df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1870df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1871df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1872df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
1873df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
1874df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1875df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1876df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1877df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
1878df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1879df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
1880df505799SBoris Brezillon );
1881df505799SBoris Brezillon 
1882df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_norb_op_parser = NAND_OP_PARSER(
1883df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1884df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1885df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1886df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1887df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
1888df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1889df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true),
1890df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1891df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
1892df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_CMD_ELEM(true)),
1893df505799SBoris Brezillon 	NAND_OP_PARSER_PATTERN(sunxi_nfc_soft_waitrdy,
1894df505799SBoris Brezillon 			       NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
1895df505799SBoris Brezillon );
1896df505799SBoris Brezillon 
sunxi_nfc_exec_op(struct nand_chip * nand,const struct nand_operation * op,bool check_only)1897df505799SBoris Brezillon static int sunxi_nfc_exec_op(struct nand_chip *nand,
1898df505799SBoris Brezillon 			     const struct nand_operation *op, bool check_only)
1899df505799SBoris Brezillon {
1900df505799SBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1901df505799SBoris Brezillon 	const struct nand_op_parser *parser;
1902df505799SBoris Brezillon 
1903ce446b4bSBoris Brezillon 	if (!check_only)
1904df505799SBoris Brezillon 		sunxi_nfc_select_chip(nand, op->cs);
1905df505799SBoris Brezillon 
1906df505799SBoris Brezillon 	if (sunxi_nand->sels[op->cs].rb >= 0)
1907df505799SBoris Brezillon 		parser = &sunxi_nfc_op_parser;
1908df505799SBoris Brezillon 	else
1909df505799SBoris Brezillon 		parser = &sunxi_nfc_norb_op_parser;
1910df505799SBoris Brezillon 
1911df505799SBoris Brezillon 	return nand_op_parser_exec_op(nand, parser, op, check_only);
1912df505799SBoris Brezillon }
1913df505799SBoris Brezillon 
19142a4d9c16SMiquel Raynal static const struct nand_controller_ops sunxi_nand_controller_ops = {
19152a4d9c16SMiquel Raynal 	.attach_chip = sunxi_nand_attach_chip,
19164c46667bSMiquel Raynal 	.setup_interface = sunxi_nfc_setup_interface,
1917df505799SBoris Brezillon 	.exec_op = sunxi_nfc_exec_op,
19182a4d9c16SMiquel Raynal };
19192a4d9c16SMiquel Raynal 
sunxi_nand_chips_cleanup(struct sunxi_nfc * nfc)19206d7fea22SSamuel Holland static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
19216d7fea22SSamuel Holland {
19226d7fea22SSamuel Holland 	struct sunxi_nand_chip *sunxi_nand;
19236d7fea22SSamuel Holland 	struct nand_chip *chip;
19246d7fea22SSamuel Holland 	int ret;
19256d7fea22SSamuel Holland 
19266d7fea22SSamuel Holland 	while (!list_empty(&nfc->chips)) {
19276d7fea22SSamuel Holland 		sunxi_nand = list_first_entry(&nfc->chips,
19286d7fea22SSamuel Holland 					      struct sunxi_nand_chip,
19296d7fea22SSamuel Holland 					      node);
19306d7fea22SSamuel Holland 		chip = &sunxi_nand->nand;
19316d7fea22SSamuel Holland 		ret = mtd_device_unregister(nand_to_mtd(chip));
19326d7fea22SSamuel Holland 		WARN_ON(ret);
19336d7fea22SSamuel Holland 		nand_cleanup(chip);
19346d7fea22SSamuel Holland 		list_del(&sunxi_nand->node);
19356d7fea22SSamuel Holland 	}
19366d7fea22SSamuel Holland }
19376d7fea22SSamuel Holland 
sunxi_nand_chip_init(struct device * dev,struct sunxi_nfc * nfc,struct device_node * np)193893db446aSBoris Brezillon static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
193993db446aSBoris Brezillon 				struct device_node *np)
194093db446aSBoris Brezillon {
1941f385ebf0SBoris Brezillon 	struct sunxi_nand_chip *sunxi_nand;
194293db446aSBoris Brezillon 	struct mtd_info *mtd;
194393db446aSBoris Brezillon 	struct nand_chip *nand;
194493db446aSBoris Brezillon 	int nsels;
194593db446aSBoris Brezillon 	int ret;
194693db446aSBoris Brezillon 	int i;
194793db446aSBoris Brezillon 	u32 tmp;
194893db446aSBoris Brezillon 
194993db446aSBoris Brezillon 	if (!of_get_property(np, "reg", &nsels))
195093db446aSBoris Brezillon 		return -EINVAL;
195193db446aSBoris Brezillon 
195293db446aSBoris Brezillon 	nsels /= sizeof(u32);
195393db446aSBoris Brezillon 	if (!nsels) {
195493db446aSBoris Brezillon 		dev_err(dev, "invalid reg property size\n");
195593db446aSBoris Brezillon 		return -EINVAL;
195693db446aSBoris Brezillon 	}
195793db446aSBoris Brezillon 
19586c721acdSBoris Brezillon 	sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels),
195993db446aSBoris Brezillon 				  GFP_KERNEL);
1960b0821cc5SZhen Lei 	if (!sunxi_nand)
196193db446aSBoris Brezillon 		return -ENOMEM;
196293db446aSBoris Brezillon 
1963f385ebf0SBoris Brezillon 	sunxi_nand->nsels = nsels;
196493db446aSBoris Brezillon 
196593db446aSBoris Brezillon 	for (i = 0; i < nsels; i++) {
196693db446aSBoris Brezillon 		ret = of_property_read_u32_index(np, "reg", i, &tmp);
196793db446aSBoris Brezillon 		if (ret) {
196893db446aSBoris Brezillon 			dev_err(dev, "could not retrieve reg property: %d\n",
196993db446aSBoris Brezillon 				ret);
197093db446aSBoris Brezillon 			return ret;
197193db446aSBoris Brezillon 		}
197293db446aSBoris Brezillon 
197393db446aSBoris Brezillon 		if (tmp > NFC_MAX_CS) {
197493db446aSBoris Brezillon 			dev_err(dev,
197593db446aSBoris Brezillon 				"invalid reg value: %u (max CS = 7)\n",
197693db446aSBoris Brezillon 				tmp);
197793db446aSBoris Brezillon 			return -EINVAL;
197893db446aSBoris Brezillon 		}
197993db446aSBoris Brezillon 
198093db446aSBoris Brezillon 		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
198193db446aSBoris Brezillon 			dev_err(dev, "CS %d already assigned\n", tmp);
198293db446aSBoris Brezillon 			return -EINVAL;
198393db446aSBoris Brezillon 		}
198493db446aSBoris Brezillon 
1985f385ebf0SBoris Brezillon 		sunxi_nand->sels[i].cs = tmp;
198693db446aSBoris Brezillon 
198793db446aSBoris Brezillon 		if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
1988ddd5ed3aSBoris Brezillon 		    tmp < 2)
1989f385ebf0SBoris Brezillon 			sunxi_nand->sels[i].rb = tmp;
1990ddd5ed3aSBoris Brezillon 		else
1991f385ebf0SBoris Brezillon 			sunxi_nand->sels[i].rb = -1;
199293db446aSBoris Brezillon 	}
199393db446aSBoris Brezillon 
1994f385ebf0SBoris Brezillon 	nand = &sunxi_nand->nand;
199593db446aSBoris Brezillon 	/* Default tR value specified in the ONFI spec (chapter 4.15.1) */
199693db446aSBoris Brezillon 	nand->controller = &nfc->controller;
19972a4d9c16SMiquel Raynal 	nand->controller->ops = &sunxi_nand_controller_ops;
19982a4d9c16SMiquel Raynal 
199993db446aSBoris Brezillon 	/*
200093db446aSBoris Brezillon 	 * Set the ECC mode to the default value in case nothing is specified
200193db446aSBoris Brezillon 	 * in the DT.
200293db446aSBoris Brezillon 	 */
2003bace41f8SMiquel Raynal 	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
200493db446aSBoris Brezillon 	nand_set_flash_node(nand, np);
200593db446aSBoris Brezillon 
200693db446aSBoris Brezillon 	mtd = nand_to_mtd(nand);
200793db446aSBoris Brezillon 	mtd->dev.parent = dev;
200893db446aSBoris Brezillon 
200900ad378fSBoris Brezillon 	ret = nand_scan(nand, nsels);
201093db446aSBoris Brezillon 	if (ret)
201193db446aSBoris Brezillon 		return ret;
201293db446aSBoris Brezillon 
201393db446aSBoris Brezillon 	ret = mtd_device_register(mtd, NULL, 0);
201493db446aSBoris Brezillon 	if (ret) {
201593db446aSBoris Brezillon 		dev_err(dev, "failed to register mtd device: %d\n", ret);
20163d84515fSMiquel Raynal 		nand_cleanup(nand);
201793db446aSBoris Brezillon 		return ret;
201893db446aSBoris Brezillon 	}
201993db446aSBoris Brezillon 
2020f385ebf0SBoris Brezillon 	list_add_tail(&sunxi_nand->node, &nfc->chips);
202193db446aSBoris Brezillon 
202293db446aSBoris Brezillon 	return 0;
202393db446aSBoris Brezillon }
202493db446aSBoris Brezillon 
sunxi_nand_chips_init(struct device * dev,struct sunxi_nfc * nfc)202593db446aSBoris Brezillon static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
202693db446aSBoris Brezillon {
202793db446aSBoris Brezillon 	struct device_node *np = dev->of_node;
202893db446aSBoris Brezillon 	struct device_node *nand_np;
202993db446aSBoris Brezillon 	int ret;
203093db446aSBoris Brezillon 
203193db446aSBoris Brezillon 	for_each_child_of_node(np, nand_np) {
203293db446aSBoris Brezillon 		ret = sunxi_nand_chip_init(dev, nfc, nand_np);
203393db446aSBoris Brezillon 		if (ret) {
203493db446aSBoris Brezillon 			of_node_put(nand_np);
20356d7fea22SSamuel Holland 			sunxi_nand_chips_cleanup(nfc);
203693db446aSBoris Brezillon 			return ret;
203793db446aSBoris Brezillon 		}
203893db446aSBoris Brezillon 	}
203993db446aSBoris Brezillon 
204093db446aSBoris Brezillon 	return 0;
204193db446aSBoris Brezillon }
204293db446aSBoris Brezillon 
sunxi_nfc_dma_init(struct sunxi_nfc * nfc,struct resource * r)2043910ef7a4SManuel Dipolt static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
2044910ef7a4SManuel Dipolt {
2045910ef7a4SManuel Dipolt 	int ret;
2046910ef7a4SManuel Dipolt 
2047910ef7a4SManuel Dipolt 	if (nfc->caps->has_mdma)
2048910ef7a4SManuel Dipolt 		return 0;
2049910ef7a4SManuel Dipolt 
2050910ef7a4SManuel Dipolt 	nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
2051910ef7a4SManuel Dipolt 	if (IS_ERR(nfc->dmac)) {
2052910ef7a4SManuel Dipolt 		ret = PTR_ERR(nfc->dmac);
2053910ef7a4SManuel Dipolt 		if (ret == -EPROBE_DEFER)
2054910ef7a4SManuel Dipolt 			return ret;
2055910ef7a4SManuel Dipolt 
2056910ef7a4SManuel Dipolt 		/* Ignore errors to fall back to PIO mode */
2057910ef7a4SManuel Dipolt 		dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret);
2058910ef7a4SManuel Dipolt 		nfc->dmac = NULL;
2059910ef7a4SManuel Dipolt 	} else {
2060910ef7a4SManuel Dipolt 		struct dma_slave_config dmac_cfg = { };
2061910ef7a4SManuel Dipolt 
2062910ef7a4SManuel Dipolt 		dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
2063910ef7a4SManuel Dipolt 		dmac_cfg.dst_addr = dmac_cfg.src_addr;
2064910ef7a4SManuel Dipolt 		dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
2065910ef7a4SManuel Dipolt 		dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
2066910ef7a4SManuel Dipolt 		dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
2067910ef7a4SManuel Dipolt 		dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
2068910ef7a4SManuel Dipolt 		dmaengine_slave_config(nfc->dmac, &dmac_cfg);
2069910ef7a4SManuel Dipolt 	}
2070910ef7a4SManuel Dipolt 	return 0;
2071910ef7a4SManuel Dipolt }
2072910ef7a4SManuel Dipolt 
sunxi_nfc_probe(struct platform_device * pdev)207393db446aSBoris Brezillon static int sunxi_nfc_probe(struct platform_device *pdev)
207493db446aSBoris Brezillon {
207593db446aSBoris Brezillon 	struct device *dev = &pdev->dev;
207693db446aSBoris Brezillon 	struct resource *r;
207793db446aSBoris Brezillon 	struct sunxi_nfc *nfc;
207893db446aSBoris Brezillon 	int irq;
207993db446aSBoris Brezillon 	int ret;
208093db446aSBoris Brezillon 
208193db446aSBoris Brezillon 	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
208293db446aSBoris Brezillon 	if (!nfc)
208393db446aSBoris Brezillon 		return -ENOMEM;
208493db446aSBoris Brezillon 
208593db446aSBoris Brezillon 	nfc->dev = dev;
20867da45139SMiquel Raynal 	nand_controller_init(&nfc->controller);
208793db446aSBoris Brezillon 	INIT_LIST_HEAD(&nfc->chips);
208893db446aSBoris Brezillon 
208981a16e15SYangtao Li 	nfc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
209093db446aSBoris Brezillon 	if (IS_ERR(nfc->regs))
209193db446aSBoris Brezillon 		return PTR_ERR(nfc->regs);
209293db446aSBoris Brezillon 
209393db446aSBoris Brezillon 	irq = platform_get_irq(pdev, 0);
2094aab478caSStephen Boyd 	if (irq < 0)
209593db446aSBoris Brezillon 		return irq;
209693db446aSBoris Brezillon 
2097*a95da272SLi Zetao 	nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
209893db446aSBoris Brezillon 	if (IS_ERR(nfc->ahb_clk)) {
209993db446aSBoris Brezillon 		dev_err(dev, "failed to retrieve ahb clk\n");
210093db446aSBoris Brezillon 		return PTR_ERR(nfc->ahb_clk);
210193db446aSBoris Brezillon 	}
210293db446aSBoris Brezillon 
2103*a95da272SLi Zetao 	nfc->mod_clk = devm_clk_get_enabled(dev, "mod");
210493db446aSBoris Brezillon 	if (IS_ERR(nfc->mod_clk)) {
210593db446aSBoris Brezillon 		dev_err(dev, "failed to retrieve mod clk\n");
2106*a95da272SLi Zetao 		return PTR_ERR(nfc->mod_clk);
210793db446aSBoris Brezillon 	}
210893db446aSBoris Brezillon 
210993db446aSBoris Brezillon 	nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
2110*a95da272SLi Zetao 	if (IS_ERR(nfc->reset))
2111*a95da272SLi Zetao 		return PTR_ERR(nfc->reset);
211293db446aSBoris Brezillon 
211393db446aSBoris Brezillon 	ret = reset_control_deassert(nfc->reset);
211493db446aSBoris Brezillon 	if (ret) {
211593db446aSBoris Brezillon 		dev_err(dev, "reset err %d\n", ret);
2116*a95da272SLi Zetao 		return ret;
211793db446aSBoris Brezillon 	}
211893db446aSBoris Brezillon 
2119a760e77dSMiquel Raynal 	nfc->caps = of_device_get_match_data(&pdev->dev);
2120a760e77dSMiquel Raynal 	if (!nfc->caps) {
2121a760e77dSMiquel Raynal 		ret = -EINVAL;
2122a760e77dSMiquel Raynal 		goto out_ahb_reset_reassert;
2123a760e77dSMiquel Raynal 	}
2124a760e77dSMiquel Raynal 
212593db446aSBoris Brezillon 	ret = sunxi_nfc_rst(nfc);
212693db446aSBoris Brezillon 	if (ret)
212793db446aSBoris Brezillon 		goto out_ahb_reset_reassert;
212893db446aSBoris Brezillon 
212993db446aSBoris Brezillon 	writel(0, nfc->regs + NFC_REG_INT);
213093db446aSBoris Brezillon 	ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
213193db446aSBoris Brezillon 			       0, "sunxi-nand", nfc);
213293db446aSBoris Brezillon 	if (ret)
213393db446aSBoris Brezillon 		goto out_ahb_reset_reassert;
213493db446aSBoris Brezillon 
2135910ef7a4SManuel Dipolt 	ret = sunxi_nfc_dma_init(nfc, r);
2136910ef7a4SManuel Dipolt 
2137910ef7a4SManuel Dipolt 	if (ret)
2138ac80c55bSPeter Ujfalusi 		goto out_ahb_reset_reassert;
2139ac80c55bSPeter Ujfalusi 
214093db446aSBoris Brezillon 	platform_set_drvdata(pdev, nfc);
214193db446aSBoris Brezillon 
214293db446aSBoris Brezillon 	ret = sunxi_nand_chips_init(dev, nfc);
214393db446aSBoris Brezillon 	if (ret) {
214493db446aSBoris Brezillon 		dev_err(dev, "failed to init nand chips\n");
214593db446aSBoris Brezillon 		goto out_release_dmac;
214693db446aSBoris Brezillon 	}
214793db446aSBoris Brezillon 
214893db446aSBoris Brezillon 	return 0;
214993db446aSBoris Brezillon 
215093db446aSBoris Brezillon out_release_dmac:
215193db446aSBoris Brezillon 	if (nfc->dmac)
215293db446aSBoris Brezillon 		dma_release_channel(nfc->dmac);
215393db446aSBoris Brezillon out_ahb_reset_reassert:
215493db446aSBoris Brezillon 	reset_control_assert(nfc->reset);
215593db446aSBoris Brezillon 
215693db446aSBoris Brezillon 	return ret;
215793db446aSBoris Brezillon }
215893db446aSBoris Brezillon 
sunxi_nfc_remove(struct platform_device * pdev)2159ec185b18SUwe Kleine-König static void sunxi_nfc_remove(struct platform_device *pdev)
216093db446aSBoris Brezillon {
216193db446aSBoris Brezillon 	struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
216293db446aSBoris Brezillon 
216393db446aSBoris Brezillon 	sunxi_nand_chips_cleanup(nfc);
216493db446aSBoris Brezillon 
216593db446aSBoris Brezillon 	reset_control_assert(nfc->reset);
216693db446aSBoris Brezillon 
216793db446aSBoris Brezillon 	if (nfc->dmac)
216893db446aSBoris Brezillon 		dma_release_channel(nfc->dmac);
216993db446aSBoris Brezillon }
217093db446aSBoris Brezillon 
2171a760e77dSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
2172a760e77dSMiquel Raynal 	.reg_io_data = NFC_REG_A10_IO_DATA,
2173a760e77dSMiquel Raynal 	.dma_maxburst = 4,
2174a760e77dSMiquel Raynal };
2175a760e77dSMiquel Raynal 
2176c7a87cebSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
2177910ef7a4SManuel Dipolt 	.has_mdma = true,
2178c7a87cebSMiquel Raynal 	.reg_io_data = NFC_REG_A23_IO_DATA,
2179c7a87cebSMiquel Raynal 	.dma_maxburst = 8,
2180c7a87cebSMiquel Raynal };
2181c7a87cebSMiquel Raynal 
218293db446aSBoris Brezillon static const struct of_device_id sunxi_nfc_ids[] = {
2183a760e77dSMiquel Raynal 	{
2184a760e77dSMiquel Raynal 		.compatible = "allwinner,sun4i-a10-nand",
2185a760e77dSMiquel Raynal 		.data = &sunxi_nfc_a10_caps,
2186a760e77dSMiquel Raynal 	},
2187c7a87cebSMiquel Raynal 	{
2188c7a87cebSMiquel Raynal 		.compatible = "allwinner,sun8i-a23-nand-controller",
2189c7a87cebSMiquel Raynal 		.data = &sunxi_nfc_a23_caps,
2190c7a87cebSMiquel Raynal 	},
219193db446aSBoris Brezillon 	{ /* sentinel */ }
219293db446aSBoris Brezillon };
219393db446aSBoris Brezillon MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
219493db446aSBoris Brezillon 
219593db446aSBoris Brezillon static struct platform_driver sunxi_nfc_driver = {
219693db446aSBoris Brezillon 	.driver = {
219793db446aSBoris Brezillon 		.name = "sunxi_nand",
219893db446aSBoris Brezillon 		.of_match_table = sunxi_nfc_ids,
219993db446aSBoris Brezillon 	},
220093db446aSBoris Brezillon 	.probe = sunxi_nfc_probe,
2201ec185b18SUwe Kleine-König 	.remove_new = sunxi_nfc_remove,
220293db446aSBoris Brezillon };
220393db446aSBoris Brezillon module_platform_driver(sunxi_nfc_driver);
220493db446aSBoris Brezillon 
2205f5f88871SBoris Brezillon MODULE_LICENSE("GPL");
220693db446aSBoris Brezillon MODULE_AUTHOR("Boris BREZILLON");
220793db446aSBoris Brezillon MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
220893db446aSBoris Brezillon MODULE_ALIAS("platform:sunxi_nand");
2209