public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
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

  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