/* * Copyright (c) 2017 Yifeng Zhao * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; #define NANDC_V6_BOOTROM_ECC 24 #define NANDC_V6_NUM_BANKS 4 #define NANDC_V6_DEF_TIMEOUT 20000 #define NANDC_V6_READ 0 #define NANDC_V6_WRITE 1 #define NANDC_REG_V6_FMCTL 0x00 #define NANDC_REG_V6_FMWAIT 0x04 #define NANDC_REG_V6_FLCTL 0x08 #define NANDC_REG_V6_BCHCTL 0x0c #define NANDC_REG_V6_DMA_CFG 0x10 #define NANDC_REG_V6_DMA_BUF0 0x14 #define NANDC_REG_V6_DMA_BUF1 0x18 #define NANDC_REG_V6_DMA_ST 0x1C #define NANDC_REG_V6_BCHST 0x20 #define NANDC_REG_V6_RANDMZ 0x150 #define NANDC_REG_V6_VER 0x160 #define NANDC_REG_V6_INTEN 0x16C #define NANDC_REG_V6_INTCLR 0x170 #define NANDC_REG_V6_INTST 0x174 #define NANDC_REG_V6_SPARE0 0x200 #define NANDC_REG_V6_SPARE1 0x230 #define NANDC_REG_V6_BANK0 0x800 #define NANDC_REG_V6_SRAM0 0x1000 #define NANDC_REG_V6_SRAM_SIZE 0x400 #define NANDC_REG_V6_DATA 0x00 #define NANDC_REG_V6_ADDR 0x04 #define NANDC_REG_V6_CMD 0x08 /* FMCTL */ #define NANDC_V6_FM_WP BIT(8) #define NANDC_V6_FM_CE_SEL_M 0xFF #define NANDC_V6_FM_CE_SEL(x) (1 << (x)) #define NANDC_V6_FM_FREADY BIT(9) /* FLCTL */ #define NANDC_V6_FL_RST BIT(0) #define NANDC_V6_FL_DIR_S 0x1 #define NANDC_V6_FL_XFER_START BIT(2) #define NANDC_V6_FL_XFER_EN BIT(3) #define NANDC_V6_FL_ST_BUF_S 0x4 #define NANDC_V6_FL_XFER_COUNT BIT(5) #define NANDC_V6_FL_ACORRECT BIT(10) #define NANDC_V6_FL_XFER_READY BIT(20) /* BCHCTL */ #define NAND_V6_BCH_REGION_S 0x5 #define NAND_V6_BCH_REGION_M 0x7 /* BCHST */ #define NANDC_V6_BCH0_ST_ERR BIT(2) #define NANDC_V6_BCH1_ST_ERR BIT(15) #define NANDC_V6_ECC_ERR_CNT0(x) ((((x & (0x1F << 3)) >> 3) \ | ((x & (1 << 27)) >> 22)) & 0x3F) #define NANDC_V6_ECC_ERR_CNT1(x) ((((x & (0x1F << 16)) >> 16) \ | ((x & (1 << 29)) >> 24)) & 0x3F) struct rk_nand { void __iomem *regs; u8 chipnr; u8 id[5]; u8 *databuf; struct udevice *dev; struct mtd_info *mtd; }; static struct rk_nand *g_rk_nand; static u32 nand_page_size; static u32 nand_page_num; static u32 nand_block_num; static void nandc_init(struct rk_nand *rknand) { writel(0x1081, rknand->regs + NANDC_REG_V6_FMWAIT); } static void rockchip_nand_wait_dev_ready(void __iomem *regs) { u32 reg; u32 timeout = NANDC_V6_DEF_TIMEOUT; while (timeout--) { udelay(1); reg = readl(regs + NANDC_REG_V6_FMCTL); if ((reg & NANDC_V6_FM_FREADY)) break; } } static void rockchip_nand_select_chip(void __iomem *regs, int chipnr) { u32 reg; reg = readl(regs + NANDC_REG_V6_FMCTL); reg &= ~NANDC_V6_FM_CE_SEL_M; if (chipnr != -1) reg |= 1 << chipnr; writel(reg, regs + NANDC_REG_V6_FMCTL); } static void rockchip_nand_read_page(void __iomem *regs, int page, int col) { void __iomem *bank_base = regs + NANDC_REG_V6_BANK0; writeb(0x00, bank_base + NANDC_REG_V6_CMD); writeb(col, bank_base + NANDC_REG_V6_ADDR); writeb(col >> 8, bank_base + NANDC_REG_V6_ADDR); writeb(page, bank_base + NANDC_REG_V6_ADDR); writeb(page >> 8, bank_base + NANDC_REG_V6_ADDR); writeb(page >> 16, bank_base + NANDC_REG_V6_ADDR); writeb(0x30, bank_base + NANDC_REG_V6_CMD); } static void rockchip_nand_pio_xfer_start(struct rk_nand *rknand, u8 dir, u8 st_buf) { u32 reg; reg = readl(rknand->regs + NANDC_REG_V6_BCHCTL); reg = (reg & (~(NAND_V6_BCH_REGION_M << NAND_V6_BCH_REGION_S))); writel(reg, rknand->regs + NANDC_REG_V6_BCHCTL); reg = (dir << NANDC_V6_FL_DIR_S) | (st_buf << NANDC_V6_FL_ST_BUF_S) | NANDC_V6_FL_XFER_EN | NANDC_V6_FL_XFER_COUNT | NANDC_V6_FL_ACORRECT; writel(reg, rknand->regs + NANDC_REG_V6_FLCTL); reg |= NANDC_V6_FL_XFER_START; writel(reg, rknand->regs + NANDC_REG_V6_FLCTL); } static int rockchip_nand_wait_pio_xfer_done(struct rk_nand *rknand) { int timeout = NANDC_V6_DEF_TIMEOUT; int reg; while (timeout--) { reg = readl(rknand->regs + NANDC_REG_V6_FLCTL); if ((reg & NANDC_V6_FL_XFER_READY) != 0) break; udelay(1); } if (timeout == 0) return -1; return 0; } static int nandc_read_page(unsigned int page, uint8_t *buf) { void __iomem *sram_base = g_rk_nand->regs + NANDC_REG_V6_SRAM0; unsigned int max_bitflips = 0; int ret, step, bch_st, ecc_step; ecc_step = nand_page_size / 1024; rockchip_nand_select_chip(g_rk_nand->regs, 0); rockchip_nand_read_page(g_rk_nand->regs, page, 0); rockchip_nand_wait_dev_ready(g_rk_nand->regs); rockchip_nand_pio_xfer_start(g_rk_nand, NANDC_V6_READ, 0); for (step = 0; step < ecc_step; step++) { int data_off = step * 1024; u8 *data = buf + data_off; ret = rockchip_nand_wait_pio_xfer_done(g_rk_nand); if (ret) return ret; bch_st = readl(g_rk_nand->regs + NANDC_REG_V6_BCHST); if (bch_st & NANDC_V6_BCH0_ST_ERR) { max_bitflips = -1; } else { ret = NANDC_V6_ECC_ERR_CNT0(bch_st); max_bitflips = max_t(unsigned int, max_bitflips, ret); } if ((step + 1) < ecc_step) rockchip_nand_pio_xfer_start(g_rk_nand, NANDC_V6_READ, (step + 1) & 0x1); memcpy_fromio(data, sram_base + NANDC_REG_V6_SRAM_SIZE * (step & 1), 1024); } rockchip_nand_select_chip(g_rk_nand->regs, -1); return max_bitflips; } static int is_badblock(unsigned int page) { int res = 0, i; u16 bad = 0xff; void __iomem *regs = g_rk_nand->regs; void __iomem *bank_base = regs + NANDC_REG_V6_BANK0; if (nandc_read_page(page, g_rk_nand->databuf) == -1) { rockchip_nand_select_chip(regs, 0); rockchip_nand_read_page(regs, page, nand_page_size); rockchip_nand_wait_dev_ready(regs); for (i = 0; i < 8; i++) { bad = readb(bank_base); if (bad) break; } if (i >= 8) res = 1; rockchip_nand_select_chip(regs, 0); } if (res) printf("%s 0x%x %x %x\n", __func__, page, res, bad); return res; } static void read_flash_id(struct rk_nand *rknand, uint8_t *id) { void __iomem *bank_base = rknand->regs + NANDC_REG_V6_BANK0; rockchip_nand_wait_dev_ready(g_rk_nand->regs); writeb(0x90, bank_base + NANDC_REG_V6_CMD); writeb(0x00, bank_base + NANDC_REG_V6_ADDR); udelay(1); id[0] = readb(bank_base); id[1] = readb(bank_base); id[2] = readb(bank_base); id[3] = readb(bank_base); id[4] = readb(bank_base); rockchip_nand_select_chip(rknand->regs, -1); if (id[0] != 0xFF && id[0] != 0x00) printf("NAND:%x %x\n", id[0], id[1]); } #ifdef CONFIG_NAND_ROCKCHIP_DT static const struct udevice_id rockchip_nandc_ids[] = { { .compatible = "rockchip,rk-nandc" }, { } }; static int spl_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) { return is_badblock((u32)ofs / nand_page_size); } static int spl_nand_read_page(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { int read_size, offset, read_len; unsigned int page; unsigned int max_pages = nand_page_num * nand_block_num; /* Convert to page number */ page = (u32)from / nand_page_size; offset = from & (nand_page_size - 1); read_len = len; *retlen = 0; while (read_len) { read_size = nand_page_size - offset; if (read_size > read_len) read_size = read_len; if (offset || read_size < nand_page_size) { if (nandc_read_page(page, g_rk_nand->databuf) < 0) return -EIO; memcpy(buf, g_rk_nand->databuf + offset, read_size); offset = 0; } else { if (nandc_read_page(page, buf) < 0) return -EIO; } page++; read_len -= read_size; buf += read_size; if (page >= max_pages) return -EIO; } *retlen = len; return 0; } static int rockchip_nandc_probe(struct udevice *dev) { const void *blob = gd->fdt_blob; struct rk_nand *rknand = dev_get_priv(dev); struct mtd_info *mtd = dev_get_uclass_priv(dev); fdt_addr_t regs; int ret = -ENODEV; int node; u8 *id; g_rk_nand = rknand; rknand->dev = dev; node = fdtdec_next_compatible(blob, 0, COMPAT_ROCKCHIP_NANDC); if (node < 0) { printf("Nand node not found\n"); return -ENODEV; } if (!fdtdec_get_is_enabled(blob, node)) { debug("Nand disabled in device tree\n"); return -ENODEV; } regs = fdt_get_base_address(blob, node); if (!regs) { debug("Nand address not found\n"); return -ENODEV; } rknand->regs = (void *)regs; nandc_init(g_rk_nand); read_flash_id(g_rk_nand, g_rk_nand->id); id = g_rk_nand->id; if (id[0] == id[1]) return -ENODEV; if (id[1] == 0xA1 || id[1] == 0xF1 || id[1] == 0xD1 || id[1] == 0xAA || id[1] == 0xDA || id[1] == 0xAC || id[1] == 0xDC || id[1] == 0xA3 || id[1] == 0xD3 || id[1] == 0x95 || id[1] == 0x48 || id[1] == 0x63) { nand_page_size = 2048; nand_page_num = 64; nand_block_num = 1024; if (id[1] == 0xDC || id[1] == 0xAC) { if ((id[0] == 0x2C && id[3] == 0xA6) || (id[0] == 0xC2 && id[3] == 0xA2)) { nand_page_size = 4096; nand_block_num = 2048; } else if (id[0] == 0x98 && id[3] == 0x26) { nand_page_size = 4096; nand_block_num = 2048; } else { nand_block_num = 4096; } } else if (id[1] == 0xDA) { nand_block_num = 2048; } else if (id[1] == 0x48) { nand_page_size = 4096; nand_page_num = 128; nand_block_num = 4096; } else if (id[1] == 0xD3) { if ((id[2] == 0xD1 && id[4] == 0x5a) || /* S34ML08G2 */ (id[3] == 0x05 && id[4] == 0x04)) { /* S34ML08G3 */ nand_block_num = 8192; } else { nand_page_size = 4096; nand_block_num = 4096; } } else if (id[1] == 0x63 && id[3] == 0x19) { /* IS34ML08G088 */ nand_page_size = 4096; nand_page_num = 64; nand_block_num = 4096; } g_rk_nand->chipnr = 1; g_rk_nand->databuf = kzalloc(nand_page_size, GFP_KERNEL); if (!g_rk_nand) return -ENOMEM; mtd->_block_isbad = spl_nand_block_isbad; mtd->_read = spl_nand_read_page; mtd->size = (size_t)nand_page_size * nand_page_num * nand_block_num; mtd->writesize = nand_page_size; mtd->erasesize = nand_page_size * nand_page_num; mtd->erasesize_shift = ffs(mtd->erasesize) - 1; mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; mtd->type = MTD_NANDFLASH; mtd->dev = rknand->dev; mtd->priv = rknand; add_mtd_device(mtd); rknand->mtd = mtd; ret = 0; } return ret; } static int rockchip_nandc_bind(struct udevice *udev) { int ret = 0; #ifdef CONFIG_MTD_BLK struct udevice *bdev; ret = blk_create_devicef(udev, "mtd_blk", "blk", IF_TYPE_MTD, BLK_MTD_NAND, 512, 0, &bdev); if (ret) printf("Cannot create block device\n"); #endif return ret; } U_BOOT_DRIVER(rk_nandc_v6) = { .name = "rk_nandc_v6", .id = UCLASS_MTD, .of_match = rockchip_nandc_ids, .bind = rockchip_nandc_bind, .probe = rockchip_nandc_probe, .priv_auto_alloc_size = sizeof(struct rk_nand), }; void board_nand_init(void) { struct udevice *dev; int ret; ret = uclass_get_device_by_driver(UCLASS_MTD, DM_GET_DRIVER(rk_nandc_v6), &dev); if (ret && ret != -ENODEV) pr_err("Failed to initialize NAND controller. (error %d)\n", ret); } int nand_spl_load_image(u32 offs, u32 size, void *buf) { return -EIO; } void nand_init(void){}; int rk_nand_init(void) { return -ENODEV; } #else void board_nand_init(void) { const void *blob = gd->fdt_blob; static int initialized; fdt_addr_t regs; int node; if (initialized) return; initialized = 1; nand_page_size = CONFIG_SYS_NAND_PAGE_SIZE; nand_page_num = CONFIG_SYS_NAND_PAGE_COUNT; if (g_rk_nand) return; node = fdtdec_next_compatible(blob, 0, COMPAT_ROCKCHIP_NANDC); if (node < 0) { printf("Nand node not found\n"); return; } if (!fdtdec_get_is_enabled(blob, node)) { debug("Nand disabled in device tree\n"); return; } regs = fdt_get_base_address(blob, node); if (!regs) { debug("Nand address not found\n"); return; } g_rk_nand = kzalloc(sizeof(*g_rk_nand), GFP_KERNEL); g_rk_nand->regs = (void *)regs; g_rk_nand->databuf = kzalloc(nand_page_size, GFP_KERNEL); nandc_init(g_rk_nand); read_flash_id(g_rk_nand, g_rk_nand->id); if (g_rk_nand->id[0] == g_rk_nand->id[1]) goto err; if (g_rk_nand->id[1] == 0xA1 || g_rk_nand->id[1] == 0xF1 || g_rk_nand->id[1] == 0xD1 || g_rk_nand->id[1] == 0xAA || g_rk_nand->id[1] == 0xDA || g_rk_nand->id[1] == 0xAC || g_rk_nand->id[1] == 0xDC || g_rk_nand->id[1] == 0xA3 || g_rk_nand->id[1] == 0xD3 || g_rk_nand->id[1] == 0x95 || g_rk_nand->id[1] == 0x48) { g_rk_nand->chipnr = 1; return; } err: kfree(g_rk_nand->databuf); kfree(g_rk_nand); g_rk_nand = NULL; } int nand_spl_load_image(u32 offs, u32 size, void *buf) { int i; unsigned int page; int force_bad_block_check = 1; unsigned int maxpages = CONFIG_SYS_NAND_SIZE / nand_page_size; /* Convert to page number */ page = offs / nand_page_size; i = 0; size = roundup(size, nand_page_size); while (i < size / nand_page_size) { /* * Check if we have crossed a block boundary, and if so * check for bad block. */ if (force_bad_block_check || !(page % nand_page_num)) { /* * Yes, new block. See if this block is good. If not, * loop until we find a good block. */ while (is_badblock(page)) { page = page + nand_page_num; /* Check i we've reached the end of flash. */ if (page >= maxpages) return -EIO; } } force_bad_block_check = 0; if (nandc_read_page(page, buf) < 0) return -EIO; page++; i++; buf = buf + nand_page_size; } return 0; } void nand_init(void) { board_nand_init(); } int rk_nand_init(void) { board_nand_init(); if (g_rk_nand && g_rk_nand->chipnr) return 0; else return -ENODEV; } #endif void nand_deselect(void) {}