[U-Boot] [PATCH v2 1/2] mtd: nand : zynq_nand: Add nand driver support for zynq

Michal Simek michal.simek at xilinx.com
Mon Sep 26 08:41:40 CEST 2016


On 23.9.2016 14:50, Siva Durga Prasad Paladugu wrote:
> Add nand flash controller driver support for zynq SoC.
> 
> Signed-off-by: Siva Durga Prasad Paladugu <sivadur at xilinx.com>
> ---
> Changes for v2:
> - corrected the from address
> ---
>  drivers/mtd/nand/Kconfig     |    7 +
>  drivers/mtd/nand/Makefile    |    1 +
>  drivers/mtd/nand/zynq_nand.c | 1186 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1194 insertions(+)
>  create mode 100644 drivers/mtd/nand/zynq_nand.c
> 
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 5ce7d6d..7e5c436 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -80,6 +80,13 @@ config NAND_ARASAN
>  	  controller. This uses the hardware ECC for read and
>  	  write operations.
>  
> +config NAND_ZYNQ
> +	bool "Support for Zynq Nand controller"
> +	select SYS_NAND_SELF_INIT
> +	help
> +	  This enables Nand driver support for Nand flash controller
> +	  found on Zynq SoC.
> +
>  comment "Generic NAND options"
>  
>  # Enhance depends when converting drivers to Kconfig which use this config
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index 1df9273..fd4bb66 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -67,6 +67,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
>  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
>  
>  else  # minimal SPL drivers
>  
> diff --git a/drivers/mtd/nand/zynq_nand.c b/drivers/mtd/nand/zynq_nand.c
> new file mode 100644
> index 0000000..a0003a4
> --- /dev/null
> +++ b/drivers/mtd/nand/zynq_nand.c
> @@ -0,0 +1,1186 @@
> +/*
> + * (C) Copyright 2013 Xilinx, Inc.
> + *
> + * Xilinx Zynq NAND Flash Controller Driver
> + * This driver is based on plat_nand.c and mxc_nand.c drivers
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <malloc.h>
> +#include <asm/io.h>
> +#include <asm/errno.h>

drivers/mtd/nand/zynq_nand.c:13:23: fatal error: asm/errno.h: No such
file or directory
 #include <asm/errno.h>
                       ^
compilation terminated.

Remove this header.


> +#include <nand.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mtd/nand_ecc.h>
> +#include <asm/arch/hardware.h>
> +
> +/* The NAND flash driver defines */
> +#define ZYNQ_NAND_CMD_PHASE		1
> +#define ZYNQ_NAND_DATA_PHASE		2
> +#define ZYNQ_NAND_ECC_SIZE		512
> +#define ZYNQ_NAND_SET_OPMODE_8BIT	(0 << 0)
> +#define ZYNQ_NAND_SET_OPMODE_16BIT	(1 << 0)
> +#define ZYNQ_NAND_ECC_STATUS		(1 << 6)
> +#define ZYNQ_MEMC_CLRCR_INT_CLR1	(1 << 4)
> +#define ZYNQ_MEMC_SR_RAW_INT_ST1	(1 << 6)
> +#define ZYNQ_MEMC_SR_INT_ST1		(1 << 4)
> +#define ZYNQ_MEMC_NAND_ECC_MODE_MASK	0xC
> +
> +/* Flash memory controller operating parameters */
> +#define ZYNQ_NAND_CLR_CONFIG	((0x1 << 1)  |	/* Disable interrupt */ \
> +				(0x1 << 4)   |	/* Clear interrupt */ \
> +				(0x1 << 6))	/* Disable ECC interrupt */
> +
> +/* Assuming 50MHz clock (20ns cycle time) and 3V operation */
> +#define ZYNQ_NAND_SET_CYCLES	((0x2 << 20) |	/* t_rr from nand_cycles */ \
> +				(0x2 << 17)  |	/* t_ar from nand_cycles */ \
> +				(0x1 << 14)  |	/* t_clr from nand_cycles */ \
> +				(0x3 << 11)  |	/* t_wp from nand_cycles */ \
> +				(0x2 << 8)   |	/* t_rea from nand_cycles */ \
> +				(0x5 << 4)   |	/* t_wc from nand_cycles */ \
> +				(0x5 << 0))	/* t_rc from nand_cycles */
> +
> +
> +#define ZYNQ_NAND_DIRECT_CMD	((0x4 << 23) |	/* Chip 0 from interface 1 */ \
> +				(0x2 << 21))	/* UpdateRegs operation */
> +
> +#define ZYNQ_NAND_ECC_CONFIG	((0x1 << 2)  |	/* ECC available on APB */ \
> +				(0x1 << 4)   |	/* ECC read at end of page */ \
> +				(0x0 << 5))	/* No Jumping */
> +
> +#define ZYNQ_NAND_ECC_CMD1	((0x80)      |	/* Write command */ \
> +				(0x00 << 8)  |	/* Read command */ \
> +				(0x30 << 16) |	/* Read End command */ \
> +				(0x1 << 24))	/* Read End command calid */
> +
> +#define ZYNQ_NAND_ECC_CMD2	((0x85)      |	/* Write col change cmd */ \
> +				(0x05 << 8)  |	/* Read col change cmd */ \
> +				(0xE0 << 16) |	/* Read col change end cmd */ \
> +				(0x1 << 24))	/* Read col change
> +							end cmd valid */
> +/* AXI Address definitions */
> +#define START_CMD_SHIFT			3
> +#define END_CMD_SHIFT			11
> +#define END_CMD_VALID_SHIFT		20
> +#define ADDR_CYCLES_SHIFT		21
> +#define CLEAR_CS_SHIFT			21
> +#define ECC_LAST_SHIFT			10
> +#define COMMAND_PHASE			(0 << 19)
> +#define DATA_PHASE			(1 << 19)
> +#define ONDIE_ECC_FEATURE_ADDR		0x90
> +#define ONDIE_ECC_FEATURE_ENABLE	0x08
> +
> +#define ZYNQ_NAND_ECC_LAST	(1 << ECC_LAST_SHIFT)	/* Set ECC_Last */
> +#define ZYNQ_NAND_CLEAR_CS	(1 << CLEAR_CS_SHIFT)	/* Clear chip select */
> +
> +/* ECC block registers bit position and bit mask */
> +#define ZYNQ_NAND_ECC_BUSY	(1 << 6)	/* ECC block is busy */
> +#define ZYNQ_NAND_ECC_MASK	0x00FFFFFF	/* ECC value mask */
> +
> +
> +/* SMC register set */
> +struct zynq_nand_smc_regs {
> +	u32 csr;		/* 0x00 */
> +	u32 reserved0[2];
> +	u32 cfr;		/* 0x0C */
> +	u32 dcr;		/* 0x10 */
> +	u32 scr;		/* 0x14 */
> +	u32 sor;		/* 0x18 */
> +	u32 reserved1[249];
> +	u32 esr;		/* 0x400 */
> +	u32 emcr;		/* 0x404 */
> +	u32 emcmd1r;		/* 0x408 */
> +	u32 emcmd2r;		/* 0x40C */
> +	u32 reserved2[2];
> +	u32 eval0r;		/* 0x418 */
> +};
> +#define zynq_nand_smc_base	((struct zynq_nand_smc_regs __iomem *)\
> +				ZYNQ_SMC_BASEADDR)
> +
> +/*
> + * struct zynq_nand_info - Defines the NAND flash driver instance
> + * @parts:		Pointer to the mtd_partition structure
> + * @nand_base:		Virtual address of the NAND flash device
> + * @end_cmd_pending:	End command is pending
> + * @end_cmd:		End command
> + */
> +struct zynq_nand_info {
> +	void __iomem	*nand_base;
> +	u8		end_cmd_pending;
> +	u8		end_cmd;
> +};
> +
> +/*
> + * struct zynq_nand_command_format - Defines NAND flash command format
> + * @start_cmd:		First cycle command (Start command)
> + * @end_cmd:		Second cycle command (Last command)
> + * @addr_cycles:	Number of address cycles required to send the address
> + * @end_cmd_valid:	The second cycle command is valid for cmd or data phase
> + */
> +struct zynq_nand_command_format {
> +	u8 start_cmd;
> +	u8 end_cmd;
> +	u8 addr_cycles;
> +	u8 end_cmd_valid;
> +};
> +
> +/*  The NAND flash operations command format */
> +static const struct zynq_nand_command_format zynq_nand_commands[] = {
> +	{NAND_CMD_READ0, NAND_CMD_READSTART, 5, ZYNQ_NAND_CMD_PHASE},
> +	{NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, ZYNQ_NAND_CMD_PHASE},
> +	{NAND_CMD_READID, NAND_CMD_NONE, 1, 0},
> +	{NAND_CMD_STATUS, NAND_CMD_NONE, 0, 0},
> +	{NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, ZYNQ_NAND_DATA_PHASE},
> +	{NAND_CMD_RNDIN, NAND_CMD_NONE, 2, 0},
> +	{NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, ZYNQ_NAND_CMD_PHASE},
> +	{NAND_CMD_RESET, NAND_CMD_NONE, 0, 0},
> +	{NAND_CMD_PARAM, NAND_CMD_NONE, 1, 0},
> +	{NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, 0},
> +	{NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, 0},
> +	{NAND_CMD_NONE, NAND_CMD_NONE, 0, 0},
> +	/* Add all the flash commands supported by the flash device */
> +};
> +
> +/* Define default oob placement schemes for large and small page devices */
> +static struct nand_ecclayout nand_oob_16 = {
> +	.eccbytes = 3,
> +	.eccpos = {0, 1, 2},
> +	.oobfree = {
> +		{ .offset = 8, .length = 8 }
> +	}
> +};
> +
> +static struct nand_ecclayout nand_oob_64 = {
> +	.eccbytes = 12,
> +	.eccpos = {
> +		   52, 53, 54, 55, 56, 57,
> +		   58, 59, 60, 61, 62, 63},
> +	.oobfree = {
> +		{ .offset = 2, .length = 50 }
> +	}
> +};
> +
> +static struct nand_ecclayout ondie_nand_oob_64 = {
> +	.eccbytes = 32,
> +
> +	.eccpos = {
> +		8, 9, 10, 11, 12, 13, 14, 15,
> +		24, 25, 26, 27, 28, 29, 30, 31,
> +		40, 41, 42, 43, 44, 45, 46, 47,
> +		56, 57, 58, 59, 60, 61, 62, 63
> +	},
> +
> +	.oobfree = {
> +		{ .offset = 4, .length = 4 },
> +		{ .offset = 20, .length = 4 },
> +		{ .offset = 36, .length = 4 },
> +		{ .offset = 52, .length = 4 }
> +	}
> +};
> +
> +/* bbt decriptors for chips with on-die ECC and
> +   chips with 64-byte OOB */
> +static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
> +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
> +
> +static struct nand_bbt_descr bbt_main_descr = {
> +	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
> +		NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
> +	.offs = 4,
> +	.len = 4,
> +	.veroffs = 20,
> +	.maxblocks = 4,
> +	.pattern = bbt_pattern
> +};
> +
> +static struct nand_bbt_descr bbt_mirror_descr = {
> +	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
> +		NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
> +	.offs = 4,
> +	.len = 4,
> +	.veroffs = 20,
> +	.maxblocks = 4,
> +	.pattern = mirror_pattern
> +};
> +
> +/*
> + * zynq_nand_waitfor_ecc_completion - Wait for ECC completion
> + *
> + * returns: status for command completion, -1 for Timeout
> + */
> +static int zynq_nand_waitfor_ecc_completion(void)
> +{
> +	unsigned long timeout;
> +	u32 status;
> +
> +	/* Wait max 10us */
> +	timeout = 10;
> +	status = readl(&zynq_nand_smc_base->esr);
> +	while (status & ZYNQ_NAND_ECC_BUSY) {
> +		status = readl(&zynq_nand_smc_base->esr);
> +		if (timeout == 0)
> +			return -1;
> +		timeout--;
> +		udelay(1);
> +	}
> +
> +	return status;
> +}
> +
> +/*
> + * zynq_nand_init_nand_flash - Initialize NAND controller
> + * @option:	Device property flags
> + *
> + * This function initializes the NAND flash interface on the NAND controller.
> + *
> + * returns:	0 on success or error value on failure
> + */
> +static int zynq_nand_init_nand_flash(int option)
> +{
> +	u32 status;
> +
> +	/* disable interrupts */
> +	writel(ZYNQ_NAND_CLR_CONFIG, &zynq_nand_smc_base->cfr);
> +	/* Initialize the NAND interface by setting cycles and operation mode */
> +	writel(ZYNQ_NAND_SET_CYCLES, &zynq_nand_smc_base->scr);
> +	if (option & NAND_BUSWIDTH_16)
> +		writel(ZYNQ_NAND_SET_OPMODE_16BIT, &zynq_nand_smc_base->sor);
> +	else
> +		writel(ZYNQ_NAND_SET_OPMODE_8BIT, &zynq_nand_smc_base->sor);
> +
> +	writel(ZYNQ_NAND_DIRECT_CMD, &zynq_nand_smc_base->dcr);
> +
> +	/* Wait till the ECC operation is complete */
> +	status = zynq_nand_waitfor_ecc_completion();
> +	if (status < 0) {
> +		printf("%s: Timeout\n", __func__);
> +		return status;
> +	}
> +
> +	/* Set the command1 and command2 register */
> +	writel(ZYNQ_NAND_ECC_CMD1, &zynq_nand_smc_base->emcmd1r);
> +	writel(ZYNQ_NAND_ECC_CMD2, &zynq_nand_smc_base->emcmd2r);
> +
> +	return 0;
> +}
> +
> +/*
> + * zynq_nand_calculate_hwecc - Calculate Hardware ECC
> + * @mtd:	Pointer to the mtd_info structure
> + * @data:	Pointer to the page data
> + * @ecc_code:	Pointer to the ECC buffer where ECC data needs to be stored
> + *
> + * This function retrieves the Hardware ECC data from the controller and returns
> + * ECC data back to the MTD subsystem.
> + *
> + * returns:	0 on success or error value on failure
> + */
> +static int zynq_nand_calculate_hwecc(struct mtd_info *mtd, const u8 *data,
> +		u8 *ecc_code)
> +{
> +	u32 ecc_value = 0;
> +	u8 ecc_reg, ecc_byte;
> +	u32 ecc_status;
> +
> +	/* Wait till the ECC operation is complete */
> +	ecc_status = zynq_nand_waitfor_ecc_completion();
> +	if (ecc_status < 0) {
> +		printf("%s: Timeout\n", __func__);
> +		return ecc_status;
> +	}
> +
> +	for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) {
> +		/* Read ECC value for each block */
> +		ecc_value = readl(&zynq_nand_smc_base->eval0r + ecc_reg);
> +
> +		/* Get the ecc status from ecc read value */
> +		ecc_status = (ecc_value >> 24) & 0xFF;
> +
> +		/* ECC value valid */
> +		if (ecc_status & ZYNQ_NAND_ECC_STATUS) {
> +			for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) {
> +				/* Copy ECC bytes to MTD buffer */
> +				*ecc_code = ecc_value & 0xFF;
> +				ecc_value = ecc_value >> 8;
> +				ecc_code++;
> +			}
> +		} else {
> +			debug("%s: ecc status failed\n", __func__);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * onehot - onehot function
> + * @value:	value to check for onehot
> + *
> + * This function checks whether a value is onehot or not.
> + * onehot is if and only if one bit is set.
> + *
> + * FIXME: Try to move this in common.h
> + */
> +static bool onehot(unsigned short value)
> +{
> +	bool onehot;
> +
> +	onehot = value && !(value & (value - 1));
> +	return onehot;
> +}
> +
> +/*
> + * zynq_nand_correct_data - ECC correction function
> + * @mtd:	Pointer to the mtd_info structure
> + * @buf:	Pointer to the page data
> + * @read_ecc:	Pointer to the ECC value read from spare data area
> + * @calc_ecc:	Pointer to the calculated ECC value
> + *
> + * This function corrects the ECC single bit errors & detects 2-bit errors.
> + *
> + * returns:	0 if no ECC errors found
> + *		1 if single bit error found and corrected.
> + *		-1 if multiple ECC errors found.
> + */
> +static int zynq_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
> +			unsigned char *read_ecc, unsigned char *calc_ecc)
> +{
> +	unsigned char bit_addr;
> +	unsigned int byte_addr;
> +	unsigned short ecc_odd, ecc_even;
> +	unsigned short read_ecc_lower, read_ecc_upper;
> +	unsigned short calc_ecc_lower, calc_ecc_upper;
> +
> +	read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff;
> +	read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff;
> +
> +	calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff;
> +	calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff;
> +
> +	ecc_odd = read_ecc_lower ^ calc_ecc_lower;
> +	ecc_even = read_ecc_upper ^ calc_ecc_upper;
> +
> +	if ((ecc_odd == 0) && (ecc_even == 0))
> +		return 0;       /* no error */
> +
> +	if (ecc_odd == (~ecc_even & 0xfff)) {
> +		/* bits [11:3] of error code is byte offset */
> +		byte_addr = (ecc_odd >> 3) & 0x1ff;
> +		/* bits [2:0] of error code is bit offset */
> +		bit_addr = ecc_odd & 0x7;
> +		/* Toggling error bit */
> +		buf[byte_addr] ^= (1 << bit_addr);
> +		return 1;
> +	}
> +
> +	if (onehot(ecc_odd | ecc_even))
> +		return 1; /* one error in parity */
> +
> +	return -1; /* Uncorrectable error */
> +}
> +
> +/*
> + * zynq_nand_read_oob - [REPLACABLE] the most common OOB data read function
> + * @mtd:	mtd info structure
> + * @chip:	nand chip info structure
> + * @page:	page number to read
> + * @sndcmd:	flag whether to issue read command or not
> + */
> +static int zynq_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
> +			int page)
> +{
> +	unsigned long data_phase_addr = 0;
> +	int data_width = 4;
> +	u8 *p;
> +
> +	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
> +
> +	p = chip->oob_poi;
> +	chip->read_buf(mtd, p, (mtd->oobsize - data_width));
> +	p += mtd->oobsize - data_width;
> +
> +	data_phase_addr = (unsigned long)chip->IO_ADDR_R;
> +	data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
> +	chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
> +	chip->read_buf(mtd, p, data_width);
> +
> +	return 0;
> +}
> +
> +/*
> + * zynq_nand_write_oob - [REPLACABLE] the most common OOB data write function
> + * @mtd:	mtd info structure
> + * @chip:	nand chip info structure
> + * @page:	page number to write
> + */
> +static int zynq_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
> +			     int page)
> +{
> +	int status = 0, data_width = 4;
> +	const u8 *buf = chip->oob_poi;
> +	unsigned long data_phase_addr = 0;
> +
> +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
> +
> +	chip->write_buf(mtd, buf, (mtd->oobsize - data_width));
> +	buf += mtd->oobsize - data_width;
> +
> +	data_phase_addr = (unsigned long)chip->IO_ADDR_W;
> +	data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
> +	data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
> +	chip->IO_ADDR_W = (void __iomem *)data_phase_addr;
> +	chip->write_buf(mtd, buf, data_width);
> +
> +	/* Send command to program the OOB data */
> +	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
> +	status = chip->waitfunc(mtd, chip);
> +
> +	return status & NAND_STATUS_FAIL ? -EIO : 0;
> +}
> +
> +/*
> + * zynq_nand_read_page_raw - [Intern] read raw page data without ecc
> + * @mtd:        mtd info structure
> + * @chip:       nand chip info structure
> + * @buf:        buffer to store read data
> + * @oob_required: must write chip->oob_poi to OOB
> + * @page:       page number to read
> + */
> +static int zynq_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
> +				   u8 *buf,  int oob_required, int page)
> +{
> +	unsigned long data_width = 4;
> +	unsigned long data_phase_addr = 0;
> +	u8 *p;
> +
> +	chip->read_buf(mtd, buf, mtd->writesize);
> +
> +	p = chip->oob_poi;
> +	chip->read_buf(mtd, p, (mtd->oobsize - data_width));
> +	p += (mtd->oobsize - data_width);
> +
> +	data_phase_addr = (unsigned long)chip->IO_ADDR_R;
> +	data_phase_addr |= ZYNQ_NAND_CLEAR_CS;
> +	chip->IO_ADDR_R = (void __iomem *)data_phase_addr;
> +
> +	chip->read_buf(mtd, p, data_width);
> +	return 0;
> +}
> +
> +static int zynq_nand_read_page_raw_nooob(struct mtd_info *mtd,
> +		struct nand_chip *chip, u8 *buf, int oob_required, int page)
> +{
> +	chip->read_buf(mtd, buf, mtd->writesize);
> +	return 0;
> +}
> +
> +static int zynq_nand_read_subpage_raw(struct mtd_info *mtd,
> +				    struct nand_chip *chip, u32 data_offs,
> +				    u32 readlen, u8 *buf)
> +{

drivers/mtd/nand/zynq_nand.c: In function 'zynq_nand_init':
drivers/mtd/nand/zynq_nand.c:1092:31: warning: assignment from
incompatible pointer type
   nand_chip->ecc.read_subpage = zynq_nand_read_subpage_raw;
                               ^


Thanks,
Michal


More information about the U-Boot mailing list