[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