Message ID | 1283179285-6539-2-git-send-email-haojian.zhuang@marvell.com |
---|---|
State | New, archived |
Headers | show |
> + STATE_PAGE_DONE = (1 << 3), > + STATE_CMD_DONE = (1 << 4), > + STATE_READY = (1 << 5), > + STATE_CMD_PREPARED = (1 << 6), > + STATE_IS_WRITE = (1 << 7), State isn't supposed to be a bit-or'ed value, if you have to use this in the code, it just means the logic is broken. You should check it again. > }; > > struct pxa3xx_nand_info { > @@ -292,7 +296,47 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) > } > } > > -static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, > +/** > + * NOTE: it is a must to set ND_RUN firstly, then write > + * command buffer, otherwise, it does not work. > + * We enable all the interrupt at the same time, and > + * let pxa3xx_nand_irq to handle all logic. > + */ > +static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) > +{ > + uint32_t ndcr; > + > + ndcr = info->reg_ndcr; > + ndcr |= NDCR_ECC_EN; > + ndcr |= info->use_dma ? NDCR_DMA_EN : NDCR_STOP_ON_UNCOR; > + ndcr |= NDCR_ND_RUN; > + > + /* clear status bits and run */ > + nand_writel(info, NDCR, 0); > + nand_writel(info, NDSR, NDSR_MASK); > + nand_writel(info, NDCR, ndcr); > +} > + > +static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) > +{ > + uint32_t ndcr; > + int timeout = NAND_STOP_DELAY; > + > + /* wait RUN bit in NDCR become 0 */ > + do { > + /* clear status bits */ > + nand_writel(info, NDSR, NDSR_MASK); Why clearing status bits every time before reading NDCR, does it have some impact to the NDCR_ND_RUN bit? > + ndcr = nand_readl(info, NDCR); > + udelay(1); Check the NDCR_ND_RUN bit before udelay() would be better, that would leave a chance that udelay() will not be called at all if the bit is already cleared (which is most likely the case in my understanding). > + } while ((ndcr & NDCR_ND_RUN) && (timeout -- > 0)); > + > + if (timeout <= 0) { > + ndcr &= ~(NDCR_ND_RUN); Feel picky, but no parentheses are needed here for a single constant. > + nand_writel(info, NDCR, ndcr); > + } > +} > + > +static void prepare_read_prog_cmd(struct pxa3xx_nand_info *info, > uint16_t cmd, int column, int page_addr) > { > const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; > @@ -319,21 +363,18 @@ static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, > > if (cmd == cmdset->program) > info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; > - > - return 0; > } > > -static int prepare_erase_cmd(struct pxa3xx_nand_info *info, > +static void prepare_erase_cmd(struct pxa3xx_nand_info *info, > uint16_t cmd, int page_addr) > { > info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); > info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); > info->ndcb1 = page_addr; > info->ndcb2 = 0; > - return 0; > } > > -static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) > +static void prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) > { > const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; > > @@ -351,10 +392,7 @@ static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) > } else if (cmd == cmdset->reset || cmd == cmdset->lock || > cmd == cmdset->unlock) { > info->ndcb0 |= NDCB0_CMD_TYPE(5); > - } else > - return -EINVAL; > - > - return 0; > + } > } If we want to remove the return type here, we must make sure that no exception cases will happen, e.g. the *info and cmd passed to these functions are absolute correct. > > static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) > @@ -402,41 +440,22 @@ static int write_cmd(struct pxa3xx_nand_info *info) > return 0; > } > > -static int handle_data_pio(struct pxa3xx_nand_info *info) > +static void handle_data_pio(struct pxa3xx_nand_info *info) > { > - int ret, timeout = CHIP_DELAY_TIMEOUT; > - > - switch (info->state) { > - case STATE_PIO_WRITING: > + if (info->state & STATE_IS_WRITE) { As mentioned, this isn't a correct state encoding. Taking an example, a state here can never be STATE_IS_WRITE | STATE_IS_READING. Try avoid using states like this. > __raw_writesl(info->mmio_base + NDDB, info->data_buff, > DIV_ROUND_UP(info->data_size, 4)); > if (info->oob_size > 0) > __raw_writesl(info->mmio_base + NDDB, info->oob_buff, > DIV_ROUND_UP(info->oob_size, 4)); > - > - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); > - > - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); > - if (!ret) { > - printk(KERN_ERR "program command time out\n"); > - return -1; > - } > - break; > - case STATE_PIO_READING: > + } > + else { > __raw_readsl(info->mmio_base + NDDB, info->data_buff, > DIV_ROUND_UP(info->data_size, 4)); > if (info->oob_size > 0) > __raw_readsl(info->mmio_base + NDDB, info->oob_buff, > DIV_ROUND_UP(info->oob_size, 4)); > - break; > - default: > - printk(KERN_ERR "%s: invalid state %d\n", __func__, > - info->state); > - return -EINVAL; > } > - > - info->state = STATE_READY; > - return 0; > } > > static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) > @@ -472,93 +491,59 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data) > > if (dcsr & DCSR_BUSERR) { > info->retcode = ERR_DMABUSERR; > - complete(&info->cmd_complete); > } > > - if (info->state == STATE_DMA_WRITING) { > - info->state = STATE_DMA_DONE; > - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); > - } else { > - info->state = STATE_READY; > - complete(&info->cmd_complete); > - } > + enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); > } > > static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) > { > struct pxa3xx_nand_info *info = devid; > - unsigned int status; > + unsigned int status, is_completed = 0; > > status = nand_readl(info, NDSR); > > - if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { > - if (status & NDSR_DBERR) > - info->retcode = ERR_DBERR; > - else if (status & NDSR_SBERR) > - info->retcode = ERR_SBERR; > - > - disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); > + if (status & NDSR_DBERR) > + info->retcode = ERR_DBERR; > + if (status & NDSR_SBERR) > + info->retcode = ERR_SBERR; > + if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { > > + info->state |= STATE_DATA_PROCESSING; > + /* whether use dma to transfer data */ > if (info->use_dma) { > - info->state = STATE_DMA_READING; > + disable_int(info, NDSR_WRDREQ); > start_data_dma(info, 0); > - } else { > - info->state = STATE_PIO_READING; > - complete(&info->cmd_complete); > - } > - } else if (status & NDSR_WRDREQ) { > - disable_int(info, NDSR_WRDREQ); > - if (info->use_dma) { > - info->state = STATE_DMA_WRITING; > - start_data_dma(info, 1); > - } else { > - info->state = STATE_PIO_WRITING; > - complete(&info->cmd_complete); > - } > - } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { > - if (status & NDSR_CS0_BBD) > - info->retcode = ERR_BBERR; > + goto NORMAL_IRQ_EXIT; > + } else > + handle_data_pio(info); > > - disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); > - info->state = STATE_READY; > - complete(&info->cmd_complete); > + info->state |= STATE_DATA_DONE; > } > - nand_writel(info, NDSR, status); > - return IRQ_HANDLED; > -} > - > -static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) > -{ > - uint32_t ndcr; > - int ret, timeout = CHIP_DELAY_TIMEOUT; > - > - if (write_cmd(info)) { > - info->retcode = ERR_SENDCMD; > - goto fail_stop; > + if (status & NDSR_CS0_CMDD) { > + info->state |= STATE_CMD_DONE; > + is_completed = 1; > } > - > - info->state = STATE_CMD_HANDLE; > - > - enable_int(info, event); > - > - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); > - if (!ret) { > - printk(KERN_ERR "command execution timed out\n"); > - info->retcode = ERR_SENDCMD; > - goto fail_stop; > + if (status & NDSR_FLASH_RDY) > + info->state |= STATE_READY; > + if (status & NDSR_CS0_PAGED) > + info->state |= STATE_PAGE_DONE; > + > + if (status & NDSR_WRCMDREQ) { > + nand_writel(info, NDSR, NDSR_WRCMDREQ); > + status &= ~NDSR_WRCMDREQ; > + info->state |= STATE_CMD_WAIT_DONE; > + nand_writel(info, NDCB0, info->ndcb0); > + nand_writel(info, NDCB0, info->ndcb1); > + nand_writel(info, NDCB0, info->ndcb2); > } > > - if (info->use_dma == 0 && info->data_size > 0) > - if (handle_data_pio(info)) > - goto fail_stop; > - > - return 0; > - > -fail_stop: > - ndcr = nand_readl(info, NDCR); > - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); > - udelay(10); > - return -ETIMEDOUT; > + /* clear NDSR to let the controller exit the IRQ */ > + nand_writel(info, NDSR, status); > + if (is_completed) > + complete(&info->cmd_complete); > +NORMAL_IRQ_EXIT: > + return IRQ_HANDLED; > } > > static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) > @@ -580,14 +565,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, > { > struct pxa3xx_nand_info *info = mtd->priv; > const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; > - int ret; > + int ret, exec_cmd = 0; > > info->use_dma = (use_dma) ? 1 : 0; > info->use_ecc = 0; > info->data_size = 0; > - info->state = STATE_READY; > - > - init_completion(&info->cmd_complete); > + info->state = 0; > > switch (command) { > case NAND_CMD_READOOB: > @@ -596,14 +579,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, > info->buf_start = mtd->writesize + column; > memset(info->data_buff, 0xFF, info->buf_count); > > - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) > - break; > - > - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); > - > + prepare_read_prog_cmd(info, cmdset->read1, column, page_addr); > /* We only are OOB, so if the data has error, does not matter */ > if (info->retcode == ERR_DBERR) > info->retcode = ERR_NONE; > + > + exec_cmd = 1; > break; > > case NAND_CMD_READ0: > @@ -613,11 +594,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, > info->buf_count = mtd->writesize + mtd->oobsize; > memset(info->data_buff, 0xFF, info->buf_count); > > - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) > - break; > - > - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); > - > + prepare_read_prog_cmd(info, cmdset->read1, column, page_addr); > if (info->retcode == ERR_DBERR) { > /* for blank page (all 0xff), HW will calculate its ECC as > * 0, which is different from the ECC information within > @@ -626,6 +603,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, > if (is_buf_blank(info->data_buff, mtd->writesize)) > info->retcode = ERR_NONE; > } > + > + exec_cmd = 1; > break; > case NAND_CMD_SEQIN: > info->buf_start = column; > @@ -639,17 +618,14 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, > case NAND_CMD_PAGEPROG: > info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; > > - if (prepare_read_prog_cmd(info, cmdset->program, > - info->seqin_column, info->seqin_page_addr)) > - break; > - > - pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); > + prepare_read_prog_cmd(info, cmdset->program, > + info->seqin_column, info->seqin_page_addr); > + info->state |= STATE_IS_WRITE; > + exec_cmd = 1; > break; > case NAND_CMD_ERASE1: > - if (prepare_erase_cmd(info, cmdset->erase, page_addr)) > - break; > - > - pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); > + prepare_erase_cmd(info, cmdset->erase, page_addr); > + exec_cmd = 1; > break; > case NAND_CMD_ERASE2: > break; > @@ -660,39 +636,37 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, > info->buf_count = (command == NAND_CMD_READID) ? > info->read_id_bytes : 1; > > - if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? > - cmdset->read_id : cmdset->read_status)) > - break; > - > - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); > + prepare_other_cmd(info, (command == NAND_CMD_READID) ? > + cmdset->read_id : cmdset->read_status); > + exec_cmd = 1; > break; > case NAND_CMD_RESET: > - if (prepare_other_cmd(info, cmdset->reset)) > - break; > - > - ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); > - if (ret == 0) { > - int timeout = 2; > - uint32_t ndcr; > - > - while (timeout--) { > - if (nand_readl(info, NDSR) & NDSR_RDY) > - break; > - msleep(10); > - } > - > - ndcr = nand_readl(info, NDCR); > - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); > - } > + prepare_other_cmd(info, cmdset->reset); > + exec_cmd = 1; > break; > default: > printk(KERN_ERR "non-supported command.\n"); > break; > } > > - if (info->retcode == ERR_DBERR) { > - printk(KERN_ERR "double bit error @ page %08x\n", page_addr); > - info->retcode = ERR_NONE; > + if (exec_cmd) { > + init_completion(&info->cmd_complete); > + info->state |= STATE_CMD_PREPARED; > + pxa3xx_nand_start(info); > + > + ret = wait_for_completion_timeout(&info->cmd_complete, > + CHIP_DELAY_TIMEOUT); > + if (!ret) { > + printk(KERN_ERR "Wait time out!!!\n"); > + } > + /* Stop State Machine for next command cycle */ > + pxa3xx_nand_stop(info); > + disable_int(info, NDCR_INT_MASK); > + info->state &= ~STATE_CMD_PREPARED; > + if (info->retcode == ERR_DBERR) { > + printk(KERN_ERR "double bit error @ page %08x\n", page_addr); > + info->retcode = ERR_NONE; > + } > } > } > > @@ -807,10 +781,7 @@ static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) > uint32_t ndcr; > uint8_t id_buff[8]; > > - if (prepare_other_cmd(info, cmdset->read_id)) { > - printk(KERN_ERR "failed to prepare command\n"); > - return -EINVAL; > - } > + prepare_other_cmd(info, cmdset->read_id); > > /* Send command */ > if (write_cmd(info)) > @@ -836,7 +807,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, > { > struct platform_device *pdev = info->pdev; > struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; > - uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ > + uint32_t ndcr = 0x0; /* enable all interrupts */ > > if (f->page_size != 2048 && f->page_size != 512) > return -EINVAL; > @@ -887,11 +858,12 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) > info->read_id_bytes = (info->page_size == 2048) ? 4 : 2; > info->reg_ndcr = ndcr; > > - if (__readid(info, &id)) > + pxa3xx_nand_cmdfunc(info->mtd, NAND_CMD_READID, 0, 0); > + id = *((uint16_t *)(info->data_buff)); > + if (id == 0) > return -ENODEV; > > /* Lookup the flash id */ > - id = (id >> 8) & 0xff; /* device id is byte 2 */ > for (i = 0; nand_flash_ids[i].name != NULL; i++) { > if (id == nand_flash_ids[i].id) { > type = &nand_flash_ids[i]; > @@ -935,8 +907,8 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, > /* we use default timing to detect id */ > f = DEFAULT_FLASH_TYPE; > pxa3xx_nand_config_flash(info, f); > - if (__readid(info, &id)) > - goto fail_detect; > + pxa3xx_nand_cmdfunc(info->mtd, NAND_CMD_READID, 0, 0); > + id = *((uint16_t *)(info->data_buff)); > > for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) { > /* we first choose the flash definition from platfrom */ > @@ -954,7 +926,6 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, > dev_warn(&info->pdev->dev, > "failed to detect configured nand flash; found %04x instead of\n", > id); > -fail_detect: > return -ENODEV; > } > > @@ -1176,8 +1147,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) > > platform_set_drvdata(pdev, NULL); > > - del_mtd_device(mtd); > - del_mtd_partitions(mtd); > irq = platform_get_irq(pdev, 0); > if (irq >= 0) > free_irq(irq, info); > @@ -1195,7 +1164,11 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) > clk_disable(info->clk); > clk_put(info->clk); > > - kfree(mtd); > + if (mtd) { > + del_mtd_device(mtd); > + del_mtd_partitions(mtd); > + kfree(mtd); > + } > return 0; > } > > @@ -1242,7 +1215,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) > struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); > struct mtd_info *mtd = info->mtd; > > - if (info->state != STATE_READY) { > + if (info->state & STATE_CMD_PREPARED) { > dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); > return -EAGAIN; > } > -- > 1.7.0.4 > >
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 3468a3d..d4aeb5f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -27,6 +27,7 @@ #include <plat/pxa3xx_nand.h> #define CHIP_DELAY_TIMEOUT (2 * HZ/10) +#define NAND_STOP_DELAY (2 * HZ/50) /* registers and bit definitions */ #define NDCR (0x00) /* Control register */ @@ -52,16 +53,18 @@ #define NDCR_ND_MODE (0x3 << 21) #define NDCR_NAND_MODE (0x0) #define NDCR_CLR_PG_CNT (0x1 << 20) -#define NDCR_CLR_ECC (0x1 << 19) +#define 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) #define NDCR_RA_START (0x1 << 15) #define NDCR_PG_PER_BLK (0x1 << 14) #define NDCR_ND_ARB_EN (0x1 << 12) +#define NDCR_INT_MASK (0xFFF) #define NDSR_MASK (0xfff) -#define NDSR_RDY (0x1 << 11) +#define NDSR_RDY (0x1 << 12) +#define NDSR_FLASH_RDY (0x1 << 11) #define NDSR_CS0_PAGED (0x1 << 10) #define NDSR_CS1_PAGED (0x1 << 9) #define NDSR_CS0_CMDD (0x1 << 8) @@ -104,13 +107,14 @@ enum { }; enum { - STATE_READY = 0, - STATE_CMD_HANDLE, - STATE_DMA_READING, - STATE_DMA_WRITING, - STATE_DMA_DONE, - STATE_PIO_READING, - STATE_PIO_WRITING, + STATE_CMD_WAIT_DONE = 1, + STATE_DATA_PROCESSING = (1 << 1), + STATE_DATA_DONE = (1 << 2), + STATE_PAGE_DONE = (1 << 3), + STATE_CMD_DONE = (1 << 4), + STATE_READY = (1 << 5), + STATE_CMD_PREPARED = (1 << 6), + STATE_IS_WRITE = (1 << 7), }; struct pxa3xx_nand_info { @@ -292,7 +296,47 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) } } -static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, +/** + * NOTE: it is a must to set ND_RUN firstly, then write + * command buffer, otherwise, it does not work. + * We enable all the interrupt at the same time, and + * let pxa3xx_nand_irq to handle all logic. + */ +static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + + ndcr = info->reg_ndcr; + ndcr |= NDCR_ECC_EN; + ndcr |= info->use_dma ? NDCR_DMA_EN : NDCR_STOP_ON_UNCOR; + ndcr |= NDCR_ND_RUN; + + /* clear status bits and run */ + nand_writel(info, NDCR, 0); + nand_writel(info, NDSR, NDSR_MASK); + nand_writel(info, NDCR, ndcr); +} + +static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr; + int timeout = NAND_STOP_DELAY; + + /* wait RUN bit in NDCR become 0 */ + do { + /* clear status bits */ + nand_writel(info, NDSR, NDSR_MASK); + ndcr = nand_readl(info, NDCR); + udelay(1); + } while ((ndcr & NDCR_ND_RUN) && (timeout -- > 0)); + + if (timeout <= 0) { + ndcr &= ~(NDCR_ND_RUN); + nand_writel(info, NDCR, ndcr); + } +} + +static void prepare_read_prog_cmd(struct pxa3xx_nand_info *info, uint16_t cmd, int column, int page_addr) { const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; @@ -319,21 +363,18 @@ static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, if (cmd == cmdset->program) info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; - - return 0; } -static int prepare_erase_cmd(struct pxa3xx_nand_info *info, +static void prepare_erase_cmd(struct pxa3xx_nand_info *info, uint16_t cmd, int page_addr) { info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); info->ndcb1 = page_addr; info->ndcb2 = 0; - return 0; } -static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) +static void prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) { const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; @@ -351,10 +392,7 @@ static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) } else if (cmd == cmdset->reset || cmd == cmdset->lock || cmd == cmdset->unlock) { info->ndcb0 |= NDCB0_CMD_TYPE(5); - } else - return -EINVAL; - - return 0; + } } static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) @@ -402,41 +440,22 @@ static int write_cmd(struct pxa3xx_nand_info *info) return 0; } -static int handle_data_pio(struct pxa3xx_nand_info *info) +static void handle_data_pio(struct pxa3xx_nand_info *info) { - int ret, timeout = CHIP_DELAY_TIMEOUT; - - switch (info->state) { - case STATE_PIO_WRITING: + if (info->state & STATE_IS_WRITE) { __raw_writesl(info->mmio_base + NDDB, info->data_buff, DIV_ROUND_UP(info->data_size, 4)); if (info->oob_size > 0) __raw_writesl(info->mmio_base + NDDB, info->oob_buff, DIV_ROUND_UP(info->oob_size, 4)); - - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); - if (!ret) { - printk(KERN_ERR "program command time out\n"); - return -1; - } - break; - case STATE_PIO_READING: + } + else { __raw_readsl(info->mmio_base + NDDB, info->data_buff, DIV_ROUND_UP(info->data_size, 4)); if (info->oob_size > 0) __raw_readsl(info->mmio_base + NDDB, info->oob_buff, DIV_ROUND_UP(info->oob_size, 4)); - break; - default: - printk(KERN_ERR "%s: invalid state %d\n", __func__, - info->state); - return -EINVAL; } - - info->state = STATE_READY; - return 0; } static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) @@ -472,93 +491,59 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data) if (dcsr & DCSR_BUSERR) { info->retcode = ERR_DMABUSERR; - complete(&info->cmd_complete); } - if (info->state == STATE_DMA_WRITING) { - info->state = STATE_DMA_DONE; - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - } else { - info->state = STATE_READY; - complete(&info->cmd_complete); - } + enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); } static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) { struct pxa3xx_nand_info *info = devid; - unsigned int status; + unsigned int status, is_completed = 0; status = nand_readl(info, NDSR); - if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { - if (status & NDSR_DBERR) - info->retcode = ERR_DBERR; - else if (status & NDSR_SBERR) - info->retcode = ERR_SBERR; - - disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); + if (status & NDSR_DBERR) + info->retcode = ERR_DBERR; + if (status & NDSR_SBERR) + info->retcode = ERR_SBERR; + if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { + info->state |= STATE_DATA_PROCESSING; + /* whether use dma to transfer data */ if (info->use_dma) { - info->state = STATE_DMA_READING; + disable_int(info, NDSR_WRDREQ); start_data_dma(info, 0); - } else { - info->state = STATE_PIO_READING; - complete(&info->cmd_complete); - } - } else if (status & NDSR_WRDREQ) { - disable_int(info, NDSR_WRDREQ); - if (info->use_dma) { - info->state = STATE_DMA_WRITING; - start_data_dma(info, 1); - } else { - info->state = STATE_PIO_WRITING; - complete(&info->cmd_complete); - } - } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { - if (status & NDSR_CS0_BBD) - info->retcode = ERR_BBERR; + goto NORMAL_IRQ_EXIT; + } else + handle_data_pio(info); - disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - info->state = STATE_READY; - complete(&info->cmd_complete); + info->state |= STATE_DATA_DONE; } - nand_writel(info, NDSR, status); - return IRQ_HANDLED; -} - -static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) -{ - uint32_t ndcr; - int ret, timeout = CHIP_DELAY_TIMEOUT; - - if (write_cmd(info)) { - info->retcode = ERR_SENDCMD; - goto fail_stop; + if (status & NDSR_CS0_CMDD) { + info->state |= STATE_CMD_DONE; + is_completed = 1; } - - info->state = STATE_CMD_HANDLE; - - enable_int(info, event); - - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); - if (!ret) { - printk(KERN_ERR "command execution timed out\n"); - info->retcode = ERR_SENDCMD; - goto fail_stop; + if (status & NDSR_FLASH_RDY) + info->state |= STATE_READY; + if (status & NDSR_CS0_PAGED) + info->state |= STATE_PAGE_DONE; + + if (status & NDSR_WRCMDREQ) { + nand_writel(info, NDSR, NDSR_WRCMDREQ); + status &= ~NDSR_WRCMDREQ; + info->state |= STATE_CMD_WAIT_DONE; + nand_writel(info, NDCB0, info->ndcb0); + nand_writel(info, NDCB0, info->ndcb1); + nand_writel(info, NDCB0, info->ndcb2); } - if (info->use_dma == 0 && info->data_size > 0) - if (handle_data_pio(info)) - goto fail_stop; - - return 0; - -fail_stop: - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); - udelay(10); - return -ETIMEDOUT; + /* clear NDSR to let the controller exit the IRQ */ + nand_writel(info, NDSR, status); + if (is_completed) + complete(&info->cmd_complete); +NORMAL_IRQ_EXIT: + return IRQ_HANDLED; } static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) @@ -580,14 +565,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, { struct pxa3xx_nand_info *info = mtd->priv; const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; - int ret; + int ret, exec_cmd = 0; info->use_dma = (use_dma) ? 1 : 0; info->use_ecc = 0; info->data_size = 0; - info->state = STATE_READY; - - init_completion(&info->cmd_complete); + info->state = 0; switch (command) { case NAND_CMD_READOOB: @@ -596,14 +579,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, info->buf_start = mtd->writesize + column; memset(info->data_buff, 0xFF, info->buf_count); - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) - break; - - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); - + prepare_read_prog_cmd(info, cmdset->read1, column, page_addr); /* We only are OOB, so if the data has error, does not matter */ if (info->retcode == ERR_DBERR) info->retcode = ERR_NONE; + + exec_cmd = 1; break; case NAND_CMD_READ0: @@ -613,11 +594,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, info->buf_count = mtd->writesize + mtd->oobsize; memset(info->data_buff, 0xFF, info->buf_count); - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) - break; - - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); - + prepare_read_prog_cmd(info, cmdset->read1, column, page_addr); if (info->retcode == ERR_DBERR) { /* for blank page (all 0xff), HW will calculate its ECC as * 0, which is different from the ECC information within @@ -626,6 +603,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, if (is_buf_blank(info->data_buff, mtd->writesize)) info->retcode = ERR_NONE; } + + exec_cmd = 1; break; case NAND_CMD_SEQIN: info->buf_start = column; @@ -639,17 +618,14 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, case NAND_CMD_PAGEPROG: info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; - if (prepare_read_prog_cmd(info, cmdset->program, - info->seqin_column, info->seqin_page_addr)) - break; - - pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); + prepare_read_prog_cmd(info, cmdset->program, + info->seqin_column, info->seqin_page_addr); + info->state |= STATE_IS_WRITE; + exec_cmd = 1; break; case NAND_CMD_ERASE1: - if (prepare_erase_cmd(info, cmdset->erase, page_addr)) - break; - - pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + prepare_erase_cmd(info, cmdset->erase, page_addr); + exec_cmd = 1; break; case NAND_CMD_ERASE2: break; @@ -660,39 +636,37 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, info->buf_count = (command == NAND_CMD_READID) ? info->read_id_bytes : 1; - if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? - cmdset->read_id : cmdset->read_status)) - break; - - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); + prepare_other_cmd(info, (command == NAND_CMD_READID) ? + cmdset->read_id : cmdset->read_status); + exec_cmd = 1; break; case NAND_CMD_RESET: - if (prepare_other_cmd(info, cmdset->reset)) - break; - - ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); - if (ret == 0) { - int timeout = 2; - uint32_t ndcr; - - while (timeout--) { - if (nand_readl(info, NDSR) & NDSR_RDY) - break; - msleep(10); - } - - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); - } + prepare_other_cmd(info, cmdset->reset); + exec_cmd = 1; break; default: printk(KERN_ERR "non-supported command.\n"); break; } - if (info->retcode == ERR_DBERR) { - printk(KERN_ERR "double bit error @ page %08x\n", page_addr); - info->retcode = ERR_NONE; + if (exec_cmd) { + init_completion(&info->cmd_complete); + info->state |= STATE_CMD_PREPARED; + pxa3xx_nand_start(info); + + ret = wait_for_completion_timeout(&info->cmd_complete, + CHIP_DELAY_TIMEOUT); + if (!ret) { + printk(KERN_ERR "Wait time out!!!\n"); + } + /* Stop State Machine for next command cycle */ + pxa3xx_nand_stop(info); + disable_int(info, NDCR_INT_MASK); + info->state &= ~STATE_CMD_PREPARED; + if (info->retcode == ERR_DBERR) { + printk(KERN_ERR "double bit error @ page %08x\n", page_addr); + info->retcode = ERR_NONE; + } } } @@ -807,10 +781,7 @@ static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) uint32_t ndcr; uint8_t id_buff[8]; - if (prepare_other_cmd(info, cmdset->read_id)) { - printk(KERN_ERR "failed to prepare command\n"); - return -EINVAL; - } + prepare_other_cmd(info, cmdset->read_id); /* Send command */ if (write_cmd(info)) @@ -836,7 +807,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, { struct platform_device *pdev = info->pdev; struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; - uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ + uint32_t ndcr = 0x0; /* enable all interrupts */ if (f->page_size != 2048 && f->page_size != 512) return -EINVAL; @@ -887,11 +858,12 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) info->read_id_bytes = (info->page_size == 2048) ? 4 : 2; info->reg_ndcr = ndcr; - if (__readid(info, &id)) + pxa3xx_nand_cmdfunc(info->mtd, NAND_CMD_READID, 0, 0); + id = *((uint16_t *)(info->data_buff)); + if (id == 0) return -ENODEV; /* Lookup the flash id */ - id = (id >> 8) & 0xff; /* device id is byte 2 */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (id == nand_flash_ids[i].id) { type = &nand_flash_ids[i]; @@ -935,8 +907,8 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, /* we use default timing to detect id */ f = DEFAULT_FLASH_TYPE; pxa3xx_nand_config_flash(info, f); - if (__readid(info, &id)) - goto fail_detect; + pxa3xx_nand_cmdfunc(info->mtd, NAND_CMD_READID, 0, 0); + id = *((uint16_t *)(info->data_buff)); for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) { /* we first choose the flash definition from platfrom */ @@ -954,7 +926,6 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, dev_warn(&info->pdev->dev, "failed to detect configured nand flash; found %04x instead of\n", id); -fail_detect: return -ENODEV; } @@ -1176,8 +1147,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - del_mtd_device(mtd); - del_mtd_partitions(mtd); irq = platform_get_irq(pdev, 0); if (irq >= 0) free_irq(irq, info); @@ -1195,7 +1164,11 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) clk_disable(info->clk); clk_put(info->clk); - kfree(mtd); + if (mtd) { + del_mtd_device(mtd); + del_mtd_partitions(mtd); + kfree(mtd); + } return 0; } @@ -1242,7 +1215,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); struct mtd_info *mtd = info->mtd; - if (info->state != STATE_READY) { + if (info->state & STATE_CMD_PREPARED) { dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); return -EAGAIN; }