[U-Boot] [RFC PATCH v1 0/6] pxa3xx_nand updates

I'm looking into the NAND support for the db-88f6820-amc board. There are a number of changes in the pxa3xx_nand driver in Linux that are relevant (not specifically to this boards but to Armada boards in general). Some of these changes are cleanups and some are actual bug fixes.
I'd really appreciate some testing on this. It doesn't make my board magically work but I feel like I'm making forward progress.
Where applicable I'll Cc the author of the equivalent Linux patch. Perhaps I should put them in the From: line or not they deserve all the credit, I just want to see these changes in u-boot.
Chris Packham (6): mtd: nand: pxa3xx_nand: Increase initial buffer size mtd: nand: pxa3xx_nand: use nand_to_mtd() mtd: nand: pxa3xx_nand: sync pxa3xx_nand_set_sdr_timing() mtd: nand: pxa3xx_nand: fix early spurious interrupt mtd: nand: pxa3xx-nand: fix random command timeouts mtd: nand: pxa3xx_nand: add support for partial chunks
drivers/mtd/nand/pxa3xx_nand.c | 203 ++++++++++++++++++++++++++--------------- 1 file changed, 127 insertions(+), 76 deletions(-)

The initial buffer is used for the initial commands used to detect a flash device (STATUS, READID and PARAM).
ONFI param page is 256 bytes, and there are three redundant copies to be read. JEDEC param page is 512 bytes, and there are also three redundant copies to be read. Hence this buffer should be at least 512 x 3. This commits rounds the buffer size to 2048.
Cc: Ezequiel Garcia ezequiel@vanguardiasur.com.ar Signed-off-by: Chris Packham judge.packham@gmail.com ---
drivers/mtd/nand/pxa3xx_nand.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dfe8966b56b6..ea0a6f3778bd 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -26,10 +26,13 @@
/* * Define a buffer size for the initial command that detects the flash device: - * STATUS, READID and PARAM. The largest of these is the PARAM command, - * needing 256 bytes. + * STATUS, READID and PARAM. + * ONFI param page is 256 bytes, and there are three redundant copies + * to be read. JEDEC param page is 512 bytes, and there are also three + * redundant copies to be read. + * Hence this buffer should be at least 512 x 3. Let's pick 2048. */ -#define INIT_BUFFER_SIZE 256 +#define INIT_BUFFER_SIZE 2048
/* registers and bit definitions */ #define NDCR (0x00) /* Control register */ @@ -838,14 +841,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break;
case NAND_CMD_PARAM: - info->buf_count = 256; + info->buf_count = INIT_BUFFER_SIZE; info->ndcb0 |= NDCB0_CMD_TYPE(0) | NDCB0_ADDR_CYC(1) | NDCB0_LEN_OVRD | command; info->ndcb1 = (column & 0xFF); - info->ndcb3 = 256; - info->data_size = 256; + info->ndcb3 = INIT_BUFFER_SIZE; + info->data_size = INIT_BUFFER_SIZE; break;
case NAND_CMD_READID: @@ -1468,6 +1471,7 @@ KEEP_CONFIG: host->row_addr_cycles = 3; else host->row_addr_cycles = 2; + return nand_scan_tail(mtd); }

On 20 October 2016 at 01:31, Chris Packham judge.packham@gmail.com wrote:
The initial buffer is used for the initial commands used to detect a flash device (STATUS, READID and PARAM).
ONFI param page is 256 bytes, and there are three redundant copies to be read. JEDEC param page is 512 bytes, and there are also three redundant copies to be read. Hence this buffer should be at least 512 x 3. This commits rounds the buffer size to 2048.
Hey Chris,
So you are basically picking the commit and commit log from Linux:
http://lists.infradead.org/pipermail/linux-mtd/2015-August/060721.html
Shouldn't you mention that somewhere?
Cc: Ezequiel Garcia ezequiel@vanguardiasur.com.ar Signed-off-by: Chris Packham judge.packham@gmail.com
drivers/mtd/nand/pxa3xx_nand.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dfe8966b56b6..ea0a6f3778bd 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -26,10 +26,13 @@
/*
- Define a buffer size for the initial command that detects the flash device:
- STATUS, READID and PARAM. The largest of these is the PARAM command,
- needing 256 bytes.
- STATUS, READID and PARAM.
- ONFI param page is 256 bytes, and there are three redundant copies
- to be read. JEDEC param page is 512 bytes, and there are also three
- redundant copies to be read.
*/
- Hence this buffer should be at least 512 x 3. Let's pick 2048.
-#define INIT_BUFFER_SIZE 256 +#define INIT_BUFFER_SIZE 2048
/* registers and bit definitions */ #define NDCR (0x00) /* Control register */ @@ -838,14 +841,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break;
case NAND_CMD_PARAM:
info->buf_count = 256;
info->buf_count = INIT_BUFFER_SIZE; info->ndcb0 |= NDCB0_CMD_TYPE(0) | NDCB0_ADDR_CYC(1) | NDCB0_LEN_OVRD | command; info->ndcb1 = (column & 0xFF);
info->ndcb3 = 256;
info->data_size = 256;
info->ndcb3 = INIT_BUFFER_SIZE;
info->data_size = INIT_BUFFER_SIZE; break; case NAND_CMD_READID:
@@ -1468,6 +1471,7 @@ KEEP_CONFIG: host->row_addr_cycles = 3; else host->row_addr_cycles = 2;
Spurious change. I suggest to drop it.
return nand_scan_tail(mtd);
}
-- 2.10.0.479.g7c56b16

On Fri, Oct 21, 2016 at 1:58 AM, Ezequiel Garcia ezequiel@vanguardiasur.com.ar wrote:
On 20 October 2016 at 01:31, Chris Packham judge.packham@gmail.com wrote:
The initial buffer is used for the initial commands used to detect a flash device (STATUS, READID and PARAM).
ONFI param page is 256 bytes, and there are three redundant copies to be read. JEDEC param page is 512 bytes, and there are also three redundant copies to be read. Hence this buffer should be at least 512 x 3. This commits rounds the buffer size to 2048.
Hey Chris,
So you are basically picking the commit and commit log from Linux:
http://lists.infradead.org/pipermail/linux-mtd/2015-August/060721.html
Shouldn't you mention that somewhere?
Indeed. I mentioned it here http://lists.denx.de/pipermail/u-boot/2016-October/270605.html and that was the intent of the Cc line below.
I should probably set the From: line to the original author and call out the Linux commit sha1. I wasn't sure of the usual u-boot practice, I could equally squash these all together and say "sync with linux".
Cc: Ezequiel Garcia ezequiel@vanguardiasur.com.ar Signed-off-by: Chris Packham judge.packham@gmail.com
drivers/mtd/nand/pxa3xx_nand.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dfe8966b56b6..ea0a6f3778bd 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -26,10 +26,13 @@
/*
- Define a buffer size for the initial command that detects the flash device:
- STATUS, READID and PARAM. The largest of these is the PARAM command,
- needing 256 bytes.
- STATUS, READID and PARAM.
- ONFI param page is 256 bytes, and there are three redundant copies
- to be read. JEDEC param page is 512 bytes, and there are also three
- redundant copies to be read.
*/
- Hence this buffer should be at least 512 x 3. Let's pick 2048.
-#define INIT_BUFFER_SIZE 256 +#define INIT_BUFFER_SIZE 2048
/* registers and bit definitions */ #define NDCR (0x00) /* Control register */ @@ -838,14 +841,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break;
case NAND_CMD_PARAM:
info->buf_count = 256;
info->buf_count = INIT_BUFFER_SIZE; info->ndcb0 |= NDCB0_CMD_TYPE(0) | NDCB0_ADDR_CYC(1) | NDCB0_LEN_OVRD | command; info->ndcb1 = (column & 0xFF);
info->ndcb3 = 256;
info->data_size = 256;
info->ndcb3 = INIT_BUFFER_SIZE;
info->data_size = INIT_BUFFER_SIZE; break; case NAND_CMD_READID:
@@ -1468,6 +1471,7 @@ KEEP_CONFIG: host->row_addr_cycles = 3; else host->row_addr_cycles = 2;
Spurious change. I suggest to drop it.
Will do.
return nand_scan_tail(mtd);
}
-- 2.10.0.479.g7c56b16
-- Ezequiel García, VanguardiaSur www.vanguardiasur.com.ar

On 20 October 2016 at 17:14, Chris Packham judge.packham@gmail.com wrote:
On Fri, Oct 21, 2016 at 1:58 AM, Ezequiel Garcia ezequiel@vanguardiasur.com.ar wrote:
On 20 October 2016 at 01:31, Chris Packham judge.packham@gmail.com wrote:
The initial buffer is used for the initial commands used to detect a flash device (STATUS, READID and PARAM).
ONFI param page is 256 bytes, and there are three redundant copies to be read. JEDEC param page is 512 bytes, and there are also three redundant copies to be read. Hence this buffer should be at least 512 x 3. This commits rounds the buffer size to 2048.
Hey Chris,
So you are basically picking the commit and commit log from Linux:
http://lists.infradead.org/pipermail/linux-mtd/2015-August/060721.html
Shouldn't you mention that somewhere?
Indeed. I mentioned it here http://lists.denx.de/pipermail/u-boot/2016-October/270605.html and that was the intent of the Cc line below.
Oh, I see. IMO, that won't do it. See, the mail is going to get lost in the internet sea, but the commit log is going to be set on git stone and pass to generations to come.
I'm not really about taking credit, but rather about creating a persistent reference between code you've copy-pasted. This will be useful, e.g. in case it turns out it's wrong, which is perfectly possible. If someone notices it's wrong in U-Boot code, it might git-blame-it and so from there realise the change is needed in Linux too.
Just a silly example, but I'm trying to make a point on why it's useful to link copy-pasted implementations.
Mentioning in the cover letter is probably great, but don't forget the actual patch serves a different purpose.
I should probably set the From: line to the original author and call out the Linux commit sha1. I wasn't sure of the usual u-boot practice, I could equally squash these all together and say "sync with linux".
I'm not sure that matters, as long as you state where is the code coming from.
Cc: Ezequiel Garcia ezequiel@vanguardiasur.com.ar Signed-off-by: Chris Packham judge.packham@gmail.com
drivers/mtd/nand/pxa3xx_nand.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dfe8966b56b6..ea0a6f3778bd 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -26,10 +26,13 @@
/*
- Define a buffer size for the initial command that detects the flash device:
- STATUS, READID and PARAM. The largest of these is the PARAM command,
- needing 256 bytes.
- STATUS, READID and PARAM.
- ONFI param page is 256 bytes, and there are three redundant copies
- to be read. JEDEC param page is 512 bytes, and there are also three
- redundant copies to be read.
*/
- Hence this buffer should be at least 512 x 3. Let's pick 2048.
-#define INIT_BUFFER_SIZE 256 +#define INIT_BUFFER_SIZE 2048
/* registers and bit definitions */ #define NDCR (0x00) /* Control register */ @@ -838,14 +841,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break;
case NAND_CMD_PARAM:
info->buf_count = 256;
info->buf_count = INIT_BUFFER_SIZE; info->ndcb0 |= NDCB0_CMD_TYPE(0) | NDCB0_ADDR_CYC(1) | NDCB0_LEN_OVRD | command; info->ndcb1 = (column & 0xFF);
info->ndcb3 = 256;
info->data_size = 256;
info->ndcb3 = INIT_BUFFER_SIZE;
info->data_size = INIT_BUFFER_SIZE; break; case NAND_CMD_READID:
@@ -1468,6 +1471,7 @@ KEEP_CONFIG: host->row_addr_cycles = 3; else host->row_addr_cycles = 2;
Spurious change. I suggest to drop it.
Will do.
return nand_scan_tail(mtd);
}
-- 2.10.0.479.g7c56b16
-- Ezequiel García, VanguardiaSur www.vanguardiasur.com.ar

Don't store struct mtd_info in struct pxa3xx_nand_host. Instead use the one that is already part of struct nand_chip. This brings us in line with current U-boot and Linux conventions.
Cc: Boris BREZILLON boris.brezillon@free-electrons.com Signed-off-by: Chris Packham judge.packham@gmail.com
---
drivers/mtd/nand/pxa3xx_nand.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ea0a6f3778bd..a7a488260a71 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -147,7 +147,6 @@ enum pxa3xx_nand_variant {
struct pxa3xx_nand_host { struct nand_chip chip; - struct mtd_info *mtd; void *info_data;
/* page size of attached chip */ @@ -380,16 +379,17 @@ static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host) struct nand_chip *chip = &host->chip; struct pxa3xx_nand_info *info = host->info_data; const struct pxa3xx_nand_flash *f = NULL; + struct mtd_info *mtd = nand_to_mtd(&host->chip); int mode, id, ntypes, i;
mode = onfi_get_async_timing_mode(chip); if (mode == ONFI_TIMING_MODE_UNKNOWN) { ntypes = ARRAY_SIZE(builtin_flash_types);
- chip->cmdfunc(host->mtd, NAND_CMD_READID, 0x00, -1); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
- id = chip->read_byte(host->mtd); - id |= chip->read_byte(host->mtd) << 0x8; + id = chip->read_byte(mtd); + id |= chip->read_byte(mtd) << 0x8;
for (i = 0; i < ntypes; i++) { f = &builtin_flash_types[i]; @@ -682,7 +682,7 @@ static void set_command_address(struct pxa3xx_nand_info *info, static void prepare_start_command(struct pxa3xx_nand_info *info, int command) { struct pxa3xx_nand_host *host = info->host[info->cs]; - struct mtd_info *mtd = host->mtd; + struct mtd_info *mtd = nand_to_mtd(&host->chip);
/* reset data and oob column point to handle data */ info->buf_start = 0; @@ -733,7 +733,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, struct mtd_info *mtd;
host = info->host[info->cs]; - mtd = host->mtd; + mtd = nand_to_mtd(&host->chip); addr_cycle = 0; exec_cmd = 1;
@@ -1220,7 +1220,7 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info) { struct pxa3xx_nand_host *host = info->host[info->cs]; - struct mtd_info *mtd = host->mtd; + struct mtd_info *mtd = nand_to_mtd(&host->chip); struct nand_chip *chip = mtd_to_nand(mtd);
info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; @@ -1272,7 +1272,7 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host) const struct nand_sdr_timings *timings; int ret;
- mtd = info->host[info->cs]->mtd; + mtd = nand_to_mtd(&info->host[info->cs]->chip); chip = mtd_to_nand(mtd);
/* configure default flash values */ @@ -1494,7 +1494,6 @@ static int alloc_nand_resource(struct pxa3xx_nand_info *info) mtd = nand_to_mtd(chip); host = (struct pxa3xx_nand_host *)chip; info->host[cs] = host; - host->mtd = mtd; host->cs = cs; host->info_data = info; host->read_id_bytes = 4; @@ -1569,7 +1568,7 @@ static int pxa3xx_nand_probe(struct pxa3xx_nand_info *info)
probe_success = 0; for (cs = 0; cs < pdata->num_cs; cs++) { - struct mtd_info *mtd = info->host[cs]->mtd; + struct mtd_info *mtd = nand_to_mtd(&info->host[cs]->chip);
/* * The mtd name matches the one used in 'mtdparts' kernel

Since the pxa3xx_nand driver was added there has been a discrepancy in pxa3xx_nand_set_sdr_timing() around the setting of tWP_min and tRP_min. This brings us into line with the current Linux code.
Cc: Antoine Ténart antoine.tenart@free-electrons.com Signed-off-by: Chris Packham judge.packham@gmail.com ---
drivers/mtd/nand/pxa3xx_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index a7a488260a71..784b7585687f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -345,9 +345,9 @@ static void pxa3xx_nand_set_sdr_timing(struct pxa3xx_nand_host *host, u32 tCH_min = DIV_ROUND_UP(t->tCH_min, 1000); u32 tCS_min = DIV_ROUND_UP(t->tCS_min, 1000); u32 tWH_min = DIV_ROUND_UP(t->tWH_min, 1000); - u32 tWP_min = DIV_ROUND_UP(t->tWC_min - tWH_min, 1000); + u32 tWP_min = DIV_ROUND_UP(t->tWC_min - t->tWH_min, 1000); u32 tREH_min = DIV_ROUND_UP(t->tREH_min, 1000); - u32 tRP_min = DIV_ROUND_UP(t->tRC_min - tREH_min, 1000); + u32 tRP_min = DIV_ROUND_UP(t->tRC_min - t->tREH_min, 1000); u32 tR = chip->chip_delay * 1000; u32 tWHR_min = DIV_ROUND_UP(t->tWHR_min, 1000); u32 tAR_min = DIV_ROUND_UP(t->tAR_min, 1000);

When the nand is first probe, and upon the first command start, the status bits should be cleared before the interrupts are unmasked.
Cc: Robert Jarzmik robert.jarzmik@free.fr Signed-off-by: Chris Packham judge.packham@gmail.com ---
drivers/mtd/nand/pxa3xx_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 784b7585687f..3c065169ce9c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -477,8 +477,8 @@ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) ndcr |= NDCR_ND_RUN;
/* clear status bits and run */ - nand_writel(info, NDCR, 0); nand_writel(info, NDSR, NDSR_MASK); + nand_writel(info, NDCR, 0); nand_writel(info, NDCR, ndcr); }

When 2 commands are submitted in a row, and the second is very quick, the completion of the second command might never come. This happens especially if the second command is quick, such as a status read after an erase
Cc: Robert Jarzmik robert.jarzmik@free.fr Signed-off-by: Chris Packham judge.packham@gmail.com ---
drivers/mtd/nand/pxa3xx_nand.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 3c065169ce9c..9a43bd11a453 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -619,8 +619,14 @@ static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info) is_ready = 1; }
+ /* + * Clear all status bit before issuing the next command, which + * can and will alter the status bits and will deserve a new + * interrupt on its own. This lets the controller exit the IRQ + */ + nand_writel(info, NDSR, status); + if (status & NDSR_WRCMDREQ) { - nand_writel(info, NDSR, NDSR_WRCMDREQ); status &= ~NDSR_WRCMDREQ; info->state = STATE_CMD_HANDLE;
@@ -641,8 +647,6 @@ static irqreturn_t pxa3xx_nand_irq(struct pxa3xx_nand_info *info) nand_writel(info, NDCB0, info->ndcb3); }
- /* clear NDSR to let the controller exit the IRQ */ - nand_writel(info, NDSR, status); if (is_completed) info->cmd_complete = 1; if (is_ready)

This commit is needed to properly support the 8-bits ECC configuration with 4KB pages.
When pages larger than 2 KB are used on platforms using the PXA3xx NAND controller, the reading/programming operations need to be split in chunks of 2 KBs or less because the controller FIFO is limited to about 2 KB (i.e a bit more than 2 KB to accommodate OOB data). Due to this requirement, the data layout on NAND is a bit strange, with ECC interleaved with data, at the end of each chunk.
When a 4-bits ECC configuration is used with 4 KB pages, the physical data layout on the NAND looks like this:
| 2048 data | 32 spare | 30 ECC | 2048 data | 32 spare | 30 ECC |
So the data chunks have an equal size, 2080 bytes for each chunk, which the driver supports properly.
When a 8-bits ECC configuration is used with 4KB pages, the physical data layout on the NAND looks like this:
| 1024 data | 30 ECC | 1024 data | 30 ECC | 1024 data | 30 ECC | 1024 data | 30 ECC | 64 spare | 30 ECC |
So, the spare area is stored in its own chunk, which has a different size than the other chunks. Since OOB is not used by UBIFS, the initial implementation of the driver has chosen to not support reading this additional "spare" chunk of data.
Unfortunately, Marvell has chosen to store the BBT signature in the OOB area. Therefore, if the driver doesn't read this spare area, Linux has no way of finding the BBT. It thinks there is no BBT, and rewrites one, which U-Boot does not recognize, causing compatibility problems between the bootloader and the kernel in terms of NAND usage.
To fix this, this commit implements the support for reading a partial last chunk. This support is currently only useful for the case of 8 bits ECC with 4 KB pages, but it will be useful in the future to enable other configurations such as 12 bits and 16 bits ECC with 4 KB pages, or 8 bits ECC with 8 KB pages, etc. All those configurations have a "last" chunk that doesn't have the same size as the other chunks.
In order to implement reading of the last chunk, this commit:
- Adds a number of new fields to the pxa3xx_nand_info to describe how many full chunks and how many chunks we have, the size of full chunks and partial chunks, both in terms of data area and spare area.
- Fills in the step_chunk_size and step_spare_size variables to describe how much data and spare should be read/written for the current read/program step.
- Reworks the state machine to accommodate doing the additional read or program step when a last partial chunk is used.
Cc: Thomas Petazzoni thomas.petazzoni@free-electrons.com Signed-off-by: Chris Packham judge.packham@gmail.com ---
drivers/mtd/nand/pxa3xx_nand.c | 154 ++++++++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 55 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 9a43bd11a453..4bf6c53c541f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -193,15 +193,44 @@ struct pxa3xx_nand_info { int use_spare; /* use spare ? */ int need_wait;
- unsigned int data_size; /* data to be read from FIFO */ - unsigned int chunk_size; /* split commands chunk size */ - unsigned int oob_size; + /* Amount of real data per full chunk */ + unsigned int chunk_size; + + /* Amount of spare data per full chunk */ unsigned int spare_size; + + /* Number of full chunks (i.e chunk_size + spare_size) */ + unsigned int nfullchunks; + + /* + * Total number of chunks. If equal to nfullchunks, then there + * are only full chunks. Otherwise, there is one last chunk of + * size (last_chunk_size + last_spare_size) + */ + unsigned int ntotalchunks; + + /* Amount of real data in the last chunk */ + unsigned int last_chunk_size; + + /* Amount of spare data in the last chunk */ + unsigned int last_spare_size; + unsigned int ecc_size; unsigned int ecc_err_cnt; unsigned int max_bitflips; int retcode;
+ /* + * Variables only valid during command + * execution. step_chunk_size and step_spare_size is the + * amount of real data and spare data in the current + * chunk. cur_chunk is the current chunk being + * read/programmed. + */ + unsigned int step_chunk_size; + unsigned int step_spare_size; + unsigned int cur_chunk; + /* cached register value */ uint32_t reg_ndcr; uint32_t ndtr0cs0; @@ -426,25 +455,6 @@ static int pxa3xx_nand_init_timings(struct pxa3xx_nand_host *host) return 0; }
-/* - * Set the data and OOB size, depending on the selected - * spare and ECC configuration. - * Only applicable to READ0, READOOB and PAGEPROG commands. - */ -static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info, - struct mtd_info *mtd) -{ - int oob_enable = info->reg_ndcr & NDCR_SPARE_EN; - - info->data_size = mtd->writesize; - if (!oob_enable) - return; - - info->oob_size = info->spare_size; - if (!info->use_ecc) - info->oob_size += info->ecc_size; -} - /** * NOTE: it is a must to set ND_RUN first, then write * command buffer, otherwise, it does not work. @@ -525,39 +535,38 @@ static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
static void handle_data_pio(struct pxa3xx_nand_info *info) { - unsigned int do_bytes = min(info->data_size, info->chunk_size); - switch (info->state) { case STATE_PIO_WRITING: - writesl(info->mmio_base + NDDB, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(do_bytes, 4)); + if (info->step_chunk_size) + writesl(info->mmio_base + NDDB, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(info->step_chunk_size, 4));
- if (info->oob_size > 0) + if (info->step_spare_size) writesl(info->mmio_base + NDDB, info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->oob_size, 4)); + DIV_ROUND_UP(info->step_spare_size, 4)); break; case STATE_PIO_READING: - drain_fifo(info, - info->data_buff + info->data_buff_pos, - DIV_ROUND_UP(do_bytes, 4)); + if (info->step_chunk_size) + drain_fifo(info, + info->data_buff + info->data_buff_pos, + DIV_ROUND_UP(info->step_chunk_size, 4));
- if (info->oob_size > 0) + if (info->step_spare_size) drain_fifo(info, info->oob_buff + info->oob_buff_pos, - DIV_ROUND_UP(info->oob_size, 4)); + DIV_ROUND_UP(info->step_spare_size, 4)); break; default: dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__, - info->state); + info->state); BUG(); }
/* Update buffer pointers for multi-page read/write */ - info->data_buff_pos += do_bytes; - info->oob_buff_pos += info->oob_size; - info->data_size -= do_bytes; + info->data_buff_pos += info->step_chunk_size; + info->oob_buff_pos += info->step_spare_size; }
static void pxa3xx_nand_irq_thread(struct pxa3xx_nand_info *info) @@ -691,9 +700,11 @@ static void prepare_start_command(struct pxa3xx_nand_info *info, int command) /* reset data and oob column point to handle data */ info->buf_start = 0; info->buf_count = 0; - info->oob_size = 0; info->data_buff_pos = 0; info->oob_buff_pos = 0; + info->step_chunk_size = 0; + info->step_spare_size = 0; + info->cur_chunk = 0; info->use_ecc = 0; info->use_spare = 1; info->retcode = ERR_NONE; @@ -705,8 +716,6 @@ static void prepare_start_command(struct pxa3xx_nand_info *info, int command) case NAND_CMD_READ0: case NAND_CMD_PAGEPROG: info->use_ecc = 1; - case NAND_CMD_READOOB: - pxa3xx_set_datasize(info, mtd); break; case NAND_CMD_PARAM: info->use_spare = 0; @@ -763,6 +772,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, if (command == NAND_CMD_READOOB) info->buf_start += mtd->writesize;
+ if (info->cur_chunk < info->nfullchunks) { + info->step_chunk_size = info->chunk_size; + info->step_spare_size = info->spare_size; + } else { + info->step_chunk_size = info->last_chunk_size; + info->step_spare_size = info->last_spare_size; + } + /* * Multiple page read needs an 'extended command type' field, * which is either naked-read or last-read according to the @@ -774,8 +791,8 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8) | NDCB0_LEN_OVRD | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->chunk_size + - info->oob_size; + info->ndcb3 = info->step_chunk_size + + info->step_spare_size; }
set_command_address(info, mtd->writesize, column, page_addr); @@ -795,8 +812,6 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, | NDCB0_EXT_CMD_TYPE(ext_cmd_type) | addr_cycle | command; - /* No data transfer in this case */ - info->data_size = 0; exec_cmd = 1; } break; @@ -808,6 +823,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break; }
+ if (info->cur_chunk < info->nfullchunks) { + info->step_chunk_size = info->chunk_size; + info->step_spare_size = info->spare_size; + } else { + info->step_chunk_size = info->last_chunk_size; + info->step_spare_size = info->last_spare_size; + } + /* Second command setting for large pages */ if (mtd->writesize > PAGE_CHUNK_SIZE) { /* @@ -818,14 +841,14 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, info->ndcb0 |= NDCB0_CMD_TYPE(0x1) | NDCB0_LEN_OVRD | NDCB0_EXT_CMD_TYPE(ext_cmd_type); - info->ndcb3 = info->chunk_size + - info->oob_size; + info->ndcb3 = info->step_chunk_size + + info->step_spare_size;
/* * This is the command dispatch that completes a chunked * page program operation. */ - if (info->data_size == 0) { + if (info->cur_chunk == info->ntotalchunks) { info->ndcb0 = NDCB0_CMD_TYPE(0x1) | NDCB0_EXT_CMD_TYPE(ext_cmd_type) | command; @@ -852,7 +875,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, | command; info->ndcb1 = (column & 0xFF); info->ndcb3 = INIT_BUFFER_SIZE; - info->data_size = INIT_BUFFER_SIZE; + info->step_chunk_size = INIT_BUFFER_SIZE; break;
case NAND_CMD_READID: @@ -862,7 +885,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, | command; info->ndcb1 = (column & 0xFF);
- info->data_size = 8; + info->step_chunk_size = 8; break; case NAND_CMD_STATUS: info->buf_count = 1; @@ -870,7 +893,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, | NDCB0_ADDR_CYC(1) | command;
- info->data_size = 8; + info->step_chunk_size = 8; break;
case NAND_CMD_ERASE1: @@ -1054,22 +1077,31 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd, } }
+ /* Only a few commands need several steps */ + if (command != NAND_CMD_PAGEPROG && + command != NAND_CMD_READ0 && + command != NAND_CMD_READOOB) + break; + + info->cur_chunk++; + /* Check if the sequence is complete */ - if (info->data_size == 0 && command != NAND_CMD_PAGEPROG) + if (info->cur_chunk == info->ntotalchunks && + command != NAND_CMD_PAGEPROG) break;
/* * After a splitted program command sequence has issued * the command dispatch, the command sequence is complete. */ - if (info->data_size == 0 && + if (info->cur_chunk == (info->ntotalchunks + 1) && command == NAND_CMD_PAGEPROG && ext_cmd_type == EXT_CMD_TYPE_DISPATCH) break;
if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB) { /* Last read: issue a 'last naked read' */ - if (info->data_size == info->chunk_size) + if (info->cur_chunk == info->ntotalchunks - 1) ext_cmd_type = EXT_CMD_TYPE_LAST_RW; else ext_cmd_type = EXT_CMD_TYPE_NAKED_RW; @@ -1079,7 +1111,7 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd, * the command dispatch must be issued to complete. */ } else if (command == NAND_CMD_PAGEPROG && - info->data_size == 0) { + info->cur_chunk == info->ntotalchunks) { ext_cmd_type = EXT_CMD_TYPE_DISPATCH; } } while (1); @@ -1305,6 +1337,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, int strength, int ecc_stepsize, int page_size) { if (strength == 1 && ecc_stepsize == 512 && page_size == 2048) { + info->nfullchunks = 1; + info->ntotalchunks = 1; info->chunk_size = 2048; info->spare_size = 40; info->ecc_size = 24; @@ -1313,6 +1347,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, ecc->strength = 1;
} else if (strength == 1 && ecc_stepsize == 512 && page_size == 512) { + info->nfullchunks = 1; + info->ntotalchunks = 1; info->chunk_size = 512; info->spare_size = 8; info->ecc_size = 8; @@ -1326,6 +1362,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, */ } else if (strength == 4 && ecc_stepsize == 512 && page_size == 2048) { info->ecc_bch = 1; + info->nfullchunks = 1; + info->ntotalchunks = 1; info->chunk_size = 2048; info->spare_size = 32; info->ecc_size = 32; @@ -1336,6 +1374,8 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info,
} else if (strength == 4 && ecc_stepsize == 512 && page_size == 4096) { info->ecc_bch = 1; + info->nfullchunks = 2; + info->ntotalchunks = 2; info->chunk_size = 2048; info->spare_size = 32; info->ecc_size = 32; @@ -1350,8 +1390,12 @@ static int pxa_ecc_init(struct pxa3xx_nand_info *info, */ } else if (strength == 8 && ecc_stepsize == 512 && page_size == 4096) { info->ecc_bch = 1; + info->nfullchunks = 4; + info->ntotalchunks = 5; info->chunk_size = 1024; info->spare_size = 0; + info->last_chunk_size = 0; + info->last_spare_size = 64; info->ecc_size = 32; ecc->mode = NAND_ECC_HW; ecc->size = info->chunk_size;
participants (2)
-
Chris Packham
-
Ezequiel Garcia