xref: /rk3399_rockchip-uboot/drivers/mtd/nand/raw/rockchip_nand_spl_v9.c (revision f6f9fe4f0f4d78852b3836677aee7e048242cfa1)
1813156edSYifeng Zhao /*
2813156edSYifeng Zhao  * Copyright (c) 2017 Yifeng Zhao <yifeng.zhao@rock-chips.com>
3813156edSYifeng Zhao  *
4813156edSYifeng Zhao  * SPDX-License-Identifier:     GPL-2.0+
5813156edSYifeng Zhao  */
6813156edSYifeng Zhao 
7813156edSYifeng Zhao #include <common.h>
8813156edSYifeng Zhao #include <dm.h>
9813156edSYifeng Zhao #include <fdtdec.h>
10813156edSYifeng Zhao #include <fdt_support.h>
11813156edSYifeng Zhao #include <inttypes.h>
12813156edSYifeng Zhao #include <nand.h>
13813156edSYifeng Zhao #include <linux/io.h>
14813156edSYifeng Zhao #include <linux/kernel.h>
15813156edSYifeng Zhao #include <linux/mtd/mtd.h>
16813156edSYifeng Zhao #include <linux/mtd/nand.h>
17813156edSYifeng Zhao #include <linux/mtd/partitions.h>
18813156edSYifeng Zhao 
19813156edSYifeng Zhao DECLARE_GLOBAL_DATA_PTR;
20813156edSYifeng Zhao 
21813156edSYifeng Zhao #define NANDC_V9_NUM_BANKS	4
22813156edSYifeng Zhao #define NANDC_V9_DEF_TIMEOUT	20000
23813156edSYifeng Zhao #define NANDC_V9_READ		0
24813156edSYifeng Zhao #define NANDC_V9_WRITE		1
25813156edSYifeng Zhao #define NANDC_REG_V9_FMCTL	0x00
26813156edSYifeng Zhao #define NANDC_REG_V9_FMWAIT	0x04
27813156edSYifeng Zhao #define NANDC_REG_V9_FLCTL	0x10
28813156edSYifeng Zhao #define NANDC_REG_V9_BCHCTL	0x20
29813156edSYifeng Zhao #define NANDC_REG_V9_DMA_CFG	0x30
30813156edSYifeng Zhao #define NANDC_REG_V9_DMA_BUF0	0x34
31813156edSYifeng Zhao #define NANDC_REG_V9_DMA_BUF1	0x38
32813156edSYifeng Zhao #define NANDC_REG_V9_DMA_ST	0x40
33813156edSYifeng Zhao #define NANDC_REG_V9_VER	0x80
34813156edSYifeng Zhao #define NANDC_REG_V9_INTEN	0x120
35813156edSYifeng Zhao #define NANDC_REG_V9_INTCLR	0x124
36813156edSYifeng Zhao #define NANDC_REG_V9_INTST	0x128
37813156edSYifeng Zhao #define NANDC_REG_V9_BCHST	0x150
38813156edSYifeng Zhao #define NANDC_REG_V9_SPARE0	0x200
39813156edSYifeng Zhao #define NANDC_REG_V9_SPARE1	0x204
40813156edSYifeng Zhao #define NANDC_REG_V9_RANDMZ	0x208
41813156edSYifeng Zhao #define NANDC_REG_V9_BANK0	0x800
42813156edSYifeng Zhao #define NANDC_REG_V9_SRAM0	0x1000
43813156edSYifeng Zhao #define NANDC_REG_V9_SRAM_SIZE	0x400
44813156edSYifeng Zhao 
45813156edSYifeng Zhao #define NANDC_REG_V9_DATA	0x00
46813156edSYifeng Zhao #define NANDC_REG_V9_ADDR	0x04
47813156edSYifeng Zhao #define NANDC_REG_V9_CMD	0x08
48813156edSYifeng Zhao 
49813156edSYifeng Zhao /* FMCTL */
50813156edSYifeng Zhao #define NANDC_V9_FM_WP		BIT(8)
51813156edSYifeng Zhao #define NANDC_V9_FM_CE_SEL_M	0xFF
52813156edSYifeng Zhao #define NANDC_V9_FM_CE_SEL(x)	(1 << (x))
53813156edSYifeng Zhao #define NANDC_V9_FM_FREADY	BIT(9)
54813156edSYifeng Zhao 
55813156edSYifeng Zhao /* FLCTL */
56813156edSYifeng Zhao #define NANDC_V9_FL_RST		BIT(0)
57813156edSYifeng Zhao #define NANDC_V9_FL_DIR_S	0x1
58813156edSYifeng Zhao #define NANDC_V9_FL_XFER_START	BIT(2)
59813156edSYifeng Zhao #define NANDC_V9_FL_XFER_EN	BIT(3)
60813156edSYifeng Zhao #define NANDC_V9_FL_ST_BUF_S	0x4
61813156edSYifeng Zhao #define NANDC_V9_FL_XFER_COUNT	BIT(5)
62813156edSYifeng Zhao #define NANDC_V9_FL_ACORRECT	BIT(10)
63813156edSYifeng Zhao #define NANDC_V9_FL_XFER_READY	BIT(20)
64813156edSYifeng Zhao 
65813156edSYifeng Zhao /* BCHCTL */
66813156edSYifeng Zhao #define NAND_V9_BCH_MODE_S	25
67813156edSYifeng Zhao #define NAND_V9_BCH_MODE_M	0x7
68813156edSYifeng Zhao 
69813156edSYifeng Zhao /* BCHST */
70813156edSYifeng Zhao #define NANDC_V9_BCH0_ST_ERR	BIT(2)
71813156edSYifeng Zhao #define NANDC_V9_BCH1_ST_ERR	BIT(18)
72813156edSYifeng Zhao #define NANDC_V9_ECC_ERR_CNT0(x) (((x) & (0x7F << 3)) >> 3)
73813156edSYifeng Zhao #define NANDC_V9_ECC_ERR_CNT1(x) (((x) & (0x7F << 19)) >> 19)
74813156edSYifeng Zhao 
75813156edSYifeng Zhao struct rk_nand {
76813156edSYifeng Zhao 	void __iomem *regs;
77813156edSYifeng Zhao 	u8 chipnr;
78813156edSYifeng Zhao 	u8 id[5];
79813156edSYifeng Zhao 	u8 *databuf;
80813156edSYifeng Zhao 	struct udevice *dev;
81813156edSYifeng Zhao 	struct mtd_info *mtd;
82813156edSYifeng Zhao };
83813156edSYifeng Zhao 
84813156edSYifeng Zhao static struct rk_nand *g_rk_nand;
85813156edSYifeng Zhao static u32 nand_page_size;
86813156edSYifeng Zhao static u32 nand_page_num;
87813156edSYifeng Zhao static u32 nand_block_num;
88813156edSYifeng Zhao 
nandc_init(struct rk_nand * rknand)89813156edSYifeng Zhao static void nandc_init(struct rk_nand *rknand)
90813156edSYifeng Zhao {
91813156edSYifeng Zhao 	writel(0, rknand->regs + NANDC_REG_V9_RANDMZ);
92813156edSYifeng Zhao 	writel(0, rknand->regs + NANDC_REG_V9_DMA_CFG);
93813156edSYifeng Zhao 	writel(0x02000001, rknand->regs + NANDC_REG_V9_BCHCTL);
94813156edSYifeng Zhao 	writel(0x1081, rknand->regs + NANDC_REG_V9_FMWAIT);
95813156edSYifeng Zhao }
96813156edSYifeng Zhao 
rockchip_nand_wait_dev_ready(void __iomem * regs)97813156edSYifeng Zhao static void rockchip_nand_wait_dev_ready(void __iomem *regs)
98813156edSYifeng Zhao {
99813156edSYifeng Zhao 	u32 reg;
100813156edSYifeng Zhao 	u32 timeout = NANDC_V9_DEF_TIMEOUT;
101813156edSYifeng Zhao 
102813156edSYifeng Zhao 	while (timeout--) {
103813156edSYifeng Zhao 		udelay(1);
104813156edSYifeng Zhao 		reg = readl(regs + NANDC_REG_V9_FMCTL);
105813156edSYifeng Zhao 
106813156edSYifeng Zhao 		if (reg & NANDC_V9_FM_FREADY)
107813156edSYifeng Zhao 			break;
108813156edSYifeng Zhao 	}
109813156edSYifeng Zhao }
110813156edSYifeng Zhao 
rockchip_nand_select_chip(void __iomem * regs,int chipnr)111813156edSYifeng Zhao static void rockchip_nand_select_chip(void __iomem *regs, int chipnr)
112813156edSYifeng Zhao {
113813156edSYifeng Zhao 	u32 reg;
114813156edSYifeng Zhao 
115813156edSYifeng Zhao 	reg = readl(regs + NANDC_REG_V9_FMCTL);
116813156edSYifeng Zhao 	reg &= ~NANDC_V9_FM_CE_SEL_M;
117813156edSYifeng Zhao 	if (chipnr != -1)
118813156edSYifeng Zhao 		reg |= 1 << chipnr;
119813156edSYifeng Zhao 	writel(reg, regs + NANDC_REG_V9_FMCTL);
120813156edSYifeng Zhao }
121813156edSYifeng Zhao 
rockchip_nand_read_page(void __iomem * regs,int page,int col)122813156edSYifeng Zhao static void rockchip_nand_read_page(void __iomem *regs,
123813156edSYifeng Zhao 				    int page, int col)
124813156edSYifeng Zhao {
125813156edSYifeng Zhao 	void __iomem *bank_base = regs + NANDC_REG_V9_BANK0;
126813156edSYifeng Zhao 
127813156edSYifeng Zhao 	writeb(0x00, bank_base + NANDC_REG_V9_CMD);
128813156edSYifeng Zhao 	writeb(col, bank_base + NANDC_REG_V9_ADDR);
129813156edSYifeng Zhao 	writeb(col >> 8, bank_base + NANDC_REG_V9_ADDR);
130813156edSYifeng Zhao 	writeb(page, bank_base + NANDC_REG_V9_ADDR);
131813156edSYifeng Zhao 	writeb(page >> 8, bank_base + NANDC_REG_V9_ADDR);
132813156edSYifeng Zhao 	writeb(page >> 16, bank_base + NANDC_REG_V9_ADDR);
133813156edSYifeng Zhao 	writeb(0x30, bank_base + NANDC_REG_V9_CMD);
134813156edSYifeng Zhao }
135813156edSYifeng Zhao 
rockchip_nand_pio_xfer_start(struct rk_nand * rknand,u8 dir,u8 st_buf)136813156edSYifeng Zhao static void rockchip_nand_pio_xfer_start(struct rk_nand *rknand,
137813156edSYifeng Zhao 					 u8 dir,
138813156edSYifeng Zhao 					 u8 st_buf)
139813156edSYifeng Zhao {
140813156edSYifeng Zhao 	u32 reg;
141813156edSYifeng Zhao 
142813156edSYifeng Zhao 	reg = (dir << NANDC_V9_FL_DIR_S) | (st_buf << NANDC_V9_FL_ST_BUF_S) |
143813156edSYifeng Zhao 	      NANDC_V9_FL_XFER_EN | NANDC_V9_FL_XFER_COUNT |
144813156edSYifeng Zhao 	      NANDC_V9_FL_ACORRECT;
145813156edSYifeng Zhao 	writel(reg, rknand->regs + NANDC_REG_V9_FLCTL);
146813156edSYifeng Zhao 
147813156edSYifeng Zhao 	reg |= NANDC_V9_FL_XFER_START;
148813156edSYifeng Zhao 	writel(reg, rknand->regs + NANDC_REG_V9_FLCTL);
149813156edSYifeng Zhao }
150813156edSYifeng Zhao 
rockchip_nand_wait_pio_xfer_done(struct rk_nand * rknand)151813156edSYifeng Zhao static int rockchip_nand_wait_pio_xfer_done(struct rk_nand *rknand)
152813156edSYifeng Zhao {
153813156edSYifeng Zhao 	int timeout = NANDC_V9_DEF_TIMEOUT;
154813156edSYifeng Zhao 	int reg;
155813156edSYifeng Zhao 
156813156edSYifeng Zhao 	while (timeout--) {
157813156edSYifeng Zhao 		reg = readl(rknand->regs + NANDC_REG_V9_FLCTL);
158813156edSYifeng Zhao 
159813156edSYifeng Zhao 		if ((reg & NANDC_V9_FL_XFER_READY)  != 0)
160813156edSYifeng Zhao 			break;
161813156edSYifeng Zhao 
162813156edSYifeng Zhao 		udelay(1);
163813156edSYifeng Zhao 	}
164813156edSYifeng Zhao 
165813156edSYifeng Zhao 	if (timeout == 0)
166813156edSYifeng Zhao 		return -1;
167813156edSYifeng Zhao 
168813156edSYifeng Zhao 	return 0;
169813156edSYifeng Zhao }
170813156edSYifeng Zhao 
nandc_read_page(unsigned int page,uint8_t * buf)171813156edSYifeng Zhao static int nandc_read_page(unsigned int page, uint8_t *buf)
172813156edSYifeng Zhao {
173813156edSYifeng Zhao 	void __iomem *sram_base = g_rk_nand->regs + NANDC_REG_V9_SRAM0;
174813156edSYifeng Zhao 	unsigned int max_bitflips = 0;
175813156edSYifeng Zhao 	int ret, step, bch_st, ecc_step;
176813156edSYifeng Zhao 
177813156edSYifeng Zhao 	ecc_step = nand_page_size / 1024;
178813156edSYifeng Zhao 	rockchip_nand_select_chip(g_rk_nand->regs, 0);
179813156edSYifeng Zhao 	rockchip_nand_read_page(g_rk_nand->regs, page, 0);
180813156edSYifeng Zhao 	rockchip_nand_wait_dev_ready(g_rk_nand->regs);
181813156edSYifeng Zhao 	rockchip_nand_pio_xfer_start(g_rk_nand, NANDC_V9_READ, 0);
182813156edSYifeng Zhao 
183813156edSYifeng Zhao 	for (step = 0; step < ecc_step; step++) {
184813156edSYifeng Zhao 		int data_off = step * 1024;
185813156edSYifeng Zhao 		u8 *data = buf + data_off;
186813156edSYifeng Zhao 
187813156edSYifeng Zhao 		ret = rockchip_nand_wait_pio_xfer_done(g_rk_nand);
188813156edSYifeng Zhao 		if (ret)
189813156edSYifeng Zhao 			return ret;
190813156edSYifeng Zhao 
191813156edSYifeng Zhao 		bch_st = readl(g_rk_nand->regs + NANDC_REG_V9_BCHST);
192813156edSYifeng Zhao 
193813156edSYifeng Zhao 		if (bch_st & NANDC_V9_BCH0_ST_ERR) {
194813156edSYifeng Zhao 			max_bitflips = -1;
195813156edSYifeng Zhao 		} else {
196813156edSYifeng Zhao 			ret = NANDC_V9_ECC_ERR_CNT0(bch_st);
197813156edSYifeng Zhao 			max_bitflips = max_t(unsigned int, max_bitflips, ret);
198813156edSYifeng Zhao 		}
199813156edSYifeng Zhao 
200813156edSYifeng Zhao 		if ((step + 1) < ecc_step)
201813156edSYifeng Zhao 			rockchip_nand_pio_xfer_start(g_rk_nand, NANDC_V9_READ,
202813156edSYifeng Zhao 						     (step + 1) & 0x1);
203813156edSYifeng Zhao 
204813156edSYifeng Zhao 		memcpy_fromio(data, sram_base + NANDC_REG_V9_SRAM_SIZE *
205813156edSYifeng Zhao 			      (step & 1), 1024);
206813156edSYifeng Zhao 	}
207813156edSYifeng Zhao 	rockchip_nand_select_chip(g_rk_nand->regs, -1);
208813156edSYifeng Zhao 
209813156edSYifeng Zhao 	return max_bitflips;
210813156edSYifeng Zhao }
211813156edSYifeng Zhao 
is_badblock(unsigned int page)212813156edSYifeng Zhao static int is_badblock(unsigned int page)
213813156edSYifeng Zhao {
214813156edSYifeng Zhao 	int res = 0, i;
215813156edSYifeng Zhao 	u16 bad = 0xff;
216813156edSYifeng Zhao 	void __iomem *regs = g_rk_nand->regs;
217813156edSYifeng Zhao 	void __iomem *bank_base = regs + NANDC_REG_V9_BANK0;
218813156edSYifeng Zhao 
219813156edSYifeng Zhao 	if (nandc_read_page(page, g_rk_nand->databuf) == -1) {
220813156edSYifeng Zhao 		rockchip_nand_select_chip(regs, 0);
221813156edSYifeng Zhao 		rockchip_nand_read_page(regs, page, nand_page_size);
222813156edSYifeng Zhao 		rockchip_nand_wait_dev_ready(regs);
223813156edSYifeng Zhao 		for (i = 0; i < 8; i++) {
224813156edSYifeng Zhao 			bad = readb(bank_base);
225813156edSYifeng Zhao 			if (bad)
226813156edSYifeng Zhao 				break;
227813156edSYifeng Zhao 		}
228813156edSYifeng Zhao 		if (i >= 8)
229813156edSYifeng Zhao 			res = 1;
230813156edSYifeng Zhao 		rockchip_nand_select_chip(regs, 0);
231813156edSYifeng Zhao 	}
232813156edSYifeng Zhao 	if (res)
233813156edSYifeng Zhao 		printf("%s 0x%x %x %x\n", __func__, page, res, bad);
234813156edSYifeng Zhao 	return res;
235813156edSYifeng Zhao }
236813156edSYifeng Zhao 
read_flash_id(struct rk_nand * rknand,uint8_t * id)237813156edSYifeng Zhao static void read_flash_id(struct rk_nand *rknand, uint8_t *id)
238813156edSYifeng Zhao {
239813156edSYifeng Zhao 	void __iomem *bank_base = rknand->regs + NANDC_REG_V9_BANK0;
240813156edSYifeng Zhao 
241813156edSYifeng Zhao 	rockchip_nand_wait_dev_ready(g_rk_nand->regs);
242813156edSYifeng Zhao 	writeb(0x90, bank_base + NANDC_REG_V9_CMD);
243813156edSYifeng Zhao 	writeb(0x00, bank_base + NANDC_REG_V9_ADDR);
244813156edSYifeng Zhao 	udelay(1);
245813156edSYifeng Zhao 	id[0] = readb(bank_base);
246813156edSYifeng Zhao 	id[1] = readb(bank_base);
247813156edSYifeng Zhao 	id[2] = readb(bank_base);
248813156edSYifeng Zhao 	id[3] = readb(bank_base);
249813156edSYifeng Zhao 	id[4] = readb(bank_base);
250813156edSYifeng Zhao 	rockchip_nand_select_chip(rknand->regs, -1);
251813156edSYifeng Zhao 	if (id[0] != 0xFF && id[0] != 0x00)
252813156edSYifeng Zhao 		printf("NAND:%x %x\n", id[0], id[1]);
253813156edSYifeng Zhao }
254813156edSYifeng Zhao 
255813156edSYifeng Zhao #ifdef CONFIG_NAND_ROCKCHIP_DT
256813156edSYifeng Zhao static const struct udevice_id rockchip_nandc_ids[] = {
257813156edSYifeng Zhao 	{ .compatible = "rockchip,rk-nandc" },
258813156edSYifeng Zhao 	{ }
259813156edSYifeng Zhao };
260813156edSYifeng Zhao 
spl_nand_block_isbad(struct mtd_info * mtd,loff_t ofs)261813156edSYifeng Zhao static int spl_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
262813156edSYifeng Zhao {
263813156edSYifeng Zhao 	return is_badblock((u32)ofs / nand_page_size);
264813156edSYifeng Zhao }
265813156edSYifeng Zhao 
spl_nand_read_page(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)266813156edSYifeng Zhao static int spl_nand_read_page(struct mtd_info *mtd, loff_t from, size_t len,
267813156edSYifeng Zhao 			      size_t *retlen, u_char *buf)
268813156edSYifeng Zhao {
269813156edSYifeng Zhao 	int read_size, offset, read_len;
270813156edSYifeng Zhao 	unsigned int page;
271813156edSYifeng Zhao 	unsigned int max_pages = nand_page_num * nand_block_num;
272813156edSYifeng Zhao 
273813156edSYifeng Zhao 	/* Convert to page number */
274813156edSYifeng Zhao 	page = (u32)from / nand_page_size;
275813156edSYifeng Zhao 	offset = from & (nand_page_size - 1);
276813156edSYifeng Zhao 	read_len = len;
277813156edSYifeng Zhao 	*retlen = 0;
278813156edSYifeng Zhao 
279813156edSYifeng Zhao 	while (read_len) {
280813156edSYifeng Zhao 		read_size = nand_page_size - offset;
281813156edSYifeng Zhao 		if (read_size > read_len)
282813156edSYifeng Zhao 			read_size = read_len;
283813156edSYifeng Zhao 		if (offset || read_size < nand_page_size) {
284813156edSYifeng Zhao 			if (nandc_read_page(page, g_rk_nand->databuf) < 0)
285813156edSYifeng Zhao 				return -EIO;
286813156edSYifeng Zhao 			memcpy(buf, g_rk_nand->databuf + offset, read_size);
287813156edSYifeng Zhao 			offset = 0;
288813156edSYifeng Zhao 		} else {
289813156edSYifeng Zhao 			if (nandc_read_page(page, buf) < 0)
290813156edSYifeng Zhao 				return -EIO;
291813156edSYifeng Zhao 		}
292813156edSYifeng Zhao 		page++;
293813156edSYifeng Zhao 		read_len -= read_size;
294813156edSYifeng Zhao 		buf += read_size;
295813156edSYifeng Zhao 		if (page >= max_pages)
296813156edSYifeng Zhao 			return -EIO;
297813156edSYifeng Zhao 	}
298813156edSYifeng Zhao 
299813156edSYifeng Zhao 	*retlen = len;
300813156edSYifeng Zhao 
301813156edSYifeng Zhao 	return 0;
302813156edSYifeng Zhao }
303813156edSYifeng Zhao 
rockchip_nandc_probe(struct udevice * dev)304813156edSYifeng Zhao static int rockchip_nandc_probe(struct udevice *dev)
305813156edSYifeng Zhao {
306813156edSYifeng Zhao 	const void *blob = gd->fdt_blob;
307813156edSYifeng Zhao 	struct rk_nand *rknand = dev_get_priv(dev);
308813156edSYifeng Zhao 	struct mtd_info *mtd = dev_get_uclass_priv(dev);
309813156edSYifeng Zhao 	fdt_addr_t regs;
310813156edSYifeng Zhao 	int ret = -ENODEV;
311813156edSYifeng Zhao 	int node;
312813156edSYifeng Zhao 	u8 *id;
313813156edSYifeng Zhao 
314813156edSYifeng Zhao 	g_rk_nand = rknand;
315813156edSYifeng Zhao 	rknand->dev = dev;
316813156edSYifeng Zhao 
317813156edSYifeng Zhao 	node = fdtdec_next_compatible(blob, 0, COMPAT_ROCKCHIP_NANDC);
318813156edSYifeng Zhao 
319813156edSYifeng Zhao 	if (node < 0) {
320813156edSYifeng Zhao 		printf("Nand node not found\n");
321813156edSYifeng Zhao 		return -ENODEV;
322813156edSYifeng Zhao 	}
323813156edSYifeng Zhao 
324813156edSYifeng Zhao 	if (!fdtdec_get_is_enabled(blob, node)) {
325813156edSYifeng Zhao 		debug("Nand disabled in device tree\n");
326813156edSYifeng Zhao 		return -ENODEV;
327813156edSYifeng Zhao 	}
328813156edSYifeng Zhao 
329813156edSYifeng Zhao 	regs = fdt_get_base_address(blob, node);
330813156edSYifeng Zhao 	if (!regs) {
331813156edSYifeng Zhao 		debug("Nand address not found\n");
332813156edSYifeng Zhao 		return -ENODEV;
333813156edSYifeng Zhao 	}
334813156edSYifeng Zhao 
335813156edSYifeng Zhao 	rknand->regs = (void *)regs;
336813156edSYifeng Zhao 
337813156edSYifeng Zhao 	nandc_init(g_rk_nand);
338813156edSYifeng Zhao 	read_flash_id(g_rk_nand, g_rk_nand->id);
339813156edSYifeng Zhao 
340813156edSYifeng Zhao 	id = g_rk_nand->id;
341813156edSYifeng Zhao 	if (id[0] == id[1])
342813156edSYifeng Zhao 		return -ENODEV;
343813156edSYifeng Zhao 
344813156edSYifeng Zhao 	if (id[1] == 0xA1 || id[1] == 0xF1 ||
345813156edSYifeng Zhao 	    id[1] == 0xD1 || id[1] == 0xAA ||
346813156edSYifeng Zhao 	    id[1] == 0xDA || id[1] == 0xAC ||
347813156edSYifeng Zhao 	    id[1] == 0xDC || id[1] == 0xA3 ||
348813156edSYifeng Zhao 	    id[1] == 0xD3 || id[1] == 0x95 ||
3496b5b58a0SJon Lin 	    id[1] == 0x48 || id[1] == 0xD7 ||
3506b5b58a0SJon Lin 	    id[1] == 0x63) {
351813156edSYifeng Zhao 		nand_page_size = 2048;
352813156edSYifeng Zhao 		nand_page_num = 64;
353813156edSYifeng Zhao 		nand_block_num = 1024;
354fb735bc0SJon Lin 		if (id[1] == 0xDC || id[1] == 0xAC) {
355813156edSYifeng Zhao 			if ((id[0] == 0x2C && id[3] == 0xA6) ||
356*f6f9fe4fSJon Lin 			    (id[0] == 0xC2 && id[3] == 0xA2) ||
357*f6f9fe4fSJon Lin 				(id[0] == 0x98 && id[3] == 0x26) ||
358*f6f9fe4fSJon Lin 				(id[0] == 0xCD && id[3] == 0xA2)) {
359813156edSYifeng Zhao 				nand_page_size = 4096;
360813156edSYifeng Zhao 				nand_block_num = 2048;
361813156edSYifeng Zhao 			} else {
362813156edSYifeng Zhao 				nand_block_num = 4096;
363813156edSYifeng Zhao 			}
364813156edSYifeng Zhao 		} else if (id[1] == 0xDA) {
365813156edSYifeng Zhao 			nand_block_num = 2048;
366813156edSYifeng Zhao 		} else if (id[1] == 0x48) {
367813156edSYifeng Zhao 			nand_page_size = 4096;
368813156edSYifeng Zhao 			nand_page_num = 128;
369813156edSYifeng Zhao 			nand_block_num = 4096;
370813156edSYifeng Zhao 		} else if (id[1] == 0xD3) {
37117ba2606SJon Lin 			if ((id[2] == 0xD1 && id[4] == 0x5a) || /* S34ML08G2 */
37217ba2606SJon Lin 			    (id[3] == 0x05 && id[4] == 0x04)) { /* S34ML08G3 */
373535bd67aSJon Lin 				nand_block_num = 8192;
374535bd67aSJon Lin 			} else {
375813156edSYifeng Zhao 				nand_page_size = 4096;
376813156edSYifeng Zhao 				nand_block_num = 4096;
377813156edSYifeng Zhao 			}
3782d08457fSJon Lin 		} else if (id[1] == 0xd7 && id[3] == 0x32) { /* TC58NVG5H2HTAI0 */
3792d08457fSJon Lin 			nand_page_size = 8192;
3802d08457fSJon Lin 			nand_page_num = 128;
3812d08457fSJon Lin 			nand_block_num = 4096;
3826b5b58a0SJon Lin 		} else if (id[1] == 0x63 && id[3] == 0x19) { /* IS34ML08G088 */
3836b5b58a0SJon Lin 			nand_page_size = 4096;
3846b5b58a0SJon Lin 			nand_page_num = 64;
3856b5b58a0SJon Lin 			nand_block_num = 4096;
386535bd67aSJon Lin 		}
387813156edSYifeng Zhao 		g_rk_nand->chipnr = 1;
388813156edSYifeng Zhao 		g_rk_nand->databuf = kzalloc(nand_page_size, GFP_KERNEL);
389813156edSYifeng Zhao 		if (!g_rk_nand)
390813156edSYifeng Zhao 			return -ENOMEM;
391813156edSYifeng Zhao 		mtd->_block_isbad = spl_nand_block_isbad;
392813156edSYifeng Zhao 		mtd->_read = spl_nand_read_page;
393813156edSYifeng Zhao 		mtd->size = (size_t)nand_page_size * nand_page_num *
394813156edSYifeng Zhao 			    nand_block_num;
395813156edSYifeng Zhao 		mtd->writesize = nand_page_size;
396813156edSYifeng Zhao 		mtd->erasesize = nand_page_size * nand_page_num;
397813156edSYifeng Zhao 		mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
398813156edSYifeng Zhao 		mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
399813156edSYifeng Zhao 		mtd->type = MTD_NANDFLASH;
400813156edSYifeng Zhao 		mtd->dev = rknand->dev;
401813156edSYifeng Zhao 		mtd->priv = rknand;
402813156edSYifeng Zhao 		add_mtd_device(mtd);
403813156edSYifeng Zhao 		rknand->mtd = mtd;
404813156edSYifeng Zhao 		ret = 0;
405813156edSYifeng Zhao 	}
406813156edSYifeng Zhao 
407813156edSYifeng Zhao 	return ret;
408813156edSYifeng Zhao }
409813156edSYifeng Zhao 
rockchip_nandc_bind(struct udevice * udev)410813156edSYifeng Zhao static int rockchip_nandc_bind(struct udevice *udev)
411813156edSYifeng Zhao {
412813156edSYifeng Zhao 	int ret = 0;
413813156edSYifeng Zhao 
414813156edSYifeng Zhao #ifdef CONFIG_MTD_BLK
415813156edSYifeng Zhao 	struct udevice *bdev;
416813156edSYifeng Zhao 
417813156edSYifeng Zhao 	ret = blk_create_devicef(udev, "mtd_blk", "blk", IF_TYPE_MTD,
418813156edSYifeng Zhao 				 BLK_MTD_NAND, 512, 0, &bdev);
419813156edSYifeng Zhao 	if (ret)
420813156edSYifeng Zhao 		printf("Cannot create block device\n");
421813156edSYifeng Zhao #endif
422813156edSYifeng Zhao 	return ret;
423813156edSYifeng Zhao }
424813156edSYifeng Zhao 
425813156edSYifeng Zhao U_BOOT_DRIVER(rk_nandc_v9) = {
426813156edSYifeng Zhao 	.name           = "rk_nandc_v9",
427813156edSYifeng Zhao 	.id             = UCLASS_MTD,
428813156edSYifeng Zhao 	.of_match       = rockchip_nandc_ids,
429813156edSYifeng Zhao 	.bind		= rockchip_nandc_bind,
430813156edSYifeng Zhao 	.probe          = rockchip_nandc_probe,
431813156edSYifeng Zhao 	.priv_auto_alloc_size = sizeof(struct rk_nand),
432813156edSYifeng Zhao };
433813156edSYifeng Zhao 
board_nand_init(void)434813156edSYifeng Zhao void board_nand_init(void)
435813156edSYifeng Zhao {
436813156edSYifeng Zhao 	struct udevice *dev;
437813156edSYifeng Zhao 	int ret;
438813156edSYifeng Zhao 
439813156edSYifeng Zhao 	ret = uclass_get_device_by_driver(UCLASS_MTD,
440813156edSYifeng Zhao 					  DM_GET_DRIVER(rk_nandc_v9),
441813156edSYifeng Zhao 					  &dev);
442813156edSYifeng Zhao 	if (ret && ret != -ENODEV)
443813156edSYifeng Zhao 		pr_err("Failed to initialize NAND controller. (error %d)\n",
444813156edSYifeng Zhao 		       ret);
445813156edSYifeng Zhao }
446813156edSYifeng Zhao 
nand_spl_load_image(u32 offs,u32 size,void * buf)447813156edSYifeng Zhao int nand_spl_load_image(u32 offs, u32 size, void *buf)
448813156edSYifeng Zhao {
449813156edSYifeng Zhao 	return -EIO;
450813156edSYifeng Zhao }
451813156edSYifeng Zhao 
nand_init(void)452813156edSYifeng Zhao void nand_init(void){};
453813156edSYifeng Zhao 
rk_nand_init(void)454813156edSYifeng Zhao int rk_nand_init(void)
455813156edSYifeng Zhao {
456813156edSYifeng Zhao 	return -ENODEV;
457813156edSYifeng Zhao }
458813156edSYifeng Zhao 
459813156edSYifeng Zhao #else
board_nand_init(void)460813156edSYifeng Zhao void board_nand_init(void)
461813156edSYifeng Zhao {
462813156edSYifeng Zhao 	const void *blob = gd->fdt_blob;
463813156edSYifeng Zhao 	static int initialized;
464813156edSYifeng Zhao 	fdt_addr_t regs;
465813156edSYifeng Zhao 	int node;
466813156edSYifeng Zhao 
467813156edSYifeng Zhao 	if (initialized)
468813156edSYifeng Zhao 		return;
469813156edSYifeng Zhao 
470813156edSYifeng Zhao 	initialized = 1;
471813156edSYifeng Zhao 	nand_page_size = CONFIG_SYS_NAND_PAGE_SIZE;
472813156edSYifeng Zhao 	nand_page_num = CONFIG_SYS_NAND_PAGE_COUNT;
473813156edSYifeng Zhao 
474813156edSYifeng Zhao 	if (g_rk_nand)
475813156edSYifeng Zhao 		return;
476813156edSYifeng Zhao 
477813156edSYifeng Zhao 	node = fdtdec_next_compatible(blob, 0, COMPAT_ROCKCHIP_NANDC);
478813156edSYifeng Zhao 
479813156edSYifeng Zhao 	if (node < 0) {
480813156edSYifeng Zhao 		printf("Nand node not found\n");
481813156edSYifeng Zhao 		return;
482813156edSYifeng Zhao 	}
483813156edSYifeng Zhao 
484813156edSYifeng Zhao 	if (!fdtdec_get_is_enabled(blob, node)) {
485813156edSYifeng Zhao 		debug("Nand disabled in device tree\n");
486813156edSYifeng Zhao 		return;
487813156edSYifeng Zhao 	}
488813156edSYifeng Zhao 
489813156edSYifeng Zhao 	regs = fdt_get_base_address(blob, node);
490813156edSYifeng Zhao 	if (!regs) {
491813156edSYifeng Zhao 		debug("Nand address not found\n");
492813156edSYifeng Zhao 		return;
493813156edSYifeng Zhao 	}
494813156edSYifeng Zhao 
495813156edSYifeng Zhao 	g_rk_nand = kzalloc(sizeof(*g_rk_nand), GFP_KERNEL);
496813156edSYifeng Zhao 	g_rk_nand->regs = (void *)regs;
497813156edSYifeng Zhao 	g_rk_nand->databuf = kzalloc(nand_page_size, GFP_KERNEL);
498813156edSYifeng Zhao 	nandc_init(g_rk_nand);
499813156edSYifeng Zhao 	read_flash_id(g_rk_nand, g_rk_nand->id);
500813156edSYifeng Zhao 
501813156edSYifeng Zhao 	if (g_rk_nand->id[0] == g_rk_nand->id[1])
502813156edSYifeng Zhao 		goto err;
503813156edSYifeng Zhao 
504813156edSYifeng Zhao 	if (g_rk_nand->id[1] == 0xA1 || g_rk_nand->id[1] == 0xF1 ||
505813156edSYifeng Zhao 	    g_rk_nand->id[1] == 0xD1 || g_rk_nand->id[1] == 0xAA ||
506813156edSYifeng Zhao 	    g_rk_nand->id[1] == 0xDA || g_rk_nand->id[1] == 0xAC ||
507813156edSYifeng Zhao 	    g_rk_nand->id[1] == 0xDC || g_rk_nand->id[1] == 0xA3 ||
508813156edSYifeng Zhao 	    g_rk_nand->id[1] == 0xD3 || g_rk_nand->id[1] == 0x95 ||
5092d08457fSJon Lin 	    g_rk_nand->id[1] == 0x48 || g_rk_nand->id[1] == 0xD7) {
510813156edSYifeng Zhao 		g_rk_nand->chipnr = 1;
511813156edSYifeng Zhao 		return;
512813156edSYifeng Zhao 	}
513813156edSYifeng Zhao 
514813156edSYifeng Zhao err:
515813156edSYifeng Zhao 	kfree(g_rk_nand->databuf);
516813156edSYifeng Zhao 	kfree(g_rk_nand);
517813156edSYifeng Zhao 	g_rk_nand = NULL;
518813156edSYifeng Zhao }
519813156edSYifeng Zhao 
nand_spl_load_image(u32 offs,u32 size,void * buf)520813156edSYifeng Zhao int nand_spl_load_image(u32 offs, u32 size, void *buf)
521813156edSYifeng Zhao {
522813156edSYifeng Zhao 	int i;
523813156edSYifeng Zhao 	unsigned int page;
524d28ec0f5SJon Lin 	int force_bad_block_check = 1;
525813156edSYifeng Zhao 	unsigned int maxpages = CONFIG_SYS_NAND_SIZE /
526813156edSYifeng Zhao 				nand_page_size;
527813156edSYifeng Zhao 
528813156edSYifeng Zhao 	/* Convert to page number */
529813156edSYifeng Zhao 	page = offs / nand_page_size;
530813156edSYifeng Zhao 	i = 0;
531813156edSYifeng Zhao 
532813156edSYifeng Zhao 	size = roundup(size, nand_page_size);
533813156edSYifeng Zhao 	while (i < size / nand_page_size) {
534813156edSYifeng Zhao 		/*
535813156edSYifeng Zhao 		 * Check if we have crossed a block boundary, and if so
536813156edSYifeng Zhao 		 * check for bad block.
537813156edSYifeng Zhao 		 */
538d28ec0f5SJon Lin 		if (force_bad_block_check || !(page % nand_page_num)) {
539813156edSYifeng Zhao 			/*
540813156edSYifeng Zhao 			 * Yes, new block. See if this block is good. If not,
541813156edSYifeng Zhao 			 * loop until we find a good block.
542813156edSYifeng Zhao 			 */
543813156edSYifeng Zhao 			while (is_badblock(page)) {
54498c66434SJon Lin 				page = page + nand_page_num;
545813156edSYifeng Zhao 				/* Check i we've reached the end of flash. */
546813156edSYifeng Zhao 				if (page >= maxpages)
547813156edSYifeng Zhao 					return -EIO;
548813156edSYifeng Zhao 			}
549813156edSYifeng Zhao 		}
550813156edSYifeng Zhao 
551d28ec0f5SJon Lin 		force_bad_block_check = 0;
552d28ec0f5SJon Lin 
553813156edSYifeng Zhao 		if (nandc_read_page(page, buf) < 0)
554813156edSYifeng Zhao 			return -EIO;
555813156edSYifeng Zhao 
556813156edSYifeng Zhao 		page++;
557813156edSYifeng Zhao 		i++;
558813156edSYifeng Zhao 		buf = buf + nand_page_size;
559813156edSYifeng Zhao 	}
560813156edSYifeng Zhao 	return 0;
561813156edSYifeng Zhao }
562813156edSYifeng Zhao 
nand_init(void)563813156edSYifeng Zhao void nand_init(void)
564813156edSYifeng Zhao {
565813156edSYifeng Zhao 	board_nand_init();
566813156edSYifeng Zhao }
567813156edSYifeng Zhao 
rk_nand_init(void)568813156edSYifeng Zhao int rk_nand_init(void)
569813156edSYifeng Zhao {
570813156edSYifeng Zhao 	board_nand_init();
571813156edSYifeng Zhao 	if (g_rk_nand && g_rk_nand->chipnr)
572813156edSYifeng Zhao 		return 0;
573813156edSYifeng Zhao 	else
574813156edSYifeng Zhao 		return -ENODEV;
575813156edSYifeng Zhao }
576813156edSYifeng Zhao #endif
577813156edSYifeng Zhao 
nand_deselect(void)578813156edSYifeng Zhao void nand_deselect(void) {}
579813156edSYifeng Zhao 
580