[U-Boot] [RFC PATCH v3 00/10] pxa3xx_nand updates

From: Ofer Heifetz oferh@marvell.com
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. This gives me enough so I can mount and read files out of a ubifs volume created by Linux on the DB-88F6820-AMC board but I don't have access to any other Armada boards to test with.
Where applicable I'll Cc the author of the equivalent Linux patch. I've had to tweak a few of them to make them fit u-boot (e.g. to handle platform data) but they should still look pretty close to their Linux counterparts.
Note this series is dependent on http://lists.denx.de/pipermail/u-boot/2016-October/270967.html
Changes in v3: - tested this patchset on armada-7040-db-D using 4bit and 8bit ECC
Ofer Heifetz (10): 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 nand: pxa3xx: Increase READ_ID buffer and make the size static mtd: pxa3xx_nand: Increase the initial chunk size mtd: pxa3xx_nand: Fix initial controller configuration mtd: pxa3xx_nand: Simplify pxa3xx_nand_scan mtd: nand: pxa3xx_nand: add support for partial chunks
drivers/mtd/nand/pxa3xx_nand.c | 294 ++++++++++++++++++++++++----------------- 1 file changed, 175 insertions(+), 119 deletions(-)

From: Ofer Heifetz oferh@marvell.com
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.
[ Linux commit c16340973fcb6461474a9f811f7f3ff2f946b24c ]
Cc: Ezequiel Garcia <ezequiel at vanguardiasur.com.ar> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.com --- drivers/mtd/nand/pxa3xx_nand.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index a3ca337..c4505f1 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -29,10 +29,13 @@ DECLARE_GLOBAL_DATA_PTR;
/* * 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 */ @@ -843,14 +846,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:

Hi Ofer,
On Wed, Dec 6, 2017 at 7:55 PM, oferh@marvell.com wrote:
From: Ofer Heifetz oferh@marvell.com
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.
[ Linux commit c16340973fcb6461474a9f811f7f3ff2f946b24c ]
Cc: Ezequiel Garcia <ezequiel at vanguardiasur.com.ar> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.com
Thanks for testing these. Since I sent the initial series I hadn't heard anything back (the nand maintainer seems to be inactive).
Did you manage to rebase this series against u-boot#master? I've just got my hands on a DB-88F78460-AMC board I was planning on porting it to upstream u-boot when I find some time. It would be another good test platform for these changes.

Hi Chris
-----Original Message----- From: Chris Packham [mailto:judge.packham@gmail.com] Sent: Wednesday, December 06, 2017 9:49 AM To: Ofer Heifetz oferh@marvell.com; Scott Wood oss@buserror.net; Scott Wood scottwood@freescale.com; Tom Rini trini@konsulko.com Cc: u-boot u-boot@lists.denx.de; Nadav Haklai nadavh@marvell.com; Stefan Roese sr@denx.de; Ezequiel Garcia ezequiel@vanguardiasur.com.ar Subject: Re: [RFC PATCH v3 01/10] mtd: nand: pxa3xx_nand: Increase initial buffer size
Hi Ofer,
On Wed, Dec 6, 2017 at 7:55 PM, oferh@marvell.com wrote:
From: Ofer Heifetz oferh@marvell.com
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.
[ Linux commit c16340973fcb6461474a9f811f7f3ff2f946b24c ]
Cc: Ezequiel Garcia <ezequiel at vanguardiasur.com.ar> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.com
Thanks for testing these. Since I sent the initial series I hadn't heard anything back (the nand maintainer seems to be inactive).
Did you manage to rebase this series against u-boot#master? I've just got my hands on a DB-88F78460-AMC board I was planning on porting it to upstream u-boot when I find some time. It would be another good test platform for these changes.
I have worked on u-boot 2017.03 branch for testing this patchset, without this 8bit ECC Failed to boot ubifs and bootrom gave ECC errors, as I see it the patch from Linux originated by Thomas Is mandatory to allow 8bit ECC to work.

From: Ofer Heifetz oferh@marvell.com
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.
Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.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 c4505f1..0eb6abb 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -150,7 +150,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 */ @@ -385,16 +384,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]; @@ -687,7 +687,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; @@ -738,7 +738,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;
@@ -1225,7 +1225,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; @@ -1277,7 +1277,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 */ @@ -1498,7 +1498,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; @@ -1616,7 +1615,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

From: Ofer Heifetz oferh@marvell.com
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.
Signed-off-by: Chris Packham <judge.packham at gmail.com> Tested-by: Ofer Heifetz oferh@marvell.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 0eb6abb..3feaf0c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -350,9 +350,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);

From: Ofer Heifetz oferh@marvell.com
When the nand is first probe, and upon the first command start, the status bits should be cleared before the interrupts are unmasked.
[ Linux commit 0b14392db2e998157d924085d7913e537ec26121 ]
Cc: Robert Jarzmik <robert.jarzmik at free.fr> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.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 3feaf0c..5d37f22 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -482,8 +482,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); }

From: Ofer Heifetz oferh@marvell.com
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
[ Linux commit 21fc0ef9652f0c809dc0d3e0a67f1e1bf6ff8255 ]
Cc: Robert Jarzmik <robert.jarzmik at free.fr> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.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 5d37f22..d83efea 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -624,8 +624,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;
@@ -646,8 +652,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)

From: Ofer Heifetz oferh@marvell.com
The read ID count should be made as large as the maximum READ_ID size, so there's no need to have dynamic size. This commit sets the hardware maximum read ID count, which should be more than enough on all cases. Also, we get rid of the read_id_bytes, and use a macro instead.
[ Linux commit b226eca2088004622434cbcc27c6401b64f22d7c]
Cc: Ezequiel García <ezequiel at vanguardiasur.com.ar> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.com --- drivers/mtd/nand/pxa3xx_nand.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index d83efea..86e6c41 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -113,6 +113,13 @@ DECLARE_GLOBAL_DATA_PTR; #define EXT_CMD_TYPE_LAST_RW 1 /* Last naked read/write */ #define EXT_CMD_TYPE_MONO 0 /* Monolithic read/write */
+/* + * This should be large enough to read 'ONFI' and 'JEDEC'. + * Let's use 7 bytes, which is the maximum ID count supported + * by the controller (see NDCR_RD_ID_CNT_MASK). + */ +#define READ_ID_BYTES 7 + /* macros for registers read/write */ #define nand_writel(info, off, val) \ writel((val), (info)->mmio_base + (off)) @@ -159,8 +166,6 @@ struct pxa3xx_nand_host { /* calculated from pxa3xx_nand_flash data */ unsigned int col_addr_cycles; unsigned int row_addr_cycles; - size_t read_id_bytes; - };
struct pxa3xx_nand_info { @@ -861,7 +866,7 @@ static int prepare_set_command(struct pxa3xx_nand_info *info, int command, break;
case NAND_CMD_READID: - info->buf_count = host->read_id_bytes; + info->buf_count = READ_ID_BYTES; info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1) | command; @@ -1241,23 +1246,10 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info)
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) { - /* - * We set 0 by hard coding here, for we don't support keep_config - * when there is more than one chip attached to the controller - */ - struct pxa3xx_nand_host *host = info->host[0]; uint32_t ndcr = nand_readl(info, NDCR);
- if (ndcr & NDCR_PAGE_SZ) { - /* Controller's FIFO size */ - info->chunk_size = 2048; - host->read_id_bytes = 4; - } else { - info->chunk_size = 512; - host->read_id_bytes = 2; - } - /* Set an initial chunk size */ + info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; info->reg_ndcr = ndcr & ~NDCR_INT_MASK; info->ndtr0cs0 = nand_readl(info, NDTR0CS0); info->ndtr1cs0 = nand_readl(info, NDTR1CS0); @@ -1287,7 +1279,7 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_host *host) /* configure default flash values */ info->reg_ndcr = 0x0; /* enable all interrupts */ info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - info->reg_ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes); + info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); info->reg_ndcr |= NDCR_SPARE_EN; /* enable spare by default */
/* use the common timing to make a try */ @@ -1504,7 +1496,6 @@ static int alloc_nand_resource(struct pxa3xx_nand_info *info) info->host[cs] = host; host->cs = cs; host->info_data = info; - host->read_id_bytes = 4; mtd->owner = THIS_MODULE;
nand_set_controller_data(chip, host);

From: Ofer Heifetz oferh@marvell.com
The chunk size represents the size of the data chunks, which is used by the controllers that allow to split transfered data.
However, the initial chunk size is used in a non-splitted way, during device identification. Therefore, it must be large enough for all the NAND commands issued during device identification. This includes NAND_CMD_PARAM which was recently changed to transfer up to 2048 bytes (for the redundant parameter pages).
Thus, the initial chunk size should be 2048 as well.
On Armada 370/XP platforms (NFCv2) booted without the keep-config devicetree property, this commit fixes a timeout on the NAND_CMD_PARAM command:
[..] pxa3xx-nand f10d0000.nand: This platform can't do DMA on this device pxa3xx-nand f10d0000.nand: Wait time out!!! nand: device found, Manufacturer ID: 0x2c, Chip ID: 0x38 nand: Micron MT29F8G08ABABAWP nand: 1024 MiB, SLC, erase size: 512 KiB, page size: 4096, OOB size: 224
[ Linux commit c7f00c29aa846b00c70bc99ddb6b1cc7e17c47d4 ]
Cc: Ezequiel García <ezequiel at vanguardiasur.com.ar> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.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 86e6c41..70aff59 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1377,7 +1377,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) goto KEEP_CONFIG;
/* Set a default chunk size */ - info->chunk_size = 512; + info->chunk_size = PAGE_CHUNK_SIZE;
ret = pxa3xx_nand_sensing(host); if (ret) {

From: Ofer Heifetz oferh@marvell.com
The Data Flash Control Register (NDCR) contains two types of parameters: those that are needed for device identification, and those that can only be set after device identification.
Therefore, the driver can't set them all at once and instead needs to configure the first group before nand_scan_ident() and the second group later.
Let's split pxa3xx_nand_config in two halves, and set the parameters that depend on the device geometry once this is known.
[ Linux commit 66e8e47eae658dc884e65695a597fdda7a109448 ]
Cc: Ezequiel Garcia <ezequiel at vanguardiasur.com.ar> Signed-off-by: Chris Packham <judge.packham at gmail.com> Tested-by: Ofer Heifetz oferh@marvell.com --- drivers/mtd/nand/pxa3xx_nand.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 70aff59..93fa0c9 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -62,7 +62,8 @@ DECLARE_GLOBAL_DATA_PTR; #define NDCR_ND_MODE (0x3 << 21) #define NDCR_NAND_MODE (0x0) #define NDCR_CLR_PG_CNT (0x1 << 20) -#define NDCR_STOP_ON_UNCOR (0x1 << 19) +#define NFCV1_NDCR_ARB_CNTL (0x1 << 19) +#define NFCV2_NDCR_STOP_ON_UNCOR (0x1 << 19) #define NDCR_RD_ID_CNT_MASK (0x7 << 16) #define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
@@ -1231,26 +1232,41 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) return NAND_STATUS_READY; }
-static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info) +static int pxa3xx_nand_config_ident(struct pxa3xx_nand_info *info) +{ + struct pxa3xx_nand_platform_data *pdata = info->pdata; + + /* Configure default flash values */ + info->chunk_size = PAGE_CHUNK_SIZE; + info->reg_ndcr = 0x0; /* enable all interrupts */ + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + info->reg_ndcr |= NDCR_RD_ID_CNT(READ_ID_BYTES); + info->reg_ndcr |= NDCR_SPARE_EN; + + return 0; +} + +static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) { struct pxa3xx_nand_host *host = info->host[info->cs]; - struct mtd_info *mtd = nand_to_mtd(&host->chip); + struct mtd_info *mtd = nand_to_mtd(&info->host[info->cs]->chip); struct nand_chip *chip = mtd_to_nand(mtd);
info->reg_ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0; info->reg_ndcr |= (chip->page_shift == 6) ? NDCR_PG_PER_BLK : 0; info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; - - return 0; }
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) { + struct pxa3xx_nand_platform_data *pdata = info->pdata; uint32_t ndcr = nand_readl(info, NDCR);
/* Set an initial chunk size */ info->chunk_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; - info->reg_ndcr = ndcr & ~NDCR_INT_MASK; + info->reg_ndcr = ndcr & + ~(NDCR_INT_MASK | NDCR_ND_ARB_EN | NFCV1_NDCR_ARB_CNTL); + info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; info->ndtr0cs0 = nand_readl(info, NDTR0CS0); info->ndtr1cs0 = nand_readl(info, NDTR1CS0); return 0; @@ -1376,8 +1392,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) goto KEEP_CONFIG;
- /* Set a default chunk size */ - info->chunk_size = PAGE_CHUNK_SIZE; + ret = pxa3xx_nand_config_ident(info); + if (ret) + return ret;
ret = pxa3xx_nand_sensing(host); if (ret) { @@ -1404,10 +1421,6 @@ KEEP_CONFIG: } }
- ret = pxa3xx_nand_config_flash(info); - if (ret) - return ret; - #ifdef CONFIG_SYS_NAND_USE_FLASH_BBT /* * We'll use a bad block table stored in-flash and don't @@ -1472,6 +1485,10 @@ KEEP_CONFIG: host->row_addr_cycles = 3; else host->row_addr_cycles = 2; + + if (!pdata->keep_config) + pxa3xx_nand_config_tail(info); + return nand_scan_tail(mtd); }

From: Ofer Heifetz oferh@marvell.com
This commit simplifies the initial configuration performed by pxa3xx_nand_scan. No functionality change is intended.
[ Linux commit 154f50fbde539c20bbf74854461d932ebdace4d5 ]
Cc: Ezequiel García <ezequiel at vanguardiasur.com.ar> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.com --- drivers/mtd/nand/pxa3xx_nand.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 93fa0c9..5f6094f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1257,7 +1257,7 @@ static void pxa3xx_nand_config_tail(struct pxa3xx_nand_info *info) info->reg_ndcr |= (mtd->writesize == 2048) ? NDCR_PAGE_SZ : 0; }
-static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) +static void pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) { struct pxa3xx_nand_platform_data *pdata = info->pdata; uint32_t ndcr = nand_readl(info, NDCR); @@ -1269,7 +1269,6 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) info->reg_ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; info->ndtr0cs0 = nand_readl(info, NDTR0CS0); info->ndtr1cs0 = nand_readl(info, NDTR1CS0); - return 0; }
static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) @@ -1389,22 +1388,21 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) int ret; uint16_t ecc_strength, ecc_step;
- if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) - goto KEEP_CONFIG; - - ret = pxa3xx_nand_config_ident(info); - if (ret) - return ret; - - ret = pxa3xx_nand_sensing(host); - if (ret) { - dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", - info->cs); - - return ret; + if (pdata->keep_config) { + pxa3xx_nand_detect_config(info); + } else { + ret = pxa3xx_nand_config_ident(info); + if (ret) + return ret; + ret = pxa3xx_nand_sensing(host); + if (ret) { + dev_info(&info->pdev->dev, + "There is no chip on cs %d!\n", + info->cs); + return ret; + } }
-KEEP_CONFIG: /* Device detection must be done with ECC disabled */ if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) nand_writel(info, NDECCCTRL, 0x0);

From: Ofer Heifetz oferh@marvell.com
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.
[ Linux commit c2cdace755b583bae540a9979bff1aa428181b8c ]
Cc: Thomas Petazzoni <thomas.petazzoni at free-electrons.com> Signed-off-by: Chris Packham <judge.packham at gmail.com> Reviewed-by: Ofer Heifetz oferh@marvell.com Tested-by: Ofer Heifetz oferh@marvell.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 5f6094f..1f88687 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -202,15 +202,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; @@ -437,25 +466,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. @@ -536,39 +546,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) @@ -702,9 +711,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; @@ -716,8 +727,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; @@ -774,6 +783,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 @@ -785,8 +802,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); @@ -806,8 +823,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; @@ -819,6 +834,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) { /* @@ -829,14 +852,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; @@ -863,7 +886,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: @@ -873,7 +896,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; @@ -881,7 +904,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: @@ -1065,22 +1088,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; @@ -1090,7 +1122,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); @@ -1317,6 +1349,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; @@ -1325,6 +1359,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; @@ -1338,6 +1374,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; @@ -1348,6 +1386,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; @@ -1362,8 +1402,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;

these patches were sent on mistake and should be ignored, I will rework this patchset and send again later
-----Original Message----- From: oferh@marvell.com [mailto:oferh@marvell.com] Sent: Wednesday, December 06, 2017 8:56 AM To: u-boot@lists.denx.de Cc: Nadav Haklai nadavh@marvell.com; sr@denx.de; Ofer Heifetz oferh@marvell.com Subject: [RFC PATCH v3 00/10] pxa3xx_nand updates
From: Ofer Heifetz oferh@marvell.com
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. This gives me enough so I can mount and read files out of a ubifs volume created by Linux on the DB- 88F6820-AMC board but I don't have access to any other Armada boards to test with.
Where applicable I'll Cc the author of the equivalent Linux patch. I've had to tweak a few of them to make them fit u-boot (e.g. to handle platform data) but they should still look pretty close to their Linux counterparts.
Note this series is dependent on http://lists.denx.de/pipermail/u-boot/2016-October/270967.html
Changes in v3:
- tested this patchset on armada-7040-db-D using 4bit and 8bit ECC
Ofer Heifetz (10): 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 nand: pxa3xx: Increase READ_ID buffer and make the size static mtd: pxa3xx_nand: Increase the initial chunk size mtd: pxa3xx_nand: Fix initial controller configuration mtd: pxa3xx_nand: Simplify pxa3xx_nand_scan mtd: nand: pxa3xx_nand: add support for partial chunks
drivers/mtd/nand/pxa3xx_nand.c | 294 ++++++++++++++++++++++++------
1 file changed, 175 insertions(+), 119 deletions(-)
-- 1.9.1
participants (3)
-
Chris Packham
-
Ofer Heifetz
-
oferh@marvell.com