diff options
Diffstat (limited to 'drivers/mtd/nand/pxa3xx_nand.c')
-rw-r--r-- | drivers/mtd/nand/pxa3xx_nand.c | 191 |
1 files changed, 119 insertions, 72 deletions
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 86fc245dc71a..d6508856da99 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -131,11 +131,23 @@ #define READ_ID_BYTES 7 /* macros for registers read/write */ -#define nand_writel(info, off, val) \ - writel_relaxed((val), (info)->mmio_base + (off)) - -#define nand_readl(info, off) \ - readl_relaxed((info)->mmio_base + (off)) +#define nand_writel(info, off, val) \ + do { \ + dev_vdbg(&info->pdev->dev, \ + "%s():%d nand_writel(0x%x, 0x%04x)\n", \ + __func__, __LINE__, (val), (off)); \ + writel_relaxed((val), (info)->mmio_base + (off)); \ + } while (0) + +#define nand_readl(info, off) \ + ({ \ + unsigned int _v; \ + _v = readl_relaxed((info)->mmio_base + (off)); \ + dev_vdbg(&info->pdev->dev, \ + "%s():%d nand_readl(0x%04x) = 0x%x\n", \ + __func__, __LINE__, (off), _v); \ + _v; \ + }) /* error code and state */ enum { @@ -199,7 +211,6 @@ struct pxa3xx_nand_info { struct dma_chan *dma_chan; dma_cookie_t dma_cookie; int drcmr_dat; - int drcmr_cmd; unsigned char *data_buff; unsigned char *oob_buff; @@ -222,15 +233,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; @@ -526,25 +566,6 @@ static int pxa3xx_nand_init(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 firstly, then write * command buffer, otherwise, it does not work. @@ -660,28 +681,28 @@ 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__, @@ -690,9 +711,8 @@ static void handle_data_pio(struct pxa3xx_nand_info *info) } /* 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) @@ -733,8 +753,9 @@ static void start_data_dma(struct pxa3xx_nand_info *info) 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, @@ -895,9 +916,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; @@ -909,8 +932,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; @@ -969,6 +990,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 @@ -980,8 +1009,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); @@ -1001,8 +1030,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; @@ -1014,6 +1041,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) { /* @@ -1024,14 +1059,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; @@ -1058,7 +1093,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: @@ -1068,7 +1103,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; @@ -1076,7 +1111,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: @@ -1217,6 +1252,7 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd, 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) { @@ -1236,22 +1272,30 @@ static void nand_cmdfunc_extended(struct mtd_info *mtd, 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; @@ -1261,7 +1305,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); @@ -1506,6 +1550,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; @@ -1514,6 +1560,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; @@ -1527,6 +1575,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; @@ -1537,6 +1587,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; @@ -1551,8 +1603,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; @@ -1738,7 +1794,7 @@ static int alloc_nand_resource(struct platform_device *pdev) if (ret < 0) return ret; - if (use_dma) { + if (!np && use_dma) { r = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (r == NULL) { dev_err(&pdev->dev, @@ -1747,15 +1803,6 @@ static int alloc_nand_resource(struct platform_device *pdev) goto fail_disable_clk; } info->drcmr_dat = r->start; - - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r == NULL) { - dev_err(&pdev->dev, - "no resource defined for cmd DMA\n"); - ret = -ENXIO; - goto fail_disable_clk; - } - info->drcmr_cmd = r->start; } irq = platform_get_irq(pdev, 0); |