[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