1/*
2 * Flash memory access on SA11x0 based devices
3 *
4 * (C) 2000 Nicolas Pitre <nico@cam.org>
5 *
6 * $Id: sa1100-flash.c,v 1.1.1.1 2007/08/03 18:52:44 Exp $
7 */
8#include <linux/module.h>
9#include <linux/types.h>
10#include <linux/ioport.h>
11#include <linux/kernel.h>
12#include <linux/init.h>
13#include <linux/errno.h>
14#include <linux/slab.h>
15#include <linux/platform_device.h>
16#include <linux/err.h>
17
18#include <linux/mtd/mtd.h>
19#include <linux/mtd/map.h>
20#include <linux/mtd/partitions.h>
21#include <linux/mtd/concat.h>
22
23#include <asm/hardware.h>
24#include <asm/io.h>
25#include <asm/sizes.h>
26#include <asm/mach/flash.h>
27
28
29struct sa_subdev_info {
30	char name[16];
31	struct map_info map;
32	struct mtd_info *mtd;
33	struct flash_platform_data *plat;
34};
35
36struct sa_info {
37	struct mtd_partition	*parts;
38	struct mtd_info		*mtd;
39	int			num_subdev;
40	unsigned int		nr_parts;
41	struct sa_subdev_info	subdev[0];
42};
43
44static void sa1100_set_vpp(struct map_info *map, int on)
45{
46	struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
47	subdev->plat->set_vpp(on);
48}
49
50static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
51{
52	if (subdev->mtd)
53		map_destroy(subdev->mtd);
54	if (subdev->map.virt)
55		iounmap(subdev->map.virt);
56	release_mem_region(subdev->map.phys, subdev->map.size);
57}
58
59static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
60{
61	unsigned long phys;
62	unsigned int size;
63	int ret;
64
65	phys = res->start;
66	size = res->end - phys + 1;
67
68	/*
69	 * Retrieve the bankwidth from the MSC registers.
70	 * We currently only implement CS0 and CS1 here.
71	 */
72	switch (phys) {
73	default:
74		printk(KERN_WARNING "SA1100 flash: unknown base address "
75		       "0x%08lx, assuming CS0\n", phys);
76
77	case SA1100_CS0_PHYS:
78		subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
79		break;
80
81	case SA1100_CS1_PHYS:
82		subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
83		break;
84	}
85
86	if (!request_mem_region(phys, size, subdev->name)) {
87		ret = -EBUSY;
88		goto out;
89	}
90
91	if (subdev->plat->set_vpp)
92		subdev->map.set_vpp = sa1100_set_vpp;
93
94	subdev->map.phys = phys;
95	subdev->map.size = size;
96	subdev->map.virt = ioremap(phys, size);
97	if (!subdev->map.virt) {
98		ret = -ENOMEM;
99		goto err;
100	}
101
102	simple_map_init(&subdev->map);
103
104	/*
105	 * Now let's probe for the actual flash.  Do it here since
106	 * specific machine settings might have been set above.
107	 */
108	subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
109	if (subdev->mtd == NULL) {
110		ret = -ENXIO;
111		goto err;
112	}
113	subdev->mtd->owner = THIS_MODULE;
114
115	printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
116		"%d-bit\n", phys, subdev->mtd->size >> 20,
117		subdev->map.bankwidth * 8);
118
119	return 0;
120
121 err:
122	sa1100_destroy_subdev(subdev);
123 out:
124	return ret;
125}
126
127static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
128{
129	int i;
130
131	if (info->mtd) {
132		if (info->nr_parts == 0)
133			del_mtd_device(info->mtd);
134#ifdef CONFIG_MTD_PARTITIONS
135		else
136			del_mtd_partitions(info->mtd);
137#endif
138#ifdef CONFIG_MTD_CONCAT
139		if (info->mtd != info->subdev[0].mtd)
140			mtd_concat_destroy(info->mtd);
141#endif
142	}
143
144	kfree(info->parts);
145
146	for (i = info->num_subdev - 1; i >= 0; i--)
147		sa1100_destroy_subdev(&info->subdev[i]);
148	kfree(info);
149
150	if (plat->exit)
151		plat->exit();
152}
153
154static struct sa_info *__init
155sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
156{
157	struct sa_info *info;
158	int nr, size, i, ret = 0;
159
160	/*
161	 * Count number of devices.
162	 */
163	for (nr = 0; ; nr++)
164		if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
165			break;
166
167	if (nr == 0) {
168		ret = -ENODEV;
169		goto out;
170	}
171
172	size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
173
174	/*
175	 * Allocate the map_info structs in one go.
176	 */
177	info = kzalloc(size, GFP_KERNEL);
178	if (!info) {
179		ret = -ENOMEM;
180		goto out;
181	}
182
183	if (plat->init) {
184		ret = plat->init();
185		if (ret)
186			goto err;
187	}
188
189	/*
190	 * Claim and then map the memory regions.
191	 */
192	for (i = 0; i < nr; i++) {
193		struct sa_subdev_info *subdev = &info->subdev[i];
194		struct resource *res;
195
196		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
197		if (!res)
198			break;
199
200		subdev->map.name = subdev->name;
201		sprintf(subdev->name, "%s-%d", plat->name, i);
202		subdev->plat = plat;
203
204		ret = sa1100_probe_subdev(subdev, res);
205		if (ret)
206			break;
207	}
208
209	info->num_subdev = i;
210
211	/*
212	 * ENXIO is special.  It means we didn't find a chip when we probed.
213	 */
214	if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
215		goto err;
216
217	/*
218	 * If we found one device, don't bother with concat support.  If
219	 * we found multiple devices, use concat if we have it available,
220	 * otherwise fail.  Either way, it'll be called "sa1100".
221	 */
222	if (info->num_subdev == 1) {
223		strcpy(info->subdev[0].name, plat->name);
224		info->mtd = info->subdev[0].mtd;
225		ret = 0;
226	} else if (info->num_subdev > 1) {
227#ifdef CONFIG_MTD_CONCAT
228		struct mtd_info *cdev[nr];
229		/*
230		 * We detected multiple devices.  Concatenate them together.
231		 */
232		for (i = 0; i < info->num_subdev; i++)
233			cdev[i] = info->subdev[i].mtd;
234
235		info->mtd = mtd_concat_create(cdev, info->num_subdev,
236					      plat->name);
237		if (info->mtd == NULL)
238			ret = -ENXIO;
239#else
240		printk(KERN_ERR "SA1100 flash: multiple devices "
241		       "found but MTD concat support disabled.\n");
242		ret = -ENXIO;
243#endif
244	}
245
246	if (ret == 0)
247		return info;
248
249 err:
250	sa1100_destroy(info, plat);
251 out:
252	return ERR_PTR(ret);
253}
254
255static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
256
257static int __init sa1100_mtd_probe(struct platform_device *pdev)
258{
259	struct flash_platform_data *plat = pdev->dev.platform_data;
260	struct mtd_partition *parts;
261	const char *part_type = NULL;
262	struct sa_info *info;
263	int err, nr_parts = 0;
264
265	if (!plat)
266		return -ENODEV;
267
268	info = sa1100_setup_mtd(pdev, plat);
269	if (IS_ERR(info)) {
270		err = PTR_ERR(info);
271		goto out;
272	}
273
274	/*
275	 * Partition selection stuff.
276	 */
277#ifdef CONFIG_MTD_PARTITIONS
278	nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0);
279	if (nr_parts > 0) {
280		info->parts = parts;
281		part_type = "dynamic";
282	} else
283#endif
284	{
285		parts = plat->parts;
286		nr_parts = plat->nr_parts;
287		part_type = "static";
288	}
289
290	if (nr_parts == 0) {
291		printk(KERN_NOTICE "SA1100 flash: no partition info "
292			"available, registering whole flash\n");
293		add_mtd_device(info->mtd);
294	} else {
295		printk(KERN_NOTICE "SA1100 flash: using %s partition "
296			"definition\n", part_type);
297		add_mtd_partitions(info->mtd, parts, nr_parts);
298	}
299
300	info->nr_parts = nr_parts;
301
302	platform_set_drvdata(pdev, info);
303	err = 0;
304
305 out:
306	return err;
307}
308
309static int __exit sa1100_mtd_remove(struct platform_device *pdev)
310{
311	struct sa_info *info = platform_get_drvdata(pdev);
312	struct flash_platform_data *plat = pdev->dev.platform_data;
313
314	platform_set_drvdata(pdev, NULL);
315	sa1100_destroy(info, plat);
316
317	return 0;
318}
319
320#ifdef CONFIG_PM
321static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state)
322{
323	struct sa_info *info = platform_get_drvdata(dev);
324	int ret = 0;
325
326	if (info)
327		ret = info->mtd->suspend(info->mtd);
328
329	return ret;
330}
331
332static int sa1100_mtd_resume(struct platform_device *dev)
333{
334	struct sa_info *info = platform_get_drvdata(dev);
335	if (info)
336		info->mtd->resume(info->mtd);
337	return 0;
338}
339
340static void sa1100_mtd_shutdown(struct platform_device *dev)
341{
342	struct sa_info *info = platform_get_drvdata(dev);
343	if (info && info->mtd->suspend(info->mtd) == 0)
344		info->mtd->resume(info->mtd);
345}
346#else
347#define sa1100_mtd_suspend NULL
348#define sa1100_mtd_resume  NULL
349#define sa1100_mtd_shutdown NULL
350#endif
351
352static struct platform_driver sa1100_mtd_driver = {
353	.probe		= sa1100_mtd_probe,
354	.remove		= __exit_p(sa1100_mtd_remove),
355	.suspend	= sa1100_mtd_suspend,
356	.resume		= sa1100_mtd_resume,
357	.shutdown	= sa1100_mtd_shutdown,
358	.driver		= {
359		.name	= "flash",
360	},
361};
362
363static int __init sa1100_mtd_init(void)
364{
365	return platform_driver_register(&sa1100_mtd_driver);
366}
367
368static void __exit sa1100_mtd_exit(void)
369{
370	platform_driver_unregister(&sa1100_mtd_driver);
371}
372
373module_init(sa1100_mtd_init);
374module_exit(sa1100_mtd_exit);
375
376MODULE_AUTHOR("Nicolas Pitre");
377MODULE_DESCRIPTION("SA1100 CFI map driver");
378MODULE_LICENSE("GPL");
379