1 /*
2  * Normal mappings of chips in physical memory
3  *
4  * Copyright (C) 2003 MontaVista Software Inc.
5  * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
6  *
7  * 031022 - [jsun] add run-time configure and partition setup
8  */
9 
10 #include <linux/module.h>
11 #include <linux/types.h>
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/slab.h>
15 #include <linux/device.h>
16 #include <linux/platform_device.h>
17 #include <linux/mtd/mtd.h>
18 #include <linux/mtd/map.h>
19 #include <linux/mtd/partitions.h>
20 #include <linux/mtd/physmap.h>
21 #include <linux/mtd/concat.h>
22 #include <linux/io.h>
23 
24 #define MAX_RESOURCES		4
25 
26 struct physmap_flash_info {
27 	struct mtd_info		*mtd[MAX_RESOURCES];
28 	struct mtd_info		*cmtd;
29 	struct map_info		map[MAX_RESOURCES];
30 	spinlock_t		vpp_lock;
31 	int			vpp_refcnt;
32 };
33 
physmap_flash_remove(struct platform_device * dev)34 static int physmap_flash_remove(struct platform_device *dev)
35 {
36 	struct physmap_flash_info *info;
37 	struct physmap_flash_data *physmap_data;
38 	int i;
39 
40 	info = platform_get_drvdata(dev);
41 	if (info == NULL)
42 		return 0;
43 
44 	physmap_data = dev_get_platdata(&dev->dev);
45 
46 	if (info->cmtd) {
47 		mtd_device_unregister(info->cmtd);
48 		if (info->cmtd != info->mtd[0])
49 			mtd_concat_destroy(info->cmtd);
50 	}
51 
52 	for (i = 0; i < MAX_RESOURCES; i++) {
53 		if (info->mtd[i] != NULL)
54 			map_destroy(info->mtd[i]);
55 	}
56 
57 	if (physmap_data->exit)
58 		physmap_data->exit(dev);
59 
60 	return 0;
61 }
62 
physmap_set_vpp(struct map_info * map,int state)63 static void physmap_set_vpp(struct map_info *map, int state)
64 {
65 	struct platform_device *pdev;
66 	struct physmap_flash_data *physmap_data;
67 	struct physmap_flash_info *info;
68 	unsigned long flags;
69 
70 	pdev = (struct platform_device *)map->map_priv_1;
71 	physmap_data = dev_get_platdata(&pdev->dev);
72 
73 	if (!physmap_data->set_vpp)
74 		return;
75 
76 	info = platform_get_drvdata(pdev);
77 
78 	spin_lock_irqsave(&info->vpp_lock, flags);
79 	if (state) {
80 		if (++info->vpp_refcnt == 1)    /* first nested 'on' */
81 			physmap_data->set_vpp(pdev, 1);
82 	} else {
83 		if (--info->vpp_refcnt == 0)    /* last nested 'off' */
84 			physmap_data->set_vpp(pdev, 0);
85 	}
86 	spin_unlock_irqrestore(&info->vpp_lock, flags);
87 }
88 
89 static const char * const rom_probe_types[] = {
90 	"cfi_probe", "jedec_probe", "qinfo_probe", "map_rom", NULL };
91 
92 static const char * const part_probe_types[] = {
93 	"cmdlinepart", "RedBoot", "afs", NULL };
94 
physmap_flash_probe(struct platform_device * dev)95 static int physmap_flash_probe(struct platform_device *dev)
96 {
97 	struct physmap_flash_data *physmap_data;
98 	struct physmap_flash_info *info;
99 	const char * const *probe_type;
100 	const char * const *part_types;
101 	int err = 0;
102 	int i;
103 	int devices_found = 0;
104 
105 	physmap_data = dev_get_platdata(&dev->dev);
106 	if (physmap_data == NULL)
107 		return -ENODEV;
108 
109 	info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info),
110 			    GFP_KERNEL);
111 	if (info == NULL) {
112 		err = -ENOMEM;
113 		goto err_out;
114 	}
115 
116 	if (physmap_data->init) {
117 		err = physmap_data->init(dev);
118 		if (err)
119 			goto err_out;
120 	}
121 
122 	platform_set_drvdata(dev, info);
123 
124 	for (i = 0; i < dev->num_resources; i++) {
125 		printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
126 		       (unsigned long long)resource_size(&dev->resource[i]),
127 		       (unsigned long long)dev->resource[i].start);
128 
129 		if (!devm_request_mem_region(&dev->dev,
130 			dev->resource[i].start,
131 			resource_size(&dev->resource[i]),
132 			dev_name(&dev->dev))) {
133 			dev_err(&dev->dev, "Could not reserve memory region\n");
134 			err = -ENOMEM;
135 			goto err_out;
136 		}
137 
138 		info->map[i].name = dev_name(&dev->dev);
139 		info->map[i].phys = dev->resource[i].start;
140 		info->map[i].size = resource_size(&dev->resource[i]);
141 		info->map[i].bankwidth = physmap_data->width;
142 		info->map[i].set_vpp = physmap_set_vpp;
143 		info->map[i].pfow_base = physmap_data->pfow_base;
144 		info->map[i].map_priv_1 = (unsigned long)dev;
145 
146 		info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
147 						 info->map[i].size);
148 		if (info->map[i].virt == NULL) {
149 			dev_err(&dev->dev, "Failed to ioremap flash region\n");
150 			err = -EIO;
151 			goto err_out;
152 		}
153 
154 		simple_map_init(&info->map[i]);
155 
156 		probe_type = rom_probe_types;
157 		if (physmap_data->probe_type == NULL) {
158 			for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
159 				info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
160 		} else
161 			info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]);
162 
163 		if (info->mtd[i] == NULL) {
164 			dev_err(&dev->dev, "map_probe failed\n");
165 			err = -ENXIO;
166 			goto err_out;
167 		} else {
168 			devices_found++;
169 		}
170 		info->mtd[i]->dev.parent = &dev->dev;
171 	}
172 
173 	if (devices_found == 1) {
174 		info->cmtd = info->mtd[0];
175 	} else if (devices_found > 1) {
176 		/*
177 		 * We detected multiple devices. Concatenate them together.
178 		 */
179 		info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
180 		if (info->cmtd == NULL)
181 			err = -ENXIO;
182 	}
183 	if (err)
184 		goto err_out;
185 
186 	spin_lock_init(&info->vpp_lock);
187 
188 	part_types = physmap_data->part_probe_types ? : part_probe_types;
189 
190 	mtd_device_parse_register(info->cmtd, part_types, NULL,
191 				  physmap_data->parts, physmap_data->nr_parts);
192 	return 0;
193 
194 err_out:
195 	physmap_flash_remove(dev);
196 	return err;
197 }
198 
199 #ifdef CONFIG_PM
physmap_flash_shutdown(struct platform_device * dev)200 static void physmap_flash_shutdown(struct platform_device *dev)
201 {
202 	struct physmap_flash_info *info = platform_get_drvdata(dev);
203 	int i;
204 
205 	for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
206 		if (mtd_suspend(info->mtd[i]) == 0)
207 			mtd_resume(info->mtd[i]);
208 }
209 #else
210 #define physmap_flash_shutdown NULL
211 #endif
212 
213 static struct platform_driver physmap_flash_driver = {
214 	.probe		= physmap_flash_probe,
215 	.remove		= physmap_flash_remove,
216 	.shutdown	= physmap_flash_shutdown,
217 	.driver		= {
218 		.name	= "physmap-flash",
219 	},
220 };
221 
222 
223 #ifdef CONFIG_MTD_PHYSMAP_COMPAT
224 static struct physmap_flash_data physmap_flash_data = {
225 	.width		= CONFIG_MTD_PHYSMAP_BANKWIDTH,
226 };
227 
228 static struct resource physmap_flash_resource = {
229 	.start		= CONFIG_MTD_PHYSMAP_START,
230 	.end		= CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
231 	.flags		= IORESOURCE_MEM,
232 };
233 
234 static struct platform_device physmap_flash = {
235 	.name		= "physmap-flash",
236 	.id		= 0,
237 	.dev		= {
238 		.platform_data	= &physmap_flash_data,
239 	},
240 	.num_resources	= 1,
241 	.resource	= &physmap_flash_resource,
242 };
243 #endif
244 
physmap_init(void)245 static int __init physmap_init(void)
246 {
247 	int err;
248 
249 	err = platform_driver_register(&physmap_flash_driver);
250 #ifdef CONFIG_MTD_PHYSMAP_COMPAT
251 	if (err == 0) {
252 		err = platform_device_register(&physmap_flash);
253 		if (err)
254 			platform_driver_unregister(&physmap_flash_driver);
255 	}
256 #endif
257 
258 	return err;
259 }
260 
physmap_exit(void)261 static void __exit physmap_exit(void)
262 {
263 #ifdef CONFIG_MTD_PHYSMAP_COMPAT
264 	platform_device_unregister(&physmap_flash);
265 #endif
266 	platform_driver_unregister(&physmap_flash_driver);
267 }
268 
269 module_init(physmap_init);
270 module_exit(physmap_exit);
271 
272 MODULE_LICENSE("GPL");
273 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
274 MODULE_DESCRIPTION("Generic configurable MTD map driver");
275 
276 /* legacy platform drivers can't hotplug or coldplg */
277 #ifndef CONFIG_MTD_PHYSMAP_COMPAT
278 /* work with hotplug and coldplug */
279 MODULE_ALIAS("platform:physmap-flash");
280 #endif
281