📄 fsl_elbc_nand.c
字号:
return 0;}static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf){ fsl_elbc_read_buf(mtd, buf, mtd->writesize); fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) mtd->ecc_stats.failed++; return 0;}/* ECC will be calculated automatically, and errors will be detected in * waitfunc. */static void fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf){ struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); ctrl->oob_poi = chip->oob_poi;}static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv){ struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct nand_chip *chip = &priv->chip; dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); /* Fill in fsl_elbc_mtd structure */ priv->mtd.priv = chip; priv->mtd.owner = THIS_MODULE; priv->fmr = 0; /* rest filled in later */ /* fill in nand_chip structure */ /* set up function call table */ chip->read_byte = fsl_elbc_read_byte; chip->write_buf = fsl_elbc_write_buf; chip->read_buf = fsl_elbc_read_buf; chip->verify_buf = fsl_elbc_verify_buf; chip->select_chip = fsl_elbc_select_chip; chip->cmdfunc = fsl_elbc_cmdfunc; chip->waitfunc = fsl_elbc_wait; chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; /* set up nand options */ chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; chip->controller = &ctrl->controller; chip->priv = priv; chip->ecc.read_page = fsl_elbc_read_page; chip->ecc.write_page = fsl_elbc_write_page; /* If CS Base Register selects full hardware ECC then use it */ if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == BR_DECC_CHK_GEN) { chip->ecc.mode = NAND_ECC_HW; /* put in small page settings and adjust later if needed */ chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; chip->ecc.size = 512; chip->ecc.bytes = 3; } else { /* otherwise fall back to default software ECC */ chip->ecc.mode = NAND_ECC_SOFT; } return 0;}static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv){ struct fsl_elbc_ctrl *ctrl = priv->ctrl; nand_release(&priv->mtd); kfree(priv->mtd.name); if (priv->vbase) iounmap(priv->vbase); ctrl->chips[priv->bank] = NULL; kfree(priv); return 0;}static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, struct device_node *node){ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_elbc_mtd *priv; struct resource res;#ifdef CONFIG_MTD_PARTITIONS static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; struct mtd_partition *parts;#endif int ret; int bank; /* get, allocate and map the memory resource */ ret = of_address_to_resource(node, 0, &res); if (ret) { dev_err(ctrl->dev, "failed to get resource\n"); return ret; } /* find which chip select it is connected to */ for (bank = 0; bank < MAX_BANKS; bank++) if ((in_be32(&lbc->bank[bank].br) & BR_V) && (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && (in_be32(&lbc->bank[bank].br) & in_be32(&lbc->bank[bank].or) & BR_BA) == res.start) break; if (bank >= MAX_BANKS) { dev_err(ctrl->dev, "address did not match any chip selects\n"); return -ENODEV; } priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; ctrl->chips[bank] = priv; priv->bank = bank; priv->ctrl = ctrl; priv->dev = ctrl->dev; priv->vbase = ioremap(res.start, res.end - res.start + 1); if (!priv->vbase) { dev_err(ctrl->dev, "failed to map chip region\n"); ret = -ENOMEM; goto err; } priv->mtd.name = kasprintf(GFP_KERNEL, "%x.flash", (unsigned)res.start); if (!priv->mtd.name) { ret = -ENOMEM; goto err; } ret = fsl_elbc_chip_init(priv); if (ret) goto err; ret = nand_scan_ident(&priv->mtd, 1); if (ret) goto err; ret = fsl_elbc_chip_init_tail(&priv->mtd); if (ret) goto err; ret = nand_scan_tail(&priv->mtd); if (ret) goto err;#ifdef CONFIG_MTD_PARTITIONS /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ ret = parse_mtd_partitions(&priv->mtd, part_probe_types, &parts, 0); if (ret < 0) goto err;#ifdef CONFIG_MTD_OF_PARTS if (ret == 0) { ret = of_mtd_parse_partitions(priv->dev, node, &parts); if (ret < 0) goto err; }#endif if (ret > 0) add_mtd_partitions(&priv->mtd, parts, ret); else#endif add_mtd_device(&priv->mtd); printk(KERN_INFO "eLBC NAND device at 0x%zx, bank %d\n", res.start, priv->bank); return 0;err: fsl_elbc_chip_remove(priv); return ret;}static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl){ struct fsl_lbc_regs __iomem *lbc = ctrl->regs; /* clear event registers */ setbits32(&lbc->ltesr, LTESR_NAND_MASK); out_be32(&lbc->lteatr, 0); /* Enable interrupts for any detected events */ out_be32(&lbc->lteir, LTESR_NAND_MASK); ctrl->read_bytes = 0; ctrl->index = 0; ctrl->addr = NULL; return 0;}static int fsl_elbc_ctrl_remove(struct of_device *ofdev){ struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev); int i; for (i = 0; i < MAX_BANKS; i++) if (ctrl->chips[i]) fsl_elbc_chip_remove(ctrl->chips[i]); if (ctrl->irq) free_irq(ctrl->irq, ctrl); if (ctrl->regs) iounmap(ctrl->regs); dev_set_drvdata(&ofdev->dev, NULL); kfree(ctrl); return 0;}/* NOTE: This interrupt is also used to report other localbus events, * such as transaction errors on other chipselects. If we want to * capture those, we'll need to move the IRQ code into a shared * LBC driver. */static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data){ struct fsl_elbc_ctrl *ctrl = data; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; __be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK; if (status) { out_be32(&lbc->ltesr, status); out_be32(&lbc->lteatr, 0); ctrl->irq_status = status; smp_wmb(); wake_up(&ctrl->irq_wait); return IRQ_HANDLED; } return IRQ_NONE;}/* fsl_elbc_ctrl_probe * * called by device layer when it finds a device matching * one our driver can handled. This code allocates all of * the resources needed for the controller only. The * resources for the NAND banks themselves are allocated * in the chip probe function.*/static int __devinit fsl_elbc_ctrl_probe(struct of_device *ofdev, const struct of_device_id *match){ struct device_node *child; struct fsl_elbc_ctrl *ctrl; int ret; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) return -ENOMEM; dev_set_drvdata(&ofdev->dev, ctrl); spin_lock_init(&ctrl->controller.lock); init_waitqueue_head(&ctrl->controller.wq); init_waitqueue_head(&ctrl->irq_wait); ctrl->regs = of_iomap(ofdev->node, 0); if (!ctrl->regs) { dev_err(&ofdev->dev, "failed to get memory region\n"); ret = -ENODEV; goto err; } ctrl->irq = of_irq_to_resource(ofdev->node, 0, NULL); if (ctrl->irq == NO_IRQ) { dev_err(&ofdev->dev, "failed to get irq resource\n"); ret = -ENODEV; goto err; } ctrl->dev = &ofdev->dev; ret = fsl_elbc_ctrl_init(ctrl); if (ret < 0) goto err; ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl); if (ret != 0) { dev_err(&ofdev->dev, "failed to install irq (%d)\n", ctrl->irq); ret = ctrl->irq; goto err; } for_each_child_of_node(ofdev->node, child) if (of_device_is_compatible(child, "fsl,elbc-fcm-nand")) fsl_elbc_chip_probe(ctrl, child); return 0;err: fsl_elbc_ctrl_remove(ofdev); return ret;}static const struct of_device_id fsl_elbc_match[] = { { .compatible = "fsl,elbc", }, {}};static struct of_platform_driver fsl_elbc_ctrl_driver = { .driver = { .name = "fsl-elbc", }, .match_table = fsl_elbc_match, .probe = fsl_elbc_ctrl_probe, .remove = fsl_elbc_ctrl_remove,};static int __init fsl_elbc_init(void){ return of_register_platform_driver(&fsl_elbc_ctrl_driver);}static void __exit fsl_elbc_exit(void){ of_unregister_platform_driver(&fsl_elbc_ctrl_driver);}module_init(fsl_elbc_init);module_exit(fsl_elbc_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Freescale");MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -