mvebu: rename recently added flash driver fix
authorFelix Fietkau <nbd@nbd.name>
Sat, 21 May 2016 16:07:12 +0000 (18:07 +0200)
committerFelix Fietkau <nbd@nbd.name>
Sat, 21 May 2016 16:08:29 +0000 (18:08 +0200)
Signed-off-by: Felix Fietkau <nbd@nbd.name>
target/linux/mvebu/patches-4.4/010-mtd-nand-pxa3xx_nand-add-support-for-partial-chunks.patch [deleted file]
target/linux/mvebu/patches-4.4/020-mtd-nand-pxa3xx_nand-add-support-for-partial-chunks.patch [new file with mode: 0644]

diff --git a/target/linux/mvebu/patches-4.4/010-mtd-nand-pxa3xx_nand-add-support-for-partial-chunks.patch b/target/linux/mvebu/patches-4.4/010-mtd-nand-pxa3xx_nand-add-support-for-partial-chunks.patch
deleted file mode 100644 (file)
index 2e67097..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
-From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
-Date: Wed, 10 Feb 2016 14:54:21 +0100
-Subject: [PATCH] mtd: nand: pxa3xx_nand: add support for partial chunks
-
-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.
-
-This commit has been tested on a Marvell Armada 398 DB board, with a
-4KB page NAND, tested in both 4 bits ECC and 8 bits ECC
-configurations. Robert Jarzmik has tested on some PXA platforms.
-
-Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
-Tested-by: Robert Jarzmik <robert.jarzmik@free.fr>
-Acked-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
-
---- a/drivers/mtd/nand/pxa3xx_nand.c
-+++ b/drivers/mtd/nand/pxa3xx_nand.c
-@@ -228,15 +228,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;
-@@ -531,25 +560,6 @@ static int pxa3xx_nand_init(struct pxa3x
-       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 firstly, then write
-  * command buffer, otherwise, it does not work.
-@@ -665,28 +675,28 @@ static void drain_fifo(struct pxa3xx_nan
- 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__,
-@@ -695,9 +705,8 @@ static void handle_data_pio(struct pxa3x
-       }
-       /* 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_data_dma_irq(void *data)
-@@ -738,8 +747,9 @@ static void start_data_dma(struct pxa3xx
-                               info->state);
-               BUG();
-       }
--      info->sg.length = info->data_size +
--              (info->oob_size ? info->spare_size + info->ecc_size : 0);
-+      info->sg.length = info->chunk_size;
-+      if (info->use_spare)
-+              info->sg.length += info->spare_size + info->ecc_size;
-       dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
-       tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction,
-@@ -900,9 +910,11 @@ static void prepare_start_command(struct
-       /* 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;
-@@ -914,8 +926,6 @@ static void prepare_start_command(struct
-       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;
-@@ -974,6 +984,14 @@ static int prepare_set_command(struct px
-               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
-@@ -985,8 +1003,8 @@ static int prepare_set_command(struct px
-                       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);
-@@ -1006,8 +1024,6 @@ static int prepare_set_command(struct px
-                               | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
-                               | addr_cycle
-                               | command;
--                      /* No data transfer in this case */
--                      info->data_size = 0;
-                       exec_cmd = 1;
-               }
-               break;
-@@ -1019,6 +1035,14 @@ static int prepare_set_command(struct px
-                       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) {
-                       /*
-@@ -1029,14 +1053,14 @@ static int prepare_set_command(struct px
-                       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;
-@@ -1063,7 +1087,7 @@ static int prepare_set_command(struct px
-                               | 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:
-@@ -1073,7 +1097,7 @@ static int prepare_set_command(struct px
-                               | command;
-               info->ndcb1 = (column & 0xFF);
--              info->data_size = 8;
-+              info->step_chunk_size = 8;
-               break;
-       case NAND_CMD_STATUS:
-               info->buf_count = 1;
-@@ -1081,7 +1105,7 @@ static int prepare_set_command(struct px
-                               | NDCB0_ADDR_CYC(1)
-                               | command;
--              info->data_size = 8;
-+              info->step_chunk_size = 8;
-               break;
-       case NAND_CMD_ERASE1:
-@@ -1220,6 +1244,7 @@ static void nand_cmdfunc_extended(struct
-       init_completion(&info->dev_ready);
-       do {
-               info->state = STATE_PREPARED;
-+
-               exec_cmd = prepare_set_command(info, command, ext_cmd_type,
-                                              column, page_addr);
-               if (!exec_cmd) {
-@@ -1239,22 +1264,30 @@ static void nand_cmdfunc_extended(struct
-                       break;
-               }
-+              /* 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;
-@@ -1264,7 +1297,7 @@ static void nand_cmdfunc_extended(struct
-                * 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);
-@@ -1514,6 +1547,8 @@ static int pxa_ecc_init(struct pxa3xx_na
-                       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;
-@@ -1522,6 +1557,8 @@ static int pxa_ecc_init(struct pxa3xx_na
-               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;
-@@ -1535,6 +1572,8 @@ static int pxa_ecc_init(struct pxa3xx_na
-        */
-       } 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;
-@@ -1545,6 +1584,8 @@ static int pxa_ecc_init(struct pxa3xx_na
-       } 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;
-@@ -1559,8 +1600,12 @@ static int pxa_ecc_init(struct pxa3xx_na
-        */
-       } 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;
diff --git a/target/linux/mvebu/patches-4.4/020-mtd-nand-pxa3xx_nand-add-support-for-partial-chunks.patch b/target/linux/mvebu/patches-4.4/020-mtd-nand-pxa3xx_nand-add-support-for-partial-chunks.patch
new file mode 100644 (file)
index 0000000..2e67097
--- /dev/null
@@ -0,0 +1,428 @@
+From: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+Date: Wed, 10 Feb 2016 14:54:21 +0100
+Subject: [PATCH] mtd: nand: pxa3xx_nand: add support for partial chunks
+
+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.
+
+This commit has been tested on a Marvell Armada 398 DB board, with a
+4KB page NAND, tested in both 4 bits ECC and 8 bits ECC
+configurations. Robert Jarzmik has tested on some PXA platforms.
+
+Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+Tested-by: Robert Jarzmik <robert.jarzmik@free.fr>
+Acked-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+
+--- a/drivers/mtd/nand/pxa3xx_nand.c
++++ b/drivers/mtd/nand/pxa3xx_nand.c
+@@ -228,15 +228,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;
+@@ -531,25 +560,6 @@ static int pxa3xx_nand_init(struct pxa3x
+       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 firstly, then write
+  * command buffer, otherwise, it does not work.
+@@ -665,28 +675,28 @@ static void drain_fifo(struct pxa3xx_nan
+ 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__,
+@@ -695,9 +705,8 @@ static void handle_data_pio(struct pxa3x
+       }
+       /* 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_data_dma_irq(void *data)
+@@ -738,8 +747,9 @@ static void start_data_dma(struct pxa3xx
+                               info->state);
+               BUG();
+       }
+-      info->sg.length = info->data_size +
+-              (info->oob_size ? info->spare_size + info->ecc_size : 0);
++      info->sg.length = info->chunk_size;
++      if (info->use_spare)
++              info->sg.length += info->spare_size + info->ecc_size;
+       dma_map_sg(info->dma_chan->device->dev, &info->sg, 1, info->dma_dir);
+       tx = dmaengine_prep_slave_sg(info->dma_chan, &info->sg, 1, direction,
+@@ -900,9 +910,11 @@ static void prepare_start_command(struct
+       /* 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;
+@@ -914,8 +926,6 @@ static void prepare_start_command(struct
+       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;
+@@ -974,6 +984,14 @@ static int prepare_set_command(struct px
+               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
+@@ -985,8 +1003,8 @@ static int prepare_set_command(struct px
+                       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);
+@@ -1006,8 +1024,6 @@ static int prepare_set_command(struct px
+                               | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
+                               | addr_cycle
+                               | command;
+-                      /* No data transfer in this case */
+-                      info->data_size = 0;
+                       exec_cmd = 1;
+               }
+               break;
+@@ -1019,6 +1035,14 @@ static int prepare_set_command(struct px
+                       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) {
+                       /*
+@@ -1029,14 +1053,14 @@ static int prepare_set_command(struct px
+                       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;
+@@ -1063,7 +1087,7 @@ static int prepare_set_command(struct px
+                               | 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:
+@@ -1073,7 +1097,7 @@ static int prepare_set_command(struct px
+                               | command;
+               info->ndcb1 = (column & 0xFF);
+-              info->data_size = 8;
++              info->step_chunk_size = 8;
+               break;
+       case NAND_CMD_STATUS:
+               info->buf_count = 1;
+@@ -1081,7 +1105,7 @@ static int prepare_set_command(struct px
+                               | NDCB0_ADDR_CYC(1)
+                               | command;
+-              info->data_size = 8;
++              info->step_chunk_size = 8;
+               break;
+       case NAND_CMD_ERASE1:
+@@ -1220,6 +1244,7 @@ static void nand_cmdfunc_extended(struct
+       init_completion(&info->dev_ready);
+       do {
+               info->state = STATE_PREPARED;
++
+               exec_cmd = prepare_set_command(info, command, ext_cmd_type,
+                                              column, page_addr);
+               if (!exec_cmd) {
+@@ -1239,22 +1264,30 @@ static void nand_cmdfunc_extended(struct
+                       break;
+               }
++              /* 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;
+@@ -1264,7 +1297,7 @@ static void nand_cmdfunc_extended(struct
+                * 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);
+@@ -1514,6 +1547,8 @@ static int pxa_ecc_init(struct pxa3xx_na
+                       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;
+@@ -1522,6 +1557,8 @@ static int pxa_ecc_init(struct pxa3xx_na
+               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;
+@@ -1535,6 +1572,8 @@ static int pxa_ecc_init(struct pxa3xx_na
+        */
+       } 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;
+@@ -1545,6 +1584,8 @@ static int pxa_ecc_init(struct pxa3xx_na
+       } 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;
+@@ -1559,8 +1600,12 @@ static int pxa_ecc_init(struct pxa3xx_na
+        */
+       } 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;