> On 18 Aug 2017, at 20:14, Paweł Jarosz <paweljarosz3...@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 <paweljarosz3...@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.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 >> >> 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ł _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot