[U-Boot] [PATCH 4/7] mxc_nand: add nand driver for MX2/MX3

Ilya Yanok yanok at emcraft.com
Fri Jul 17 12:48:38 CEST 2009


Hi Scott,

please review the updated patch (will be posted as a follow-up).

Scott Wood wrote:
> Please look at drivers/mtd/nand/mpc5121_nfc.c, which AFAICT is very
> similar hardware, and see if anything can be factored out into common
> code, and try to keep the rest looking the same except where the hardware
> actually differs.
>   

Hmm... For now we just can't spend enough effort for this...

>> +static struct nand_ecclayout nand_hw_eccoob_16 = {
>> +	.eccbytes = 5,
>> +	.eccpos = {6, 7, 8, 9, 10},
>> +	.oobfree = {{0, 6}, {12, 4}, }
>> +};
>>     
>
> This implies the bad block marker is one byte at offset 11 (it's all
> that's left), but I don't see any override of the bad block pattern.
>   

This is surely a bug. Fixed.

>> +static void *mxc_nand_memcpy32(void *dest, void *source, size_t size)
>> +{
>> +	uint32_t *s = source, *d = dest;
>> +
>> +	size >>= 2;
>> +	while (size--)
>> +		*d++ = *s++;
>> +	return dest;
>> +}
>>     
>
> This should probably be using I/O accessors (possibly raw) -- and should
> take uint32_t *, not void *.
>   

Fixed.

>> +/*
>> + * This function requests the NANDFC to perform a read of the
>> + * NAND device status and returns the current status.
>> + */
>> +static uint16_t get_dev_status(struct mxc_nand_host *host)
>> +{
>> +	void __iomem *main_buf = host->regs->main_area1;
>> +	uint32_t store;
>> +	uint16_t ret, tmp;
>> +	/* Issue status request to NAND device */
>> +
>> +	/* store the main area1 first word, later do recovery */
>> +	store = readl(main_buf);
>> +	/*
>> +	 * NANDFC buffer 1 is used for device status to prevent
>> +	 * corruption of read/write buffer on status requests.
>> +	 */
>> +	writew(1, &host->regs->nfc_buf_addr);
>>     
>
> But it looks like buffer 1 is used for data with large page flash.
>   

Well, we save first word of the buffer and then recover it.

> Other drivers don't seem to have any problem with status reads clobbering
> the buffer...
>
>   
>> +/* This functions is used by upper layer to checks if device is ready */
>> +static int mxc_nand_dev_ready(struct mtd_info *mtd)
>>     
>
> "This functions is"?  I'd say this comment is pretty redundant with respect
> to the function name anyway...
>   

Fixed.

>> +static u_char mxc_nand_read_byte(struct mtd_info *mtd)
>> +{
>> +	struct nand_chip *nand_chip = mtd->priv;
>> +	struct mxc_nand_host *host = nand_chip->priv;
>> +	uint8_t ret = 0;
>> +	uint16_t col, rd_word;
>> +	uint16_t __iomem *main_buf =
>> +		(uint16_t __iomem *)host->regs->main_area0;
>> +	uint16_t __iomem *spare_buf =
>> +		(uint16_t __iomem *)host->regs->spare_area0;
>>     
>
> According to Magnus Lilja, "the nand flash controller can only handle 32
> bit read/write operations, any other size will cause an abort (or
> something like that)".  But now we're accessing it as 16-bit?
>   

16-bit accesses work quite well. Problem was with 8-bit accesses.

>> +static uint16_t mxc_nand_read_word(struct mtd_info *mtd)
>> +{
>> +	struct nand_chip *nand_chip = mtd->priv;
>> +	struct mxc_nand_host *host = nand_chip->priv;
>> +	uint16_t col, rd_word, ret;
>> +	uint16_t __iomem *p;
>> +
>> +	MTDDEBUG(MTD_DEBUG_LEVEL3,
>> +	      "mxc_nand_read_word(col = %d)\n", host->col_addr);
>> +
>> +	col = host->col_addr;
>> +	/* Adjust saved column address */
>> +	if (col < mtd->writesize && host->spare_only)
>> +		col += mtd->writesize;
>> +
>> +	if (col < mtd->writesize) {
>> +		p = (uint16_t __iomem *)(host->regs->main_area0 + (col >> 1));
>> +	} else {
>> +		p = (uint16_t __iomem *)(host->regs->spare_area0 +
>> +				((col - mtd->writesize) >> 1));
>> +	}
>> +
>> +	if (col & 1) {
>> +		rd_word = readw(p);
>> +		ret = (rd_word >> 8) & 0xff;
>> +		rd_word = readw(&p[1]);
>> +		ret |= (rd_word << 8) & 0xff00;
>> +
>>     
>
> col should never be odd if you're reading words.
>   

It can be odd if previously we've read a byte.

>> +/*
>> + * Write data of length len to buffer buf. The data to be
>> + * written on NAND Flash is first copied to RAMbuffer. After the Data Input
>> + * Operation by the NFC, the data is written to NAND Flash
>> + */
>> +static void mxc_nand_write_buf(struct mtd_info *mtd,
>> +				const u_char *buf, int len)
>> +{
>> +	struct nand_chip *nand_chip = mtd->priv;
>> +	struct mxc_nand_host *host = nand_chip->priv;
>> +	int n, col, i = 0;
>> +
>> +	MTDDEBUG(MTD_DEBUG_LEVEL3,
>> +	      "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr,
>> +	      len);
>> +
>> +	col = host->col_addr;
>> +
>> +	/* Adjust saved column address */
>> +	if (col < mtd->writesize && host->spare_only)
>> +		col += mtd->writesize;
>> +
>> +	n = mtd->writesize + mtd->oobsize - col;
>> +	n = min(len, n);
>> +
>> +	MTDDEBUG(MTD_DEBUG_LEVEL3,
>> +	      "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n);
>> +
>> +	while (n) {
>>     
>
> Safer to do while (n > 0), especially when the code is this complex.
>   

Fixed.

>> +		void __iomem *p;
>> +
>> +		if (col < mtd->writesize) {
>> +			p = host->regs->main_area0 + (col & ~3);
>> +		} else {
>> +			p = host->regs->spare_area0 -
>> +						mtd->writesize + (col & ~3);
>> +		}
>> +
>> +		MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__,
>> +		      __LINE__, p);
>> +
>> +		if (((col | (int)&buf[i]) & 3) || n < 16) {
>>     
>
> Do not cast pointers to "int".  Use "uintptr_t" or "unsigned long" if you
> must.
>
> Why < 16 and not < 4?
>   

Fixed.

>> +			uint32_t data = 0;
>> +
>> +			if (col & 3 || n < 4)
>> +				data = readl(p);
>>     
>
> If that condition doesn't hold, the data we use is zero?
>   

If that condition doesn't hold we are going to rewrite a whole 32-bit
word so there is no need to read it's old content. But I've changed that
piece of code as you suggested anyway.

>> +
>> +			switch (col & 3) {
>> +			case 0:
>> +				if (n) {
>> +					data = (data & 0xffffff00) |
>> +					    (buf[i++] << 0);
>> +					n--;
>> +					col++;
>> +				}
>> +			case 1:
>> +				if (n) {
>> +					data = (data & 0xffff00ff) |
>> +					    (buf[i++] << 8);
>> +					n--;
>> +					col++;
>> +				}
>> +			case 2:
>> +				if (n) {
>> +					data = (data & 0xff00ffff) |
>> +					    (buf[i++] << 16);
>> +					n--;
>> +					col++;
>> +				}
>> +			case 3:
>> +				if (n) {
>> +					data = (data & 0x00ffffff) |
>> +					    (buf[i++] << 24);
>> +					n--;
>> +					col++;
>> +				}
>> +			}
>> +			writel(data, p);
>>     
>
> Might I suggest this?
>
> union {
> 	uint32_t word;
> 	uint8_t bytes[4];
> } nfc_word;
>
> nfc_word.word = readl(p);
> nfc_word.bytes[col & 3] = buf[i++];
> n--;
> col++;
>
> writel(nfc_word.word, p);
>
> As a side benefit, you lose the endian dependency.
>   

Thanks! I've used your code.

>> +			mxc_nand_memcpy32(p, (void *)&buf[i], m);
>>     
>
> Unnecessary cast.
>   

Fixed.

>> +	/* Update saved column address */
>> +	host->col_addr = col;
>> +
>> +}
>>     
>
> No blank lines before the brace at the end of a block.
>   

Fixed.

>> +
>> +/*
>> + * Used by the upper layer to verify the data in NAND Flash
>> + * with the data in the buf.
>> + */
>> +static int mxc_nand_verify_buf(struct mtd_info *mtd,
>> +				const u_char *buf, int len)
>> +{
>> +	return -EFAULT;
>> +}
>>     
>
> Umm...
>   

I've added verify_buf function.

>> +	case NAND_CMD_SEQIN:
>> +		if (column >= mtd->writesize) {
>> +			/*
>> +			 * FIXME: before send SEQIN command for write OOB,
>> +			 * We must read one page out.
>> +			 * For K9F1GXX has no READ1 command to set current HW
>> +			 * pointer to spare area, we must write the whole page
>> +			 * including OOB together.
>> +			 */
>> +			if (host->pagesize_2k) {
>> +				/* call ourself to read a page */
>> +				mxc_nand_command(mtd, NAND_CMD_READ0, 0,
>> +						page_addr);
>> +			}
>>     
>
> Should this be #ifdef HWECC?  And update the comment to indicate the
> actual problem, which is the unusual hardware ECC implementation.  I
> don't see what the lack of a "READ1" command has to do with it.
>   

I've updated the comment.

> And is this actually a FIXME if it's already being done?
>
>   
>> +#ifdef CONFIG_MXC_NAND_HWECC
>> +	this->ecc.calculate = mxc_nand_calculate_ecc;
>> +	this->ecc.hwctl = mxc_nand_enable_hwecc;
>> +	this->ecc.correct = mxc_nand_correct_data;
>> +	this->ecc.mode = NAND_ECC_HW;
>> +	this->ecc.size = 512;
>> +	this->ecc.bytes = 3;
>> +	this->ecc.layout = &nand_hw_eccoob_8;
>> +	tmp = readw(&host->regs->nfc_config1);
>> +	tmp |= NFC_ECC_EN;
>> +	writew(tmp, &host->regs->nfc_config1);
>> +#else
>> +	this->ecc.size = 512;
>> +	this->ecc.bytes = 3;
>> +	this->ecc.layout = &nand_hw_eccoob_8;
>> +	this->ecc.mode = NAND_ECC_SOFT;
>> +	tmp = readw(&host->regs->nfc_config1);
>> +	tmp &= ~NFC_ECC_EN;
>> +	writew(tmp, &host->regs->nfc_config1);
>> +#endif
>>     
>
> Soft ECC only supports 256-byte ECC blocks (anything you set here to the
> contrary will just be overwritten), and you'll need a layout that
> accommodates that with enough ECC bytes.
>
> Alternatively, you can implement 512-byte soft ECC if you don't want to
> waste those 3 extra OOB bytes. :-)
>
>   
>> +	host->pagesize_2k = 0;
>>     
>
> So large page is currently unsupported?
>   

Linux driver was fixed recently and now it claims to support 2K page
size... I've added all needed fixes but I can understand how this driver
should detect the pagesize... Linux driver calls nand_scan_ident()
itself for this... Do you think I can calls nand_scan_ident() from my
board_nand_init() function?

Regards, Ilya.



More information about the U-Boot mailing list