[adm5120] refactor kernel code (part 1), mark it as broken now
[openwrt/svn-archive/archive.git] / target / linux / adm5120-2.6 / files / drivers / mtd / maps / adm5120-flash.c
1 /*
2 * $Id$
3 *
4 * Platform driver for NOR flash devices on ADM5120 based boards
5 *
6 * Copyright (C) 2007 OpenWrt.org
7 * Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu>
8 *
9 * This file was derived from: drivers/mtd/map/physmap.c
10 * Copyright (C) 2003 MontaVista Software Inc.
11 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the
25 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27 *
28 */
29
30 #include <linux/module.h>
31 #include <linux/types.h>
32 #include <linux/kernel.h>
33 #include <linux/init.h>
34 #include <linux/slab.h>
35 #include <linux/device.h>
36
37 #include <linux/platform_device.h>
38 #include <linux/mtd/mtd.h>
39 #include <linux/mtd/map.h>
40 #include <linux/mtd/partitions.h>
41
42 #include <asm/io.h>
43
44 #include <asm/mach-adm5120/adm5120_defs.h>
45 #include <asm/mach-adm5120/adm5120_switch.h>
46 #include <asm/mach-adm5120/adm5120_mpmc.h>
47 #include <asm/mach-adm5120/adm5120_platform.h>
48
49 #define DRV_NAME "adm5120-flash"
50 #define DRV_DESC "ADM5120 flash MAP driver"
51 #define MAX_PARSED_PARTS 8
52
53 #define MAP_DBG(m, f, a...) printk(KERN_DEBUG "%s: " f, (m->name) , ## a)
54 #define MAP_ERR(m, f, a...) printk(KERN_ERR "%s: " f, (m->name) , ## a)
55 #define MAP_INFO(m, f, a...) printk(KERN_INFO "%s: " f, (m->name) , ## a)
56
57 struct adm5120_map_info {
58 struct map_info map;
59 void (*switch_bank)(unsigned);
60 unsigned long chip_size;
61 };
62
63 struct adm5120_flash_info {
64 struct mtd_info *mtd;
65 struct resource *res;
66 struct platform_device *dev;
67 struct adm5120_map_info amap;
68 #ifdef CONFIG_MTD_PARTITIONS
69 int nr_parts;
70 struct mtd_partition *parts[MAX_PARSED_PARTS];
71 #endif
72 };
73
74 struct flash_desc {
75 u32 phys;
76 u32 srs_shift;
77 u32 mpmc_reg;
78 };
79
80 /*
81 * Globals
82 */
83 static DEFINE_SPINLOCK(adm5120_flash_spin);
84 #define FLASH_LOCK() spin_lock(&adm5120_flash_spin)
85 #define FLASH_UNLOCK() spin_unlock(&adm5120_flash_spin)
86
87 static u32 flash_bankwidths[4] = { 1, 2, 4, 0 };
88
89 static u32 flash_sizes[8] = {
90 0, 512*1024, 1024*1024, 2*1024*1024,
91 4*1024*1024, 0, 0, 0
92 };
93
94 static struct flash_desc flash_descs[2] = {
95 {
96 .phys = ADM5120_SRAM0_BASE,
97 .mpmc_reg = MPMC_REG_SC1,
98 .srs_shift = MEMCTRL_SRS0_SHIFT,
99 }, {
100 .phys = ADM5120_SRAM1_BASE,
101 .mpmc_reg = MPMC_REG_SC0,
102 .srs_shift = MEMCTRL_SRS1_SHIFT,
103 }
104 };
105
106 static const char *probe_types[] = {
107 "cfi_probe",
108 "jedec_probe",
109 "map_rom",
110 NULL
111 };
112
113 #ifdef CONFIG_MTD_PARTITIONS
114 static const char *parse_types[] = {
115 "cmdlinepart",
116 #ifdef CONFIG_MTD_REDBOOT_PARTS
117 "RedBoot",
118 #endif
119 #ifdef CONFIG_MTD_MYLOADER_PARTS
120 "MyLoader",
121 #endif
122 };
123 #endif
124
125 #define BANK_SIZE (2<<20)
126 #define BANK_SIZE_MAX (4<<20)
127 #define BANK_OFFS_MASK (BANK_SIZE-1)
128 #define BANK_START_MASK (~BANK_OFFS_MASK)
129
130 static inline struct adm5120_map_info *map_to_amap(struct map_info *map)
131 {
132 return (struct adm5120_map_info *)map;
133 }
134
135 static void adm5120_flash_switchbank(struct map_info *map,
136 unsigned long ofs)
137 {
138 struct adm5120_map_info *amap = map_to_amap(map);
139 unsigned bank;
140
141 if (amap->switch_bank == NULL)
142 return;
143
144 bank = (ofs & BANK_START_MASK) >> 21;
145 if (bank > 1)
146 BUG();
147
148 MAP_DBG(map, "ofs=%lu, switching to bank %u\n", ofs, bank);
149 amap->switch_bank(bank);
150 }
151
152 static map_word adm5120_flash_read(struct map_info *map, unsigned long ofs)
153 {
154 struct adm5120_map_info *amap = map_to_amap(map);
155 map_word ret;
156
157 MAP_DBG(map, "reading from ofs %lu\n", ofs);
158
159 if (ofs >= amap->chip_size)
160 return map_word_ff(map);
161
162 FLASH_LOCK();
163 adm5120_flash_switchbank(map, ofs);
164 ret = inline_map_read(map, (ofs & BANK_OFFS_MASK));
165 FLASH_UNLOCK();
166
167 return ret;
168 }
169
170 static void adm5120_flash_write(struct map_info *map, const map_word datum,
171 unsigned long ofs)
172 {
173 struct adm5120_map_info *amap = map_to_amap(map);
174
175 MAP_DBG(map,"writing to ofs %lu\n", ofs);
176
177 if (ofs > amap->chip_size)
178 return;
179
180 FLASH_LOCK();
181 adm5120_flash_switchbank(map, ofs);
182 inline_map_write(map, datum, (ofs & BANK_OFFS_MASK));
183 FLASH_UNLOCK();
184 }
185
186 static void adm5120_flash_copy_from(struct map_info *map, void *to,
187 unsigned long from, ssize_t len)
188 {
189 struct adm5120_map_info *amap = map_to_amap(map);
190 char *p;
191 ssize_t t;
192
193 MAP_DBG(map, "copying %lu byte(s) from %lu to %lX\n",
194 (unsigned long)len, from, (unsigned long)to);
195
196 if (from > amap->chip_size)
197 return;
198
199 p = (char *)to;
200 while (len > 0) {
201 if (len > BANK_SIZE - (from & BANK_OFFS_MASK))
202 t = BANK_SIZE - (from & BANK_OFFS_MASK);
203 else
204 t = len;
205
206 MAP_DBG(map, "copying %lu byte(s) from %lu to %lX\n",
207 (unsigned long)t, (from & BANK_OFFS_MASK),
208 (unsigned long)p);
209
210 FLASH_LOCK();
211 adm5120_flash_switchbank(map, from);
212 inline_map_copy_from(map, to, (from & BANK_OFFS_MASK), t);
213 FLASH_UNLOCK();
214 p += t;
215 from += t;
216 len -= t;
217 }
218 }
219
220 static int adm5120_flash_initres(struct adm5120_flash_info *info)
221 {
222 struct map_info *map = &info->amap.map;
223 int err = 0;
224
225 info->res = request_mem_region(map->phys, map->size, map->name);
226 if (info->res == NULL) {
227 MAP_ERR(map, "could not reserve memory region\n");
228 err = -ENOMEM;
229 goto out;
230 }
231
232 map->virt = ioremap_nocache(map->phys, map->size);
233 if (map->virt == NULL) {
234 MAP_ERR(map, "failed to ioremap flash region\n");
235 err = -ENOMEM;
236 goto out;
237 }
238
239 out:
240 return err;
241 }
242
243 #define SWITCH_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))
244 #define SWITCH_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))=(v)
245 #define MPMC_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_MPMC_BASE)+(r))
246 #define MPMC_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_MPMC_BASE)+(r))=(v)
247
248 static int adm5120_flash_initinfo(struct adm5120_flash_info *info,
249 struct platform_device *dev)
250 {
251 struct map_info *map = &info->amap.map;
252 struct adm5120_flash_platform_data *pdata = dev->dev.platform_data;
253 struct flash_desc *fdesc;
254 u32 t;
255
256 map->name = dev->dev.bus_id;
257
258 if (dev->id > 1) {
259 MAP_ERR(map, "invalid flash id\n");
260 goto err_out;
261 }
262
263 fdesc = &flash_descs[dev->id];
264
265 /* get memory window size */
266 t = SWITCH_READ(SWITCH_REG_MEMCTRL) >> fdesc->srs_shift;
267 t &= MEMCTRL_SRS_MASK;
268 info->amap.chip_size = flash_sizes[t];
269 if (info->amap.chip_size == 0) {
270 MAP_ERR(map, "invalid flash size detected\n");
271 goto err_out;
272 }
273
274 /* get flash bus width */
275 t = MPMC_READ(fdesc->mpmc_reg) & SC_MW_MASK;
276 map->bankwidth = flash_bankwidths[t];
277 if (map->bankwidth == 0) {
278 MAP_ERR(map, "invalid bus width detected\n");
279 goto err_out;
280 }
281
282 map->phys = fdesc->phys;
283 map->size = BANK_SIZE_MAX;
284
285 simple_map_init(map);
286 map->read = adm5120_flash_read;
287 map->write = adm5120_flash_write;
288 map->copy_from = adm5120_flash_copy_from;
289
290 if (pdata) {
291 map->set_vpp = pdata->set_vpp;
292 info->amap.switch_bank = pdata->switch_bank;
293 }
294
295 info->dev = dev;
296
297 MAP_INFO(map, "probing at 0x%lX, size:%ldKiB, width:%d bits\n",
298 (unsigned long)map->phys,
299 (unsigned long)info->amap.chip_size >> 10,
300 map->bankwidth*8);
301
302 return 0;
303
304 err_out:
305 return -ENODEV;
306 }
307
308 static void adm5120_flash_initbanks(struct adm5120_flash_info *info)
309 {
310 struct map_info *map = &info->amap.map;
311
312 if (info->mtd->size <= BANK_SIZE)
313 /* no bank switching needed */
314 return;
315
316 if (info->amap.switch_bank) {
317 info->amap.chip_size = info->mtd->size;
318 return;
319 }
320
321 MAP_ERR(map, "reduce visibility from %ldKiB to %ldKiB\n",
322 (unsigned long)map->size >> 10,
323 (unsigned long)info->mtd->size >> 10);
324
325 info->mtd->size = info->amap.chip_size;
326 }
327
328 #ifdef CONFIG_MTD_PARTITIONS
329 static int adm5120_flash_initparts(struct adm5120_flash_info *info)
330 {
331 struct adm5120_flash_platform_data *pdata = info->dev->dev.platform_data;
332 struct map_info *map = &info->amap.map;
333 int num_parsers;
334 const char *parser[2];
335 int err = 0;
336 int nr_parts;
337 int i;
338
339 info->nr_parts = 0;
340
341 if (pdata == NULL)
342 goto out;
343
344 if (pdata->nr_parts) {
345 MAP_INFO(map, "adding static partitions\n");
346 err = add_mtd_partitions(info->mtd, pdata->parts,
347 pdata->nr_parts);
348 if (err == 0) {
349 info->nr_parts += pdata->nr_parts;
350 goto out;
351 }
352 }
353
354 num_parsers = ARRAY_SIZE(parse_types);
355 if (num_parsers > MAX_PARSED_PARTS)
356 num_parsers = MAX_PARSED_PARTS;
357
358 parser[1] = NULL;
359 for (i=0; i<num_parsers; i++) {
360 parser[0] = parse_types[i];
361
362 MAP_INFO(map, "parsing \"%s\" partitions\n",
363 parser[0]);
364 nr_parts = parse_mtd_partitions(info->mtd, parser,
365 &info->parts[i], 0);
366
367 if (nr_parts <= 0)
368 continue;
369
370 MAP_INFO(map, "adding \"%s\" partitions\n",
371 parser[0]);
372
373 err = add_mtd_partitions(info->mtd, info->parts[i], nr_parts);
374 if (err)
375 break;
376
377 info->nr_parts += nr_parts;
378 }
379 out:
380 return err;
381 }
382 #else
383 static int adm5120_flash_initparts(struct adm5120_flash_info *info)
384 {
385 return 0;
386 }
387 #endif /* CONFIG_MTD_PARTITIONS */
388
389 #ifdef CONFIG_MTD_PARTITIONS
390 static void adm5120_flash_remove_mtd(struct adm5120_flash_info *info)
391 {
392 int i;
393
394 if (info->nr_parts) {
395 del_mtd_partitions(info->mtd);
396 for (i=0; i<MAX_PARSED_PARTS; i++)
397 if (info->parts[i] != NULL)
398 kfree(info->parts[i]);
399 } else {
400 del_mtd_device(info->mtd);
401 }
402 }
403 #else
404 static void adm5120_flash_remove_mtd(struct adm5120_flash_info *info)
405 {
406 del_mtd_device(info->mtd);
407 }
408 #endif
409
410 static int adm5120_flash_remove(struct platform_device *dev)
411 {
412 struct adm5120_flash_info *info;
413
414 info = platform_get_drvdata(dev);
415 if (info == NULL)
416 return 0;
417
418 platform_set_drvdata(dev, NULL);
419
420 if (info->mtd != NULL) {
421 adm5120_flash_remove_mtd(info);
422 map_destroy(info->mtd);
423 }
424
425 if (info->amap.map.virt != NULL)
426 iounmap(info->amap.map.virt);
427
428 if (info->res != NULL) {
429 release_resource(info->res);
430 kfree(info->res);
431 }
432
433 return 0;
434 }
435
436 static int adm5120_flash_probe(struct platform_device *dev)
437 {
438 struct adm5120_flash_info *info;
439 struct map_info *map;
440 const char **probe_type;
441 int err;
442
443 info = kzalloc(sizeof(*info), GFP_KERNEL);
444 if (info == NULL) {
445 err = -ENOMEM;
446 goto err_out;
447 }
448
449 platform_set_drvdata(dev, info);
450
451 err = adm5120_flash_initinfo(info, dev);
452 if (err)
453 goto err_out;
454
455 err = adm5120_flash_initres(info);
456 if (err)
457 goto err_out;
458
459 map = &info->amap.map;
460 for (probe_type = probe_types; info->mtd == NULL && *probe_type != NULL;
461 probe_type++)
462 info->mtd = do_map_probe(*probe_type, map);
463
464 if (info->mtd == NULL) {
465 MAP_ERR(map, "map_probe failed\n");
466 err = -ENXIO;
467 goto err_out;
468 }
469
470 adm5120_flash_initbanks(info);
471
472 if (info->mtd->size < info->amap.chip_size) {
473 /* readjust resources */
474 iounmap(map->virt);
475 release_resource(info->res);
476 kfree(info->res);
477
478 info->amap.chip_size = info->mtd->size;
479 map->size = info->mtd->size;
480 MAP_INFO(map, "reducing map size to %ldKiB\n",
481 (unsigned long)map->size >> 10);
482 err = adm5120_flash_initres(info);
483 if (err)
484 goto err_out;
485 }
486
487 MAP_INFO(map, "found at 0x%lX, size:%ldKiB, width:%d bits\n",
488 (unsigned long)map->phys, (unsigned long)map->size >> 10,
489 map->bankwidth*8);
490
491 info->mtd->owner = THIS_MODULE;
492
493 err = adm5120_flash_initparts(info);
494 if (err)
495 goto err_out;
496
497 if (info->nr_parts == 0) {
498 MAP_INFO(map, "no partitions available, registering whole flash\n");
499 add_mtd_device(info->mtd);
500 }
501
502 return 0;
503
504 err_out:
505 adm5120_flash_remove(dev);
506 return err;
507 }
508
509 #ifdef CONFIG_PM
510 static int adm5120_flash_suspend(struct platform_device *dev, pm_message_t state)
511 {
512 struct adm5120_flash_info *info = platform_get_drvdata(dev);
513 int ret = 0;
514
515 if (info)
516 ret = info->mtd->suspend(info->mtd);
517
518 return ret;
519 }
520
521 static int adm5120_flash_resume(struct platform_device *dev)
522 {
523 struct adm5120_flash_info *info = platform_get_drvdata(dev);
524
525 if (info)
526 info->mtd->resume(info->mtd);
527
528 return 0;
529 }
530
531 static void adm5120_flash_shutdown(struct platform_device *dev)
532 {
533 struct adm5120_flash_info *info = platform_get_drvdata(dev);
534
535 if (info && info->mtd->suspend(info->mtd) == 0)
536 info->mtd->resume(info->mtd);
537 }
538 #endif
539
540 static struct platform_driver adm5120_flash_driver = {
541 .probe = adm5120_flash_probe,
542 .remove = adm5120_flash_remove,
543 #ifdef CONFIG_PM
544 .suspend = adm5120_flash_suspend,
545 .resume = adm5120_flash_resume,
546 .shutdown = adm5120_flash_shutdown,
547 #endif
548 .driver = {
549 .name = DRV_NAME,
550 },
551 };
552
553 static int __init adm5120_flash_init(void)
554 {
555 int err;
556
557 err = platform_driver_register(&adm5120_flash_driver);
558
559 return err;
560 }
561
562 static void __exit adm5120_flash_exit(void)
563 {
564 platform_driver_unregister(&adm5120_flash_driver);
565 }
566
567 module_init(adm5120_flash_init);
568 module_exit(adm5120_flash_exit);
569
570 MODULE_LICENSE("GPL");
571 MODULE_AUTHOR("Gabor Juhos <juhosg@freemail.hu>");
572 MODULE_DESCRIPTION(DRV_DESC);