
This patch is a review feedback against Denali NAND controller driver. http://patchwork.ozlabs.org/patch/333077/
This is not applicable to the mainline.
---
Hi Chin,
This patch fixes some issues.
[1] Fix denali_write_oob() handler.
As for v7, "nand markbad" did not work at all.
With this patch, it works.
[2] Make denali_read_oob() 10x faster.
One of the fatal issues of v7 is "nand bad" command is extremely slow.
This is the benchmark of v7
=> time nand bad
Device 0 bad blocks:
time: 11.300 seconds, 11300 ticks
It is really really painful to wait more than 10 seconds just for bad block scanning to boot Linux.
In v7, denali_read_oob() calls denali_read_page_raw(). This causes the transfering main area data and memcpy of it, which leads to significant performance regression.
Like Linux Kernel, dedicated denali_read_oob() must be impilemented.
With this patch, "nand bad" command gets much faster!
This is my benchmark:
=> time nand bad
Device 0 bad blocks:
time: 0.998 seconds, 998 ticks
[3] Remove false comment
/* Writes OOB data to the device. * This code unused under normal U-Boot console as normally page write raw * to be used for write oob data with main data. */ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
This comment is telling a lie. write_oob_data() is called from "nand markbad" command. It must be deleted.
Signed-off-by: Masahiro Yamada yamada.m@jp.panasonic.com Cc: Chin Liang See clsee@altera.com Cc: Artem Bityutskiy artem.bityutskiy@linux.intel.com Cc: David Woodhouse David.Woodhouse@intel.com Cc: Brian Norris computersforpeace@gmail.com Cc: Scott Wood scottwood@freescale.com --- drivers/mtd/nand/denali.c | 136 +++++++++++++++++++++++++++++++--------------- 1 file changed, 91 insertions(+), 45 deletions(-)
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 348e244..dcde3e6 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -527,46 +527,34 @@ static void setup_ecc_for_xfer(bool ecc_en, bool transfer_spare) static int denali_send_pipeline_cmd(bool ecc_en, bool transfer_spare, int access_type, int op) { - uint32_t addr = 0x0, cmd = 0x0, irq_status = 0, irq_mask = 0; - uint32_t page_count = 1; /* always read a page */ - - if (op == DENALI_READ) - irq_mask = INTR_STATUS__LOAD_COMP; - else if (op == DENALI_WRITE) - irq_mask = INTR_STATUS__PROGRAM_COMP | - INTR_STATUS__PROGRAM_FAIL; - else - BUG(); + uint32_t addr = 0x0, cmd = 0x0, irq_status; + static uint32_t page_count = 1; + + setup_ecc_for_xfer(ecc_en, transfer_spare);
/* clear interrupts */ clear_interrupts();
- /* setup ECC and transfer spare reg */ - setup_ecc_for_xfer(ecc_en, transfer_spare); - addr = BANK(denali.flash_bank) | denali.page;
/* setup the acccess type */ cmd = MODE_10 | addr; - index_addr((uint32_t)cmd, access_type); + index_addr(cmd, access_type);
/* setup the pipeline command */ - if (access_type == SPARE_ACCESS && op == DENALI_WRITE) - index_addr((uint32_t)cmd, DENALI_BUFFER_WRITE); - else if (access_type == SPARE_ACCESS && op == DENALI_READ) - index_addr((uint32_t)cmd, DENALI_BUFFER_LOAD); - else - index_addr((uint32_t)cmd, 0x2000 | op | page_count); + index_addr(cmd, 0x2000 | op | page_count);
- /* wait for command to be accepted */ - irq_status = wait_for_irq(irq_mask); - if ((irq_status & irq_mask) != irq_mask) - return -EIO; + cmd = MODE_01 | addr; + writel(cmd, denali.flash_mem + INDEX_CTRL_REG);
- if (access_type != SPARE_ACCESS) { - cmd = MODE_01 | addr; - writel(cmd, denali.flash_mem + INDEX_CTRL_REG); + if (op == DENALI_READ) { + /* wait for command to be accepted */ + irq_status = wait_for_irq(INTR_STATUS__LOAD_COMP); + + if (irq_status == 0) + return -EIO; } + return 0; }
@@ -586,6 +574,29 @@ static int write_data_to_flash_mem(const void *buf, int len) return i * 4; /* intent is to return the number of bytes read */ }
+/* helper function that simply reads a buffer from the flash */ +static int read_data_from_flash_mem(uint8_t *buf, int len) +{ + uint32_t i, *buf32; + + /* + * we assume that len will be a multiple of 4, if not + * it would be nice to know about it ASAP rather than + * have random failures... + * This assumption is based on the fact that this + * function is designed to be used to read flash pages, + * which are typically multiples of 4... + */ + + BUG_ON((len % 4) != 0); + + /* transfer the data from the flash */ + buf32 = (uint32_t *)buf; + for (i = 0; i < len / 4; i++) + *buf32++ = readl(denali.flash_mem + INDEX_DATA_REG); + return i * 4; /* intent is to return the number of bytes read */ +} + static void denali_mode_main_access(void) { uint32_t addr, cmd; @@ -602,29 +613,64 @@ static void denali_mode_main_spare_access(void) index_addr(cmd, MAIN_SPARE_ACCESS); }
-/* Writes OOB data to the device. - * This code unused under normal U-Boot console as normally page write raw - * to be used for write oob data with main data. - */ +/* writes OOB data to the device */ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) { - uint32_t cmd; + uint32_t irq_status; + uint32_t irq_mask = INTR_STATUS__PROGRAM_COMP | + INTR_STATUS__PROGRAM_FAIL; + int status = 0;
denali.page = page; - debug("* write_oob_data *\n");
- /* We need to write to buffer first through MAP00 command */ - cmd = MODE_00 | BANK(denali.flash_bank); - writel(cmd, denali.flash_mem + INDEX_CTRL_REG); + if (denali_send_pipeline_cmd(false, true, SPARE_ACCESS, + DENALI_WRITE) == 0) { + write_data_to_flash_mem(buf, mtd->oobsize);
- /* send the data into flash buffer */ - write_data_to_flash_mem(buf, mtd->oobsize); + /* wait for operation to complete */ + irq_status = wait_for_irq(irq_mask);
- /* activate the write through MAP10 commands */ - if (denali_send_pipeline_cmd(false, false, SPARE_ACCESS, DENALI_WRITE)) - return -EIO; + if (irq_status == 0) { + dev_err(denali->dev, "OOB write failed\n"); + status = -EIO; + } + } else { + printf("unable to send pipeline command\n"); + status = -EIO; + } + return status; +}
- return 0; +/* reads OOB data from the device */ +static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + uint32_t irq_mask = INTR_STATUS__LOAD_COMP, + irq_status = 0, addr = 0x0, cmd = 0x0; + + denali.page = page; + + if (denali_send_pipeline_cmd(false, true, SPARE_ACCESS, + DENALI_READ) == 0) { + read_data_from_flash_mem(buf, mtd->oobsize); + + /* wait for command to be accepted + * can always use status0 bit as the mask is identical for each + * bank. */ + irq_status = wait_for_irq(irq_mask); + + if (irq_status == 0) + printf("page on OOB timeout %d\n", denali.page); + + /* We set the device back to MAIN_ACCESS here as I observed + * instability with the controller if you do a block erase + * and the last transaction was a SPARE_ACCESS. Block erase + * is reliable (according to the MTD test infrastructure) + * if you are in MAIN_ACCESS. + */ + addr = BANK(denali.flash_bank) | denali.page; + cmd = MODE_10 | addr; + index_addr(cmd, MAIN_ACCESS); + } }
/* this function examines buffers to see if they contain data that @@ -908,9 +954,9 @@ static uint8_t denali_read_byte(struct mtd_info *mtd) static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - debug("denali_read_oob at page %08x\n", page); - denali.page = page; - return denali_read_page_raw(mtd, chip, denali.buf.buf, 1, page); + read_oob_data(mtd, chip->oob_poi, page); + + return 0; }
static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)