1 | /* |
2 | * pcmciamtd.c - MTD driver for PCMCIA flash memory cards |
3 | * |
4 | * Author: Simon Evans <spse@secret.org.uk> |
5 | * |
6 | * Copyright (C) 2002 Simon Evans |
7 | * |
8 | * Licence: GPL |
9 | * |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/timer.h> |
15 | #include <linux/init.h> |
16 | #include <asm/io.h> |
17 | |
18 | #include <pcmcia/cistpl.h> |
19 | #include <pcmcia/ds.h> |
20 | |
21 | #include <linux/mtd/map.h> |
22 | #include <linux/mtd/mtd.h> |
23 | |
24 | #define info(format, arg...) printk(KERN_INFO "pcmciamtd: " format "\n" , ## arg) |
25 | |
26 | #define DRIVER_DESC "PCMCIA Flash memory card driver" |
27 | |
28 | /* Size of the PCMCIA address space: 26 bits = 64 MB */ |
29 | #define MAX_PCMCIA_ADDR 0x4000000 |
30 | |
31 | struct pcmciamtd_dev { |
32 | struct pcmcia_device *p_dev; |
33 | void __iomem *win_base; /* ioremapped address of PCMCIA window */ |
34 | unsigned int win_size; /* size of window */ |
35 | unsigned int offset; /* offset into card the window currently points at */ |
36 | struct map_info pcmcia_map; |
37 | struct mtd_info *mtd_info; |
38 | int vpp; |
39 | char mtd_name[sizeof(struct cistpl_vers_1_t)]; |
40 | }; |
41 | |
42 | |
43 | /* Module parameters */ |
44 | |
45 | /* 2 = do 16-bit transfers, 1 = do 8-bit transfers */ |
46 | static int bankwidth = 2; |
47 | |
48 | /* Speed of memory accesses, in ns */ |
49 | static int mem_speed; |
50 | |
51 | /* Force the size of an SRAM card */ |
52 | static int force_size; |
53 | |
54 | /* Force Vpp */ |
55 | static int vpp; |
56 | |
57 | /* Set Vpp */ |
58 | static int setvpp; |
59 | |
60 | /* Force card to be treated as FLASH, ROM or RAM */ |
61 | static int mem_type; |
62 | |
63 | MODULE_LICENSE("GPL" ); |
64 | MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>" ); |
65 | MODULE_DESCRIPTION(DRIVER_DESC); |
66 | module_param(bankwidth, int, 0); |
67 | MODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)" ); |
68 | module_param(mem_speed, int, 0); |
69 | MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns" ); |
70 | module_param(force_size, int, 0); |
71 | MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)" ); |
72 | module_param(setvpp, int, 0); |
73 | MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)" ); |
74 | module_param(vpp, int, 0); |
75 | MODULE_PARM_DESC(vpp, "Vpp value in 1/10ths eg 33=3.3V 120=12V (Dangerous)" ); |
76 | module_param(mem_type, int, 0); |
77 | MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)" ); |
78 | |
79 | |
80 | /* read/write{8,16} copy_{from,to} routines with window remapping |
81 | * to access whole card |
82 | */ |
83 | static void __iomem *remap_window(struct map_info *map, unsigned long to) |
84 | { |
85 | struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; |
86 | struct resource *win = (struct resource *) map->map_priv_2; |
87 | unsigned int offset; |
88 | int ret; |
89 | |
90 | if (!pcmcia_dev_present(p_dev: dev->p_dev)) { |
91 | pr_debug("device removed\n" ); |
92 | return NULL; |
93 | } |
94 | |
95 | offset = to & ~(dev->win_size-1); |
96 | if (offset != dev->offset) { |
97 | pr_debug("Remapping window from 0x%8.8x to 0x%8.8x\n" , |
98 | dev->offset, offset); |
99 | ret = pcmcia_map_mem_page(p_dev: dev->p_dev, res: win, offset); |
100 | if (ret != 0) |
101 | return NULL; |
102 | dev->offset = offset; |
103 | } |
104 | return dev->win_base + (to & (dev->win_size-1)); |
105 | } |
106 | |
107 | |
108 | static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs) |
109 | { |
110 | void __iomem *addr; |
111 | map_word d = {{0}}; |
112 | |
113 | addr = remap_window(map, to: ofs); |
114 | if(!addr) |
115 | return d; |
116 | |
117 | d.x[0] = readb(addr); |
118 | pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n" , ofs, addr, d.x[0]); |
119 | return d; |
120 | } |
121 | |
122 | |
123 | static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs) |
124 | { |
125 | void __iomem *addr; |
126 | map_word d = {{0}}; |
127 | |
128 | addr = remap_window(map, to: ofs); |
129 | if(!addr) |
130 | return d; |
131 | |
132 | d.x[0] = readw(addr); |
133 | pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n" , ofs, addr, d.x[0]); |
134 | return d; |
135 | } |
136 | |
137 | |
138 | static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long from, ssize_t len) |
139 | { |
140 | struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; |
141 | unsigned long win_size = dev->win_size; |
142 | |
143 | pr_debug("to = %p from = %lu len = %zd\n" , to, from, len); |
144 | while(len) { |
145 | int toread = win_size - (from & (win_size-1)); |
146 | void __iomem *addr; |
147 | |
148 | if(toread > len) |
149 | toread = len; |
150 | |
151 | addr = remap_window(map, to: from); |
152 | if(!addr) |
153 | return; |
154 | |
155 | pr_debug("memcpy from %p to %p len = %d\n" , addr, to, toread); |
156 | memcpy_fromio(to, addr, toread); |
157 | len -= toread; |
158 | to += toread; |
159 | from += toread; |
160 | } |
161 | } |
162 | |
163 | |
164 | static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long adr) |
165 | { |
166 | void __iomem *addr = remap_window(map, to: adr); |
167 | |
168 | if(!addr) |
169 | return; |
170 | |
171 | pr_debug("adr = 0x%08lx (%p) data = 0x%02lx\n" , adr, addr, d.x[0]); |
172 | writeb(val: d.x[0], addr); |
173 | } |
174 | |
175 | |
176 | static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long adr) |
177 | { |
178 | void __iomem *addr = remap_window(map, to: adr); |
179 | if(!addr) |
180 | return; |
181 | |
182 | pr_debug("adr = 0x%08lx (%p) data = 0x%04lx\n" , adr, addr, d.x[0]); |
183 | writew(val: d.x[0], addr); |
184 | } |
185 | |
186 | |
187 | static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const void *from, ssize_t len) |
188 | { |
189 | struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; |
190 | unsigned long win_size = dev->win_size; |
191 | |
192 | pr_debug("to = %lu from = %p len = %zd\n" , to, from, len); |
193 | while(len) { |
194 | int towrite = win_size - (to & (win_size-1)); |
195 | void __iomem *addr; |
196 | |
197 | if(towrite > len) |
198 | towrite = len; |
199 | |
200 | addr = remap_window(map, to); |
201 | if(!addr) |
202 | return; |
203 | |
204 | pr_debug("memcpy from %p to %p len = %d\n" , from, addr, towrite); |
205 | memcpy_toio(addr, from, towrite); |
206 | len -= towrite; |
207 | to += towrite; |
208 | from += towrite; |
209 | } |
210 | } |
211 | |
212 | |
213 | /* read/write{8,16} copy_{from,to} routines with direct access */ |
214 | |
215 | #define DEV_REMOVED(x) (!(pcmcia_dev_present(((struct pcmciamtd_dev *)map->map_priv_1)->p_dev))) |
216 | |
217 | static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) |
218 | { |
219 | void __iomem *win_base = (void __iomem *)map->map_priv_2; |
220 | map_word d = {{0}}; |
221 | |
222 | if(DEV_REMOVED(map)) |
223 | return d; |
224 | |
225 | d.x[0] = readb(addr: win_base + ofs); |
226 | pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n" , |
227 | ofs, win_base + ofs, d.x[0]); |
228 | return d; |
229 | } |
230 | |
231 | |
232 | static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) |
233 | { |
234 | void __iomem *win_base = (void __iomem *)map->map_priv_2; |
235 | map_word d = {{0}}; |
236 | |
237 | if(DEV_REMOVED(map)) |
238 | return d; |
239 | |
240 | d.x[0] = readw(addr: win_base + ofs); |
241 | pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n" , |
242 | ofs, win_base + ofs, d.x[0]); |
243 | return d; |
244 | } |
245 | |
246 | |
247 | static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) |
248 | { |
249 | void __iomem *win_base = (void __iomem *)map->map_priv_2; |
250 | |
251 | if(DEV_REMOVED(map)) |
252 | return; |
253 | |
254 | pr_debug("to = %p from = %lu len = %zd\n" , to, from, len); |
255 | memcpy_fromio(to, win_base + from, len); |
256 | } |
257 | |
258 | |
259 | static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr) |
260 | { |
261 | void __iomem *win_base = (void __iomem *)map->map_priv_2; |
262 | |
263 | if(DEV_REMOVED(map)) |
264 | return; |
265 | |
266 | pr_debug("adr = 0x%08lx (%p) data = 0x%02lx\n" , |
267 | adr, win_base + adr, d.x[0]); |
268 | writeb(val: d.x[0], addr: win_base + adr); |
269 | } |
270 | |
271 | |
272 | static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr) |
273 | { |
274 | void __iomem *win_base = (void __iomem *)map->map_priv_2; |
275 | |
276 | if(DEV_REMOVED(map)) |
277 | return; |
278 | |
279 | pr_debug("adr = 0x%08lx (%p) data = 0x%04lx\n" , |
280 | adr, win_base + adr, d.x[0]); |
281 | writew(val: d.x[0], addr: win_base + adr); |
282 | } |
283 | |
284 | |
285 | static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) |
286 | { |
287 | void __iomem *win_base = (void __iomem *)map->map_priv_2; |
288 | |
289 | if(DEV_REMOVED(map)) |
290 | return; |
291 | |
292 | pr_debug("to = %lu from = %p len = %zd\n" , to, from, len); |
293 | memcpy_toio(win_base + to, from, len); |
294 | } |
295 | |
296 | |
297 | static DEFINE_MUTEX(pcmcia_vpp_lock); |
298 | static int pcmcia_vpp_refcnt; |
299 | static void pcmciamtd_set_vpp(struct map_info *map, int on) |
300 | { |
301 | struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; |
302 | struct pcmcia_device *link = dev->p_dev; |
303 | |
304 | pr_debug("dev = %p on = %d vpp = %d\n\n" , dev, on, dev->vpp); |
305 | mutex_lock(&pcmcia_vpp_lock); |
306 | if (on) { |
307 | if (++pcmcia_vpp_refcnt == 1) /* first nested 'on' */ |
308 | pcmcia_fixup_vpp(p_dev: link, new_vpp: dev->vpp); |
309 | } else { |
310 | if (--pcmcia_vpp_refcnt == 0) /* last nested 'off' */ |
311 | pcmcia_fixup_vpp(p_dev: link, new_vpp: 0); |
312 | } |
313 | mutex_unlock(lock: &pcmcia_vpp_lock); |
314 | } |
315 | |
316 | |
317 | static void pcmciamtd_release(struct pcmcia_device *link) |
318 | { |
319 | struct pcmciamtd_dev *dev = link->priv; |
320 | |
321 | pr_debug("link = 0x%p\n" , link); |
322 | |
323 | if (link->resource[2]->end) { |
324 | if(dev->win_base) { |
325 | iounmap(addr: dev->win_base); |
326 | dev->win_base = NULL; |
327 | } |
328 | } |
329 | pcmcia_disable_device(p_dev: link); |
330 | } |
331 | |
332 | |
333 | static int pcmciamtd_cistpl_format(struct pcmcia_device *p_dev, |
334 | tuple_t *tuple, |
335 | void *priv_data) |
336 | { |
337 | cisparse_t parse; |
338 | |
339 | if (!pcmcia_parse_tuple(tuple, parse: &parse)) { |
340 | cistpl_format_t *t = &parse.format; |
341 | (void)t; /* Shut up, gcc */ |
342 | pr_debug("Format type: %u, Error Detection: %u, offset = %u, length =%u\n" , |
343 | t->type, t->edc, t->offset, t->length); |
344 | } |
345 | return -ENOSPC; |
346 | } |
347 | |
348 | static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev, |
349 | tuple_t *tuple, |
350 | void *priv_data) |
351 | { |
352 | cisparse_t parse; |
353 | int i; |
354 | |
355 | if (!pcmcia_parse_tuple(tuple, parse: &parse)) { |
356 | cistpl_jedec_t *t = &parse.jedec; |
357 | for (i = 0; i < t->nid; i++) |
358 | pr_debug("JEDEC: 0x%02x 0x%02x\n" , |
359 | t->id[i].mfr, t->id[i].info); |
360 | } |
361 | return -ENOSPC; |
362 | } |
363 | |
364 | static int pcmciamtd_cistpl_device(struct pcmcia_device *p_dev, |
365 | tuple_t *tuple, |
366 | void *priv_data) |
367 | { |
368 | struct pcmciamtd_dev *dev = priv_data; |
369 | cisparse_t parse; |
370 | cistpl_device_t *t = &parse.device; |
371 | int i; |
372 | |
373 | if (pcmcia_parse_tuple(tuple, parse: &parse)) |
374 | return -EINVAL; |
375 | |
376 | pr_debug("Common memory:\n" ); |
377 | dev->pcmcia_map.size = t->dev[0].size; |
378 | /* from here on: DEBUG only */ |
379 | for (i = 0; i < t->ndev; i++) { |
380 | pr_debug("Region %d, type = %u\n" , i, t->dev[i].type); |
381 | pr_debug("Region %d, wp = %u\n" , i, t->dev[i].wp); |
382 | pr_debug("Region %d, speed = %u ns\n" , i, t->dev[i].speed); |
383 | pr_debug("Region %d, size = %u bytes\n" , i, t->dev[i].size); |
384 | } |
385 | return 0; |
386 | } |
387 | |
388 | static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev, |
389 | tuple_t *tuple, |
390 | void *priv_data) |
391 | { |
392 | struct pcmciamtd_dev *dev = priv_data; |
393 | cisparse_t parse; |
394 | cistpl_device_geo_t *t = &parse.device_geo; |
395 | int i; |
396 | |
397 | if (pcmcia_parse_tuple(tuple, parse: &parse)) |
398 | return -EINVAL; |
399 | |
400 | dev->pcmcia_map.bankwidth = t->geo[0].buswidth; |
401 | /* from here on: DEBUG only */ |
402 | for (i = 0; i < t->ngeo; i++) { |
403 | pr_debug("region: %d bankwidth = %u\n" , i, t->geo[i].buswidth); |
404 | pr_debug("region: %d erase_block = %u\n" , i, t->geo[i].erase_block); |
405 | pr_debug("region: %d read_block = %u\n" , i, t->geo[i].read_block); |
406 | pr_debug("region: %d write_block = %u\n" , i, t->geo[i].write_block); |
407 | pr_debug("region: %d partition = %u\n" , i, t->geo[i].partition); |
408 | pr_debug("region: %d interleave = %u\n" , i, t->geo[i].interleave); |
409 | } |
410 | return 0; |
411 | } |
412 | |
413 | |
414 | static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev, int *new_name) |
415 | { |
416 | int i; |
417 | |
418 | if (p_dev->prod_id[0]) { |
419 | dev->mtd_name[0] = '\0'; |
420 | for (i = 0; i < 4; i++) { |
421 | if (i) |
422 | strcat(p: dev->mtd_name, q: " " ); |
423 | if (p_dev->prod_id[i]) |
424 | strcat(p: dev->mtd_name, q: p_dev->prod_id[i]); |
425 | } |
426 | pr_debug("Found name: %s\n" , dev->mtd_name); |
427 | } |
428 | |
429 | pcmcia_loop_tuple(p_dev, CISTPL_FORMAT, loop_tuple: pcmciamtd_cistpl_format, NULL); |
430 | pcmcia_loop_tuple(p_dev, CISTPL_JEDEC_C, loop_tuple: pcmciamtd_cistpl_jedec, NULL); |
431 | pcmcia_loop_tuple(p_dev, CISTPL_DEVICE, loop_tuple: pcmciamtd_cistpl_device, priv_data: dev); |
432 | pcmcia_loop_tuple(p_dev, CISTPL_DEVICE_GEO, loop_tuple: pcmciamtd_cistpl_geo, priv_data: dev); |
433 | |
434 | if(!dev->pcmcia_map.size) |
435 | dev->pcmcia_map.size = MAX_PCMCIA_ADDR; |
436 | |
437 | if(!dev->pcmcia_map.bankwidth) |
438 | dev->pcmcia_map.bankwidth = 2; |
439 | |
440 | if(force_size) { |
441 | dev->pcmcia_map.size = force_size << 20; |
442 | pr_debug("size forced to %dM\n" , force_size); |
443 | } |
444 | |
445 | if(bankwidth) { |
446 | dev->pcmcia_map.bankwidth = bankwidth; |
447 | pr_debug("bankwidth forced to %d\n" , bankwidth); |
448 | } |
449 | |
450 | dev->pcmcia_map.name = dev->mtd_name; |
451 | if(!dev->mtd_name[0]) { |
452 | strcpy(p: dev->mtd_name, q: "PCMCIA Memory card" ); |
453 | *new_name = 1; |
454 | } |
455 | |
456 | pr_debug("Device: Size: %lu Width:%d Name: %s\n" , |
457 | dev->pcmcia_map.size, |
458 | dev->pcmcia_map.bankwidth << 3, dev->mtd_name); |
459 | } |
460 | |
461 | |
462 | static int pcmciamtd_config(struct pcmcia_device *link) |
463 | { |
464 | struct pcmciamtd_dev *dev = link->priv; |
465 | struct mtd_info *mtd = NULL; |
466 | int ret; |
467 | int i, j = 0; |
468 | static char *probes[] = { "jedec_probe" , "cfi_probe" }; |
469 | int new_name = 0; |
470 | |
471 | pr_debug("link=0x%p\n" , link); |
472 | |
473 | card_settings(dev, p_dev: link, new_name: &new_name); |
474 | |
475 | dev->pcmcia_map.phys = NO_XIP; |
476 | dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; |
477 | dev->pcmcia_map.copy_to = pcmcia_copy_to_remap; |
478 | if (dev->pcmcia_map.bankwidth == 1) { |
479 | dev->pcmcia_map.read = pcmcia_read8_remap; |
480 | dev->pcmcia_map.write = pcmcia_write8_remap; |
481 | } else { |
482 | dev->pcmcia_map.read = pcmcia_read16_remap; |
483 | dev->pcmcia_map.write = pcmcia_write16_remap; |
484 | } |
485 | if(setvpp == 1) |
486 | dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; |
487 | |
488 | /* Request a memory window for PCMCIA. Some architeures can map windows |
489 | * up to the maximum that PCMCIA can support (64MiB) - this is ideal and |
490 | * we aim for a window the size of the whole card - otherwise we try |
491 | * smaller windows until we succeed |
492 | */ |
493 | |
494 | link->resource[2]->flags |= WIN_MEMORY_TYPE_CM | WIN_ENABLE; |
495 | link->resource[2]->flags |= (dev->pcmcia_map.bankwidth == 1) ? |
496 | WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; |
497 | link->resource[2]->start = 0; |
498 | link->resource[2]->end = (force_size) ? force_size << 20 : |
499 | MAX_PCMCIA_ADDR; |
500 | dev->win_size = 0; |
501 | |
502 | do { |
503 | int ret; |
504 | pr_debug("requesting window with size = %luKiB memspeed = %d\n" , |
505 | (unsigned long) resource_size(link->resource[2]) >> 10, |
506 | mem_speed); |
507 | ret = pcmcia_request_window(p_dev: link, res: link->resource[2], speed: mem_speed); |
508 | pr_debug("ret = %d dev->win_size = %d\n" , ret, dev->win_size); |
509 | if(ret) { |
510 | j++; |
511 | link->resource[2]->start = 0; |
512 | link->resource[2]->end = (force_size) ? |
513 | force_size << 20 : MAX_PCMCIA_ADDR; |
514 | link->resource[2]->end >>= j; |
515 | } else { |
516 | pr_debug("Got window of size %luKiB\n" , (unsigned long) |
517 | resource_size(link->resource[2]) >> 10); |
518 | dev->win_size = resource_size(res: link->resource[2]); |
519 | break; |
520 | } |
521 | } while (link->resource[2]->end >= 0x1000); |
522 | |
523 | pr_debug("dev->win_size = %d\n" , dev->win_size); |
524 | |
525 | if(!dev->win_size) { |
526 | dev_err(&dev->p_dev->dev, "Cannot allocate memory window\n" ); |
527 | pcmciamtd_release(link); |
528 | return -ENODEV; |
529 | } |
530 | pr_debug("Allocated a window of %dKiB\n" , dev->win_size >> 10); |
531 | |
532 | /* Get write protect status */ |
533 | dev->win_base = ioremap(offset: link->resource[2]->start, |
534 | size: resource_size(res: link->resource[2])); |
535 | if(!dev->win_base) { |
536 | dev_err(&dev->p_dev->dev, "ioremap(%pR) failed\n" , |
537 | link->resource[2]); |
538 | pcmciamtd_release(link); |
539 | return -ENODEV; |
540 | } |
541 | pr_debug("mapped window dev = %p @ %pR, base = %p\n" , |
542 | dev, link->resource[2], dev->win_base); |
543 | |
544 | dev->offset = 0; |
545 | dev->pcmcia_map.map_priv_1 = (unsigned long)dev; |
546 | dev->pcmcia_map.map_priv_2 = (unsigned long)link->resource[2]; |
547 | |
548 | dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp; |
549 | if(setvpp == 2) { |
550 | link->vpp = dev->vpp; |
551 | } else { |
552 | link->vpp = 0; |
553 | } |
554 | |
555 | link->config_index = 0; |
556 | pr_debug("Setting Configuration\n" ); |
557 | ret = pcmcia_enable_device(p_dev: link); |
558 | if (ret != 0) { |
559 | if (dev->win_base) { |
560 | iounmap(addr: dev->win_base); |
561 | dev->win_base = NULL; |
562 | } |
563 | return -ENODEV; |
564 | } |
565 | |
566 | if(mem_type == 1) { |
567 | mtd = do_map_probe(name: "map_ram" , map: &dev->pcmcia_map); |
568 | } else if(mem_type == 2) { |
569 | mtd = do_map_probe(name: "map_rom" , map: &dev->pcmcia_map); |
570 | } else { |
571 | for(i = 0; i < ARRAY_SIZE(probes); i++) { |
572 | pr_debug("Trying %s\n" , probes[i]); |
573 | mtd = do_map_probe(name: probes[i], map: &dev->pcmcia_map); |
574 | if(mtd) |
575 | break; |
576 | |
577 | pr_debug("FAILED: %s\n" , probes[i]); |
578 | } |
579 | } |
580 | |
581 | if(!mtd) { |
582 | pr_debug("Can not find an MTD\n" ); |
583 | pcmciamtd_release(link); |
584 | return -ENODEV; |
585 | } |
586 | |
587 | dev->mtd_info = mtd; |
588 | mtd->owner = THIS_MODULE; |
589 | |
590 | if(new_name) { |
591 | int size = 0; |
592 | char unit = ' '; |
593 | /* Since we are using a default name, make it better by adding |
594 | * in the size |
595 | */ |
596 | if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ |
597 | size = mtd->size >> 10; |
598 | unit = 'K'; |
599 | } else { |
600 | size = mtd->size >> 20; |
601 | unit = 'M'; |
602 | } |
603 | snprintf(buf: dev->mtd_name, size: sizeof(dev->mtd_name), fmt: "%d%ciB %s" , size, unit, "PCMCIA Memory card" ); |
604 | } |
605 | |
606 | /* If the memory found is fits completely into the mapped PCMCIA window, |
607 | use the faster non-remapping read/write functions */ |
608 | if(mtd->size <= dev->win_size) { |
609 | pr_debug("Using non remapping memory functions\n" ); |
610 | dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; |
611 | if (dev->pcmcia_map.bankwidth == 1) { |
612 | dev->pcmcia_map.read = pcmcia_read8; |
613 | dev->pcmcia_map.write = pcmcia_write8; |
614 | } else { |
615 | dev->pcmcia_map.read = pcmcia_read16; |
616 | dev->pcmcia_map.write = pcmcia_write16; |
617 | } |
618 | dev->pcmcia_map.copy_from = pcmcia_copy_from; |
619 | dev->pcmcia_map.copy_to = pcmcia_copy_to; |
620 | } |
621 | |
622 | if (mtd_device_register(mtd, NULL, 0)) { |
623 | map_destroy(mtd); |
624 | dev->mtd_info = NULL; |
625 | dev_err(&dev->p_dev->dev, |
626 | "Could not register the MTD device\n" ); |
627 | pcmciamtd_release(link); |
628 | return -ENODEV; |
629 | } |
630 | dev_info(&dev->p_dev->dev, "mtd%d: %s\n" , mtd->index, mtd->name); |
631 | return 0; |
632 | } |
633 | |
634 | |
635 | static int pcmciamtd_suspend(struct pcmcia_device *dev) |
636 | { |
637 | pr_debug("EVENT_PM_RESUME\n" ); |
638 | |
639 | /* get_lock(link); */ |
640 | |
641 | return 0; |
642 | } |
643 | |
644 | static int pcmciamtd_resume(struct pcmcia_device *dev) |
645 | { |
646 | pr_debug("EVENT_PM_SUSPEND\n" ); |
647 | |
648 | /* free_lock(link); */ |
649 | |
650 | return 0; |
651 | } |
652 | |
653 | |
654 | static void pcmciamtd_detach(struct pcmcia_device *link) |
655 | { |
656 | struct pcmciamtd_dev *dev = link->priv; |
657 | |
658 | pr_debug("link=0x%p\n" , link); |
659 | |
660 | if(dev->mtd_info) { |
661 | mtd_device_unregister(master: dev->mtd_info); |
662 | dev_info(&dev->p_dev->dev, "mtd%d: Removing\n" , |
663 | dev->mtd_info->index); |
664 | map_destroy(mtd: dev->mtd_info); |
665 | } |
666 | |
667 | pcmciamtd_release(link); |
668 | } |
669 | |
670 | |
671 | static int pcmciamtd_probe(struct pcmcia_device *link) |
672 | { |
673 | struct pcmciamtd_dev *dev; |
674 | |
675 | /* Create new memory card device */ |
676 | dev = kzalloc(size: sizeof(*dev), GFP_KERNEL); |
677 | if (!dev) return -ENOMEM; |
678 | pr_debug("dev=0x%p\n" , dev); |
679 | |
680 | dev->p_dev = link; |
681 | link->priv = dev; |
682 | |
683 | return pcmciamtd_config(link); |
684 | } |
685 | |
686 | static const struct pcmcia_device_id pcmciamtd_ids[] = { |
687 | PCMCIA_DEVICE_FUNC_ID(1), |
688 | PCMCIA_DEVICE_PROD_ID123("IO DATA" , "PCS-2M" , "2MB SRAM" , 0x547e66dc, 0x1fed36cd, 0x36eadd21), |
689 | PCMCIA_DEVICE_PROD_ID12("IBM" , "2MB SRAM" , 0xb569a6e5, 0x36eadd21), |
690 | PCMCIA_DEVICE_PROD_ID12("IBM" , "4MB FLASH" , 0xb569a6e5, 0x8bc54d2a), |
691 | PCMCIA_DEVICE_PROD_ID12("IBM" , "8MB FLASH" , 0xb569a6e5, 0x6df1be3e), |
692 | PCMCIA_DEVICE_PROD_ID12("Intel" , "S2E20SW" , 0x816cc815, 0xd14c9dcf), |
693 | PCMCIA_DEVICE_PROD_ID12("Intel" , "S2E8 SW" , 0x816cc815, 0xa2d7dedb), |
694 | PCMCIA_DEVICE_PROD_ID12("intel" , "SERIES2-02 " , 0x40ade711, 0x145cea5c), |
695 | PCMCIA_DEVICE_PROD_ID12("intel" , "SERIES2-04 " , 0x40ade711, 0x42064dda), |
696 | PCMCIA_DEVICE_PROD_ID12("intel" , "SERIES2-20 " , 0x40ade711, 0x25ee5cb0), |
697 | PCMCIA_DEVICE_PROD_ID12("intel" , "VALUE SERIES 100 " , 0x40ade711, 0xdf8506d8), |
698 | PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC." , "SRAM 256K Bytes" , 0x54d0c69c, 0xad12c29c), |
699 | PCMCIA_DEVICE_PROD_ID12("Maxtor" , "MAXFL MobileMax Flash Memory Card" , 0xb68968c8, 0x2dfb47b0), |
700 | PCMCIA_DEVICE_PROD_ID123("M-Systems" , "M-SYS Flash Memory Card" , "(c) M-Systems" , 0x7ed2ad87, 0x675dc3fb, 0x7aef3965), |
701 | PCMCIA_DEVICE_PROD_ID12("PRETEC" , " 2MB SRAM CARD" , 0xebf91155, 0x805360ca), |
702 | PCMCIA_DEVICE_PROD_ID12("PRETEC" , " 4MB SRAM CARD" , 0xebf91155, 0x20b6bf17), |
703 | PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON" , "WWB101EN20" , 0xf9876baf, 0xad0b207b), |
704 | PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON" , "WWB513EN20" , 0xf9876baf, 0xe8d884ad), |
705 | PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies" , " 4MB FLASH Card" , 0x96fd8277, 0x737a5b05), |
706 | PCMCIA_DEVICE_PROD_ID12("Starfish, Inc." , "REX-3000" , 0x05ddca47, 0xe7d67bca), |
707 | PCMCIA_DEVICE_PROD_ID12("Starfish, Inc." , "REX-4100" , 0x05ddca47, 0x7bc32944), |
708 | /* the following was commented out in pcmcia-cs-3.2.7 */ |
709 | /* PCMCIA_DEVICE_PROD_ID12("RATOC Systems,Inc.", "SmartMedia ADAPTER PC Card", 0xf4a2fefe, 0x5885b2ae), */ |
710 | #ifdef CONFIG_MTD_PCMCIA_ANONYMOUS |
711 | { .match_flags = PCMCIA_DEV_ID_MATCH_ANONYMOUS, }, |
712 | #endif |
713 | PCMCIA_DEVICE_NULL |
714 | }; |
715 | MODULE_DEVICE_TABLE(pcmcia, pcmciamtd_ids); |
716 | |
717 | static struct pcmcia_driver pcmciamtd_driver = { |
718 | .name = "pcmciamtd" , |
719 | .probe = pcmciamtd_probe, |
720 | .remove = pcmciamtd_detach, |
721 | .owner = THIS_MODULE, |
722 | .id_table = pcmciamtd_ids, |
723 | .suspend = pcmciamtd_suspend, |
724 | .resume = pcmciamtd_resume, |
725 | }; |
726 | |
727 | |
728 | static int __init init_pcmciamtd(void) |
729 | { |
730 | if(bankwidth && bankwidth != 1 && bankwidth != 2) { |
731 | info("bad bankwidth (%d), using default" , bankwidth); |
732 | bankwidth = 2; |
733 | } |
734 | if(force_size && (force_size < 1 || force_size > 64)) { |
735 | info("bad force_size (%d), using default" , force_size); |
736 | force_size = 0; |
737 | } |
738 | if(mem_type && mem_type != 1 && mem_type != 2) { |
739 | info("bad mem_type (%d), using default" , mem_type); |
740 | mem_type = 0; |
741 | } |
742 | return pcmcia_register_driver(driver: &pcmciamtd_driver); |
743 | } |
744 | |
745 | |
746 | static void __exit exit_pcmciamtd(void) |
747 | { |
748 | pr_debug(DRIVER_DESC " unloading" ); |
749 | pcmcia_unregister_driver(driver: &pcmciamtd_driver); |
750 | } |
751 | |
752 | module_init(init_pcmciamtd); |
753 | module_exit(exit_pcmciamtd); |
754 | |