
On 18 Aug 2017, at 20:14, Paweł Jarosz paweljarosz3691@gmail.com wrote:
Hi,
W dniu 18.08.2017 o 14:45, Philipp Tomsich pisze:
On Thu, 17 Aug 2017, Paweł Jarosz wrote:
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 paweljarosz3691@gmail.com
Changes since v1:
- none
Changes since v2:
- fixed correct ecc checking
Changes since v3:
- 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 71d678f..c308497 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -69,6 +69,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..d8f4439 --- /dev/null +++ b/drivers/mtd/nand/rockchip_nand.c @@ -0,0 +1,660 @@ +/*
- 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_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
For the Rockchip drivers we have usually used structures to represent the layout. I know that this will be very sparse for this particular case, but it seems to be worth this for consistency.
If it's not a problem i would leave this as is. The reason for that is rockchip kernel nand driver (currently in backing stage) as it would be easier to maintain both of them and add new features. At least one driver in u-boot (sunxi nand) is doing the same thing.
I feel strongly about this one, as this is a matter of maintainability and readability (and looking at the sunxi NAND driver, I would have similar concerns… although at least they don’t multiply-add banks into addresses).
I really don’t see the point of living with code sequences like: void __iomem *bank_base = rknand->regs + NANDC_REG_V6_BANK0 + rknand->selected_bank * 0x100; if (ctrl & NAND_ALE) bank_base += NANDC_REG_V6_ADDR; chip->IO_ADDR_W = bank_base; when you in fact you want to write: chip->IO_ADDR_W = ®s->bank[selected_bank].addr;
The only reason for using the “base + OFFSET” addressing would be if you tried to support two closely related controllers that only differed in the offset of registers (in which it would actually read more like “base + driver_data->reg_offsets[REGISTER_NAME]”… and even then, I’d insist on hiding this in a wrapper around readl/writel.
So for the sake of clarity, please change this to either (a) use a structure to reflect the register layout (b) add read/write accessor functions, so the address arithmetic is properly abstracted away.
+#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))
BIT(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)
This might benefit from using functions from bitfield.h?
+struct rk_nand {
- uint32_t banks[NANDC_V6_NUM_BANKS];
Is the number of banks fixed or should this be dynamically allocated based on a device-tree property (or driver-data, if it is dependent on how the controller was synthesized)?
This should be dynamically allocated and i will change this in the next version.
- 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} }
The indentation looks odd on those.
+};
+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);
Why the 0x1081?
0x1081 is the default (and safe) async timing register value set by old rockchip loaders. Currently nand driver doesn't support setting timings. This will change in the feature as it is on my TODO list for this driver.
I had to look up the FMWAIT register in one of the chip manuals to figure out what exactly this is trying to do. And if I see this correctly, you are setting CSRW, RWPW and RWCS timings at once.
Also: Why is this a safe value and are the reset-values of the controller unsafe? You might want to leave at least a comment here as a starting point for people who might need to touch this driver in the future.
+}
+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;
Once converted to a structure, this could be as simple to read as bank_base = &rknand->bank[banknr];
chip->IO_ADDR_R = bank_base;
chip->IO_ADDR_W = bank_base;
reg |= 1 << banknr;
- }
- writel(reg, rknand->regs + NANDC_REG_V6_FMCTL);
Why not a clrsetbits?
- 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;
See above.
- if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_ALE)
bank_base += NANDC_REG_V6_ADDR;
This is not the bank_base you are modifying. So this really is if (ctrl & NAND_ALE) chip->IO_ADDR_W = &rknand->bank[i].addr; else if (ctrl & NAND_CLE) chip->IO_ADDR_W = &rknand->bank[i].cmd; else chip->IO_ADDR_W = &rknand->bank[i];
Correct. Banks have three separate registers for io, addr cycle, cmd cycle. When writing to addr: Nand pin ALE is being set to high. To cmd: Nand pin CLE is being set to high. IO is for data. Offsets from bank base are:
#define NANDC_REG_V6_DATA 0x00 #define NANDC_REG_V6_ADDR 0x04 #define NANDC_REG_V6_CMD 0x08
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);
+}
See above.
+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;
- }
Could you use symbolic constants?
I will change this in the next version.
- 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);
clrsetbits?
- 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);
setbits?
+}
+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”))
I didn’t see documentation for the DTS binding? This I just miss this?
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 107a892..4a8a8d7 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)
Regards, Paweł