[U-Boot] [PATCH v3 1/7] nand: Add zynq nand controller driver support
Scott Wood
scottwood at freescale.com
Fri Feb 28 01:19:44 CET 2014
On Mon, 2014-02-17 at 17:56 +0530, Siva Durga Prasad Paladugu wrote:
> +/* Generic flash bbt decriptors */
> +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
> +};
If this BBT description is only for chips with on-die ECC, chips with
64-byte OOB, etc., say so in a comment -- and don't label them "Generic
flash bbt descriptors". :-)
> +/*
> + * 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) == 1)
Why are you comparing a bool to an integer?
> + } else if (page_addr != -1) /* Erase */
> + cmd_data = page_addr;
> + else if (column != -1) { /* Change read/write column, read id etc */
> + /* Adjust columns for 16 bit bus width */
Please use braces here.
> + if ((chip->options & NAND_BUSWIDTH_16) &&
> + ((command == NAND_CMD_READ0) ||
> + (command == NAND_CMD_SEQIN) ||
> + (command == NAND_CMD_RNDOUT) ||
> + (command == NAND_CMD_RNDIN)))
> + column >>= 1;
> + cmd_data = column;
> + }
> +
> + writel(cmd_data, cmd_addr);
> +
> + if (curr_cmd->end_cmd_valid) {
> + xnand->end_cmd = curr_cmd->end_cmd;
> + xnand->end_cmd_pending = 1;
> + }
> +
> + ndelay(100);
> +
> + if ((command == NAND_CMD_READ0) ||
> + (command == NAND_CMD_RESET) ||
> + (command == NAND_CMD_PARAM) ||
> + (command == NAND_CMD_GET_FEATURES))
> + /* wait until command is processed */
> + nand_wait_ready(mtd);
> +
> +}
Don't put newlines before an ending brace.
> +
> +/*
> + * zynq_nand_read_buf - read chip data into buffer
> + * @mtd: MTD device structure
> + * @buf: buffer to store date
> + * @len: number of bytes to read
> + */
> +static void zynq_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> +
> + /* Make sure that buf is 32 bit aligned */
> + if (((int)buf & 0x3) != 0) {
> + if (((int)buf & 0x1) != 0) {
> + if (len) {
> + *buf = readb(chip->IO_ADDR_R);
> + buf += 1;
> + len--;
> + }
> + }
> +
> + if (((int)buf & 0x3) != 0) {
> + if (len >= 2) {
> + *(u16 *)buf = readw(chip->IO_ADDR_R);
> + buf += 2;
> + len -= 2;
> + }
> + }
> + }
Cast pointers to "unsigned long" or "uintptr_t", not "int".
Doesn't the casting of buf violate C99 aliasing rules? You can access
arbitrary pointers as char pointers, but not the other way around.
> + xnand = calloc(1, sizeof(struct zynq_nand_info));
> + if (!xnand) {
> + printf("%s: failed to allocate\n", __func__);
> + goto free;
> + }
> +
> + xnand->nand_base = (void *)ZYNQ_NAND_BASEADDR;
This is a bit confusing -- ZYNQ_NAND_BASEADDR looks like an I/O
address, but you don't have __iomem and aren't using I/O accessors, and
the contents appear to be normal software data.
> + if ((maf_id == 0x2c) && ((dev_id == 0xf1) ||
> + (dev_id == 0xa1) || (dev_id == 0xb1) ||
> + (dev_id == 0xaa) || (dev_id == 0xba) ||
> + (dev_id == 0xda) || (dev_id == 0xca) ||
> + (dev_id == 0xac) || (dev_id == 0xbc) ||
> + (dev_id == 0xdc) || (dev_id == 0xcc) ||
> + (dev_id == 0xa3) || (dev_id == 0xb3) ||
> + (dev_id == 0xd3) || (dev_id == 0xc3))) {
> + nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES,
> + ONDIE_ECC_FEATURE_ADDR, -1);
> + for (i = 0; i < 4; i++)
> + writeb(set_feature[i], nand_chip->IO_ADDR_W);
I wonder how much of this on-die ECC stuff belongs in the common code rathen than a controller driver.
> + /* Wait for 1us after writing data with SET_FEATURES command */
> + ndelay(1000);
> +
> + nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
> + ONDIE_ECC_FEATURE_ADDR, -1);
> + nand_chip->read_buf(mtd, get_feature, 4);
> +
> + if (get_feature[0] & ONDIE_ECC_FEATURE_ENABLE) {
> + debug("%s: OnDie ECC flash\n", __func__);
> + ondie_ecc_enabled = 1;
> + } else {
> + printf("%s: Unable to detect OnDie ECC\n", __func__);
Why does the lack of on-die ECC warrant a message?
> + if (ondie_ecc_enabled) {
> + /* Bypass the controller ECC block */
> + ecc_cfg = readl(&zynq_nand_smc_base->emcr);
> + ecc_cfg &= ~0xc;
> + writel(ecc_cfg, &zynq_nand_smc_base->emcr);
What is 0xc? Don't use magic numbers.
> + /* The software ECC routines won't work
> + * with the SMC controller
> + */
> + nand_chip->ecc.mode = NAND_ECC_HW;
> + nand_chip->ecc.strength = 1;
> + nand_chip->ecc.read_page = zynq_nand_read_page_raw_nooob;
> + nand_chip->ecc.read_subpage = zynq_nand_read_subpage_raw;
> + nand_chip->ecc.write_page = zynq_nand_write_page_raw;
> + nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw;
> + nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw;
> + nand_chip->ecc.read_oob = zynq_nand_read_oob;
> + nand_chip->ecc.write_oob = zynq_nand_write_oob;
> + nand_chip->ecc.size = mtd->writesize;
> + nand_chip->ecc.bytes = 0;
> +
> + /* NAND with on-die ECC supports subpage reads */
> + nand_chip->options |= NAND_SUBPAGE_READ;
> +
> + /* On-Die ECC spare bytes offset 8 is used for ECC codes */
> + if (ondie_ecc_enabled) {
> + nand_chip->ecc.layout = &ondie_nand_oob_64;
> + /* Use the BBT pattern descriptors */
> + nand_chip->bbt_td = &bbt_main_descr;
> + nand_chip->bbt_md = &bbt_mirror_descr;
> + }
On-die ECC implies 64 bytes of OOB?
> + } else {
> + /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
> + nand_chip->ecc.mode = NAND_ECC_HW;
> + nand_chip->ecc.strength = 1;
> + nand_chip->ecc.size = ZYNQ_NAND_ECC_SIZE;
> + nand_chip->ecc.bytes = 3;
> + nand_chip->ecc.calculate = zynq_nand_calculate_hwecc;
> + nand_chip->ecc.correct = zynq_nand_correct_data;
> + nand_chip->ecc.hwctl = NULL;
> + nand_chip->ecc.read_page = zynq_nand_read_page_hwecc;
> + nand_chip->ecc.write_page = zynq_nand_write_page_hwecc;
> + nand_chip->ecc.read_page_raw = zynq_nand_read_page_raw;
> + nand_chip->ecc.write_page_raw = zynq_nand_write_page_raw;
> + nand_chip->ecc.read_oob = zynq_nand_read_oob;
> + nand_chip->ecc.write_oob = zynq_nand_write_oob;
> +
> + switch (mtd->writesize) {
> + case 512:
> + ecc_page_size = 0x1;
> + /* Set the ECC memory config register */
> + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size),
> + &zynq_nand_smc_base->emcr);
> + break;
> + case 1024:
> + ecc_page_size = 0x2;
> + /* Set the ECC memory config register */
> + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size),
> + &zynq_nand_smc_base->emcr);
> + break;
> + case 2048:
> + ecc_page_size = 0x3;
> + /* Set the ECC memory config register */
> + writel((ZYNQ_NAND_ECC_CONFIG | ecc_page_size),
> + &zynq_nand_smc_base->emcr);
> + break;
> + default:
> + /* The software ECC routines won't work with
> + * the SMC controller
> + */
> + nand_chip->ecc.mode = NAND_ECC_SOFT;
> + nand_chip->ecc.calculate = nand_calculate_ecc;
> + nand_chip->ecc.correct = nand_correct_data;
> + nand_chip->ecc.read_page = zynq_nand_read_page_swecc;
> + nand_chip->ecc.write_page = zynq_nand_write_page_swecc;
> + nand_chip->ecc.size = 256;
What's going on here? Are you using soft ECC or not?
> + break;
> + }
> +
> + if (mtd->oobsize == 16)
> + nand_chip->ecc.layout = &nand_oob_16;
> + else if (mtd->oobsize == 64)
> + nand_chip->ecc.layout = &nand_oob_64;
> + }
What if oobsize > 64?
> +
> + /* Second phase scan */
> + if (nand_scan_tail(mtd)) {
> + printf("%s: nand_scan_tailfailed\n", __func__);
s/tailfailed/tail failed/
-Scott
More information about the U-Boot
mailing list