1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e644f7d6STodd Poynor /*
3e644f7d6STodd Poynor * Map driver for Intel XScale PXA2xx platforms.
4e644f7d6STodd Poynor *
5e644f7d6STodd Poynor * Author: Nicolas Pitre
6e644f7d6STodd Poynor * Copyright: (C) 2001 MontaVista Software Inc.
7e644f7d6STodd Poynor */
8e644f7d6STodd Poynor
9e644f7d6STodd Poynor #include <linux/module.h>
10e644f7d6STodd Poynor #include <linux/types.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
12e644f7d6STodd Poynor #include <linux/kernel.h>
13e644f7d6STodd Poynor #include <linux/platform_device.h>
14e644f7d6STodd Poynor #include <linux/mtd/mtd.h>
15e644f7d6STodd Poynor #include <linux/mtd/map.h>
16e644f7d6STodd Poynor #include <linux/mtd/partitions.h>
17e644f7d6STodd Poynor
18e644f7d6STodd Poynor #include <asm/io.h>
19e644f7d6STodd Poynor #include <asm/mach/flash.h>
20e644f7d6STodd Poynor
21ccaf5f05SNicolas Pitre #define CACHELINESIZE 32
22ccaf5f05SNicolas Pitre
pxa2xx_map_inval_cache(struct map_info * map,unsigned long from,ssize_t len)23e644f7d6STodd Poynor static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
24e644f7d6STodd Poynor ssize_t len)
25e644f7d6STodd Poynor {
26ccaf5f05SNicolas Pitre unsigned long start = (unsigned long)map->cached + from;
27ccaf5f05SNicolas Pitre unsigned long end = start + len;
28ccaf5f05SNicolas Pitre
29ccaf5f05SNicolas Pitre start &= ~(CACHELINESIZE - 1);
30ccaf5f05SNicolas Pitre while (start < end) {
31ccaf5f05SNicolas Pitre /* invalidate D cache line */
32ccaf5f05SNicolas Pitre asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start));
33ccaf5f05SNicolas Pitre start += CACHELINESIZE;
34ccaf5f05SNicolas Pitre }
35e644f7d6STodd Poynor }
36e644f7d6STodd Poynor
37e644f7d6STodd Poynor struct pxa2xx_flash_info {
38e644f7d6STodd Poynor struct mtd_info *mtd;
39e644f7d6STodd Poynor struct map_info map;
40e644f7d6STodd Poynor };
41e644f7d6STodd Poynor
420984c891SArtem Bityutskiy static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
43e644f7d6STodd Poynor
pxa2xx_flash_probe(struct platform_device * pdev)4406f25510SBill Pemberton static int pxa2xx_flash_probe(struct platform_device *pdev)
45e644f7d6STodd Poynor {
46d20d5a57SJingoo Han struct flash_platform_data *flash = dev_get_platdata(&pdev->dev);
47e644f7d6STodd Poynor struct pxa2xx_flash_info *info;
48e644f7d6STodd Poynor struct resource *res;
49e644f7d6STodd Poynor
50e644f7d6STodd Poynor res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
51e644f7d6STodd Poynor if (!res)
52e644f7d6STodd Poynor return -ENODEV;
53e644f7d6STodd Poynor
542bfefa4cSJulia Lawall info = kzalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL);
55e644f7d6STodd Poynor if (!info)
56e644f7d6STodd Poynor return -ENOMEM;
57e644f7d6STodd Poynor
587c4bb4f8SGeert Uytterhoeven info->map.name = flash->name;
59e644f7d6STodd Poynor info->map.bankwidth = flash->width;
60e644f7d6STodd Poynor info->map.phys = res->start;
6128f65c11SJoe Perches info->map.size = resource_size(res);
62e644f7d6STodd Poynor
63e644f7d6STodd Poynor info->map.virt = ioremap(info->map.phys, info->map.size);
64e644f7d6STodd Poynor if (!info->map.virt) {
65e644f7d6STodd Poynor printk(KERN_WARNING "Failed to ioremap %s\n",
66e644f7d6STodd Poynor info->map.name);
672399401fSZheng Yongjun kfree(info);
68e644f7d6STodd Poynor return -ENOMEM;
69e644f7d6STodd Poynor }
7097ef08aeSChristoph Hellwig info->map.cached = ioremap_cache(info->map.phys, info->map.size);
71e644f7d6STodd Poynor if (!info->map.cached)
72e644f7d6STodd Poynor printk(KERN_WARNING "Failed to ioremap cached %s\n",
73e644f7d6STodd Poynor info->map.name);
74e644f7d6STodd Poynor info->map.inval_cache = pxa2xx_map_inval_cache;
75e644f7d6STodd Poynor simple_map_init(&info->map);
76e644f7d6STodd Poynor
77e644f7d6STodd Poynor printk(KERN_NOTICE
78e644f7d6STodd Poynor "Probing %s at physical address 0x%08lx"
79e644f7d6STodd Poynor " (%d-bit bankwidth)\n",
80e644f7d6STodd Poynor info->map.name, (unsigned long)info->map.phys,
81e644f7d6STodd Poynor info->map.bankwidth * 8);
82e644f7d6STodd Poynor
83e644f7d6STodd Poynor info->mtd = do_map_probe(flash->map_name, &info->map);
84e644f7d6STodd Poynor
85e644f7d6STodd Poynor if (!info->mtd) {
86e644f7d6STodd Poynor iounmap((void *)info->map.virt);
87e644f7d6STodd Poynor if (info->map.cached)
88e644f7d6STodd Poynor iounmap(info->map.cached);
892399401fSZheng Yongjun kfree(info);
90e644f7d6STodd Poynor return -EIO;
91e644f7d6STodd Poynor }
922451581fSFrans Klaver info->mtd->dev.parent = &pdev->dev;
93e644f7d6STodd Poynor
9442d7fbe2SArtem Bityutskiy mtd_device_parse_register(info->mtd, probes, NULL, flash->parts,
9542d7fbe2SArtem Bityutskiy flash->nr_parts);
96e644f7d6STodd Poynor
977a192ec3SMing Lei platform_set_drvdata(pdev, info);
98e644f7d6STodd Poynor return 0;
99e644f7d6STodd Poynor }
100e644f7d6STodd Poynor
pxa2xx_flash_remove(struct platform_device * dev)101*a105291cSUwe Kleine-König static void pxa2xx_flash_remove(struct platform_device *dev)
102e644f7d6STodd Poynor {
1037a192ec3SMing Lei struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
104e644f7d6STodd Poynor
1053dfad123SJamie Iles mtd_device_unregister(info->mtd);
106e644f7d6STodd Poynor
107e644f7d6STodd Poynor map_destroy(info->mtd);
108e644f7d6STodd Poynor iounmap(info->map.virt);
109e644f7d6STodd Poynor if (info->map.cached)
1107769aea2SArd Biesheuvel iounmap(info->map.cached);
111e644f7d6STodd Poynor kfree(info);
112e644f7d6STodd Poynor }
113e644f7d6STodd Poynor
114e644f7d6STodd Poynor #ifdef CONFIG_PM
pxa2xx_flash_shutdown(struct platform_device * dev)1157a192ec3SMing Lei static void pxa2xx_flash_shutdown(struct platform_device *dev)
116e644f7d6STodd Poynor {
1177a192ec3SMing Lei struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
118e644f7d6STodd Poynor
1193fe4bae8SArtem Bityutskiy if (info && mtd_suspend(info->mtd) == 0)
120ead995f8SArtem Bityutskiy mtd_resume(info->mtd);
121e644f7d6STodd Poynor }
122e644f7d6STodd Poynor #else
123e644f7d6STodd Poynor #define pxa2xx_flash_shutdown NULL
124e644f7d6STodd Poynor #endif
125e644f7d6STodd Poynor
1267a192ec3SMing Lei static struct platform_driver pxa2xx_flash_driver = {
1277a192ec3SMing Lei .driver = {
128e644f7d6STodd Poynor .name = "pxa2xx-flash",
1297a192ec3SMing Lei },
130e644f7d6STodd Poynor .probe = pxa2xx_flash_probe,
131*a105291cSUwe Kleine-König .remove_new = pxa2xx_flash_remove,
132e644f7d6STodd Poynor .shutdown = pxa2xx_flash_shutdown,
133e644f7d6STodd Poynor };
134e644f7d6STodd Poynor
135f99640deSAxel Lin module_platform_driver(pxa2xx_flash_driver);
136e644f7d6STodd Poynor
137e644f7d6STodd Poynor MODULE_LICENSE("GPL");
1382f82af08SNicolas Pitre MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net>");
139e644f7d6STodd Poynor MODULE_DESCRIPTION("MTD map driver for Intel XScale PXA2xx");
140