[PATCH 2/6] pxa3xx_nand: rework irq logic
Eric Miao
eric.y.miao at gmail.com
Thu Sep 16 22:47:28 EDT 2010
> + 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
>
>
More information about the linux-mtd
mailing list