blob: d2059261ca8b07d8c7068cb08b855cb548a8fe5d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * 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>
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010015#include <linux/device.h>
16#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/mtd/mtd.h>
18#include <linux/mtd/map.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/mtd/partitions.h>
Adrian Bunk2b9175c2005-11-29 14:49:38 +000020#include <linux/mtd/physmap.h>
Stefan Roesedf66e712008-02-01 15:26:54 +010021#include <linux/mtd/concat.h>
Atsushi Nemoto3136e902008-11-26 10:26:29 +000022#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070023
Stefan Roesedf66e712008-02-01 15:26:54 +010024#define MAX_RESOURCES 4
25
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010026struct physmap_flash_info {
Stefan Roesedf66e712008-02-01 15:26:54 +010027 struct mtd_info *mtd[MAX_RESOURCES];
28 struct mtd_info *cmtd;
29 struct map_info map[MAX_RESOURCES];
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010030 int nr_parts;
Atsushi Nemotoe4808142009-02-11 13:12:17 -080031 struct mtd_partition *parts;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032};
33
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010034static int physmap_flash_remove(struct platform_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070035{
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010036 struct physmap_flash_info *info;
37 struct physmap_flash_data *physmap_data;
Stefan Roesedf66e712008-02-01 15:26:54 +010038 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010040 info = platform_get_drvdata(dev);
41 if (info == NULL)
42 return 0;
43 platform_set_drvdata(dev, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010045 physmap_data = dev->dev.platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -040047 if (info->cmtd) {
Jamie Iles984e6d82011-05-23 10:22:45 +010048 mtd_device_unregister(info->cmtd);
49 if (info->nr_parts)
50 kfree(info->parts);
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -040051 if (info->cmtd != info->mtd[0])
52 mtd_concat_destroy(info->cmtd);
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -040053 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
Stefan Roesedf66e712008-02-01 15:26:54 +010055 for (i = 0; i < MAX_RESOURCES; i++) {
Atsushi Nemotoe4808142009-02-11 13:12:17 -080056 if (info->mtd[i] != NULL)
Stefan Roesedf66e712008-02-01 15:26:54 +010057 map_destroy(info->mtd[i]);
Stefan Roesedf66e712008-02-01 15:26:54 +010058 }
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010059 return 0;
60}
61
Alexey Korolevd8140832008-12-16 18:22:39 +000062static const char *rom_probe_types[] = {
63 "cfi_probe",
64 "jedec_probe",
65 "qinfo_probe",
66 "map_rom",
67 NULL };
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010068static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010069
70static int physmap_flash_probe(struct platform_device *dev)
71{
72 struct physmap_flash_data *physmap_data;
73 struct physmap_flash_info *info;
74 const char **probe_type;
Stefan Roesedf66e712008-02-01 15:26:54 +010075 int err = 0;
76 int i;
77 int devices_found = 0;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010078
79 physmap_data = dev->dev.platform_data;
80 if (physmap_data == NULL)
81 return -ENODEV;
82
Atsushi Nemoto3136e902008-11-26 10:26:29 +000083 info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info),
84 GFP_KERNEL);
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010085 if (info == NULL) {
86 err = -ENOMEM;
87 goto err_out;
88 }
Lennert Buytenhek73566ed2006-05-07 17:16:36 +010089
90 platform_set_drvdata(dev, info);
91
Stefan Roesedf66e712008-02-01 15:26:54 +010092 for (i = 0; i < dev->num_resources; i++) {
93 printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
H Hartley Sweeten1d90d2c2010-06-14 11:57:54 -050094 (unsigned long long)resource_size(&dev->resource[i]),
Stefan Roesedf66e712008-02-01 15:26:54 +010095 (unsigned long long)dev->resource[i].start);
96
Atsushi Nemoto3136e902008-11-26 10:26:29 +000097 if (!devm_request_mem_region(&dev->dev,
98 dev->resource[i].start,
H Hartley Sweeten1d90d2c2010-06-14 11:57:54 -050099 resource_size(&dev->resource[i]),
Kay Sievers160bbab2008-12-23 10:00:14 +0000100 dev_name(&dev->dev))) {
Stefan Roesedf66e712008-02-01 15:26:54 +0100101 dev_err(&dev->dev, "Could not reserve memory region\n");
102 err = -ENOMEM;
103 goto err_out;
104 }
105
Kay Sievers160bbab2008-12-23 10:00:14 +0000106 info->map[i].name = dev_name(&dev->dev);
Stefan Roesedf66e712008-02-01 15:26:54 +0100107 info->map[i].phys = dev->resource[i].start;
H Hartley Sweeten1d90d2c2010-06-14 11:57:54 -0500108 info->map[i].size = resource_size(&dev->resource[i]);
Stefan Roesedf66e712008-02-01 15:26:54 +0100109 info->map[i].bankwidth = physmap_data->width;
110 info->map[i].set_vpp = physmap_data->set_vpp;
Alexey Korolevd8140832008-12-16 18:22:39 +0000111 info->map[i].pfow_base = physmap_data->pfow_base;
Stefan Roesedf66e712008-02-01 15:26:54 +0100112
Atsushi Nemoto3136e902008-11-26 10:26:29 +0000113 info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
114 info->map[i].size);
Stefan Roesedf66e712008-02-01 15:26:54 +0100115 if (info->map[i].virt == NULL) {
116 dev_err(&dev->dev, "Failed to ioremap flash region\n");
Roel Kluin895fb492009-11-11 21:47:06 +0100117 err = -EIO;
Stefan Roesedf66e712008-02-01 15:26:54 +0100118 goto err_out;
119 }
120
121 simple_map_init(&info->map[i]);
122
123 probe_type = rom_probe_types;
Barry Song78ef7fa2010-01-15 15:50:14 +0800124 if (physmap_data->probe_type == NULL) {
125 for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
126 info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
127 } else
128 info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]);
129
Stefan Roesedf66e712008-02-01 15:26:54 +0100130 if (info->mtd[i] == NULL) {
131 dev_err(&dev->dev, "map_probe failed\n");
132 err = -ENXIO;
133 goto err_out;
134 } else {
135 devices_found++;
136 }
137 info->mtd[i]->owner = THIS_MODULE;
David Brownell87f39f02009-03-26 00:42:50 -0700138 info->mtd[i]->dev.parent = &dev->dev;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100139 }
140
Stefan Roesedf66e712008-02-01 15:26:54 +0100141 if (devices_found == 1) {
142 info->cmtd = info->mtd[0];
143 } else if (devices_found > 1) {
144 /*
145 * We detected multiple devices. Concatenate them together.
146 */
Kay Sievers160bbab2008-12-23 10:00:14 +0000147 info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
Stefan Roesedf66e712008-02-01 15:26:54 +0100148 if (info->cmtd == NULL)
149 err = -ENXIO;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100150 }
Stefan Roesedf66e712008-02-01 15:26:54 +0100151 if (err)
152 goto err_out;
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100153
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -0400154 err = parse_mtd_partitions(info->cmtd, part_probe_types,
Jamie Iles984e6d82011-05-23 10:22:45 +0100155 &info->parts, 0);
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -0400156 if (err > 0) {
Jamie Iles984e6d82011-05-23 10:22:45 +0100157 mtd_device_register(info->cmtd, info->parts, err);
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -0400158 info->nr_parts = err;
159 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -0400162 if (physmap_data->nr_parts) {
163 printk(KERN_NOTICE "Using physmap partition information\n");
Jamie Iles984e6d82011-05-23 10:22:45 +0100164 mtd_device_register(info->cmtd, physmap_data->parts,
165 physmap_data->nr_parts);
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -0400166 return 0;
167 }
H Hartley Sweeten8ce110a2009-10-20 12:23:33 -0400168
Jamie Iles984e6d82011-05-23 10:22:45 +0100169 mtd_device_register(info->cmtd, NULL, 0);
170
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100171 return 0;
172
173err_out:
174 physmap_flash_remove(dev);
175 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176}
177
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200178#ifdef CONFIG_PM
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200179static void physmap_flash_shutdown(struct platform_device *dev)
180{
181 struct physmap_flash_info *info = platform_get_drvdata(dev);
Stefan Roesedf66e712008-02-01 15:26:54 +0100182 int i;
183
Anton Vorontsov4a5691c2008-03-28 14:16:09 -0700184 for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
Robert Jarzmik7b249192008-07-22 09:39:00 +0200185 if (info->mtd[i]->suspend && info->mtd[i]->resume)
186 if (info->mtd[i]->suspend(info->mtd[i]) == 0)
187 info->mtd[i]->resume(info->mtd[i]);
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200188}
akpm@linux-foundation.orgd5476682008-02-03 12:56:03 -0800189#else
akpm@linux-foundation.orgd5476682008-02-03 12:56:03 -0800190#define physmap_flash_shutdown NULL
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200191#endif
192
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100193static struct platform_driver physmap_flash_driver = {
194 .probe = physmap_flash_probe,
195 .remove = physmap_flash_remove,
Lennert Buytenhek17c2dae2006-09-21 23:16:48 +0200196 .shutdown = physmap_flash_shutdown,
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100197 .driver = {
198 .name = "physmap-flash",
Kay Sievers41d867c2008-04-18 13:44:26 -0700199 .owner = THIS_MODULE,
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100200 },
201};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800204#ifdef CONFIG_MTD_PHYSMAP_COMPAT
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100205static struct physmap_flash_data physmap_flash_data = {
206 .width = CONFIG_MTD_PHYSMAP_BANKWIDTH,
207};
208
209static struct resource physmap_flash_resource = {
210 .start = CONFIG_MTD_PHYSMAP_START,
Sascha Hauer6d4f8222006-06-27 14:38:15 +0100211 .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100212 .flags = IORESOURCE_MEM,
213};
214
215static struct platform_device physmap_flash = {
216 .name = "physmap-flash",
217 .id = 0,
218 .dev = {
219 .platform_data = &physmap_flash_data,
220 },
221 .num_resources = 1,
222 .resource = &physmap_flash_resource,
223};
224
225void physmap_configure(unsigned long addr, unsigned long size,
226 int bankwidth, void (*set_vpp)(struct map_info *, int))
227{
228 physmap_flash_resource.start = addr;
229 physmap_flash_resource.end = addr + size - 1;
230 physmap_flash_data.width = bankwidth;
231 physmap_flash_data.set_vpp = set_vpp;
232}
233
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100234void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
235{
236 physmap_flash_data.nr_parts = num_parts;
237 physmap_flash_data.parts = parts;
238}
239#endif
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100240
241static int __init physmap_init(void)
242{
243 int err;
244
245 err = platform_driver_register(&physmap_flash_driver);
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800246#ifdef CONFIG_MTD_PHYSMAP_COMPAT
H Hartley Sweeten1ca5d2f2010-04-02 17:46:30 -0500247 if (err == 0) {
248 err = platform_device_register(&physmap_flash);
249 if (err)
250 platform_driver_unregister(&physmap_flash_driver);
251 }
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100252#endif
253
254 return err;
255}
256
257static void __exit physmap_exit(void)
258{
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800259#ifdef CONFIG_MTD_PHYSMAP_COMPAT
Lennert Buytenhek73566ed2006-05-07 17:16:36 +0100260 platform_device_unregister(&physmap_flash);
261#endif
262 platform_driver_unregister(&physmap_flash_driver);
263}
264
265module_init(physmap_init);
266module_exit(physmap_exit);
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268MODULE_LICENSE("GPL");
269MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
270MODULE_DESCRIPTION("Generic configurable MTD map driver");
Kay Sievers41d867c2008-04-18 13:44:26 -0700271
272/* legacy platform drivers can't hotplug or coldplg */
Mike Frysingerdcb3e132008-12-01 14:23:40 -0800273#ifndef CONFIG_MTD_PHYSMAP_COMPAT
Kay Sievers41d867c2008-04-18 13:44:26 -0700274/* work with hotplug and coldplug */
275MODULE_ALIAS("platform:physmap-flash");
276#endif