Commit dcfb81d61da1367e52f7f7e3ceff0d0044c3c7ee
Committed by
David Woodhouse
1 parent
a321590246
Exists in
master
and in
7 other branches
mtd: NOR flash driver for OMAP-L137/AM17x
OMAP-L137/AM17x has limited number of dedicated EMIFA address pins, enough to interface directly to an SDRAM. If a device such as an asynchronous flash needs to be attached to the EMIFA, then either GPIO pins or a chip select may be used to control the flash device's upper address lines. This patch adds support for the NOR flash on the OMAP-L137/ AM17x user interface daughter board using the latch-addr-flash MTD mapping driver which allows flashes to be partially physically addressed. The upper address lines are set by a board specific code which is a separate patch. Signed-off-by: David Griego <dgriego@mvista.com> Signed-off-by: Aleksey Makarov <amakarov@ru.mvista.com> Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com> Signed-off-by: Savinay Dharmappa <savinay.dharmappa@ti.com> Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Showing 4 changed files with 311 additions and 0 deletions Side-by-side Diff
drivers/mtd/maps/Kconfig
... | ... | @@ -552,5 +552,14 @@ |
552 | 552 | |
553 | 553 | When built as a module, it will be called pismo.ko |
554 | 554 | |
555 | +config MTD_LATCH_ADDR | |
556 | + tristate "Latch-assisted Flash Chip Support" | |
557 | + depends on MTD_COMPLEX_MAPPINGS | |
558 | + help | |
559 | + Map driver which allows flashes to be partially physically addressed | |
560 | + and have the upper address lines set by a board specific code. | |
561 | + | |
562 | + If compiled as a module, it will be called latch-addr-flash. | |
563 | + | |
555 | 564 | endmenu |
drivers/mtd/maps/Makefile
drivers/mtd/maps/latch-addr-flash.c
1 | +/* | |
2 | + * Interface for NOR flash driver whose high address lines are latched | |
3 | + * | |
4 | + * Copyright © 2000 Nicolas Pitre <nico@cam.org> | |
5 | + * Copyright © 2005-2008 Analog Devices Inc. | |
6 | + * Copyright © 2008 MontaVista Software, Inc. <source@mvista.com> | |
7 | + * | |
8 | + * This file is licensed under the terms of the GNU General Public License | |
9 | + * version 2. This program is licensed "as is" without any warranty of any | |
10 | + * kind, whether express or implied. | |
11 | + */ | |
12 | + | |
13 | +#include <linux/init.h> | |
14 | +#include <linux/kernel.h> | |
15 | +#include <linux/module.h> | |
16 | +#include <linux/mtd/mtd.h> | |
17 | +#include <linux/mtd/map.h> | |
18 | +#include <linux/mtd/partitions.h> | |
19 | +#include <linux/platform_device.h> | |
20 | +#include <linux/mtd/latch-addr-flash.h> | |
21 | +#include <linux/slab.h> | |
22 | + | |
23 | +#define DRIVER_NAME "latch-addr-flash" | |
24 | + | |
25 | +struct latch_addr_flash_info { | |
26 | + struct mtd_info *mtd; | |
27 | + struct map_info map; | |
28 | + struct resource *res; | |
29 | + | |
30 | + void (*set_window)(unsigned long offset, void *data); | |
31 | + void *data; | |
32 | + | |
33 | + /* cache; could be found out of res */ | |
34 | + unsigned long win_mask; | |
35 | + | |
36 | + int nr_parts; | |
37 | + struct mtd_partition *parts; | |
38 | + | |
39 | + spinlock_t lock; | |
40 | +}; | |
41 | + | |
42 | +static map_word lf_read(struct map_info *map, unsigned long ofs) | |
43 | +{ | |
44 | + struct latch_addr_flash_info *info; | |
45 | + map_word datum; | |
46 | + | |
47 | + info = (struct latch_addr_flash_info *)map->map_priv_1; | |
48 | + | |
49 | + spin_lock(&info->lock); | |
50 | + | |
51 | + info->set_window(ofs, info->data); | |
52 | + datum = inline_map_read(map, info->win_mask & ofs); | |
53 | + | |
54 | + spin_unlock(&info->lock); | |
55 | + | |
56 | + return datum; | |
57 | +} | |
58 | + | |
59 | +static void lf_write(struct map_info *map, map_word datum, unsigned long ofs) | |
60 | +{ | |
61 | + struct latch_addr_flash_info *info; | |
62 | + | |
63 | + info = (struct latch_addr_flash_info *)map->map_priv_1; | |
64 | + | |
65 | + spin_lock(&info->lock); | |
66 | + | |
67 | + info->set_window(ofs, info->data); | |
68 | + inline_map_write(map, datum, info->win_mask & ofs); | |
69 | + | |
70 | + spin_unlock(&info->lock); | |
71 | +} | |
72 | + | |
73 | +static void lf_copy_from(struct map_info *map, void *to, | |
74 | + unsigned long from, ssize_t len) | |
75 | +{ | |
76 | + struct latch_addr_flash_info *info = | |
77 | + (struct latch_addr_flash_info *) map->map_priv_1; | |
78 | + unsigned n; | |
79 | + | |
80 | + while (len > 0) { | |
81 | + n = info->win_mask + 1 - (from & info->win_mask); | |
82 | + if (n > len) | |
83 | + n = len; | |
84 | + | |
85 | + spin_lock(&info->lock); | |
86 | + | |
87 | + info->set_window(from, info->data); | |
88 | + memcpy_fromio(to, map->virt + (from & info->win_mask), n); | |
89 | + | |
90 | + spin_unlock(&info->lock); | |
91 | + | |
92 | + to += n; | |
93 | + from += n; | |
94 | + len -= n; | |
95 | + } | |
96 | +} | |
97 | + | |
98 | +static char *rom_probe_types[] = { "cfi_probe", NULL }; | |
99 | + | |
100 | +static char *part_probe_types[] = { "cmdlinepart", NULL }; | |
101 | + | |
102 | +static int latch_addr_flash_remove(struct platform_device *dev) | |
103 | +{ | |
104 | + struct latch_addr_flash_info *info; | |
105 | + struct latch_addr_flash_data *latch_addr_data; | |
106 | + | |
107 | + info = platform_get_drvdata(dev); | |
108 | + if (info == NULL) | |
109 | + return 0; | |
110 | + platform_set_drvdata(dev, NULL); | |
111 | + | |
112 | + latch_addr_data = dev->dev.platform_data; | |
113 | + | |
114 | + if (info->mtd != NULL) { | |
115 | + if (mtd_has_partitions()) { | |
116 | + if (info->nr_parts) { | |
117 | + del_mtd_partitions(info->mtd); | |
118 | + kfree(info->parts); | |
119 | + } else if (latch_addr_data->nr_parts) { | |
120 | + del_mtd_partitions(info->mtd); | |
121 | + } else { | |
122 | + del_mtd_device(info->mtd); | |
123 | + } | |
124 | + } else { | |
125 | + del_mtd_device(info->mtd); | |
126 | + } | |
127 | + map_destroy(info->mtd); | |
128 | + } | |
129 | + | |
130 | + if (info->map.virt != NULL) | |
131 | + iounmap(info->map.virt); | |
132 | + | |
133 | + if (info->res != NULL) | |
134 | + release_mem_region(info->res->start, resource_size(info->res)); | |
135 | + | |
136 | + kfree(info); | |
137 | + | |
138 | + if (latch_addr_data->done) | |
139 | + latch_addr_data->done(latch_addr_data->data); | |
140 | + | |
141 | + return 0; | |
142 | +} | |
143 | + | |
144 | +static int __devinit latch_addr_flash_probe(struct platform_device *dev) | |
145 | +{ | |
146 | + struct latch_addr_flash_data *latch_addr_data; | |
147 | + struct latch_addr_flash_info *info; | |
148 | + resource_size_t win_base = dev->resource->start; | |
149 | + resource_size_t win_size = resource_size(dev->resource); | |
150 | + char **probe_type; | |
151 | + int chipsel; | |
152 | + int err; | |
153 | + | |
154 | + latch_addr_data = dev->dev.platform_data; | |
155 | + if (latch_addr_data == NULL) | |
156 | + return -ENODEV; | |
157 | + | |
158 | + pr_notice("latch-addr platform flash device: %#llx byte " | |
159 | + "window at %#.8llx\n", | |
160 | + (unsigned long long)win_size, (unsigned long long)win_base); | |
161 | + | |
162 | + chipsel = dev->id; | |
163 | + | |
164 | + if (latch_addr_data->init) { | |
165 | + err = latch_addr_data->init(latch_addr_data->data, chipsel); | |
166 | + if (err != 0) | |
167 | + return err; | |
168 | + } | |
169 | + | |
170 | + info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL); | |
171 | + if (info == NULL) { | |
172 | + err = -ENOMEM; | |
173 | + goto done; | |
174 | + } | |
175 | + | |
176 | + platform_set_drvdata(dev, info); | |
177 | + | |
178 | + info->res = request_mem_region(win_base, win_size, DRIVER_NAME); | |
179 | + if (info->res == NULL) { | |
180 | + dev_err(&dev->dev, "Could not reserve memory region\n"); | |
181 | + err = -EBUSY; | |
182 | + goto free_info; | |
183 | + } | |
184 | + | |
185 | + info->map.name = DRIVER_NAME; | |
186 | + info->map.size = latch_addr_data->size; | |
187 | + info->map.bankwidth = latch_addr_data->width; | |
188 | + | |
189 | + info->map.phys = NO_XIP; | |
190 | + info->map.virt = ioremap(win_base, win_size); | |
191 | + if (!info->map.virt) { | |
192 | + err = -ENOMEM; | |
193 | + goto free_res; | |
194 | + } | |
195 | + | |
196 | + info->map.map_priv_1 = (unsigned long)info; | |
197 | + | |
198 | + info->map.read = lf_read; | |
199 | + info->map.copy_from = lf_copy_from; | |
200 | + info->map.write = lf_write; | |
201 | + info->set_window = latch_addr_data->set_window; | |
202 | + info->data = latch_addr_data->data; | |
203 | + info->win_mask = win_size - 1; | |
204 | + | |
205 | + spin_lock_init(&info->lock); | |
206 | + | |
207 | + for (probe_type = rom_probe_types; !info->mtd && *probe_type; | |
208 | + probe_type++) | |
209 | + info->mtd = do_map_probe(*probe_type, &info->map); | |
210 | + | |
211 | + if (info->mtd == NULL) { | |
212 | + dev_err(&dev->dev, "map_probe failed\n"); | |
213 | + err = -ENODEV; | |
214 | + goto iounmap; | |
215 | + } | |
216 | + info->mtd->owner = THIS_MODULE; | |
217 | + | |
218 | + if (mtd_has_partitions()) { | |
219 | + | |
220 | + err = parse_mtd_partitions(info->mtd, | |
221 | + (const char **)part_probe_types, | |
222 | + &info->parts, 0); | |
223 | + if (err > 0) { | |
224 | + add_mtd_partitions(info->mtd, info->parts, err); | |
225 | + return 0; | |
226 | + } | |
227 | + if (latch_addr_data->nr_parts) { | |
228 | + pr_notice("Using latch-addr-flash partition information\n"); | |
229 | + add_mtd_partitions(info->mtd, latch_addr_data->parts, | |
230 | + latch_addr_data->nr_parts); | |
231 | + return 0; | |
232 | + } | |
233 | + } | |
234 | + add_mtd_device(info->mtd); | |
235 | + return 0; | |
236 | + | |
237 | +iounmap: | |
238 | + iounmap(info->map.virt); | |
239 | +free_res: | |
240 | + release_mem_region(info->res->start, resource_size(info->res)); | |
241 | +free_info: | |
242 | + kfree(info); | |
243 | +done: | |
244 | + if (latch_addr_data->done) | |
245 | + latch_addr_data->done(latch_addr_data->data); | |
246 | + return err; | |
247 | +} | |
248 | + | |
249 | +static struct platform_driver latch_addr_flash_driver = { | |
250 | + .probe = latch_addr_flash_probe, | |
251 | + .remove = __devexit_p(latch_addr_flash_remove), | |
252 | + .driver = { | |
253 | + .name = DRIVER_NAME, | |
254 | + }, | |
255 | +}; | |
256 | + | |
257 | +static int __init latch_addr_flash_init(void) | |
258 | +{ | |
259 | + return platform_driver_register(&latch_addr_flash_driver); | |
260 | +} | |
261 | +module_init(latch_addr_flash_init); | |
262 | + | |
263 | +static void __exit latch_addr_flash_exit(void) | |
264 | +{ | |
265 | + platform_driver_unregister(&latch_addr_flash_driver); | |
266 | +} | |
267 | +module_exit(latch_addr_flash_exit); | |
268 | + | |
269 | +MODULE_AUTHOR("David Griego <dgriego@mvista.com>"); | |
270 | +MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper " | |
271 | + "address lines being set board specifically"); | |
272 | +MODULE_LICENSE("GPL v2"); |
include/linux/mtd/latch-addr-flash.h
1 | +/* | |
2 | + * Interface for NOR flash driver whose high address lines are latched | |
3 | + * | |
4 | + * Copyright © 2008 MontaVista Software, Inc. <source@mvista.com> | |
5 | + * | |
6 | + * This file is licensed under the terms of the GNU General Public License | |
7 | + * version 2. This program is licensed "as is" without any warranty of any | |
8 | + * kind, whether express or implied. | |
9 | + */ | |
10 | +#ifndef __LATCH_ADDR_FLASH__ | |
11 | +#define __LATCH_ADDR_FLASH__ | |
12 | + | |
13 | +struct map_info; | |
14 | +struct mtd_partition; | |
15 | + | |
16 | +struct latch_addr_flash_data { | |
17 | + unsigned int width; | |
18 | + unsigned int size; | |
19 | + | |
20 | + int (*init)(void *data, int cs); | |
21 | + void (*done)(void *data); | |
22 | + void (*set_window)(unsigned long offset, void *data); | |
23 | + void *data; | |
24 | + | |
25 | + unsigned int nr_parts; | |
26 | + struct mtd_partition *parts; | |
27 | +}; | |
28 | + | |
29 | +#endif |