11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Flash memory access on SA11x0 based devices 31da177e4SLinus Torvalds * 42f82af08SNicolas Pitre * (C) 2000 Nicolas Pitre <nico@fluxnic.net> 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds #include <linux/module.h> 71da177e4SLinus Torvalds #include <linux/types.h> 81da177e4SLinus Torvalds #include <linux/ioport.h> 91da177e4SLinus Torvalds #include <linux/kernel.h> 101da177e4SLinus Torvalds #include <linux/init.h> 111da177e4SLinus Torvalds #include <linux/errno.h> 121da177e4SLinus Torvalds #include <linux/slab.h> 13d052d1beSRussell King #include <linux/platform_device.h> 141da177e4SLinus Torvalds #include <linux/err.h> 1599730225SRussell King #include <linux/io.h> 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 181da177e4SLinus Torvalds #include <linux/mtd/map.h> 191da177e4SLinus Torvalds #include <linux/mtd/partitions.h> 201da177e4SLinus Torvalds #include <linux/mtd/concat.h> 211da177e4SLinus Torvalds 22a09e64fbSRussell King #include <mach/hardware.h> 231da177e4SLinus Torvalds #include <asm/sizes.h> 241da177e4SLinus Torvalds #include <asm/mach/flash.h> 251da177e4SLinus Torvalds 261da177e4SLinus Torvalds struct sa_subdev_info { 271da177e4SLinus Torvalds char name[16]; 281da177e4SLinus Torvalds struct map_info map; 291da177e4SLinus Torvalds struct mtd_info *mtd; 3057725f0aSRussell King struct flash_platform_data *plat; 311da177e4SLinus Torvalds }; 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds struct sa_info { 341da177e4SLinus Torvalds struct mtd_info *mtd; 351da177e4SLinus Torvalds int num_subdev; 361da177e4SLinus Torvalds struct sa_subdev_info subdev[0]; 371da177e4SLinus Torvalds }; 381da177e4SLinus Torvalds 39ee478af8SPaul Parsons static DEFINE_SPINLOCK(sa1100_vpp_lock); 40ee478af8SPaul Parsons static int sa1100_vpp_refcnt; 411da177e4SLinus Torvalds static void sa1100_set_vpp(struct map_info *map, int on) 421da177e4SLinus Torvalds { 431da177e4SLinus Torvalds struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map); 44ee478af8SPaul Parsons unsigned long flags; 45ee478af8SPaul Parsons 46ee478af8SPaul Parsons spin_lock_irqsave(&sa1100_vpp_lock, flags); 47ee478af8SPaul Parsons if (on) { 48ee478af8SPaul Parsons if (++sa1100_vpp_refcnt == 1) /* first nested 'on' */ 49ee478af8SPaul Parsons subdev->plat->set_vpp(1); 50ee478af8SPaul Parsons } else { 51ee478af8SPaul Parsons if (--sa1100_vpp_refcnt == 0) /* last nested 'off' */ 52ee478af8SPaul Parsons subdev->plat->set_vpp(0); 53ee478af8SPaul Parsons } 54ee478af8SPaul Parsons spin_unlock_irqrestore(&sa1100_vpp_lock, flags); 551da177e4SLinus Torvalds } 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds static void sa1100_destroy_subdev(struct sa_subdev_info *subdev) 581da177e4SLinus Torvalds { 591da177e4SLinus Torvalds if (subdev->mtd) 601da177e4SLinus Torvalds map_destroy(subdev->mtd); 611da177e4SLinus Torvalds if (subdev->map.virt) 621da177e4SLinus Torvalds iounmap(subdev->map.virt); 631da177e4SLinus Torvalds release_mem_region(subdev->map.phys, subdev->map.size); 641da177e4SLinus Torvalds } 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res) 671da177e4SLinus Torvalds { 681da177e4SLinus Torvalds unsigned long phys; 691da177e4SLinus Torvalds unsigned int size; 701da177e4SLinus Torvalds int ret; 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds phys = res->start; 731da177e4SLinus Torvalds size = res->end - phys + 1; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds /* 761da177e4SLinus Torvalds * Retrieve the bankwidth from the MSC registers. 771da177e4SLinus Torvalds * We currently only implement CS0 and CS1 here. 781da177e4SLinus Torvalds */ 791da177e4SLinus Torvalds switch (phys) { 801da177e4SLinus Torvalds default: 811da177e4SLinus Torvalds printk(KERN_WARNING "SA1100 flash: unknown base address " 821da177e4SLinus Torvalds "0x%08lx, assuming CS0\n", phys); 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds case SA1100_CS0_PHYS: 851da177e4SLinus Torvalds subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; 861da177e4SLinus Torvalds break; 871da177e4SLinus Torvalds 881da177e4SLinus Torvalds case SA1100_CS1_PHYS: 891da177e4SLinus Torvalds subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; 901da177e4SLinus Torvalds break; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds if (!request_mem_region(phys, size, subdev->name)) { 941da177e4SLinus Torvalds ret = -EBUSY; 951da177e4SLinus Torvalds goto out; 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 9857725f0aSRussell King if (subdev->plat->set_vpp) 991da177e4SLinus Torvalds subdev->map.set_vpp = sa1100_set_vpp; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds subdev->map.phys = phys; 1021da177e4SLinus Torvalds subdev->map.size = size; 1031da177e4SLinus Torvalds subdev->map.virt = ioremap(phys, size); 1041da177e4SLinus Torvalds if (!subdev->map.virt) { 1051da177e4SLinus Torvalds ret = -ENOMEM; 1061da177e4SLinus Torvalds goto err; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds simple_map_init(&subdev->map); 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds /* 1121da177e4SLinus Torvalds * Now let's probe for the actual flash. Do it here since 1131da177e4SLinus Torvalds * specific machine settings might have been set above. 1141da177e4SLinus Torvalds */ 11557725f0aSRussell King subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map); 1161da177e4SLinus Torvalds if (subdev->mtd == NULL) { 1171da177e4SLinus Torvalds ret = -ENXIO; 1181da177e4SLinus Torvalds goto err; 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds subdev->mtd->owner = THIS_MODULE; 1211da177e4SLinus Torvalds 122794d579aSRussell King printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n", 123794d579aSRussell King phys, (unsigned)(subdev->mtd->size >> 20), 1241da177e4SLinus Torvalds subdev->map.bankwidth * 8); 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds return 0; 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds err: 1291da177e4SLinus Torvalds sa1100_destroy_subdev(subdev); 1301da177e4SLinus Torvalds out: 1311da177e4SLinus Torvalds return ret; 1321da177e4SLinus Torvalds } 1331da177e4SLinus Torvalds 1340d2ef7d7SRussell King static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat) 1351da177e4SLinus Torvalds { 1361da177e4SLinus Torvalds int i; 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds if (info->mtd) { 1392fe2e24eSJamie Iles mtd_device_unregister(info->mtd); 1401da177e4SLinus Torvalds if (info->mtd != info->subdev[0].mtd) 1411da177e4SLinus Torvalds mtd_concat_destroy(info->mtd); 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds for (i = info->num_subdev - 1; i >= 0; i--) 1451da177e4SLinus Torvalds sa1100_destroy_subdev(&info->subdev[i]); 1461da177e4SLinus Torvalds kfree(info); 1470d2ef7d7SRussell King 1480d2ef7d7SRussell King if (plat->exit) 1490d2ef7d7SRussell King plat->exit(); 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds 1527bf350b7SArtem Bityutskiy static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev, 1537bf350b7SArtem Bityutskiy struct flash_platform_data *plat) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds struct sa_info *info; 1561da177e4SLinus Torvalds int nr, size, i, ret = 0; 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds /* 1591da177e4SLinus Torvalds * Count number of devices. 1601da177e4SLinus Torvalds */ 1611da177e4SLinus Torvalds for (nr = 0; ; nr++) 1621da177e4SLinus Torvalds if (!platform_get_resource(pdev, IORESOURCE_MEM, nr)) 1631da177e4SLinus Torvalds break; 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds if (nr == 0) { 1661da177e4SLinus Torvalds ret = -ENODEV; 1671da177e4SLinus Torvalds goto out; 1681da177e4SLinus Torvalds } 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr; 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds /* 1731da177e4SLinus Torvalds * Allocate the map_info structs in one go. 1741da177e4SLinus Torvalds */ 17595b93a0cSBurman Yan info = kzalloc(size, GFP_KERNEL); 1761da177e4SLinus Torvalds if (!info) { 1771da177e4SLinus Torvalds ret = -ENOMEM; 1781da177e4SLinus Torvalds goto out; 1791da177e4SLinus Torvalds } 1801da177e4SLinus Torvalds 1810d2ef7d7SRussell King if (plat->init) { 1820d2ef7d7SRussell King ret = plat->init(); 1830d2ef7d7SRussell King if (ret) 1840d2ef7d7SRussell King goto err; 1850d2ef7d7SRussell King } 1860d2ef7d7SRussell King 1871da177e4SLinus Torvalds /* 1881da177e4SLinus Torvalds * Claim and then map the memory regions. 1891da177e4SLinus Torvalds */ 1901da177e4SLinus Torvalds for (i = 0; i < nr; i++) { 1911da177e4SLinus Torvalds struct sa_subdev_info *subdev = &info->subdev[i]; 1921da177e4SLinus Torvalds struct resource *res; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds res = platform_get_resource(pdev, IORESOURCE_MEM, i); 1951da177e4SLinus Torvalds if (!res) 1961da177e4SLinus Torvalds break; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds subdev->map.name = subdev->name; 19914e66f76SRussell King sprintf(subdev->name, "%s-%d", plat->name, i); 20057725f0aSRussell King subdev->plat = plat; 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds ret = sa1100_probe_subdev(subdev, res); 2031da177e4SLinus Torvalds if (ret) 2041da177e4SLinus Torvalds break; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds info->num_subdev = i; 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds /* 2101da177e4SLinus Torvalds * ENXIO is special. It means we didn't find a chip when we probed. 2111da177e4SLinus Torvalds */ 2121da177e4SLinus Torvalds if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0)) 2131da177e4SLinus Torvalds goto err; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds /* 2161da177e4SLinus Torvalds * If we found one device, don't bother with concat support. If 2171da177e4SLinus Torvalds * we found multiple devices, use concat if we have it available, 2181da177e4SLinus Torvalds * otherwise fail. Either way, it'll be called "sa1100". 2191da177e4SLinus Torvalds */ 2201da177e4SLinus Torvalds if (info->num_subdev == 1) { 22114e66f76SRussell King strcpy(info->subdev[0].name, plat->name); 2221da177e4SLinus Torvalds info->mtd = info->subdev[0].mtd; 2231da177e4SLinus Torvalds ret = 0; 2241da177e4SLinus Torvalds } else if (info->num_subdev > 1) { 2251da177e4SLinus Torvalds struct mtd_info *cdev[nr]; 2261da177e4SLinus Torvalds /* 2271da177e4SLinus Torvalds * We detected multiple devices. Concatenate them together. 2281da177e4SLinus Torvalds */ 2291da177e4SLinus Torvalds for (i = 0; i < info->num_subdev; i++) 2301da177e4SLinus Torvalds cdev[i] = info->subdev[i].mtd; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds info->mtd = mtd_concat_create(cdev, info->num_subdev, 23314e66f76SRussell King plat->name); 2341da177e4SLinus Torvalds if (info->mtd == NULL) 2351da177e4SLinus Torvalds ret = -ENXIO; 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds if (ret == 0) 2391da177e4SLinus Torvalds return info; 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds err: 2420d2ef7d7SRussell King sa1100_destroy(info, plat); 2431da177e4SLinus Torvalds out: 2441da177e4SLinus Torvalds return ERR_PTR(ret); 2451da177e4SLinus Torvalds } 2461da177e4SLinus Torvalds 2470984c891SArtem Bityutskiy static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL }; 2481da177e4SLinus Torvalds 24906f25510SBill Pemberton static int sa1100_mtd_probe(struct platform_device *pdev) 2501da177e4SLinus Torvalds { 251*d20d5a57SJingoo Han struct flash_platform_data *plat = dev_get_platdata(&pdev->dev); 2521da177e4SLinus Torvalds struct sa_info *info; 253769dc431SDmitry Eremin-Solenikov int err; 2541da177e4SLinus Torvalds 25557725f0aSRussell King if (!plat) 2561da177e4SLinus Torvalds return -ENODEV; 2571da177e4SLinus Torvalds 25857725f0aSRussell King info = sa1100_setup_mtd(pdev, plat); 2591da177e4SLinus Torvalds if (IS_ERR(info)) { 2601da177e4SLinus Torvalds err = PTR_ERR(info); 2611da177e4SLinus Torvalds goto out; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds /* 2651da177e4SLinus Torvalds * Partition selection stuff. 2661da177e4SLinus Torvalds */ 26742d7fbe2SArtem Bityutskiy mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts, 26842d7fbe2SArtem Bityutskiy plat->nr_parts); 269822e5e72SRussell King 2703ae5eaecSRussell King platform_set_drvdata(pdev, info); 2711da177e4SLinus Torvalds err = 0; 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds out: 2741da177e4SLinus Torvalds return err; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 2773ae5eaecSRussell King static int __exit sa1100_mtd_remove(struct platform_device *pdev) 2781da177e4SLinus Torvalds { 2793ae5eaecSRussell King struct sa_info *info = platform_get_drvdata(pdev); 280*d20d5a57SJingoo Han struct flash_platform_data *plat = dev_get_platdata(&pdev->dev); 2810d2ef7d7SRussell King 2820d2ef7d7SRussell King sa1100_destroy(info, plat); 2830d2ef7d7SRussell King 2841da177e4SLinus Torvalds return 0; 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 2873ae5eaecSRussell King static struct platform_driver sa1100_mtd_driver = { 2881da177e4SLinus Torvalds .probe = sa1100_mtd_probe, 2891da177e4SLinus Torvalds .remove = __exit_p(sa1100_mtd_remove), 2903ae5eaecSRussell King .driver = { 291bcc8f3e0SUwe Kleine-König .name = "sa1100-mtd", 29241d867c9SKay Sievers .owner = THIS_MODULE, 2933ae5eaecSRussell King }, 2941da177e4SLinus Torvalds }; 2951da177e4SLinus Torvalds 296f99640deSAxel Lin module_platform_driver(sa1100_mtd_driver); 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds MODULE_AUTHOR("Nicolas Pitre"); 2991da177e4SLinus Torvalds MODULE_DESCRIPTION("SA1100 CFI map driver"); 3001da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 301bcc8f3e0SUwe Kleine-König MODULE_ALIAS("platform:sa1100-mtd"); 302