[U-Boot] [U-Boot, v4, 10/19] mtd: nand: add the rockchip nand controller driver

Philipp Tomsich philipp.tomsich at theobroma-systems.com
Fri Aug 18 12:45:50 UTC 2017



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 at 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 at rock-chips.com>
> + * Copyright (c) 2017 Paweł Jarosz <paweljarosz3691 at 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.

> +
> +#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)?

> +	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?

> +}
> +
> +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];

> +		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?

> +	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"))
> +                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)
>


More information about the U-Boot mailing list