[PATCH 3/6] mtd: atmel_nand: add Nand Flash Controller (NFC) support
Jean-Christophe PLAGNIOL-VILLARD
plagnioj at jcrosoft.com
Mon Jun 10 09:22:11 EDT 2013
> +
> /*
> * Minimal-overhead PIO for data access.
> */
> @@ -1341,6 +1409,9 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
>
> host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
>
> + /* load the nfc driver if there is */
> + of_platform_populate(np, NULL, NULL, host->dev);
what is this?????
> +
> if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
> return 0; /* Not using PMECC */
>
> @@ -1452,6 +1523,238 @@ static int __init atmel_hw_nand_init_params(struct platform_device *pdev,
> return 0;
> }
>
> +/* SMC interrupt service routine */
> +static irqreturn_t hsmc_interrupt(int irq, void *dev_id)
> +{
> + struct atmel_nand_host *host = dev_id;
> + u32 status, mask, pending;
> + irqreturn_t ret = IRQ_HANDLED;
> +
> + status = nfc_readl(host->nfc->hsmc_regs, SR);
> + mask = nfc_readl(host->nfc->hsmc_regs, IMR);
> + pending = status & mask;
> +
> + if (pending & NFC_SR_XFR_DONE) {
> + complete(&host->nfc->comp_nfc);
> + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE);
> + } else if (pending & NFC_SR_RB_EDGE) {
> + complete(&host->nfc->comp_nfc);
> + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE);
> + } else if (pending & NFC_SR_CMD_DONE) {
> + complete(&host->nfc->comp_nfc);
> + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_CMD_DONE);
> + } else {
> + ret = IRQ_NONE;
> + }
> +
> + return ret;
> +}
> +
> +/* NFC(Nand Flash Controller) related functions */
> +static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
> +{
> + unsigned long timeout;
> + init_completion(&host->nfc->comp_nfc);
> +
> + /* Enable interrupt that need to wait for */
> + nfc_writel(host->nfc->hsmc_regs, IER, flag);
> +
> + timeout = wait_for_completion_timeout(&host->nfc->comp_nfc,
> + msecs_to_jiffies(NFC_TIME_OUT_MS));
> + if (timeout)
> + return 0;
> +
> + /* Time out to wait for the interrupt */
> + dev_err(host->dev, "Time out to wait for interrupt: 0x%08x\n", flag);
> + return -ETIMEDOUT;
> +}
> +
> +static int nfc_send_command(struct atmel_nand_host *host,
> + unsigned int cmd, unsigned int addr, unsigned char cycle0)
> +{
> + unsigned long timeout;
> + dev_dbg(host->dev,
> + "nfc_cmd: 0x%08x, addr1234: 0x%08x, cycle0: 0x%02x\n",
> + cmd, addr, cycle0);
> +
> + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS);
> + while (nfc_cmd_readl(NFCADDR_CMD_NFCBUSY, host->nfc->base_cmd_regs)
> + & NFCADDR_CMD_NFCBUSY) {
> + if (time_after(jiffies, timeout)) {
> + dev_err(host->dev,
> + "Time out to wait CMD_NFCBUSY ready!\n");
> + return -ETIMEDOUT;
> + }
> + }
> + nfc_writel(host->nfc->hsmc_regs, CYCLE0, cycle0);
> + nfc_cmd_addr1234_writel(cmd, addr, host->nfc->base_cmd_regs);
> + return nfc_wait_interrupt(host, NFC_SR_CMD_DONE);
> +}
> +
> +static int nfc_device_ready(struct mtd_info *mtd)
> +{
> + struct nand_chip *nand_chip = mtd->priv;
> + struct atmel_nand_host *host = nand_chip->priv;
> + if (!nfc_wait_interrupt(host, NFC_SR_RB_EDGE))
> + return 1;
> + return 0;
> +}
> +
> +static void nfc_select_chip(struct mtd_info *mtd, int chip)
> +{
> + struct nand_chip *nand_chip = mtd->priv;
> + struct atmel_nand_host *host = nand_chip->priv;
> +
> + if (chip == -1)
> + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_DISABLE);
> + else
> + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE);
> +}
> +
> +static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
> + unsigned int *addr1234, unsigned int *cycle0)
> +{
> + struct nand_chip *chip = mtd->priv;
> +
> + int acycle = 0;
> + unsigned char addr_bytes[8];
> + int index = 0, bit_shift;
> +
> + BUG_ON(addr1234 == NULL || cycle0 == NULL);
> +
> + *cycle0 = 0;
> + *addr1234 = 0;
> +
> + if (column != -1) {
> + if (chip->options & NAND_BUSWIDTH_16)
> + column >>= 1;
> + addr_bytes[acycle++] = column & 0xff;
> + if (mtd->writesize > 512)
> + addr_bytes[acycle++] = (column >> 8) & 0xff;
> + }
> +
> + if (page_addr != -1) {
> + addr_bytes[acycle++] = page_addr & 0xff;
> + addr_bytes[acycle++] = (page_addr >> 8) & 0xff;
> + if (chip->chipsize > (128 << 20))
> + addr_bytes[acycle++] = (page_addr >> 16) & 0xff;
> + }
> +
> + if (acycle > 4)
> + *cycle0 = addr_bytes[index++];
> +
> + for (bit_shift = 0; index < acycle; bit_shift += 8)
> + *addr1234 += addr_bytes[index++] << bit_shift;
> +
> + return acycle << 19; /* return acycle in cmd register */
> +}
> +
> +static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
> + int column, int page_addr)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + unsigned long timeout;
> + unsigned int nfc_addr_cmd = 0;
> +
> + unsigned int cmd1 = command << 2;
> +
> + /* Set default settings: no cmd2, no addr cycle. read from nand */
> + unsigned int cmd2 = 0;
> + unsigned int vcmd2 = 0;
> + int acycle = NFCADDR_CMD_ACYCLE_NONE;
> + int csid = NFCADDR_CMD_CSID_3;
> + int dataen = NFCADDR_CMD_DATADIS;
> + int nfcwr = NFCADDR_CMD_NFCRD;
> + unsigned int addr1234 = 0;
> + unsigned int cycle0 = 0;
> + bool do_addr = true;
> +
> + dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n",
> + __func__, command, column, page_addr);
> +
> + switch (command) {
> + case NAND_CMD_RESET:
> + nfc_addr_cmd = cmd1 | acycle | csid | dataen | nfcwr;
> + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
> + udelay(chip->chip_delay);
> +
> + nfc_nand_command(mtd, NAND_CMD_STATUS, -1, -1);
> + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS);
> + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) {
> + if (time_after(jiffies, timeout)) {
> + dev_err(host->dev,
> + "Time out to wait status ready!\n");
> + break;
> + }
> + }
> + return;
> + case NAND_CMD_STATUS:
> + do_addr = false;
> + break;
> + case NAND_CMD_PARAM:
> + case NAND_CMD_READID:
> + do_addr = false;
> + acycle = NFCADDR_CMD_ACYCLE_1;
> + if (column != -1)
> + addr1234 = column;
> + break;
> + case NAND_CMD_RNDOUT:
> + cmd2 = NAND_CMD_RNDOUTSTART << 10;
> + vcmd2 = NFCADDR_CMD_VCMD2;
> + break;
> + case NAND_CMD_READ0:
> + case NAND_CMD_READOOB:
> + if (command == NAND_CMD_READOOB) {
> + column += mtd->writesize;
> + command = NAND_CMD_READ0; /* only READ0 is valid */
> + cmd1 = command << 2;
> + }
> +
> + cmd2 = NAND_CMD_READSTART << 10;
> + vcmd2 = NFCADDR_CMD_VCMD2;
> + break;
> + /* For prgramming command, the cmd need set to write enable */
> + case NAND_CMD_PAGEPROG:
> + case NAND_CMD_SEQIN:
> + case NAND_CMD_RNDIN:
> + nfcwr = NFCADDR_CMD_NFCWR;
> + break;
> + default:
> + break;
> + }
> +
> + if (do_addr)
> + acycle = nfc_make_addr(mtd, column, page_addr, &addr1234,
> + &cycle0);
> +
> + nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
> + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
> +
> + /*
> + * Program and erase have their own busy handlers status, sequential
> + * in, and deplete1 need no delay.
> + */
> + switch (command) {
> + case NAND_CMD_CACHEDPROG:
> + case NAND_CMD_PAGEPROG:
> + case NAND_CMD_ERASE1:
> + case NAND_CMD_ERASE2:
> + case NAND_CMD_RNDIN:
> + case NAND_CMD_STATUS:
> + case NAND_CMD_RNDOUT:
> + case NAND_CMD_SEQIN:
> + case NAND_CMD_READID:
> + return;
> +
> + case NAND_CMD_READ0:
> + /* fall through */
> + default:
> + nfc_wait_interrupt(host, NFC_SR_RB_EDGE);
> + }
> +}
> +
> +static struct platform_driver atmel_nand_nfc_driver;
> /*
> * Probe for the NAND device.
> */
> @@ -1462,7 +1765,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> struct nand_chip *nand_chip;
> struct resource *mem;
> struct mtd_part_parser_data ppdata = {};
> - int res;
> + int res, irq;
> struct pinctrl *pinctrl;
>
> mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> @@ -1478,6 +1781,10 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + res = platform_driver_register(&atmel_nand_nfc_driver);
> + if (res)
> + printk(KERN_ERR "atmel_nand: can't register NFC driver\n");
> +
and this????
> host->io_base = devm_request_and_ioremap(&pdev->dev, mem);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -1505,7 +1812,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> /* Set address of NAND IO lines */
> nand_chip->IO_ADDR_R = host->io_base;
> nand_chip->IO_ADDR_W = host->io_base;
> - nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
>
> pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
> if (IS_ERR(pinctrl)) {
> @@ -1514,44 +1820,34 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> goto err_nand_ioremap;
> }
>
Best Regards,
J.
More information about the linux-mtd
mailing list