From: Scott Wood <scottwood@freescale.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v3 1/7] nand: Add zynq nand controller driver support
Date: Thu, 27 Feb 2014 18:19:44 -0600 [thread overview]
Message-ID: <1393546784.2697.56.camel@snotra.buserror.net> (raw)
In-Reply-To: <95070e4f-822c-4223-bfcb-a927c141792c@CO9EHSMHS032.ehs.local>
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
next prev parent reply other threads:[~2014-02-28 0:19 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <1392639991-18798-1-git-send-email-sivadur@xilinx.com>
2014-02-17 12:26 ` [U-Boot] [PATCH v3 1/7] nand: Add zynq nand controller driver support Siva Durga Prasad Paladugu
2014-02-25 8:50 ` Michal Simek
2014-02-28 0:19 ` Scott Wood [this message]
2014-02-17 12:26 ` [U-Boot] [PATCH v3 2/7] zynq-common: Define CONFIG_NAND_ZYNQ Siva Durga Prasad Paladugu
2014-02-17 12:26 ` [U-Boot] [PATCH v3 3/7] zynq: Add zynq_zc770 xm011 board support Siva Durga Prasad Paladugu
2014-02-17 12:26 ` [U-Boot] [PATCH v3 4/7] zynq: zc770: Add base dts for zc770_xm011 Siva Durga Prasad Paladugu
2014-02-17 12:26 ` [U-Boot] [PATCH v3 5/7] zynq: Add support for auto nandboot Siva Durga Prasad Paladugu
2014-02-17 13:04 ` Andrew Murray
2014-02-17 14:33 ` Jagan Teki
2014-02-17 14:35 ` Jagan Teki
2014-02-17 12:26 ` [U-Boot] [PATCH v3 6/7] doc: README.zynq: Updated with nand support addition Siva Durga Prasad Paladugu
2014-02-17 12:26 ` [U-Boot] [PATCH v3 7/7] zynq: Enable nand env. support Siva Durga Prasad Paladugu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1393546784.2697.56.camel@snotra.buserror.net \
--to=scottwood@freescale.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox