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;
        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.
 
 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__,
        }
 
        /* 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)
                                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,
        /* 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;
        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;
                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
                        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);
                                | NDCB0_EXT_CMD_TYPE(ext_cmd_type)
                                | addr_cycle
                                | command;
-                       /* No data transfer in this case */
-                       info->data_size = 0;
                        exec_cmd = 1;
                }
                break;
                        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) {
                        /*
                        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;
                                | 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:
                                | command;
                info->ndcb1 = (column & 0xFF);
 
-               info->data_size = 8;
+               info->step_chunk_size = 8;
                break;
        case NAND_CMD_STATUS:
                info->buf_count = 1;
                                | NDCB0_ADDR_CYC(1)
                                | command;
 
-               info->data_size = 8;
+               info->step_chunk_size = 8;
                break;
 
        case NAND_CMD_ERASE1:
        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) {
                        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;
                 * 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);
                        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;
                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;
         */
        } 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;
 
        } 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;
         */
        } 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;