• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/mtd/maps/
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
26struct physmap_flash_info {
27	struct mtd_info		*mtd[MAX_RESOURCES];
28	struct mtd_info		*cmtd;
29	struct map_info		map[MAX_RESOURCES];
30#ifdef CONFIG_MTD_PARTITIONS
31	int			nr_parts;
32	struct mtd_partition	*parts;
33#endif
34};
35
36static int physmap_flash_remove(struct platform_device *dev)
37{
38	struct physmap_flash_info *info;
39	struct physmap_flash_data *physmap_data;
40	int i;
41
42	info = platform_get_drvdata(dev);
43	if (info == NULL)
44		return 0;
45	platform_set_drvdata(dev, NULL);
46
47	physmap_data = dev->dev.platform_data;
48
49	if (info->cmtd) {
50#ifdef CONFIG_MTD_PARTITIONS
51		if (info->nr_parts || physmap_data->nr_parts) {
52			del_mtd_partitions(info->cmtd);
53
54			if (info->nr_parts)
55				kfree(info->parts);
56		} else {
57			del_mtd_device(info->cmtd);
58		}
59#else
60		del_mtd_device(info->cmtd);
61#endif
62#ifdef CONFIG_MTD_CONCAT
63		if (info->cmtd != info->mtd[0])
64			mtd_concat_destroy(info->cmtd);
65#endif
66	}
67
68	for (i = 0; i < MAX_RESOURCES; i++) {
69		if (info->mtd[i] != NULL)
70			map_destroy(info->mtd[i]);
71	}
72	return 0;
73}
74
75static const char *rom_probe_types[] = {
76					"cfi_probe",
77					"jedec_probe",
78					"qinfo_probe",
79					"map_rom",
80					NULL };
81#ifdef CONFIG_MTD_PARTITIONS
82static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
83#endif
84
85static int physmap_flash_probe(struct platform_device *dev)
86{
87	struct physmap_flash_data *physmap_data;
88	struct physmap_flash_info *info;
89	const char **probe_type;
90	int err = 0;
91	int i;
92	int devices_found = 0;
93
94	physmap_data = dev->dev.platform_data;
95	if (physmap_data == NULL)
96		return -ENODEV;
97
98	info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info),
99			    GFP_KERNEL);
100	if (info == NULL) {
101		err = -ENOMEM;
102		goto err_out;
103	}
104
105	platform_set_drvdata(dev, info);
106
107	for (i = 0; i < dev->num_resources; i++) {
108		printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
109		       (unsigned long long)resource_size(&dev->resource[i]),
110		       (unsigned long long)dev->resource[i].start);
111
112		if (!devm_request_mem_region(&dev->dev,
113			dev->resource[i].start,
114			resource_size(&dev->resource[i]),
115			dev_name(&dev->dev))) {
116			dev_err(&dev->dev, "Could not reserve memory region\n");
117			err = -ENOMEM;
118			goto err_out;
119		}
120
121		info->map[i].name = dev_name(&dev->dev);
122		info->map[i].phys = dev->resource[i].start;
123		info->map[i].size = resource_size(&dev->resource[i]);
124		info->map[i].bankwidth = physmap_data->width;
125		info->map[i].set_vpp = physmap_data->set_vpp;
126		info->map[i].pfow_base = physmap_data->pfow_base;
127
128		info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
129						 info->map[i].size);
130		if (info->map[i].virt == NULL) {
131			dev_err(&dev->dev, "Failed to ioremap flash region\n");
132			err = -EIO;
133			goto err_out;
134		}
135
136		simple_map_init(&info->map[i]);
137
138		probe_type = rom_probe_types;
139		if (physmap_data->probe_type == NULL) {
140			for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
141				info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
142		} else
143			info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]);
144
145		if (info->mtd[i] == NULL) {
146			dev_err(&dev->dev, "map_probe failed\n");
147			err = -ENXIO;
148			goto err_out;
149		} else {
150			devices_found++;
151		}
152		info->mtd[i]->owner = THIS_MODULE;
153		info->mtd[i]->dev.parent = &dev->dev;
154	}
155
156	if (devices_found == 1) {
157		info->cmtd = info->mtd[0];
158	} else if (devices_found > 1) {
159		/*
160		 * We detected multiple devices. Concatenate them together.
161		 */
162#ifdef CONFIG_MTD_CONCAT
163		info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
164		if (info->cmtd == NULL)
165			err = -ENXIO;
166#else
167		printk(KERN_ERR "physmap-flash: multiple devices "
168		       "found but MTD concat support disabled.\n");
169		err = -ENXIO;
170#endif
171	}
172	if (err)
173		goto err_out;
174
175#ifdef CONFIG_MTD_PARTITIONS
176	err = parse_mtd_partitions(info->cmtd, part_probe_types,
177				&info->parts, 0);
178	if (err > 0) {
179		add_mtd_partitions(info->cmtd, info->parts, err);
180		info->nr_parts = err;
181		return 0;
182	}
183
184	if (physmap_data->nr_parts) {
185		printk(KERN_NOTICE "Using physmap partition information\n");
186		add_mtd_partitions(info->cmtd, physmap_data->parts,
187				   physmap_data->nr_parts);
188		return 0;
189	}
190#endif
191
192	add_mtd_device(info->cmtd);
193	return 0;
194
195err_out:
196	physmap_flash_remove(dev);
197	return err;
198}
199
200#ifdef CONFIG_PM
201static void physmap_flash_shutdown(struct platform_device *dev)
202{
203	struct physmap_flash_info *info = platform_get_drvdata(dev);
204	int i;
205
206	for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
207		if (info->mtd[i]->suspend && info->mtd[i]->resume)
208			if (info->mtd[i]->suspend(info->mtd[i]) == 0)
209				info->mtd[i]->resume(info->mtd[i]);
210}
211#else
212#define physmap_flash_shutdown NULL
213#endif
214
215static struct platform_driver physmap_flash_driver = {
216	.probe		= physmap_flash_probe,
217	.remove		= physmap_flash_remove,
218	.shutdown	= physmap_flash_shutdown,
219	.driver		= {
220		.name	= "physmap-flash",
221		.owner	= THIS_MODULE,
222	},
223};
224
225
226#ifdef CONFIG_MTD_PHYSMAP_COMPAT
227static struct physmap_flash_data physmap_flash_data = {
228	.width		= CONFIG_MTD_PHYSMAP_BANKWIDTH,
229};
230
231static struct resource physmap_flash_resource = {
232	.start		= CONFIG_MTD_PHYSMAP_START,
233	.end		= CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
234	.flags		= IORESOURCE_MEM,
235};
236
237static struct platform_device physmap_flash = {
238	.name		= "physmap-flash",
239	.id		= 0,
240	.dev		= {
241		.platform_data	= &physmap_flash_data,
242	},
243	.num_resources	= 1,
244	.resource	= &physmap_flash_resource,
245};
246
247void physmap_configure(unsigned long addr, unsigned long size,
248		int bankwidth, void (*set_vpp)(struct map_info *, int))
249{
250	physmap_flash_resource.start = addr;
251	physmap_flash_resource.end = addr + size - 1;
252	physmap_flash_data.width = bankwidth;
253	physmap_flash_data.set_vpp = set_vpp;
254}
255
256#ifdef CONFIG_MTD_PARTITIONS
257void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
258{
259	physmap_flash_data.nr_parts = num_parts;
260	physmap_flash_data.parts = parts;
261}
262#endif
263#endif
264
265static int __init physmap_init(void)
266{
267	int err;
268
269	err = platform_driver_register(&physmap_flash_driver);
270#ifdef CONFIG_MTD_PHYSMAP_COMPAT
271	if (err == 0) {
272		err = platform_device_register(&physmap_flash);
273		if (err)
274			platform_driver_unregister(&physmap_flash_driver);
275	}
276#endif
277
278	return err;
279}
280
281static void __exit physmap_exit(void)
282{
283#ifdef CONFIG_MTD_PHYSMAP_COMPAT
284	platform_device_unregister(&physmap_flash);
285#endif
286	platform_driver_unregister(&physmap_flash_driver);
287}
288
289module_init(physmap_init);
290module_exit(physmap_exit);
291
292MODULE_LICENSE("GPL");
293MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
294MODULE_DESCRIPTION("Generic configurable MTD map driver");
295
296/* legacy platform drivers can't hotplug or coldplg */
297#ifndef CONFIG_MTD_PHYSMAP_COMPAT
298/* work with hotplug and coldplug */
299MODULE_ALIAS("platform:physmap-flash");
300#endif
301