[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