diff options
Diffstat (limited to 'drivers/mtd/maps/physmap_of.c')
-rw-r--r-- | drivers/mtd/maps/physmap_of.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c new file mode 100644 index 000000000000..7efe744ad31e --- /dev/null +++ b/drivers/mtd/maps/physmap_of.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * Normal mappings of chips in physical memory for OF devices | ||
3 | * | ||
4 | * Copyright (C) 2006 MontaVista Software Inc. | ||
5 | * Author: Vitaly Wool <vwool@ru.mvista.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/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 | #include <asm/prom.h> | ||
25 | #include <asm/of_device.h> | ||
26 | #include <asm/of_platform.h> | ||
27 | |||
28 | struct physmap_flash_info { | ||
29 | struct mtd_info *mtd; | ||
30 | struct map_info map; | ||
31 | struct resource *res; | ||
32 | #ifdef CONFIG_MTD_PARTITIONS | ||
33 | int nr_parts; | ||
34 | struct mtd_partition *parts; | ||
35 | #endif | ||
36 | }; | ||
37 | |||
38 | static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; | ||
39 | #ifdef CONFIG_MTD_PARTITIONS | ||
40 | static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; | ||
41 | #endif | ||
42 | |||
43 | #ifdef CONFIG_MTD_PARTITIONS | ||
44 | static int parse_flash_partitions(struct device_node *node, | ||
45 | struct mtd_partition **parts) | ||
46 | { | ||
47 | int i, plen, retval = -ENOMEM; | ||
48 | const u32 *part; | ||
49 | const char *name; | ||
50 | |||
51 | part = get_property(node, "partitions", &plen); | ||
52 | if (part == NULL) | ||
53 | goto err; | ||
54 | |||
55 | retval = plen / (2 * sizeof(u32)); | ||
56 | *parts = kzalloc(retval * sizeof(struct mtd_partition), GFP_KERNEL); | ||
57 | if (*parts == NULL) { | ||
58 | printk(KERN_ERR "Can't allocate the flash partition data!\n"); | ||
59 | goto err; | ||
60 | } | ||
61 | |||
62 | name = get_property(node, "partition-names", &plen); | ||
63 | |||
64 | for (i = 0; i < retval; i++) { | ||
65 | (*parts)[i].offset = *part++; | ||
66 | (*parts)[i].size = *part & ~1; | ||
67 | if (*part++ & 1) /* bit 0 set signifies read only partition */ | ||
68 | (*parts)[i].mask_flags = MTD_WRITEABLE; | ||
69 | |||
70 | if (name != NULL && plen > 0) { | ||
71 | int len = strlen(name) + 1; | ||
72 | |||
73 | (*parts)[i].name = (char *)name; | ||
74 | plen -= len; | ||
75 | name += len; | ||
76 | } else | ||
77 | (*parts)[i].name = "unnamed"; | ||
78 | } | ||
79 | err: | ||
80 | return retval; | ||
81 | } | ||
82 | #endif | ||
83 | |||
84 | static int of_physmap_remove(struct of_device *dev) | ||
85 | { | ||
86 | struct physmap_flash_info *info; | ||
87 | |||
88 | info = dev_get_drvdata(&dev->dev); | ||
89 | if (info == NULL) | ||
90 | return 0; | ||
91 | dev_set_drvdata(&dev->dev, NULL); | ||
92 | |||
93 | if (info->mtd != NULL) { | ||
94 | #ifdef CONFIG_MTD_PARTITIONS | ||
95 | if (info->nr_parts) { | ||
96 | del_mtd_partitions(info->mtd); | ||
97 | kfree(info->parts); | ||
98 | } else { | ||
99 | del_mtd_device(info->mtd); | ||
100 | } | ||
101 | #else | ||
102 | del_mtd_device(info->mtd); | ||
103 | #endif | ||
104 | map_destroy(info->mtd); | ||
105 | } | ||
106 | |||
107 | if (info->map.virt != NULL) | ||
108 | iounmap(info->map.virt); | ||
109 | |||
110 | if (info->res != NULL) { | ||
111 | release_resource(info->res); | ||
112 | kfree(info->res); | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int __devinit of_physmap_probe(struct of_device *dev, const struct of_device_id *match) | ||
119 | { | ||
120 | struct device_node *dp = dev->node; | ||
121 | struct resource res; | ||
122 | struct physmap_flash_info *info; | ||
123 | const char **probe_type; | ||
124 | const char *of_probe; | ||
125 | const u32 *width; | ||
126 | int err; | ||
127 | |||
128 | |||
129 | if (of_address_to_resource(dp, 0, &res)) { | ||
130 | dev_err(&dev->dev, "Can't get the flash mapping!\n"); | ||
131 | err = -EINVAL; | ||
132 | goto err_out; | ||
133 | } | ||
134 | |||
135 | dev_dbg(&dev->dev, "physmap flash device: %.8llx at %.8llx\n", | ||
136 | (unsigned long long)res.end - res.start + 1, | ||
137 | (unsigned long long)res.start); | ||
138 | |||
139 | info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); | ||
140 | if (info == NULL) { | ||
141 | err = -ENOMEM; | ||
142 | goto err_out; | ||
143 | } | ||
144 | memset(info, 0, sizeof(*info)); | ||
145 | |||
146 | dev_set_drvdata(&dev->dev, info); | ||
147 | |||
148 | info->res = request_mem_region(res.start, res.end - res.start + 1, | ||
149 | dev->dev.bus_id); | ||
150 | if (info->res == NULL) { | ||
151 | dev_err(&dev->dev, "Could not reserve memory region\n"); | ||
152 | err = -ENOMEM; | ||
153 | goto err_out; | ||
154 | } | ||
155 | |||
156 | width = get_property(dp, "bank-width", NULL); | ||
157 | if (width == NULL) { | ||
158 | dev_err(&dev->dev, "Can't get the flash bank width!\n"); | ||
159 | err = -EINVAL; | ||
160 | goto err_out; | ||
161 | } | ||
162 | |||
163 | info->map.name = dev->dev.bus_id; | ||
164 | info->map.phys = res.start; | ||
165 | info->map.size = res.end - res.start + 1; | ||
166 | info->map.bankwidth = *width; | ||
167 | |||
168 | info->map.virt = ioremap(info->map.phys, info->map.size); | ||
169 | if (info->map.virt == NULL) { | ||
170 | dev_err(&dev->dev, "Failed to ioremap flash region\n"); | ||
171 | err = EIO; | ||
172 | goto err_out; | ||
173 | } | ||
174 | |||
175 | simple_map_init(&info->map); | ||
176 | |||
177 | of_probe = get_property(dp, "probe-type", NULL); | ||
178 | if (of_probe == NULL) { | ||
179 | probe_type = rom_probe_types; | ||
180 | for (; info->mtd == NULL && *probe_type != NULL; probe_type++) | ||
181 | info->mtd = do_map_probe(*probe_type, &info->map); | ||
182 | } else if (!strcmp(of_probe, "CFI")) | ||
183 | info->mtd = do_map_probe("cfi_probe", &info->map); | ||
184 | else if (!strcmp(of_probe, "JEDEC")) | ||
185 | info->mtd = do_map_probe("jedec_probe", &info->map); | ||
186 | else { | ||
187 | if (strcmp(of_probe, "ROM")) | ||
188 | dev_dbg(&dev->dev, "map_probe: don't know probe type " | ||
189 | "'%s', mapping as rom\n"); | ||
190 | info->mtd = do_map_probe("mtd_rom", &info->map); | ||
191 | } | ||
192 | if (info->mtd == NULL) { | ||
193 | dev_err(&dev->dev, "map_probe failed\n"); | ||
194 | err = -ENXIO; | ||
195 | goto err_out; | ||
196 | } | ||
197 | info->mtd->owner = THIS_MODULE; | ||
198 | |||
199 | #ifdef CONFIG_MTD_PARTITIONS | ||
200 | err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); | ||
201 | if (err > 0) { | ||
202 | add_mtd_partitions(info->mtd, info->parts, err); | ||
203 | } else if ((err = parse_flash_partitions(dp, &info->parts)) > 0) { | ||
204 | dev_info(&dev->dev, "Using OF partition information\n"); | ||
205 | add_mtd_partitions(info->mtd, info->parts, err); | ||
206 | info->nr_parts = err; | ||
207 | } else | ||
208 | #endif | ||
209 | |||
210 | add_mtd_device(info->mtd); | ||
211 | return 0; | ||
212 | |||
213 | err_out: | ||
214 | of_physmap_remove(dev); | ||
215 | return err; | ||
216 | |||
217 | return 0; | ||
218 | |||
219 | |||
220 | } | ||
221 | |||
222 | static struct of_device_id of_physmap_match[] = { | ||
223 | { | ||
224 | .type = "rom", | ||
225 | .compatible = "direct-mapped" | ||
226 | }, | ||
227 | { }, | ||
228 | }; | ||
229 | |||
230 | MODULE_DEVICE_TABLE(of, of_physmap_match); | ||
231 | |||
232 | |||
233 | static struct of_platform_driver of_physmap_flash_driver = { | ||
234 | .name = "physmap-flash", | ||
235 | .match_table = of_physmap_match, | ||
236 | .probe = of_physmap_probe, | ||
237 | .remove = of_physmap_remove, | ||
238 | }; | ||
239 | |||
240 | static int __init of_physmap_init(void) | ||
241 | { | ||
242 | return of_register_platform_driver(&of_physmap_flash_driver); | ||
243 | } | ||
244 | |||
245 | static void __exit of_physmap_exit(void) | ||
246 | { | ||
247 | of_unregister_platform_driver(&of_physmap_flash_driver); | ||
248 | } | ||
249 | |||
250 | module_init(of_physmap_init); | ||
251 | module_exit(of_physmap_exit); | ||
252 | |||
253 | MODULE_LICENSE("GPL"); | ||
254 | MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>"); | ||
255 | MODULE_DESCRIPTION("Configurable MTD map driver for OF"); | ||