[U-Boot] [PATCH 1/2] mtd: denali: improve nand_read_oob and fix nand_write_oob
Masahiro Yamada
yamada.m at jp.panasonic.com
Fri Apr 18 13:30:45 CEST 2014
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 at jp.panasonic.com>
Cc: Chin Liang See <clsee at altera.com>
Cc: Artem Bityutskiy <artem.bityutskiy at linux.intel.com>
Cc: David Woodhouse <David.Woodhouse at intel.com>
Cc: Brian Norris <computersforpeace at gmail.com>
Cc: Scott Wood <scottwood at 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)
--
1.8.3.2
More information about the U-Boot
mailing list