summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/nand/Kconfig8
-rw-r--r--drivers/mtd/nand/Makefile2
-rw-r--r--drivers/mtd/nand/nand_bbt.c6
-rw-r--r--drivers/mtd/nand/rockchip_nand.c36
-rw-r--r--drivers/mtd/nand/rockchip_nand_v9.c691
5 files changed, 737 insertions, 6 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index b43d86d050..f3372d65f7 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -79,9 +79,17 @@ config NAND_PXA3XX
config NAND_ROCKCHIP
bool "Support for NAND on Rockchip SoCs"
select SYS_NAND_SELF_INIT
+ default n
---help---
Enable support for Rockchip nand.
+config NAND_ROCKCHIP_V9
+ bool "Support for NAND V9 on Rockchip SoCs"
+ select SYS_NAND_SELF_INIT
+ default n
+ ---help---
+ Enable support for Rockchip nand v9.
+
config NAND_SUNXI
bool "Support for NAND on Allwinner SoCs"
depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 6067b89837..de8675d227 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -70,7 +70,7 @@ obj-$(CONFIG_NAND_PLAT) += nand_plat.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
obj-$(CONFIG_NAND_ROCKCHIP) += rockchip_nand.o
-
+obj-$(CONFIG_NAND_ROCKCHIP_V9) += rockchip_nand_v9.o
else # minimal SPL drivers
obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 74c4c9a3c8..aa4cf30c3f 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -487,8 +487,10 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
int ret;
BUG_ON(bd->options & NAND_BBT_NO_OOB);
-
- ret = scan_block_fast(mtd, bd, from, buf, numpages);
+ if (this->block_bad)
+ ret = this->block_bad(mtd, from);
+ else
+ ret = scan_block_fast(mtd, bd, from, buf, numpages);
if (ret < 0)
return ret;
diff --git a/drivers/mtd/nand/rockchip_nand.c b/drivers/mtd/nand/rockchip_nand.c
index d8f443972a..b8607c82ee 100644
--- a/drivers/mtd/nand/rockchip_nand.c
+++ b/drivers/mtd/nand/rockchip_nand.c
@@ -307,7 +307,6 @@ static void rockchip_nand_write_extra_oob(struct mtd_info *mtd, u8 *oob)
rockchip_nand_write_buf(mtd, oob + offset, len);
}
-
static int rockchip_nand_hw_syndrome_pio_read_page(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf,
@@ -535,6 +534,33 @@ static int rockchip_nand_ecc_init(struct mtd_info *mtd,
return 0;
}
+static int rockchip_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ int page, res = 0, i;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 bad = 0xff;
+ int chipnr = (int)(ofs >> chip->chip_shift);
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ chip->select_chip(mtd, chipnr);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ if(rockchip_nand_hw_syndrome_pio_read_page(mtd,
+ chip, chip->buffers->databuf, 0, page) == -1) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
+ for (i = 0; i < 8; i++) {
+ bad = chip->read_byte(mtd);
+ if (bad)
+ break;
+ }
+ if (i >= 8)
+ res = 1;
+ }
+ chip->select_chip(mtd, -1);
+ if (res)
+ printf("%s 0x%x %x %x\n", __func__, page, res, bad);
+ return res;
+}
+
static int rockchip_nand_chip_init(int node, struct rk_nand *rknand, int devnum)
{
const void *blob = gd->fdt_blob;
@@ -553,6 +579,9 @@ static int rockchip_nand_chip_init(int node, struct rk_nand *rknand, int devnum)
chip->read_byte = rockchip_nand_read_byte;
chip->dev_ready = rockchip_nand_dev_ready;
chip->controller = &rknand->controller;
+ chip->block_bad = rockchip_nand_block_bad;
+ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+ chip->options = NAND_NO_SUBPAGE_WRITE;
rknand->banks[devnum] = fdtdec_get_int(blob, node, "reg", -1);
@@ -625,7 +654,7 @@ void board_nand_init(void)
goto err;
}
- regs = fdtdec_get_addr(blob, node, "reg");
+ regs = fdt_get_base_address(blob, node);
if (regs == FDT_ADDR_T_NONE) {
debug("Nand address not found\n");
goto err;
@@ -652,9 +681,10 @@ err:
int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
{
struct mtd_info *mtd;
+ size_t length = size;
mtd = get_nand_dev_by_index(0);
- return nand_read_skip_bad(mtd, offs, &size, NULL, size, (u_char *)dst);
+ return nand_read_skip_bad(mtd, offs, &length, NULL, size, (u_char *)dst);
}
void nand_deselect(void) {}
diff --git a/drivers/mtd/nand/rockchip_nand_v9.c b/drivers/mtd/nand/rockchip_nand_v9.c
new file mode 100644
index 0000000000..b2ff609d33
--- /dev/null
+++ b/drivers/mtd/nand/rockchip_nand_v9.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2017 Yifeng Zhao <yifeng.zhao@rock-chips.com>
+ * Copyright (c) 2017 Paweł Jarosz <paweljarosz3691@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <inttypes.h>
+#include <nand.h>
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define NANDC_V9_BOOTROM_ECC 70
+#define NANDC_V9_NUM_BANKS 4
+#define NANDC_V9_DEF_TIMEOUT 20000
+#define NANDC_V9_READ 0
+#define NANDC_V9_WRITE 1
+#define NANDC_REG_V9_FMCTL 0x00
+#define NANDC_REG_V9_FMWAIT 0x04
+#define NANDC_REG_V9_FLCTL 0x10
+#define NANDC_REG_V9_BCHCTL 0x20
+#define NANDC_REG_V9_DMA_CFG 0x30
+#define NANDC_REG_V9_DMA_BUF0 0x34
+#define NANDC_REG_V9_DMA_BUF1 0x38
+#define NANDC_REG_V9_DMA_ST 0x40
+#define NANDC_REG_V9_VER 0x80
+#define NANDC_REG_V9_INTEN 0x120
+#define NANDC_REG_V9_INTCLR 0x124
+#define NANDC_REG_V9_INTST 0x128
+#define NANDC_REG_V9_BCHST 0x150
+#define NANDC_REG_V9_SPARE0 0x200
+#define NANDC_REG_V9_SPARE1 0x204
+#define NANDC_REG_V9_RANDMZ 0x208
+#define NANDC_REG_V9_BANK0 0x800
+#define NANDC_REG_V9_SRAM0 0x1000
+#define NANDC_REG_V9_SRAM_SIZE 0x400
+
+#define NANDC_REG_V9_DATA 0x00
+#define NANDC_REG_V9_ADDR 0x04
+#define NANDC_REG_V9_CMD 0x08
+
+/* FMCTL */
+#define NANDC_V9_FM_WP BIT(8)
+#define NANDC_V9_FM_CE_SEL_M 0xFF
+#define NANDC_V9_FM_CE_SEL(x) (1 << (x))
+#define NANDC_V9_FM_FREADY BIT(9)
+
+/* FLCTL */
+#define NANDC_V9_FL_RST BIT(0)
+#define NANDC_V9_FL_DIR_S 0x1
+#define NANDC_V9_FL_XFER_START BIT(2)
+#define NANDC_V9_FL_XFER_EN BIT(3)
+#define NANDC_V9_FL_ST_BUF_S 0x4
+#define NANDC_V9_FL_XFER_COUNT BIT(5)
+#define NANDC_V9_FL_ACORRECT BIT(10)
+#define NANDC_V9_FL_XFER_READY BIT(20)
+
+/* BCHCTL */
+#define NAND_V9_BCH_MODE_S 25
+#define NAND_V9_BCH_MODE_M 0x7
+
+/* BCHST */
+#define NANDC_V9_BCH0_ST_ERR BIT(2)
+#define NANDC_V9_BCH1_ST_ERR BIT(18)
+#define NANDC_V9_ECC_ERR_CNT0(x) (((x) & (0x7F << 3)) >> 3)
+#define NANDC_V9_ECC_ERR_CNT1(x) (((x) & (0x7F << 19)) >> 19)
+
+struct rk_nand {
+ uint32_t banks[NANDC_V9_NUM_BANKS];
+ struct nand_hw_control controller;
+ uint32_t ecc_strength;
+ uint32_t max_ecc_strength;
+ struct mtd_info mtd;
+ bool bootromblocks;
+ void __iomem *regs;
+ int selected_bank;
+};
+
+static struct nand_ecclayout nand_oob_fix = {
+ .eccbytes = 24,
+ .eccpos = {
+ 4, 5, 6, 7, 8, 9, 10
+ },
+ .oobfree = {
+ {
+ .offset = 0,
+ .length = 4
+ }
+ }
+};
+
+static inline struct rk_nand *to_rknand(struct nand_hw_control *ctrl)
+{
+ return container_of(ctrl, struct rk_nand, controller);
+}
+
+static void rockchip_nand_init(struct rk_nand *rknand)
+{
+ writel(0, rknand->regs + NANDC_REG_V9_RANDMZ);
+ writel(0, rknand->regs + NANDC_REG_V9_DMA_CFG);
+ writel(0, rknand->regs + NANDC_REG_V9_BCHCTL);
+ writel(NANDC_V9_FM_WP, rknand->regs + NANDC_REG_V9_FMCTL);
+ writel(0x1081, rknand->regs + NANDC_REG_V9_FMWAIT);
+}
+
+static void rockchip_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ void __iomem *bank_base;
+ uint32_t reg;
+ int banknr;
+
+ reg = readl(rknand->regs + NANDC_REG_V9_FMCTL);
+ reg &= ~NANDC_V9_FM_CE_SEL_M;
+
+ if (chipnr == -1) {
+ banknr = -1;
+ } else {
+ banknr = rknand->banks[chipnr];
+ bank_base = rknand->regs + NANDC_REG_V9_BANK0 + banknr * 0x100;
+
+ chip->IO_ADDR_R = bank_base;
+ chip->IO_ADDR_W = bank_base;
+
+ reg |= 1 << banknr;
+ }
+ writel(reg, rknand->regs + NANDC_REG_V9_FMCTL);
+
+ rknand->selected_bank = banknr;
+}
+
+static void rockchip_nand_cmd_ctrl(struct mtd_info *mtd,
+ int dat,
+ unsigned int ctrl)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ void __iomem *bank_base = rknand->regs + NANDC_REG_V9_BANK0
+ + rknand->selected_bank * 0x100;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if (ctrl & NAND_ALE)
+ bank_base += NANDC_REG_V9_ADDR;
+ else if (ctrl & NAND_CLE)
+ bank_base += NANDC_REG_V9_CMD;
+ chip->IO_ADDR_W = bank_base;
+ }
+
+ if (dat != NAND_CMD_NONE)
+ writeb(dat & 0xFF, chip->IO_ADDR_W);
+}
+
+static void rockchip_nand_read_buf(struct mtd_info *mtd,
+ uint8_t *buf,
+ int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ int offs = 0;
+ void __iomem *bank_base = rknand->regs + NANDC_REG_V9_BANK0
+ + rknand->selected_bank * 0x100;
+
+ for (offs = 0; offs < len; offs++)
+ buf[offs] = readb(bank_base);
+}
+
+static void rockchip_nand_write_buf(struct mtd_info *mtd,
+ const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ int offs = 0;
+ void __iomem *bank_base = rknand->regs + NANDC_REG_V9_BANK0
+ + rknand->selected_bank * 0x100;
+
+ for (offs = 0; offs < len; offs++)
+ writeb(buf[offs], bank_base);
+}
+
+static uint8_t rockchip_nand_read_byte(struct mtd_info *mtd)
+{
+ uint8_t ret;
+
+ rockchip_nand_read_buf(mtd, &ret, 1);
+
+ return ret;
+}
+
+static int rockchip_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct rk_nand *rknand = to_rknand(chip->controller);
+
+ if (readl(rknand->regs + NANDC_REG_V9_FMCTL) & NANDC_V9_FM_FREADY)
+ return 1;
+
+ return 0;
+}
+
+static int rockchip_nand_hw_ecc_setup(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc,
+ uint32_t strength)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ u32 reg;
+
+ ecc->strength = strength;
+ ecc->bytes = DIV_ROUND_UP(ecc->strength * 14, 8);
+ ecc->bytes = ALIGN(ecc->bytes, 2);
+
+ switch (ecc->strength) {
+ case 70:
+ reg = 0x00000001;
+ break;
+ case 60:
+ reg = 0x06000001;
+ break;
+ case 40:
+ reg = 0x04000001;
+ break;
+ case 16:
+ reg = 0x02000001;
+ break;
+ default:
+ return -EINVAL;
+ }
+ writel(reg, rknand->regs + NANDC_REG_V9_BCHCTL);
+
+ return 0;
+}
+
+static void rockchip_nand_pio_xfer_start(struct rk_nand *rknand,
+ u8 dir,
+ u8 st_buf)
+{
+ u32 reg;
+
+ reg = (dir << NANDC_V9_FL_DIR_S) | (st_buf << NANDC_V9_FL_ST_BUF_S) |
+ NANDC_V9_FL_XFER_EN | NANDC_V9_FL_XFER_COUNT |
+ NANDC_V9_FL_ACORRECT;
+ writel(reg, rknand->regs + NANDC_REG_V9_FLCTL);
+
+ reg |= NANDC_V9_FL_XFER_START;
+ writel(reg, rknand->regs + NANDC_REG_V9_FLCTL);
+}
+
+static int rockchip_nand_wait_pio_xfer_done(struct rk_nand *rknand)
+{
+ int timeout = NANDC_V9_DEF_TIMEOUT;
+ int reg;
+
+ while (timeout--) {
+ reg = readl(rknand->regs + NANDC_REG_V9_FLCTL);
+
+ if ((reg & NANDC_V9_FL_XFER_READY) != 0)
+ break;
+
+ udelay(1);
+ }
+
+ if (timeout == 0)
+ return -1;
+
+ return 0;
+}
+
+static void rockchip_nand_read_extra_oob(struct mtd_info *mtd, u8 *oob)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int offset = ((ecc->bytes + ecc->prepad) * ecc->steps);
+ int len = mtd->oobsize - offset;
+
+ if (len <= 0)
+ return;
+
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset + mtd->writesize, -1);
+
+ rockchip_nand_read_buf(mtd, oob + offset, len);
+}
+
+static void rockchip_nand_write_extra_oob(struct mtd_info *mtd, u8 *oob)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ int offset = ((ecc->bytes + ecc->prepad) * ecc->steps);
+ int len = mtd->oobsize - offset;
+
+ if (len <= 0)
+ return;
+
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset + mtd->writesize, -1);
+
+ rockchip_nand_write_buf(mtd, oob + offset, len);
+}
+
+static int rockchip_nand_hw_syndrome_pio_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ uint8_t *buf,
+ int oob_required,
+ int page)
+{
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ void __iomem *sram_base = rknand->regs + NANDC_REG_V9_SRAM0;
+ unsigned int max_bitflips = 0;
+ int ret, step, bch_st;
+ int offset = page * mtd->writesize;
+
+ if (rknand->bootromblocks && (offset < (4 * mtd->erasesize)))
+ rockchip_nand_hw_ecc_setup(mtd, ecc, NANDC_V9_BOOTROM_ECC);
+
+ rockchip_nand_pio_xfer_start(rknand, NANDC_V9_READ, 0);
+
+ for (step = 0; step < ecc->steps; step++) {
+ int data_off = step * ecc->size;
+ int oob_off = step * (ecc->bytes + ecc->prepad);
+ u8 *data = buf + data_off;
+ u8 *oob = chip->oob_poi + oob_off;
+
+ ret = rockchip_nand_wait_pio_xfer_done(rknand);
+ if (ret)
+ return ret;
+
+ bch_st = readl(rknand->regs + NANDC_REG_V9_BCHST);
+
+ if (bch_st & NANDC_V9_BCH0_ST_ERR) {
+ mtd->ecc_stats.failed++;
+ max_bitflips = -1;
+ } else {
+ ret = NANDC_V9_ECC_ERR_CNT0(bch_st);
+ mtd->ecc_stats.corrected += ret;
+ max_bitflips = max_t(unsigned int, max_bitflips, ret);
+ }
+
+ if ((step + 1) < ecc->steps)
+ rockchip_nand_pio_xfer_start(rknand, NANDC_V9_READ,
+ (step + 1) & 0x1);
+
+ memcpy_fromio(data, sram_base + NANDC_REG_V9_SRAM_SIZE *
+ (step & 1), ecc->size);
+
+ if (step & 1)
+ memcpy_fromio(oob, rknand->regs + NANDC_REG_V9_SPARE1, 4);
+ else
+ memcpy_fromio(oob, rknand->regs + NANDC_REG_V9_SPARE0, 4);
+ }
+
+ rockchip_nand_read_extra_oob(mtd, chip->oob_poi);
+
+ if (rknand->bootromblocks)
+ rockchip_nand_hw_ecc_setup(mtd, ecc, rknand->ecc_strength);
+
+ return max_bitflips;
+}
+
+static uint32_t rockchip_nand_make_bootrom_compat(struct mtd_info *mtd,
+ int page,
+ const u8 *oob,
+ bool bootromblocks)
+{
+ int pages_per_block = mtd->erasesize / mtd->writesize;
+ int offset = page * mtd->writesize;
+
+ if ((offset < (2 * mtd->erasesize)) || !(page % 2) ||
+ (offset >= (7 * mtd->erasesize)) || !bootromblocks)
+ return oob[3] | (oob[2] << 8) | (oob[1] << 16) | (oob[0] << 24);
+
+ return (page % pages_per_block + 1) * 4;
+}
+
+static int rockchip_nand_hw_syndrome_pio_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf,
+ int oob_required,
+ int page)
+{
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ void __iomem *sram_base = rknand->regs + NANDC_REG_V9_SRAM0;
+ int ret, index, step = 0;
+ int offset = page * mtd->writesize;
+ int data_off = step * ecc->size;
+ int oob_off = step * (ecc->bytes + ecc->prepad);
+ const u8 *data = buf + data_off;
+ const u8 *oob = chip->oob_poi + oob_off;
+
+ if (rknand->bootromblocks && (offset < (7 * mtd->erasesize)))
+ rockchip_nand_hw_ecc_setup(mtd, ecc, NANDC_V9_BOOTROM_ECC);
+
+ index = rockchip_nand_make_bootrom_compat(mtd, page, oob,
+ rknand->bootromblocks);
+
+ memcpy_toio(sram_base, data, ecc->size);
+ memcpy_toio(rknand->regs + NANDC_REG_V9_SPARE0, &index, ecc->prepad);
+
+ for (step = 1; step <= ecc->steps; step++) {
+ rockchip_nand_pio_xfer_start(rknand, NANDC_V9_WRITE,
+ (step - 1) & 0x1);
+ data_off = step * ecc->size;
+ oob_off = step * (ecc->bytes + ecc->prepad);
+ data = buf + data_off;
+ oob = chip->oob_poi + oob_off;
+
+ if (step < ecc->steps) {
+ memcpy_toio(sram_base + NANDC_REG_V9_SRAM_SIZE *
+ (step & 1), data, ecc->size);
+ if (step & 1)
+ memcpy_toio(rknand->regs + NANDC_REG_V9_SPARE1,
+ oob, ecc->prepad);
+ else
+ memcpy_toio(rknand->regs + NANDC_REG_V9_SPARE0,
+ oob, ecc->prepad);
+ }
+
+ ret = rockchip_nand_wait_pio_xfer_done(rknand);
+ if (ret)
+ return ret;
+ }
+
+ rockchip_nand_write_extra_oob(mtd, chip->oob_poi);
+
+ if (rknand->bootromblocks)
+ rockchip_nand_hw_ecc_setup(mtd, ecc, rknand->ecc_strength);
+
+ return 0;
+}
+
+static const u8 strengths[] = {70, 60, 40, 16};
+
+static int rockchip_nand_ecc_max_strength(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ uint32_t max_strength, index;
+
+ max_strength = ((mtd->oobsize / ecc->steps) - ecc->prepad) * 8 / 14;
+
+ for (index = 0; index < ARRAY_SIZE(strengths); index++)
+ if (max_strength >= strengths[index])
+ break;
+
+ if (index >= ARRAY_SIZE(strengths))
+ return -ENOTSUPP;
+
+ return strengths[index];
+}
+
+static bool rockchip_nand_strength_is_valid(int strength)
+{
+ uint32_t index;
+
+ for (index = 0; index < ARRAY_SIZE(strengths); index++)
+ if (strength == strengths[index])
+ break;
+
+ if (index == ARRAY_SIZE(strengths))
+ return false;
+
+ return true;
+}
+
+static int rockchip_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct rk_nand *rknand = to_rknand(chip->controller);
+ uint32_t strength;
+ int index;
+
+ ecc->prepad = 4;
+ ecc->steps = mtd->writesize / ecc->size;
+
+ if (fdtdec_get_bool(gd->fdt_blob, chip->flash_node,
+ "rockchip,protect-bootrom-blocks"))
+ rknand->bootromblocks = true;
+ else
+ rknand->bootromblocks = false;
+
+ if (rockchip_nand_strength_is_valid(ecc->strength))
+ strength = ecc->strength;
+ else
+ strength = rockchip_nand_ecc_max_strength(mtd, ecc);
+
+ rknand->max_ecc_strength = 70;
+ if (strength > rknand->max_ecc_strength)
+ strength = rknand->max_ecc_strength;
+
+ rockchip_nand_hw_ecc_setup(mtd, ecc, strength);
+
+ rknand->ecc_strength = ecc->strength;
+
+ nand_oob_fix.eccbytes = ecc->bytes * ecc->steps;
+ for (index = 0; index < ecc->bytes; index++)
+ nand_oob_fix.eccpos[index] = index + ecc->prepad;
+ ecc->layout = &nand_oob_fix;
+
+ if (mtd->oobsize < ((ecc->bytes + ecc->prepad) * ecc->steps)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_nand_ecc_init(struct mtd_info *mtd,
+ struct nand_ecc_ctrl *ecc)
+{
+ int ret;
+
+ switch (ecc->mode) {
+ case NAND_ECC_HW_SYNDROME:
+ ret = rockchip_nand_hw_ecc_ctrl_init(mtd, ecc);
+ if (ret)
+ return ret;
+ ecc->read_page = rockchip_nand_hw_syndrome_pio_read_page;
+ ecc->write_page = rockchip_nand_hw_syndrome_pio_write_page;
+ break;
+ case NAND_ECC_SOFT_BCH:
+ case NAND_ECC_NONE:
+ case NAND_ECC_SOFT:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_nand_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ int page, res = 0, i;
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u16 bad = 0xff;
+ int chipnr = (int)(ofs >> chip->chip_shift);
+
+ page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+ chip->select_chip(mtd, chipnr);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ if(rockchip_nand_hw_syndrome_pio_read_page(mtd,
+ chip, chip->buffers->databuf, 0, page) == -1) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
+ for (i = 0; i < 8; i++) {
+ bad = chip->read_byte(mtd);
+ if (bad)
+ break;
+ }
+ if (i >= 8)
+ res = 1;
+ }
+ chip->select_chip(mtd, -1);
+ if (res)
+ printf("%s 0x%x %x %x\n", __func__, page, res, bad);
+ return res;
+}
+
+static int rockchip_nand_chip_init(int node, struct rk_nand *rknand, int devnum)
+{
+ const void *blob = gd->fdt_blob;
+ struct nand_chip *chip;
+ struct mtd_info *mtd;
+ int ret;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+
+ chip->chip_delay = 50;
+ chip->flash_node = node;
+ chip->select_chip = rockchip_nand_select_chip;
+ chip->cmd_ctrl = rockchip_nand_cmd_ctrl;
+ chip->read_buf = rockchip_nand_read_buf;
+ chip->write_buf = rockchip_nand_write_buf;
+ chip->read_byte = rockchip_nand_read_byte;
+ chip->dev_ready = rockchip_nand_dev_ready;
+ chip->controller = &rknand->controller;
+ chip->block_bad = rockchip_nand_block_bad;
+ chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+ chip->options = NAND_NO_SUBPAGE_WRITE;
+
+ rknand->banks[devnum] = fdtdec_get_int(blob, node, "reg", -1);
+
+ if (rknand->banks[devnum] < 0)
+ return -EINVAL;
+
+ mtd = nand_to_mtd(chip);
+ mtd->name = "rknand";
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return ret;
+
+ ret = rockchip_nand_ecc_init(mtd, &chip->ecc);
+ if (ret) {
+ debug("rockchip_nand_ecc_init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret) {
+ debug("nand_scan_tail failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = nand_register(devnum, mtd);
+ if (ret) {
+ debug("Failed to register mtd device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_nand_chips_init(int node, struct rk_nand *rknand)
+{
+ const void *blob = gd->fdt_blob;
+ int nand_node;
+ int ret, i = 0;
+
+ for (nand_node = fdt_first_subnode(blob, node); nand_node >= 0;
+ nand_node = fdt_next_subnode(blob, nand_node)) {
+ ret = rockchip_nand_chip_init(nand_node, rknand, i++);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void board_nand_init(void)
+{
+ const void *blob = gd->fdt_blob;
+ struct rk_nand *rknand;
+ fdt_addr_t regs;
+ int node;
+ int ret;
+
+ rknand = kzalloc(sizeof(*rknand), GFP_KERNEL);
+
+ node = fdtdec_next_compatible(blob, 0, COMPAT_ROCKCHIP_NANDC);
+
+ if (node < 0) {
+ debug("Nand node not found\n");
+ goto err;
+ }
+
+ if (!fdtdec_get_is_enabled(blob, node)) {
+ debug("Nand disabled in device tree\n");
+ goto err;
+ }
+
+ regs = fdt_get_base_address(blob, node);
+ if (regs == FDT_ADDR_T_NONE) {
+ debug("Nand address not found\n");
+ goto err;
+ }
+
+ rknand->regs = (void *)regs;
+
+ spin_lock_init(&rknand->controller.lock);
+ init_waitqueue_head(&rknand->controller.wq);
+
+ rockchip_nand_init(rknand);
+
+ ret = rockchip_nand_chips_init(node, rknand);
+ if (ret) {
+ debug("Failed to init nand chips\n");
+ goto err;
+ }
+
+ return;
+err:
+ kfree(rknand);
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+{
+ struct mtd_info *mtd;
+ size_t length = size;
+
+ mtd = get_nand_dev_by_index(0);
+ return nand_read_skip_bad(mtd, offs, &length, NULL, size, (u_char *)dst);
+}
+
+void nand_deselect(void) {}