Hi Michal, > -----Original Message----- > From: Michal Simek [mailto:michal.si...@xilinx.com] > Sent: Monday, September 26, 2016 12:12 PM > To: Siva Durga Prasad Paladugu <siva...@xilinx.com>; u-boot@lists.denx.de > Cc: o...@buserror.net; Siva Durga Prasad Paladugu <siva...@xilinx.com> > Subject: Re: [PATCH v2 1/2] mtd: nand : zynq_nand: Add nand driver support > for zynq > > 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 <siva...@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.
It might be due to latest changes on errno by Masahiro. I made this series on top of commit on friday "201c9d884dcadb4e76981c30e9915f73de2d09b5" Will anyway rebase it on top of latest master and send v3. Thanks, Siva > > > > +#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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot