123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- 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;
|