1/*
2 * $Id: physmap.c,v 1.1.1.1 2007/08/03 18:52:44 Exp $
3 *
4 * Normal mappings of chips in physical memory
5 *
6 * Copyright (C) 2003 MontaVista Software Inc.
7 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
8 *
9 * 031022 - [jsun] add run-time configure and partition setup
10 */
11
12#include <linux/module.h>
13#include <linux/types.h>
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/slab.h>
17#include <linux/device.h>
18#include <linux/platform_device.h>
19#include <linux/mtd/mtd.h>
20#include <linux/mtd/map.h>
21#include <linux/mtd/partitions.h>
22#include <linux/mtd/physmap.h>
23#include <asm/io.h>
24
25struct physmap_flash_info {
26	struct mtd_info		*mtd;
27	struct map_info		map;
28	struct resource		*res;
29#ifdef CONFIG_MTD_PARTITIONS
30	int			nr_parts;
31	struct mtd_partition	*parts;
32#endif
33};
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
41	info = platform_get_drvdata(dev);
42	if (info == NULL)
43		return 0;
44	platform_set_drvdata(dev, NULL);
45
46	physmap_data = dev->dev.platform_data;
47
48	if (info->mtd != NULL) {
49#ifdef CONFIG_MTD_PARTITIONS
50		if (info->nr_parts) {
51			del_mtd_partitions(info->mtd);
52			kfree(info->parts);
53		} else if (physmap_data->nr_parts) {
54			del_mtd_partitions(info->mtd);
55		} else {
56			del_mtd_device(info->mtd);
57		}
58#else
59		del_mtd_device(info->mtd);
60#endif
61		map_destroy(info->mtd);
62	}
63
64	if (info->map.virt != NULL)
65		iounmap(info->map.virt);
66
67	if (info->res != NULL) {
68		release_resource(info->res);
69		kfree(info->res);
70	}
71
72	return 0;
73}
74
75static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
76#ifdef CONFIG_MTD_PARTITIONS
77static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
78#endif
79
80static int physmap_flash_probe(struct platform_device *dev)
81{
82	struct physmap_flash_data *physmap_data;
83	struct physmap_flash_info *info;
84	const char **probe_type;
85	int err;
86
87	physmap_data = dev->dev.platform_data;
88	if (physmap_data == NULL)
89		return -ENODEV;
90
91       	printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
92	    (unsigned long long)(dev->resource->end - dev->resource->start + 1),
93	    (unsigned long long)dev->resource->start);
94
95	info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
96	if (info == NULL) {
97		err = -ENOMEM;
98		goto err_out;
99	}
100
101	platform_set_drvdata(dev, info);
102
103	info->res = request_mem_region(dev->resource->start,
104			dev->resource->end - dev->resource->start + 1,
105			dev->dev.bus_id);
106	if (info->res == NULL) {
107		dev_err(&dev->dev, "Could not reserve memory region\n");
108		err = -ENOMEM;
109		goto err_out;
110	}
111
112	info->map.name = dev->dev.bus_id;
113	info->map.phys = dev->resource->start;
114	info->map.size = dev->resource->end - dev->resource->start + 1;
115	info->map.bankwidth = physmap_data->width;
116	info->map.set_vpp = physmap_data->set_vpp;
117
118	info->map.virt = ioremap(info->map.phys, info->map.size);
119	if (info->map.virt == NULL) {
120		dev_err(&dev->dev, "Failed to ioremap flash region\n");
121		err = EIO;
122		goto err_out;
123	}
124
125	simple_map_init(&info->map);
126
127	probe_type = rom_probe_types;
128	for (; info->mtd == NULL && *probe_type != NULL; probe_type++)
129		info->mtd = do_map_probe(*probe_type, &info->map);
130	if (info->mtd == NULL) {
131		dev_err(&dev->dev, "map_probe failed\n");
132		err = -ENXIO;
133		goto err_out;
134	}
135	info->mtd->owner = THIS_MODULE;
136
137#ifdef CONFIG_MTD_PARTITIONS
138	err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0);
139	if (err > 0) {
140		add_mtd_partitions(info->mtd, info->parts, err);
141		return 0;
142	}
143
144	if (physmap_data->nr_parts) {
145		printk(KERN_NOTICE "Using physmap partition information\n");
146		add_mtd_partitions(info->mtd, physmap_data->parts,
147						physmap_data->nr_parts);
148		return 0;
149	}
150#endif
151
152	add_mtd_device(info->mtd);
153	return 0;
154
155err_out:
156	physmap_flash_remove(dev);
157	return err;
158}
159
160#ifdef CONFIG_PM
161static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state)
162{
163	struct physmap_flash_info *info = platform_get_drvdata(dev);
164	int ret = 0;
165
166	if (info)
167		ret = info->mtd->suspend(info->mtd);
168
169	return ret;
170}
171
172static int physmap_flash_resume(struct platform_device *dev)
173{
174	struct physmap_flash_info *info = platform_get_drvdata(dev);
175	if (info)
176		info->mtd->resume(info->mtd);
177	return 0;
178}
179
180static void physmap_flash_shutdown(struct platform_device *dev)
181{
182	struct physmap_flash_info *info = platform_get_drvdata(dev);
183	if (info && info->mtd->suspend(info->mtd) == 0)
184		info->mtd->resume(info->mtd);
185}
186#endif
187
188static struct platform_driver physmap_flash_driver = {
189	.probe		= physmap_flash_probe,
190	.remove		= physmap_flash_remove,
191#ifdef CONFIG_PM
192	.suspend	= physmap_flash_suspend,
193	.resume		= physmap_flash_resume,
194	.shutdown	= physmap_flash_shutdown,
195#endif
196	.driver		= {
197		.name	= "physmap-flash",
198	},
199};
200
201
202#ifdef CONFIG_MTD_PHYSMAP_LEN
203#if CONFIG_MTD_PHYSMAP_LEN != 0
204#warning using PHYSMAP compat code
205#define PHYSMAP_COMPAT
206#endif
207#endif
208
209#ifdef PHYSMAP_COMPAT
210static struct physmap_flash_data physmap_flash_data = {
211	.width		= CONFIG_MTD_PHYSMAP_BANKWIDTH,
212};
213
214static struct resource physmap_flash_resource = {
215	.start		= CONFIG_MTD_PHYSMAP_START,
216	.end		= CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
217	.flags		= IORESOURCE_MEM,
218};
219
220static struct platform_device physmap_flash = {
221	.name		= "physmap-flash",
222	.id		= 0,
223	.dev		= {
224		.platform_data	= &physmap_flash_data,
225	},
226	.num_resources	= 1,
227	.resource	= &physmap_flash_resource,
228};
229
230void physmap_configure(unsigned long addr, unsigned long size,
231		int bankwidth, void (*set_vpp)(struct map_info *, int))
232{
233	physmap_flash_resource.start = addr;
234	physmap_flash_resource.end = addr + size - 1;
235	physmap_flash_data.width = bankwidth;
236	physmap_flash_data.set_vpp = set_vpp;
237}
238
239#ifdef CONFIG_MTD_PARTITIONS
240void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
241{
242	physmap_flash_data.nr_parts = num_parts;
243	physmap_flash_data.parts = parts;
244}
245#endif
246#endif
247
248static int __init physmap_init(void)
249{
250	int err;
251
252	err = platform_driver_register(&physmap_flash_driver);
253#ifdef PHYSMAP_COMPAT
254	if (err == 0)
255		platform_device_register(&physmap_flash);
256#endif
257
258	return err;
259}
260
261static void __exit physmap_exit(void)
262{
263#ifdef PHYSMAP_COMPAT
264	platform_device_unregister(&physmap_flash);
265#endif
266	platform_driver_unregister(&physmap_flash_driver);
267}
268
269module_init(physmap_init);
270module_exit(physmap_exit);
271
272MODULE_LICENSE("GPL");
273MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
274MODULE_DESCRIPTION("Generic configurable MTD map driver");
275