Add basic Rockchip nand driver.

Driver in current state has 16, 24, 40, 60 per 1024B BCH/ECC ability and 8 bit 
asynchronous flash interface support. Other features will come later.

Signed-off-by: Paweł Jarosz <paweljarosz3...@gmail.com>
---
Changes since v1:
- none

 drivers/mtd/nand/Kconfig         |   6 +
 drivers/mtd/nand/Makefile        |   1 +
 drivers/mtd/nand/rockchip_nand.c | 660 +++++++++++++++++++++++++++++++++++++++
 include/fdtdec.h                 |   1 +
 lib/fdtdec.c                     |   1 +
 5 files changed, 669 insertions(+)
 create mode 100644 drivers/mtd/nand/rockchip_nand.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 3508c62..57a870d 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -66,6 +66,12 @@ config NAND_PXA3XX
          This enables the driver for the NAND flash device found on
          PXA3xx processors (NFCv1) and also on Armada 370/XP (NFCv2).
 
+config NAND_ROCKCHIP
+       bool "Support for NAND on Rockchip SoCs"
+       select SYS_NAND_SELF_INIT
+       ---help---
+       Enable support for Rockchip nand.
+
 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 c3d4a99..0659253 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o
 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
 
 else  # minimal SPL drivers
 
diff --git a/drivers/mtd/nand/rockchip_nand.c b/drivers/mtd/nand/rockchip_nand.c
new file mode 100644
index 0000000..c997c43
--- /dev/null
+++ b/drivers/mtd/nand/rockchip_nand.c
@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2017 Yifeng Zhao <yifeng.z...@rock-chips.com>
+ * Copyright (c) 2017 Paweł Jarosz <paweljarosz3...@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_V6_BOOTROM_ECC   24
+#define        NANDC_V6_NUM_BANKS      8
+#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 {
+       uint32_t banks[NANDC_V6_NUM_BANKS];
+       struct nand_hw_control controller;
+       uint32_t 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_V6_RANDMZ);
+       writel(0, rknand->regs + NANDC_REG_V6_DMA_CFG);
+       writel(0, rknand->regs + NANDC_REG_V6_BCHCTL);
+       writel(NANDC_V6_FM_WP, rknand->regs + NANDC_REG_V6_FMCTL);
+       writel(0x1081, rknand->regs + NANDC_REG_V6_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_V6_FMCTL);
+       reg &= ~NANDC_V6_FM_CE_SEL_M;
+
+       if (chipnr == -1) {
+               banknr = -1;
+       } else {
+               banknr = rknand->banks[chipnr];
+               bank_base = rknand->regs + NANDC_REG_V6_BANK0 + banknr * 0x100;
+
+               chip->IO_ADDR_R = bank_base;
+               chip->IO_ADDR_W = bank_base;
+
+               reg |= 1 << banknr;
+       }
+       writel(reg, rknand->regs + NANDC_REG_V6_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_V6_BANK0
+                               + rknand->selected_bank * 0x100;
+
+       if (ctrl & NAND_CTRL_CHANGE) {
+               if (ctrl & NAND_ALE)
+                       bank_base += NANDC_REG_V6_ADDR;
+               else if (ctrl & NAND_CLE)
+                       bank_base += NANDC_REG_V6_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_V6_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_V6_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_V6_FMCTL) & NANDC_V6_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 60:
+               reg = 0x00040010;
+               break;
+       case 40:
+               reg = 0x00040000;
+               break;
+       case 24:
+               reg = 0x00000010;
+               break;
+       case 16:
+               reg = 0x00000000;
+               break;
+       default:
+               return -EINVAL;
+       }
+       writel(reg, rknand->regs + NANDC_REG_V6_BCHCTL);
+
+       return 0;
+}
+
+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))) |
+             (rknand->selected_bank << 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 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_V6_SRAM0;
+       unsigned int max_bitflips = 0;
+       int ret, step, bch_st;
+       int offset = page * mtd->writesize;
+
+       if (rknand->bootromblocks && (offset < (7 * mtd->erasesize)))
+               rockchip_nand_hw_ecc_setup(mtd, ecc, NANDC_V6_BOOTROM_ECC);
+
+       rockchip_nand_pio_xfer_start(rknand, NANDC_V6_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_V6_BCHST);
+
+               if (bch_st & NANDC_V6_BCH0_ST_ERR) {
+                       mtd->ecc_stats.failed++;
+                       max_bitflips = -1;
+               } else {
+                       ret = NANDC_V6_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_V6_READ,
+                                                       (step + 1) & 0x1);
+
+               memcpy_fromio(data, sram_base + NANDC_REG_V6_SRAM_SIZE *
+                                                       (step & 1), ecc->size);
+
+               if (step & 1)
+                       memcpy_fromio(oob, rknand->regs + NANDC_REG_V6_SPARE1, 
4);
+               else
+                       memcpy_fromio(oob, rknand->regs + NANDC_REG_V6_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_V6_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_V6_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_V6_SPARE0, &index, ecc->prepad);
+
+       for (step = 1; step <= ecc->steps; step++) {
+               rockchip_nand_pio_xfer_start(rknand, NANDC_V6_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_V6_SRAM_SIZE * 
+                                   (step & 1), data, ecc->size);
+                       if (step & 1)
+                               memcpy_toio(rknand->regs + NANDC_REG_V6_SPARE1,
+                                           oob, ecc->prepad);
+                       else
+                               memcpy_toio(rknand->regs + NANDC_REG_V6_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);
+
+       rockchip_nand_hw_ecc_setup(mtd, ecc, rknand->ecc_strength);
+
+       return 0;
+}
+
+static const u8 strengths[] = {60, 40, 24, 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);
+
+       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_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;
+
+       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 = fdtdec_get_addr(blob, node, "reg");
+       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;
+
+       mtd = get_nand_dev_by_index(0);
+       return nand_read_skip_bad(mtd, offs, &size, NULL, size, (u_char *)dst);
+}
+
+void nand_deselect(void) {}
diff --git a/include/fdtdec.h b/include/fdtdec.h
index 4a0947c..0e68788 100644
--- a/include/fdtdec.h
+++ b/include/fdtdec.h
@@ -157,6 +157,7 @@ enum fdt_compat_id {
        COMPAT_ALTERA_SOCFPGA_F2SDR0,           /* SoCFPGA fpga2SDRAM0 bridge */
        COMPAT_ALTERA_SOCFPGA_F2SDR1,           /* SoCFPGA fpga2SDRAM1 bridge */
        COMPAT_ALTERA_SOCFPGA_F2SDR2,           /* SoCFPGA fpga2SDRAM2 bridge */
+       COMPAT_ROCKCHIP_NANDC,                  /* Rockchip NAND controller */
 
        COMPAT_COUNT,
 };
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index d2dbd0f..6e814cf 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -70,6 +70,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
        COMPAT(ALTERA_SOCFPGA_F2SDR0, "altr,socfpga-fpga2sdram0-bridge"),
        COMPAT(ALTERA_SOCFPGA_F2SDR1, "altr,socfpga-fpga2sdram1-bridge"),
        COMPAT(ALTERA_SOCFPGA_F2SDR2, "altr,socfpga-fpga2sdram2-bridge"),
+       COMPAT(ROCKCHIP_NANDC, "rockchip,nandc"),
 };
 
 const char *fdtdec_get_compatible(enum fdt_compat_id id)
-- 
2.7.4

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to