xref: /rk3399_rockchip-uboot/drivers/mtd/nand/raw/rockchip_nand_spl.c (revision f6f9fe4f0f4d78852b3836677aee7e048242cfa1)
157e25cf7SYifeng Zhao /*
257e25cf7SYifeng Zhao  * Copyright (c) 2017 Yifeng Zhao <yifeng.zhao@rock-chips.com>
357e25cf7SYifeng Zhao  *
457e25cf7SYifeng Zhao  * SPDX-License-Identifier:     GPL-2.0+
557e25cf7SYifeng Zhao  */
657e25cf7SYifeng Zhao 
757e25cf7SYifeng Zhao #include <common.h>
8e65bf00aSYifeng Zhao #include <dm.h>
957e25cf7SYifeng Zhao #include <fdtdec.h>
103a692737SJason Zhu #include <fdt_support.h>
1157e25cf7SYifeng Zhao #include <inttypes.h>
1257e25cf7SYifeng Zhao #include <nand.h>
1357e25cf7SYifeng Zhao #include <linux/kernel.h>
1457e25cf7SYifeng Zhao #include <linux/mtd/mtd.h>
1557e25cf7SYifeng Zhao #include <linux/mtd/nand.h>
1657e25cf7SYifeng Zhao #include <linux/mtd/partitions.h>
1757e25cf7SYifeng Zhao #include <linux/io.h>
1857e25cf7SYifeng Zhao 
1957e25cf7SYifeng Zhao DECLARE_GLOBAL_DATA_PTR;
2057e25cf7SYifeng Zhao 
2157e25cf7SYifeng Zhao #define NANDC_V6_BOOTROM_ECC	24
2257e25cf7SYifeng Zhao #define NANDC_V6_NUM_BANKS	4
2357e25cf7SYifeng Zhao #define NANDC_V6_DEF_TIMEOUT	20000
2457e25cf7SYifeng Zhao #define NANDC_V6_READ		0
2557e25cf7SYifeng Zhao #define NANDC_V6_WRITE		1
2657e25cf7SYifeng Zhao 
2757e25cf7SYifeng Zhao #define	NANDC_REG_V6_FMCTL	0x00
2857e25cf7SYifeng Zhao #define	NANDC_REG_V6_FMWAIT	0x04
2957e25cf7SYifeng Zhao #define	NANDC_REG_V6_FLCTL	0x08
3057e25cf7SYifeng Zhao #define	NANDC_REG_V6_BCHCTL	0x0c
3157e25cf7SYifeng Zhao #define	NANDC_REG_V6_DMA_CFG	0x10
3257e25cf7SYifeng Zhao #define	NANDC_REG_V6_DMA_BUF0	0x14
3357e25cf7SYifeng Zhao #define	NANDC_REG_V6_DMA_BUF1	0x18
3457e25cf7SYifeng Zhao #define	NANDC_REG_V6_DMA_ST	0x1C
3557e25cf7SYifeng Zhao #define	NANDC_REG_V6_BCHST	0x20
3657e25cf7SYifeng Zhao #define	NANDC_REG_V6_RANDMZ	0x150
3757e25cf7SYifeng Zhao #define	NANDC_REG_V6_VER	0x160
3857e25cf7SYifeng Zhao #define	NANDC_REG_V6_INTEN	0x16C
3957e25cf7SYifeng Zhao #define	NANDC_REG_V6_INTCLR	0x170
4057e25cf7SYifeng Zhao #define	NANDC_REG_V6_INTST	0x174
4157e25cf7SYifeng Zhao #define	NANDC_REG_V6_SPARE0	0x200
4257e25cf7SYifeng Zhao #define	NANDC_REG_V6_SPARE1	0x230
4357e25cf7SYifeng Zhao #define	NANDC_REG_V6_BANK0	0x800
4457e25cf7SYifeng Zhao #define	NANDC_REG_V6_SRAM0	0x1000
4557e25cf7SYifeng Zhao #define	NANDC_REG_V6_SRAM_SIZE	0x400
4657e25cf7SYifeng Zhao 
4757e25cf7SYifeng Zhao #define NANDC_REG_V6_DATA	0x00
4857e25cf7SYifeng Zhao #define NANDC_REG_V6_ADDR	0x04
4957e25cf7SYifeng Zhao #define NANDC_REG_V6_CMD	0x08
5057e25cf7SYifeng Zhao 
5157e25cf7SYifeng Zhao /* FMCTL */
5257e25cf7SYifeng Zhao #define NANDC_V6_FM_WP		BIT(8)
5357e25cf7SYifeng Zhao #define NANDC_V6_FM_CE_SEL_M	0xFF
5457e25cf7SYifeng Zhao #define NANDC_V6_FM_CE_SEL(x)	(1 << (x))
5557e25cf7SYifeng Zhao #define NANDC_V6_FM_FREADY	BIT(9)
5657e25cf7SYifeng Zhao 
5757e25cf7SYifeng Zhao /* FLCTL */
5857e25cf7SYifeng Zhao #define NANDC_V6_FL_RST		BIT(0)
5957e25cf7SYifeng Zhao #define NANDC_V6_FL_DIR_S	0x1
6057e25cf7SYifeng Zhao #define NANDC_V6_FL_XFER_START	BIT(2)
6157e25cf7SYifeng Zhao #define NANDC_V6_FL_XFER_EN	BIT(3)
6257e25cf7SYifeng Zhao #define NANDC_V6_FL_ST_BUF_S	0x4
6357e25cf7SYifeng Zhao #define NANDC_V6_FL_XFER_COUNT	BIT(5)
6457e25cf7SYifeng Zhao #define NANDC_V6_FL_ACORRECT	BIT(10)
6557e25cf7SYifeng Zhao #define NANDC_V6_FL_XFER_READY	BIT(20)
6657e25cf7SYifeng Zhao 
6757e25cf7SYifeng Zhao /* BCHCTL */
6857e25cf7SYifeng Zhao #define NAND_V6_BCH_REGION_S	0x5
6957e25cf7SYifeng Zhao #define NAND_V6_BCH_REGION_M	0x7
7057e25cf7SYifeng Zhao 
7157e25cf7SYifeng Zhao /* BCHST */
7257e25cf7SYifeng Zhao #define NANDC_V6_BCH0_ST_ERR	BIT(2)
7357e25cf7SYifeng Zhao #define NANDC_V6_BCH1_ST_ERR	BIT(15)
7457e25cf7SYifeng Zhao #define NANDC_V6_ECC_ERR_CNT0(x) ((((x & (0x1F << 3)) >> 3) \
7557e25cf7SYifeng Zhao 				| ((x & (1 << 27)) >> 22)) & 0x3F)
7657e25cf7SYifeng Zhao #define NANDC_V6_ECC_ERR_CNT1(x) ((((x & (0x1F << 16)) >> 16) \
7757e25cf7SYifeng Zhao 				| ((x & (1 << 29)) >> 24)) & 0x3F)
7857e25cf7SYifeng Zhao 
7957e25cf7SYifeng Zhao struct rk_nand {
8057e25cf7SYifeng Zhao 	void __iomem *regs;
8157e25cf7SYifeng Zhao 	u8 chipnr;
8257e25cf7SYifeng Zhao 	u8 id[5];
8357e25cf7SYifeng Zhao 	u8 *databuf;
84e65bf00aSYifeng Zhao 	struct udevice *dev;
85e65bf00aSYifeng Zhao 	struct mtd_info *mtd;
8657e25cf7SYifeng Zhao };
8757e25cf7SYifeng Zhao 
886f8d5eccSYifeng Zhao static struct rk_nand *g_rk_nand;
896f8d5eccSYifeng Zhao static u32 nand_page_size;
906f8d5eccSYifeng Zhao static u32 nand_page_num;
916f8d5eccSYifeng Zhao static u32 nand_block_num;
9257e25cf7SYifeng Zhao 
nandc_init(struct rk_nand * rknand)9357e25cf7SYifeng Zhao static void nandc_init(struct rk_nand *rknand)
9457e25cf7SYifeng Zhao {
9557e25cf7SYifeng Zhao 	writel(0x1081, rknand->regs + NANDC_REG_V6_FMWAIT);
9657e25cf7SYifeng Zhao }
9757e25cf7SYifeng Zhao 
rockchip_nand_wait_dev_ready(void __iomem * regs)9857e25cf7SYifeng Zhao static void rockchip_nand_wait_dev_ready(void __iomem *regs)
9957e25cf7SYifeng Zhao {
10057e25cf7SYifeng Zhao 	u32 reg;
10157e25cf7SYifeng Zhao 	u32 timeout = NANDC_V6_DEF_TIMEOUT;
10257e25cf7SYifeng Zhao 
10357e25cf7SYifeng Zhao 	while (timeout--) {
10457e25cf7SYifeng Zhao 		udelay(1);
10557e25cf7SYifeng Zhao 		reg = readl(regs + NANDC_REG_V6_FMCTL);
10657e25cf7SYifeng Zhao 
10757e25cf7SYifeng Zhao 		if ((reg & NANDC_V6_FM_FREADY))
10857e25cf7SYifeng Zhao 			break;
10957e25cf7SYifeng Zhao 	}
11057e25cf7SYifeng Zhao }
11157e25cf7SYifeng Zhao 
rockchip_nand_select_chip(void __iomem * regs,int chipnr)11257e25cf7SYifeng Zhao static void rockchip_nand_select_chip(void __iomem *regs, int chipnr)
11357e25cf7SYifeng Zhao {
11457e25cf7SYifeng Zhao 	u32 reg;
11557e25cf7SYifeng Zhao 
11657e25cf7SYifeng Zhao 	reg = readl(regs + NANDC_REG_V6_FMCTL);
11757e25cf7SYifeng Zhao 	reg &= ~NANDC_V6_FM_CE_SEL_M;
11857e25cf7SYifeng Zhao 	if (chipnr != -1)
11957e25cf7SYifeng Zhao 		reg |= 1 << chipnr;
12057e25cf7SYifeng Zhao 	writel(reg, regs + NANDC_REG_V6_FMCTL);
12157e25cf7SYifeng Zhao }
12257e25cf7SYifeng Zhao 
rockchip_nand_read_page(void __iomem * regs,int page,int col)12357e25cf7SYifeng Zhao static void rockchip_nand_read_page(void __iomem *regs,
12457e25cf7SYifeng Zhao 				    int page, int col)
12557e25cf7SYifeng Zhao {
12657e25cf7SYifeng Zhao 	void __iomem *bank_base = regs + NANDC_REG_V6_BANK0;
12757e25cf7SYifeng Zhao 
12857e25cf7SYifeng Zhao 	writeb(0x00, bank_base + NANDC_REG_V6_CMD);
12957e25cf7SYifeng Zhao 	writeb(col, bank_base + NANDC_REG_V6_ADDR);
13057e25cf7SYifeng Zhao 	writeb(col >> 8, bank_base + NANDC_REG_V6_ADDR);
13157e25cf7SYifeng Zhao 	writeb(page, bank_base + NANDC_REG_V6_ADDR);
13257e25cf7SYifeng Zhao 	writeb(page >> 8, bank_base + NANDC_REG_V6_ADDR);
13357e25cf7SYifeng Zhao 	writeb(page >> 16, bank_base + NANDC_REG_V6_ADDR);
13457e25cf7SYifeng Zhao 	writeb(0x30, bank_base + NANDC_REG_V6_CMD);
13557e25cf7SYifeng Zhao }
13657e25cf7SYifeng Zhao 
rockchip_nand_pio_xfer_start(struct rk_nand * rknand,u8 dir,u8 st_buf)13757e25cf7SYifeng Zhao static void rockchip_nand_pio_xfer_start(struct rk_nand *rknand,
13857e25cf7SYifeng Zhao 					 u8 dir,
13957e25cf7SYifeng Zhao 					 u8 st_buf)
14057e25cf7SYifeng Zhao {
14157e25cf7SYifeng Zhao 	u32 reg;
14257e25cf7SYifeng Zhao 
14357e25cf7SYifeng Zhao 	reg = readl(rknand->regs + NANDC_REG_V6_BCHCTL);
14457e25cf7SYifeng Zhao 	reg = (reg & (~(NAND_V6_BCH_REGION_M << NAND_V6_BCH_REGION_S)));
14557e25cf7SYifeng Zhao 	writel(reg, rknand->regs + NANDC_REG_V6_BCHCTL);
14657e25cf7SYifeng Zhao 
14757e25cf7SYifeng Zhao 	reg = (dir << NANDC_V6_FL_DIR_S) | (st_buf << NANDC_V6_FL_ST_BUF_S) |
14857e25cf7SYifeng Zhao 		  NANDC_V6_FL_XFER_EN | NANDC_V6_FL_XFER_COUNT |
14957e25cf7SYifeng Zhao 		  NANDC_V6_FL_ACORRECT;
15057e25cf7SYifeng Zhao 	writel(reg, rknand->regs + NANDC_REG_V6_FLCTL);
15157e25cf7SYifeng Zhao 
15257e25cf7SYifeng Zhao 	reg |= NANDC_V6_FL_XFER_START;
15357e25cf7SYifeng Zhao 	writel(reg, rknand->regs + NANDC_REG_V6_FLCTL);
15457e25cf7SYifeng Zhao }
15557e25cf7SYifeng Zhao 
rockchip_nand_wait_pio_xfer_done(struct rk_nand * rknand)15657e25cf7SYifeng Zhao static int rockchip_nand_wait_pio_xfer_done(struct rk_nand *rknand)
15757e25cf7SYifeng Zhao {
15857e25cf7SYifeng Zhao 	int timeout = NANDC_V6_DEF_TIMEOUT;
15957e25cf7SYifeng Zhao 	int reg;
16057e25cf7SYifeng Zhao 
16157e25cf7SYifeng Zhao 	while (timeout--) {
16257e25cf7SYifeng Zhao 		reg = readl(rknand->regs + NANDC_REG_V6_FLCTL);
16357e25cf7SYifeng Zhao 
16457e25cf7SYifeng Zhao 		if ((reg & NANDC_V6_FL_XFER_READY) != 0)
16557e25cf7SYifeng Zhao 			break;
16657e25cf7SYifeng Zhao 
16757e25cf7SYifeng Zhao 		udelay(1);
16857e25cf7SYifeng Zhao 	}
16957e25cf7SYifeng Zhao 
17057e25cf7SYifeng Zhao 	if (timeout == 0)
17157e25cf7SYifeng Zhao 		return -1;
17257e25cf7SYifeng Zhao 
17357e25cf7SYifeng Zhao 	return 0;
17457e25cf7SYifeng Zhao }
17557e25cf7SYifeng Zhao 
nandc_read_page(unsigned int page,uint8_t * buf)17657e25cf7SYifeng Zhao static int nandc_read_page(unsigned int page, uint8_t *buf)
17757e25cf7SYifeng Zhao {
17857e25cf7SYifeng Zhao 	void __iomem *sram_base = g_rk_nand->regs + NANDC_REG_V6_SRAM0;
17957e25cf7SYifeng Zhao 	unsigned int max_bitflips = 0;
18057e25cf7SYifeng Zhao 	int ret, step, bch_st, ecc_step;
18157e25cf7SYifeng Zhao 
1826f8d5eccSYifeng Zhao 	ecc_step = nand_page_size / 1024;
18357e25cf7SYifeng Zhao 	rockchip_nand_select_chip(g_rk_nand->regs, 0);
18457e25cf7SYifeng Zhao 	rockchip_nand_read_page(g_rk_nand->regs, page, 0);
18557e25cf7SYifeng Zhao 	rockchip_nand_wait_dev_ready(g_rk_nand->regs);
18657e25cf7SYifeng Zhao 	rockchip_nand_pio_xfer_start(g_rk_nand, NANDC_V6_READ, 0);
18757e25cf7SYifeng Zhao 
18857e25cf7SYifeng Zhao 	for (step = 0; step < ecc_step; step++) {
18957e25cf7SYifeng Zhao 		int data_off = step * 1024;
19057e25cf7SYifeng Zhao 		u8 *data = buf + data_off;
19157e25cf7SYifeng Zhao 
19257e25cf7SYifeng Zhao 		ret = rockchip_nand_wait_pio_xfer_done(g_rk_nand);
19357e25cf7SYifeng Zhao 		if (ret)
19457e25cf7SYifeng Zhao 			return ret;
19557e25cf7SYifeng Zhao 
19657e25cf7SYifeng Zhao 		bch_st = readl(g_rk_nand->regs + NANDC_REG_V6_BCHST);
19757e25cf7SYifeng Zhao 
19857e25cf7SYifeng Zhao 		if (bch_st & NANDC_V6_BCH0_ST_ERR) {
19957e25cf7SYifeng Zhao 			max_bitflips = -1;
20057e25cf7SYifeng Zhao 		} else {
20157e25cf7SYifeng Zhao 			ret = NANDC_V6_ECC_ERR_CNT0(bch_st);
20257e25cf7SYifeng Zhao 			max_bitflips = max_t(unsigned int, max_bitflips, ret);
20357e25cf7SYifeng Zhao 		}
20457e25cf7SYifeng Zhao 
20557e25cf7SYifeng Zhao 		if ((step + 1) < ecc_step)
20657e25cf7SYifeng Zhao 			rockchip_nand_pio_xfer_start(g_rk_nand, NANDC_V6_READ,
20757e25cf7SYifeng Zhao 						     (step + 1) & 0x1);
20857e25cf7SYifeng Zhao 
20957e25cf7SYifeng Zhao 		memcpy_fromio(data, sram_base + NANDC_REG_V6_SRAM_SIZE *
21057e25cf7SYifeng Zhao 			      (step & 1), 1024);
21157e25cf7SYifeng Zhao 	}
21257e25cf7SYifeng Zhao 	rockchip_nand_select_chip(g_rk_nand->regs, -1);
21357e25cf7SYifeng Zhao 
21457e25cf7SYifeng Zhao 	return max_bitflips;
21557e25cf7SYifeng Zhao }
21657e25cf7SYifeng Zhao 
is_badblock(unsigned int page)21757e25cf7SYifeng Zhao static int is_badblock(unsigned int page)
21857e25cf7SYifeng Zhao {
21957e25cf7SYifeng Zhao 	int res = 0, i;
22057e25cf7SYifeng Zhao 	u16 bad = 0xff;
22157e25cf7SYifeng Zhao 	void __iomem *regs = g_rk_nand->regs;
22257e25cf7SYifeng Zhao 	void __iomem *bank_base = regs + NANDC_REG_V6_BANK0;
22357e25cf7SYifeng Zhao 
22457e25cf7SYifeng Zhao 	if (nandc_read_page(page, g_rk_nand->databuf) == -1) {
22557e25cf7SYifeng Zhao 		rockchip_nand_select_chip(regs, 0);
2266f8d5eccSYifeng Zhao 		rockchip_nand_read_page(regs, page, nand_page_size);
22757e25cf7SYifeng Zhao 		rockchip_nand_wait_dev_ready(regs);
22857e25cf7SYifeng Zhao 		for (i = 0; i < 8; i++) {
22957e25cf7SYifeng Zhao 			bad = readb(bank_base);
23057e25cf7SYifeng Zhao 			if (bad)
23157e25cf7SYifeng Zhao 				break;
23257e25cf7SYifeng Zhao 		}
23357e25cf7SYifeng Zhao 		if (i >= 8)
23457e25cf7SYifeng Zhao 			res = 1;
23557e25cf7SYifeng Zhao 		rockchip_nand_select_chip(regs, 0);
23657e25cf7SYifeng Zhao 	}
23757e25cf7SYifeng Zhao 	if (res)
23857e25cf7SYifeng Zhao 		printf("%s 0x%x %x %x\n", __func__, page, res, bad);
23957e25cf7SYifeng Zhao 	return res;
24057e25cf7SYifeng Zhao }
24157e25cf7SYifeng Zhao 
read_flash_id(struct rk_nand * rknand,uint8_t * id)24257e25cf7SYifeng Zhao static void read_flash_id(struct rk_nand *rknand, uint8_t *id)
24357e25cf7SYifeng Zhao {
24457e25cf7SYifeng Zhao 	void __iomem *bank_base = rknand->regs + NANDC_REG_V6_BANK0;
24557e25cf7SYifeng Zhao 
24657e25cf7SYifeng Zhao 	rockchip_nand_wait_dev_ready(g_rk_nand->regs);
24757e25cf7SYifeng Zhao 	writeb(0x90, bank_base + NANDC_REG_V6_CMD);
24857e25cf7SYifeng Zhao 	writeb(0x00, bank_base + NANDC_REG_V6_ADDR);
24957e25cf7SYifeng Zhao 	udelay(1);
25057e25cf7SYifeng Zhao 	id[0] = readb(bank_base);
25157e25cf7SYifeng Zhao 	id[1] = readb(bank_base);
25257e25cf7SYifeng Zhao 	id[2] = readb(bank_base);
25357e25cf7SYifeng Zhao 	id[3] = readb(bank_base);
25457e25cf7SYifeng Zhao 	id[4] = readb(bank_base);
25557e25cf7SYifeng Zhao 	rockchip_nand_select_chip(rknand->regs, -1);
256e65bf00aSYifeng Zhao 	if (id[0] != 0xFF && id[0] != 0x00)
257e65bf00aSYifeng Zhao 		printf("NAND:%x %x\n", id[0], id[1]);
25857e25cf7SYifeng Zhao }
25957e25cf7SYifeng Zhao 
260e65bf00aSYifeng Zhao #ifdef CONFIG_NAND_ROCKCHIP_DT
261e65bf00aSYifeng Zhao static const struct udevice_id rockchip_nandc_ids[] = {
262e65bf00aSYifeng Zhao 	{ .compatible = "rockchip,rk-nandc" },
263e65bf00aSYifeng Zhao 	{ }
264e65bf00aSYifeng Zhao };
265e65bf00aSYifeng Zhao 
spl_nand_block_isbad(struct mtd_info * mtd,loff_t ofs)266e65bf00aSYifeng Zhao static int spl_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
267e65bf00aSYifeng Zhao {
2686f8d5eccSYifeng Zhao 	return is_badblock((u32)ofs / nand_page_size);
269e65bf00aSYifeng Zhao }
270e65bf00aSYifeng Zhao 
spl_nand_read_page(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)271e65bf00aSYifeng Zhao static int spl_nand_read_page(struct mtd_info *mtd, loff_t from, size_t len,
272e65bf00aSYifeng Zhao 			      size_t *retlen, u_char *buf)
273e65bf00aSYifeng Zhao {
274e65bf00aSYifeng Zhao 	int read_size, offset, read_len;
275e65bf00aSYifeng Zhao 	unsigned int page;
2766f8d5eccSYifeng Zhao 	unsigned int max_pages = nand_page_num * nand_block_num;
277e65bf00aSYifeng Zhao 
278e65bf00aSYifeng Zhao 	/* Convert to page number */
2796f8d5eccSYifeng Zhao 	page = (u32)from / nand_page_size;
2806f8d5eccSYifeng Zhao 	offset = from & (nand_page_size - 1);
281e65bf00aSYifeng Zhao 	read_len = len;
282e65bf00aSYifeng Zhao 	*retlen = 0;
283e65bf00aSYifeng Zhao 
284e65bf00aSYifeng Zhao 	while (read_len) {
2856f8d5eccSYifeng Zhao 		read_size = nand_page_size - offset;
286e65bf00aSYifeng Zhao 		if (read_size > read_len)
287e65bf00aSYifeng Zhao 			read_size = read_len;
2886f8d5eccSYifeng Zhao 		if (offset || read_size < nand_page_size) {
289e65bf00aSYifeng Zhao 			if (nandc_read_page(page, g_rk_nand->databuf) < 0)
290e65bf00aSYifeng Zhao 				return -EIO;
291e65bf00aSYifeng Zhao 			memcpy(buf, g_rk_nand->databuf + offset, read_size);
292e65bf00aSYifeng Zhao 			offset = 0;
293e65bf00aSYifeng Zhao 		} else {
294e65bf00aSYifeng Zhao 			if (nandc_read_page(page, buf) < 0)
295e65bf00aSYifeng Zhao 				return -EIO;
296e65bf00aSYifeng Zhao 		}
297e65bf00aSYifeng Zhao 		page++;
298e65bf00aSYifeng Zhao 		read_len -= read_size;
299e65bf00aSYifeng Zhao 		buf += read_size;
300e65bf00aSYifeng Zhao 		if (page >= max_pages)
301e65bf00aSYifeng Zhao 			return -EIO;
302e65bf00aSYifeng Zhao 	}
303e65bf00aSYifeng Zhao 
304e65bf00aSYifeng Zhao 	*retlen = len;
305e65bf00aSYifeng Zhao 
306e65bf00aSYifeng Zhao 	return 0;
307e65bf00aSYifeng Zhao }
308e65bf00aSYifeng Zhao 
rockchip_nandc_probe(struct udevice * dev)309e65bf00aSYifeng Zhao static int rockchip_nandc_probe(struct udevice *dev)
310e65bf00aSYifeng Zhao {
311e65bf00aSYifeng Zhao 	const void *blob = gd->fdt_blob;
312e65bf00aSYifeng Zhao 	struct rk_nand *rknand = dev_get_priv(dev);
313e65bf00aSYifeng Zhao 	struct mtd_info *mtd = dev_get_uclass_priv(dev);
314e65bf00aSYifeng Zhao 	fdt_addr_t regs;
315e65bf00aSYifeng Zhao 	int ret = -ENODEV;
316e65bf00aSYifeng Zhao 	int node;
3176f8d5eccSYifeng Zhao 	u8 *id;
318e65bf00aSYifeng Zhao 
319e65bf00aSYifeng Zhao 	g_rk_nand = rknand;
320e65bf00aSYifeng Zhao 	rknand->dev = dev;
321e65bf00aSYifeng Zhao 
322e65bf00aSYifeng Zhao 	node = fdtdec_next_compatible(blob, 0, COMPAT_ROCKCHIP_NANDC);
323e65bf00aSYifeng Zhao 
324e65bf00aSYifeng Zhao 	if (node < 0) {
325e65bf00aSYifeng Zhao 		printf("Nand node not found\n");
326e65bf00aSYifeng Zhao 		return -ENODEV;
327e65bf00aSYifeng Zhao 	}
328e65bf00aSYifeng Zhao 
329e65bf00aSYifeng Zhao 	if (!fdtdec_get_is_enabled(blob, node)) {
330e65bf00aSYifeng Zhao 		debug("Nand disabled in device tree\n");
331e65bf00aSYifeng Zhao 		return -ENODEV;
332e65bf00aSYifeng Zhao 	}
333e65bf00aSYifeng Zhao 
334e65bf00aSYifeng Zhao 	regs = fdt_get_base_address(blob, node);
335e65bf00aSYifeng Zhao 	if (!regs) {
336e65bf00aSYifeng Zhao 		debug("Nand address not found\n");
337e65bf00aSYifeng Zhao 		return -ENODEV;
338e65bf00aSYifeng Zhao 	}
339e65bf00aSYifeng Zhao 
340e65bf00aSYifeng Zhao 	rknand->regs = (void *)regs;
341e65bf00aSYifeng Zhao 
342e65bf00aSYifeng Zhao 	nandc_init(g_rk_nand);
343e65bf00aSYifeng Zhao 	read_flash_id(g_rk_nand, g_rk_nand->id);
344e65bf00aSYifeng Zhao 
3456f8d5eccSYifeng Zhao 	id = g_rk_nand->id;
3466f8d5eccSYifeng Zhao 	if (id[0] == id[1])
347e65bf00aSYifeng Zhao 		return -ENODEV;
348e65bf00aSYifeng Zhao 
3496f8d5eccSYifeng Zhao 	if (id[1] == 0xA1 || id[1] == 0xF1 ||
3506f8d5eccSYifeng Zhao 	    id[1] == 0xD1 || id[1] == 0xAA ||
3516f8d5eccSYifeng Zhao 	    id[1] == 0xDA || id[1] == 0xAC ||
3526f8d5eccSYifeng Zhao 	    id[1] == 0xDC || id[1] == 0xA3 ||
3536f8d5eccSYifeng Zhao 	    id[1] == 0xD3 || id[1] == 0x95 ||
3546b5b58a0SJon Lin 	    id[1] == 0x48 || id[1] == 0x63) {
3556f8d5eccSYifeng Zhao 		nand_page_size = 2048;
3566f8d5eccSYifeng Zhao 		nand_page_num = 64;
3576f8d5eccSYifeng Zhao 		nand_block_num = 1024;
358fb735bc0SJon Lin 		if (id[1] == 0xDC || id[1] == 0xAC) {
3596f8d5eccSYifeng Zhao 			if ((id[0] == 0x2C && id[3] == 0xA6) ||
360*f6f9fe4fSJon Lin 			    (id[0] == 0xC2 && id[3] == 0xA2) ||
361*f6f9fe4fSJon Lin 				(id[0] == 0x98 && id[3] == 0x26) ||
362*f6f9fe4fSJon Lin 				(id[0] == 0xCD && id[3] == 0xA2)) {
3636f8d5eccSYifeng Zhao 				nand_page_size = 4096;
3646f8d5eccSYifeng Zhao 				nand_block_num = 2048;
3656f8d5eccSYifeng Zhao 			} else {
3666f8d5eccSYifeng Zhao 				nand_block_num = 4096;
3676f8d5eccSYifeng Zhao 			}
3686f8d5eccSYifeng Zhao 		} else if (id[1] == 0xDA) {
3696f8d5eccSYifeng Zhao 			nand_block_num = 2048;
3706f8d5eccSYifeng Zhao 		} else if (id[1] == 0x48) {
3716f8d5eccSYifeng Zhao 			nand_page_size = 4096;
3726f8d5eccSYifeng Zhao 			nand_page_num = 128;
3736f8d5eccSYifeng Zhao 			nand_block_num = 4096;
3746f8d5eccSYifeng Zhao 		} else if (id[1] == 0xD3) {
37517ba2606SJon Lin 			if ((id[2] == 0xD1 && id[4] == 0x5a) || /* S34ML08G2 */
37617ba2606SJon Lin 			    (id[3] == 0x05 && id[4] == 0x04)) { /* S34ML08G3 */
37717ba2606SJon Lin 				nand_block_num = 8192;
37817ba2606SJon Lin 			} else {
3796f8d5eccSYifeng Zhao 				nand_page_size = 4096;
3806f8d5eccSYifeng Zhao 				nand_block_num = 4096;
3816f8d5eccSYifeng Zhao 			}
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;
38617ba2606SJon Lin 		}
387e65bf00aSYifeng Zhao 		g_rk_nand->chipnr = 1;
3886f8d5eccSYifeng Zhao 		g_rk_nand->databuf = kzalloc(nand_page_size, GFP_KERNEL);
389e65bf00aSYifeng Zhao 		if (!g_rk_nand)
390e65bf00aSYifeng Zhao 			return -ENOMEM;
391e65bf00aSYifeng Zhao 		mtd->_block_isbad = spl_nand_block_isbad;
392e65bf00aSYifeng Zhao 		mtd->_read = spl_nand_read_page;
3936f8d5eccSYifeng Zhao 		mtd->size = (size_t)nand_page_size * nand_page_num *
3946f8d5eccSYifeng Zhao 			    nand_block_num;
3956f8d5eccSYifeng Zhao 		mtd->writesize = nand_page_size;
3966f8d5eccSYifeng Zhao 		mtd->erasesize = nand_page_size * nand_page_num;
3976f8d5eccSYifeng Zhao 		mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
3986f8d5eccSYifeng Zhao 		mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
399e65bf00aSYifeng Zhao 		mtd->type = MTD_NANDFLASH;
400e65bf00aSYifeng Zhao 		mtd->dev = rknand->dev;
401e65bf00aSYifeng Zhao 		mtd->priv = rknand;
402e65bf00aSYifeng Zhao 		add_mtd_device(mtd);
403e65bf00aSYifeng Zhao 		rknand->mtd = mtd;
404e65bf00aSYifeng Zhao 		ret = 0;
405e65bf00aSYifeng Zhao 	}
406e65bf00aSYifeng Zhao 
407e65bf00aSYifeng Zhao 	return ret;
408e65bf00aSYifeng Zhao }
409e65bf00aSYifeng Zhao 
rockchip_nandc_bind(struct udevice * udev)410e65bf00aSYifeng Zhao static int rockchip_nandc_bind(struct udevice *udev)
411e65bf00aSYifeng Zhao {
412e65bf00aSYifeng Zhao 	int ret = 0;
413e65bf00aSYifeng Zhao 
414e65bf00aSYifeng Zhao #ifdef CONFIG_MTD_BLK
415e65bf00aSYifeng Zhao 	struct udevice *bdev;
416e65bf00aSYifeng Zhao 
417e65bf00aSYifeng Zhao 	ret = blk_create_devicef(udev, "mtd_blk", "blk", IF_TYPE_MTD,
418e65bf00aSYifeng Zhao 				 BLK_MTD_NAND, 512, 0, &bdev);
419e65bf00aSYifeng Zhao 	if (ret)
420e65bf00aSYifeng Zhao 		printf("Cannot create block device\n");
421e65bf00aSYifeng Zhao #endif
422e65bf00aSYifeng Zhao 	return ret;
423e65bf00aSYifeng Zhao }
424e65bf00aSYifeng Zhao 
425e65bf00aSYifeng Zhao U_BOOT_DRIVER(rk_nandc_v6) = {
426e65bf00aSYifeng Zhao 	.name           = "rk_nandc_v6",
427e65bf00aSYifeng Zhao 	.id             = UCLASS_MTD,
428e65bf00aSYifeng Zhao 	.of_match       = rockchip_nandc_ids,
429e65bf00aSYifeng Zhao 	.bind		= rockchip_nandc_bind,
430e65bf00aSYifeng Zhao 	.probe          = rockchip_nandc_probe,
431e65bf00aSYifeng Zhao 	.priv_auto_alloc_size = sizeof(struct rk_nand),
432e65bf00aSYifeng Zhao };
433e65bf00aSYifeng Zhao 
board_nand_init(void)434e65bf00aSYifeng Zhao void board_nand_init(void)
435e65bf00aSYifeng Zhao {
436e65bf00aSYifeng Zhao 	struct udevice *dev;
437e65bf00aSYifeng Zhao 	int ret;
438e65bf00aSYifeng Zhao 
439e65bf00aSYifeng Zhao 	ret = uclass_get_device_by_driver(UCLASS_MTD,
440e65bf00aSYifeng Zhao 					  DM_GET_DRIVER(rk_nandc_v6),
441e65bf00aSYifeng Zhao 					  &dev);
442e65bf00aSYifeng Zhao 	if (ret && ret != -ENODEV)
443e65bf00aSYifeng Zhao 		pr_err("Failed to initialize NAND controller. (error %d)\n",
444e65bf00aSYifeng Zhao 		       ret);
445e65bf00aSYifeng Zhao }
446e65bf00aSYifeng Zhao 
nand_spl_load_image(u32 offs,u32 size,void * buf)447e65bf00aSYifeng Zhao int nand_spl_load_image(u32 offs, u32 size, void *buf)
448e65bf00aSYifeng Zhao {
449e65bf00aSYifeng Zhao 	return -EIO;
450e65bf00aSYifeng Zhao }
451e65bf00aSYifeng Zhao 
nand_init(void)452e65bf00aSYifeng Zhao void nand_init(void){};
453e65bf00aSYifeng Zhao 
rk_nand_init(void)454e65bf00aSYifeng Zhao int rk_nand_init(void)
455e65bf00aSYifeng Zhao {
456e65bf00aSYifeng Zhao 	return -ENODEV;
457e65bf00aSYifeng Zhao }
458e65bf00aSYifeng Zhao 
459e65bf00aSYifeng Zhao #else
board_nand_init(void)46057e25cf7SYifeng Zhao void board_nand_init(void)
46157e25cf7SYifeng Zhao {
46257e25cf7SYifeng Zhao 	const void *blob = gd->fdt_blob;
4636c65ad50SJason Zhu 	static int initialized;
46457e25cf7SYifeng Zhao 	fdt_addr_t regs;
46557e25cf7SYifeng Zhao 	int node;
46657e25cf7SYifeng Zhao 
4676c65ad50SJason Zhu 	if (initialized)
4686c65ad50SJason Zhu 		return;
4696c65ad50SJason Zhu 
4706c65ad50SJason Zhu 	initialized = 1;
4716f8d5eccSYifeng Zhao 	nand_page_size = CONFIG_SYS_NAND_PAGE_SIZE;
4726f8d5eccSYifeng Zhao 	nand_page_num = CONFIG_SYS_NAND_PAGE_COUNT;
4736c65ad50SJason Zhu 
47457e25cf7SYifeng Zhao 	if (g_rk_nand)
47557e25cf7SYifeng Zhao 		return;
47657e25cf7SYifeng Zhao 
47757e25cf7SYifeng Zhao 	node = fdtdec_next_compatible(blob, 0, COMPAT_ROCKCHIP_NANDC);
47857e25cf7SYifeng Zhao 
47957e25cf7SYifeng Zhao 	if (node < 0) {
48057e25cf7SYifeng Zhao 		printf("Nand node not found\n");
481e65bf00aSYifeng Zhao 		return;
48257e25cf7SYifeng Zhao 	}
48357e25cf7SYifeng Zhao 
48457e25cf7SYifeng Zhao 	if (!fdtdec_get_is_enabled(blob, node)) {
48557e25cf7SYifeng Zhao 		debug("Nand disabled in device tree\n");
486e65bf00aSYifeng Zhao 		return;
48757e25cf7SYifeng Zhao 	}
48857e25cf7SYifeng Zhao 
48957e25cf7SYifeng Zhao 	regs = fdt_get_base_address(blob, node);
490e65bf00aSYifeng Zhao 	if (!regs) {
49157e25cf7SYifeng Zhao 		debug("Nand address not found\n");
492e65bf00aSYifeng Zhao 		return;
49357e25cf7SYifeng Zhao 	}
49457e25cf7SYifeng Zhao 
49557e25cf7SYifeng Zhao 	g_rk_nand = kzalloc(sizeof(*g_rk_nand), GFP_KERNEL);
49657e25cf7SYifeng Zhao 	g_rk_nand->regs = (void *)regs;
4976f8d5eccSYifeng Zhao 	g_rk_nand->databuf = kzalloc(nand_page_size, GFP_KERNEL);
49857e25cf7SYifeng Zhao 	nandc_init(g_rk_nand);
49957e25cf7SYifeng Zhao 	read_flash_id(g_rk_nand, g_rk_nand->id);
500e65bf00aSYifeng Zhao 
501e65bf00aSYifeng Zhao 	if (g_rk_nand->id[0] == g_rk_nand->id[1])
502e65bf00aSYifeng Zhao 		goto err;
503e65bf00aSYifeng Zhao 
504e65bf00aSYifeng Zhao 	if (g_rk_nand->id[1] == 0xA1 || g_rk_nand->id[1] == 0xF1 ||
505e65bf00aSYifeng Zhao 	    g_rk_nand->id[1] == 0xD1 || g_rk_nand->id[1] == 0xAA ||
506e65bf00aSYifeng Zhao 	    g_rk_nand->id[1] == 0xDA || g_rk_nand->id[1] == 0xAC ||
507e65bf00aSYifeng Zhao 	    g_rk_nand->id[1] == 0xDC || g_rk_nand->id[1] == 0xA3 ||
508e65bf00aSYifeng Zhao 	    g_rk_nand->id[1] == 0xD3 || g_rk_nand->id[1] == 0x95 ||
509e65bf00aSYifeng Zhao 	    g_rk_nand->id[1] == 0x48) {
51057e25cf7SYifeng Zhao 		g_rk_nand->chipnr = 1;
51157e25cf7SYifeng Zhao 		return;
512e65bf00aSYifeng Zhao 	}
513e65bf00aSYifeng Zhao 
51457e25cf7SYifeng Zhao err:
515e65bf00aSYifeng Zhao 	kfree(g_rk_nand->databuf);
51657e25cf7SYifeng Zhao 	kfree(g_rk_nand);
517e65bf00aSYifeng Zhao 	g_rk_nand = NULL;
51857e25cf7SYifeng Zhao }
51957e25cf7SYifeng Zhao 
nand_spl_load_image(u32 offs,u32 size,void * buf)52057e25cf7SYifeng Zhao int nand_spl_load_image(u32 offs, u32 size, void *buf)
52157e25cf7SYifeng Zhao {
52257e25cf7SYifeng Zhao 	int i;
52357e25cf7SYifeng Zhao 	unsigned int page;
524d28ec0f5SJon Lin 	int force_bad_block_check = 1;
52557e25cf7SYifeng Zhao 	unsigned int maxpages = CONFIG_SYS_NAND_SIZE /
5266f8d5eccSYifeng Zhao 				nand_page_size;
52757e25cf7SYifeng Zhao 
52857e25cf7SYifeng Zhao 	/* Convert to page number */
5296f8d5eccSYifeng Zhao 	page = offs / nand_page_size;
53057e25cf7SYifeng Zhao 	i = 0;
53157e25cf7SYifeng Zhao 
5326f8d5eccSYifeng Zhao 	size = roundup(size, nand_page_size);
5336f8d5eccSYifeng Zhao 	while (i < size / nand_page_size) {
53457e25cf7SYifeng Zhao 		/*
53557e25cf7SYifeng Zhao 		 * Check if we have crossed a block boundary, and if so
53657e25cf7SYifeng Zhao 		 * check for bad block.
53757e25cf7SYifeng Zhao 		 */
538d28ec0f5SJon Lin 		if (force_bad_block_check || !(page % nand_page_num)) {
53957e25cf7SYifeng Zhao 			/*
54057e25cf7SYifeng Zhao 			 * Yes, new block. See if this block is good. If not,
54157e25cf7SYifeng Zhao 			 * loop until we find a good block.
54257e25cf7SYifeng Zhao 			 */
54357e25cf7SYifeng Zhao 			while (is_badblock(page)) {
54498c66434SJon Lin 				page = page + nand_page_num;
54557e25cf7SYifeng Zhao 				/* Check i we've reached the end of flash. */
54657e25cf7SYifeng Zhao 				if (page >= maxpages)
54757e25cf7SYifeng Zhao 					return -EIO;
54857e25cf7SYifeng Zhao 			}
54957e25cf7SYifeng Zhao 		}
55057e25cf7SYifeng Zhao 
551d28ec0f5SJon Lin 		force_bad_block_check = 0;
552d28ec0f5SJon Lin 
55357e25cf7SYifeng Zhao 		if (nandc_read_page(page, buf) < 0)
55457e25cf7SYifeng Zhao 			return -EIO;
55557e25cf7SYifeng Zhao 
55657e25cf7SYifeng Zhao 		page++;
55757e25cf7SYifeng Zhao 		i++;
5586f8d5eccSYifeng Zhao 		buf = buf + nand_page_size;
55957e25cf7SYifeng Zhao 	}
56057e25cf7SYifeng Zhao 	return 0;
56157e25cf7SYifeng Zhao }
56257e25cf7SYifeng Zhao 
nand_init(void)56357e25cf7SYifeng Zhao void nand_init(void)
56457e25cf7SYifeng Zhao {
56557e25cf7SYifeng Zhao 	board_nand_init();
56657e25cf7SYifeng Zhao }
56757e25cf7SYifeng Zhao 
rk_nand_init(void)56857e25cf7SYifeng Zhao int rk_nand_init(void)
56957e25cf7SYifeng Zhao {
57057e25cf7SYifeng Zhao 	board_nand_init();
57157e25cf7SYifeng Zhao 	if (g_rk_nand && g_rk_nand->chipnr)
57257e25cf7SYifeng Zhao 		return 0;
57357e25cf7SYifeng Zhao 	else
57457e25cf7SYifeng Zhao 		return -ENODEV;
57557e25cf7SYifeng Zhao }
576e65bf00aSYifeng Zhao #endif
57757e25cf7SYifeng Zhao 
nand_deselect(void)57857e25cf7SYifeng Zhao void nand_deselect(void) {}
579e65bf00aSYifeng Zhao 
580