📄 s3c2410.c
字号:
writel(ctrl, info->regs + S3C2410_NFCONF);}static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode){ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); unsigned long ctrl; ctrl = readl(info->regs + S3C2440_NFCONT); writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);}static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); ecc_code[0] = readb(info->regs + S3C2410_NFECC + 0); ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); return 0;}static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); ecc_code[0] = ecc; ecc_code[1] = ecc >> 8; ecc_code[2] = ecc >> 16; pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); return 0;}/* over-ride the standard functions for a little more speed. We can * use read/write block to move the data buffers to/from the controller*/static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len){ struct nand_chip *this = mtd->priv; readsb(this->IO_ADDR_R, buf, len);}static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len){ struct nand_chip *this = mtd->priv; writesb(this->IO_ADDR_W, buf, len);}/* device management functions */static int s3c2410_nand_remove(struct device *dev){ struct s3c2410_nand_info *info = to_nand_info(dev); dev_set_drvdata(dev, NULL); if (info == NULL) return 0; /* first thing we need to do is release all our mtds * and their partitions, then go through freeing the * resources used */ if (info->mtds != NULL) { struct s3c2410_nand_mtd *ptr = info->mtds; int mtdno; for (mtdno = 0; mtdno < info->mtd_count; mtdno++, ptr++) { pr_debug("releasing mtd %d (%p)\n", mtdno, ptr); nand_release(&ptr->mtd); } kfree(info->mtds); } /* free the common resources */ if (info->clk != NULL && !IS_ERR(info->clk)) { clk_disable(info->clk); clk_unuse(info->clk); clk_put(info->clk); } if (info->regs != NULL) { iounmap(info->regs); info->regs = NULL; } if (info->area != NULL) { release_resource(info->area); kfree(info->area); info->area = NULL; } kfree(info); return 0;}#ifdef CONFIG_MTD_PARTITIONSstatic int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *mtd, struct s3c2410_nand_set *set){ if (set == NULL) return add_mtd_device(&mtd->mtd); if (set->nr_partitions > 0 && set->partitions != NULL) { return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); } return add_mtd_device(&mtd->mtd);}#elsestatic int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *mtd, struct s3c2410_nand_set *set){ return add_mtd_device(&mtd->mtd);}#endif/* s3c2410_nand_init_chip * * init a single instance of an chip */static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd, struct s3c2410_nand_set *set){ struct nand_chip *chip = &nmtd->chip; chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; chip->hwcontrol = s3c2410_nand_hwcontrol; chip->dev_ready = s3c2410_nand_devready; chip->write_buf = s3c2410_nand_write_buf; chip->read_buf = s3c2410_nand_read_buf; chip->select_chip = s3c2410_nand_select_chip; chip->chip_delay = 50; chip->priv = nmtd; chip->options = 0; chip->controller = &info->controller; if (info->is_s3c2440) { chip->IO_ADDR_R = info->regs + S3C2440_NFDATA; chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; chip->hwcontrol = s3c2440_nand_hwcontrol; } nmtd->info = info; nmtd->mtd.priv = chip; nmtd->set = set; if (hardware_ecc) { chip->correct_data = s3c2410_nand_correct_data; chip->enable_hwecc = s3c2410_nand_enable_hwecc; chip->calculate_ecc = s3c2410_nand_calculate_ecc; chip->eccmode = NAND_ECC_HW3_512; chip->autooob = &nand_hw_eccoob; if (info->is_s3c2440) { chip->enable_hwecc = s3c2440_nand_enable_hwecc; chip->calculate_ecc = s3c2440_nand_calculate_ecc; } } else { chip->eccmode = NAND_ECC_SOFT; }}/* s3c2410_nand_probe * * called by device layer when it finds a device matching * one our driver can handled. This code checks to see if * it can allocate all necessary resources then calls the * nand layer to look for devices*/static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440){ struct platform_device *pdev = to_platform_device(dev); struct s3c2410_platform_nand *plat = to_nand_plat(dev); struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; struct resource *res; int err = 0; int size; int nr_sets; int setno; pr_debug("s3c2410_nand_probe(%p)\n", dev); info = kmalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { printk(KERN_ERR PFX "no memory for flash info\n"); err = -ENOMEM; goto exit_error; } memzero(info, sizeof(*info)); dev_set_drvdata(dev, info); spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); /* get the clock source and enable it */ info->clk = clk_get(dev, "nand"); if (IS_ERR(info->clk)) { printk(KERN_ERR PFX "failed to get clock"); err = -ENOENT; goto exit_error; } clk_use(info->clk); clk_enable(info->clk); /* allocate and map the resource */ /* currently we assume we have the one resource */ res = pdev->resource; size = res->end - res->start + 1; info->area = request_mem_region(res->start, size, pdev->name); if (info->area == NULL) { printk(KERN_ERR PFX "cannot reserve register region\n"); err = -ENOENT; goto exit_error; } info->device = dev; info->platform = plat; info->regs = ioremap(res->start, size); info->is_s3c2440 = is_s3c2440; if (info->regs == NULL) { printk(KERN_ERR PFX "cannot reserve register region\n"); err = -EIO; goto exit_error; } printk(KERN_INFO PFX "mapped registers at %p\n", info->regs); /* initialise the hardware */ err = s3c2410_nand_inithw(info, dev); if (err != 0) goto exit_error; sets = (plat != NULL) ? plat->sets : NULL; nr_sets = (plat != NULL) ? plat->nr_sets : 1; info->mtd_count = nr_sets; /* allocate our information */ size = nr_sets * sizeof(*info->mtds); info->mtds = kmalloc(size, GFP_KERNEL); if (info->mtds == NULL) { printk(KERN_ERR PFX "failed to allocate mtd storage\n"); err = -ENOMEM; goto exit_error; } memzero(info->mtds, size); /* initialise all possible chips */ nmtd = info->mtds; for (setno = 0; setno < nr_sets; setno++, nmtd++) { pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info); s3c2410_nand_init_chip(info, nmtd, sets); nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); if (nmtd->scan_res == 0) { s3c2410_nand_add_partition(info, nmtd, sets); } if (sets != NULL) sets++; } pr_debug("initialised ok\n"); return 0; exit_error: s3c2410_nand_remove(dev); if (err == 0) err = -EINVAL; return err;}/* driver device registration */static int s3c2410_nand_probe(struct device *dev){ return s3c24xx_nand_probe(dev, 0);}static int s3c2440_nand_probe(struct device *dev){ return s3c24xx_nand_probe(dev, 1);}static struct device_driver s3c2410_nand_driver = { .name = "s3c2410-nand", .bus = &platform_bus_type, .probe = s3c2410_nand_probe, .remove = s3c2410_nand_remove,};static struct device_driver s3c2440_nand_driver = { .name = "s3c2440-nand", .bus = &platform_bus_type, .probe = s3c2440_nand_probe, .remove = s3c2410_nand_remove,};static int __init s3c2410_nand_init(void){ printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); driver_register(&s3c2440_nand_driver); return driver_register(&s3c2410_nand_driver);}static void __exit s3c2410_nand_exit(void){ driver_unregister(&s3c2440_nand_driver); driver_unregister(&s3c2410_nand_driver);}module_init(s3c2410_nand_init);module_exit(s3c2410_nand_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -