- pre2
[davej-history.git] / drivers / mtd / pmc551.c
blobfdc80cbcfd202435310d839b9f960f199697a3a4
1 /*
2 * $Id: pmc551.c,v 1.8 2000/07/14 07:53:31 dwmw2 Exp $
4 * PMC551 PCI Mezzanine Ram Device
6 * Author:
7 * Mark Ferrell
8 * Copyright 1999,2000 Nortel Networks
10 * License:
11 * As part of this driver was derrived from the slram.c driver it falls
12 * under the same license, which is GNU General Public License v2
14 * Description:
15 * This driver is intended to support the PMC551 PCI Ram device from
16 * Ramix Inc. The PMC551 is a PMC Mezzanine module for cPCI embeded
17 * systems. The device contains a single SROM that initally programs the
18 * V370PDC chipset onboard the device, and various banks of DRAM/SDRAM
19 * onboard. This driver implements this PCI Ram device as an MTD (Memory
20 * Technologies Device) so that it can be used to hold a filesystem, or
21 * for added swap space in embeded systems. Since the memory on this
22 * board isn't as fast as main memory we do not try to hook it into main
23 * memeory as that would simply reduce performance on the system. Using
24 * it as a block device allows us to use it as high speed swap or for a
25 * high speed disk device of some sort. Which becomes very usefull on
26 * diskless systems in the embeded market I might add.
28 * Credits:
29 * Saeed Karamooz <saeed@ramix.com> of Ramix INC. for the initial
30 * example code of how to initialize this device and for help with
31 * questions I had concerning operation of the device.
33 * Most of the MTD code for this driver was originally written for the
34 * slram.o module in the MTD drivers package written by David Hinds
35 * <dhinds@allegro.stanford.edu> which allows the mapping of system
36 * memory into an mtd device. Since the PMC551 memory module is
37 * accessed in the same fashion as system memory, the slram.c code
38 * became a very nice fit to the needs of this driver. All we added was
39 * PCI detection/initialization to the driver and automaticly figure out
40 * the size via the PCI detection.o, later changes by Corey Minyard
41 * settup the card to utilize a 1M sliding apature.
43 * Corey Minyard <minyard@nortelnetworks.com>
44 * * Modified driver to utilize a sliding apature instead of mapping all
45 * memory into kernel space which turned out to be very wastefull.
46 * * Located a bug in the SROM's initialization sequence that made the
47 * memory unussable, added a fix to code to touch up the DRAM some.
49 * Bugs/FIXME's:
50 * * MUST fix the init function to not spin on a register
51 * waiting for it to set .. this does not safely handle busted devices
52 * that never reset the register correctly which will cause the system to
53 * hang w/ a reboot beeing the only chance at recover.
56 #include <linux/config.h>
57 #include <linux/kernel.h>
58 #include <linux/module.h>
59 #include <asm/uaccess.h>
60 #include <linux/types.h>
61 #include <linux/sched.h>
62 #include <linux/init.h>
63 #include <linux/ptrace.h>
64 #include <linux/malloc.h>
65 #include <linux/string.h>
66 #include <linux/timer.h>
67 #include <linux/major.h>
68 #include <linux/fs.h>
69 #include <linux/ioctl.h>
70 #include <asm/io.h>
71 #include <asm/system.h>
72 #include <asm/segment.h>
73 #include <stdarg.h>
74 #include <linux/pci.h>
76 #include <linux/mtd/mtd.h>
77 #include <linux/mtd/pmc551.h>
78 #include <linux/mtd/compatmac.h>
80 #if LINUX_VERSION_CODE > 0x20300
81 #define PCI_BASE_ADDRESS(dev) (dev->resource[0].start)
82 #else
83 #define PCI_BASE_ADDRESS(dev) (dev->base_address[0])
84 #endif
86 static struct mtd_info *pmc551list = NULL;
88 static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr)
90 struct mypriv *priv = mtd->priv;
91 u32 start_addr_highbits;
92 u32 end_addr_highbits;
93 u32 start_addr_lowbits;
94 u32 end_addr_lowbits;
95 unsigned long end;
97 end = instr->addr + instr->len;
99 /* Is it too much memory? The second check find if we wrap around
100 past the end of a u32. */
101 if ((end > mtd->size) || (end < instr->addr)) {
102 return -EINVAL;
105 start_addr_highbits = instr->addr & PMC551_ADDR_HIGH_MASK;
106 end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
107 start_addr_lowbits = instr->addr & PMC551_ADDR_LOW_MASK;
108 end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
110 pci_write_config_dword ( priv->dev,
111 PMC551_PCI_MEM_MAP0,
112 (priv->mem_map0_base_val
113 | start_addr_highbits));
114 if (start_addr_highbits == end_addr_highbits) {
115 /* The whole thing fits within one access, so just one shot
116 will do it. */
117 memset(priv->start + start_addr_lowbits,
118 0xff,
119 instr->len);
120 } else {
121 /* We have to do multiple writes to get all the data
122 written. */
123 memset(priv->start + start_addr_lowbits,
124 0xff,
125 priv->aperture_size - start_addr_lowbits);
126 start_addr_highbits += priv->aperture_size;
127 while (start_addr_highbits != end_addr_highbits) {
128 pci_write_config_dword ( priv->dev,
129 PMC551_PCI_MEM_MAP0,
130 (priv->mem_map0_base_val
131 | start_addr_highbits));
132 memset(priv->start,
133 0xff,
134 priv->aperture_size);
135 start_addr_highbits += priv->aperture_size;
137 priv->curr_mem_map0_val = (priv->mem_map0_base_val
138 | start_addr_highbits);
139 pci_write_config_dword ( priv->dev,
140 PMC551_PCI_MEM_MAP0,
141 priv->curr_mem_map0_val);
142 memset(priv->start,
143 0xff,
144 end_addr_lowbits);
147 instr->state = MTD_ERASE_DONE;
149 if (instr->callback) {
150 (*(instr->callback))(instr);
153 return 0;
157 static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr)
161 static int pmc551_read (struct mtd_info *mtd,
162 loff_t from,
163 size_t len,
164 size_t *retlen,
165 u_char *buf)
167 struct mypriv *priv = (struct mypriv *)mtd->priv;
168 u32 start_addr_highbits;
169 u32 end_addr_highbits;
170 u32 start_addr_lowbits;
171 u32 end_addr_lowbits;
172 unsigned long end;
173 u_char *copyto = buf;
176 /* Is it past the end? */
177 if (from > mtd->size) {
178 return -EINVAL;
181 end = from + len;
182 start_addr_highbits = from & PMC551_ADDR_HIGH_MASK;
183 end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
184 start_addr_lowbits = from & PMC551_ADDR_LOW_MASK;
185 end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
188 /* Only rewrite the first value if it doesn't match our current
189 values. Most operations are on the same page as the previous
190 value, so this is a pretty good optimization. */
191 if (priv->curr_mem_map0_val !=
192 (priv->mem_map0_base_val | start_addr_highbits)) {
193 priv->curr_mem_map0_val = (priv->mem_map0_base_val
194 | start_addr_highbits);
195 pci_write_config_dword ( priv->dev,
196 PMC551_PCI_MEM_MAP0,
197 priv->curr_mem_map0_val);
200 if (start_addr_highbits == end_addr_highbits) {
201 /* The whole thing fits within one access, so just one shot
202 will do it. */
203 memcpy(copyto,
204 priv->start + start_addr_lowbits,
205 len);
206 copyto += len;
207 } else {
208 /* We have to do multiple writes to get all the data
209 written. */
210 memcpy(copyto,
211 priv->start + start_addr_lowbits,
212 priv->aperture_size - start_addr_lowbits);
213 copyto += priv->aperture_size - start_addr_lowbits;
214 start_addr_highbits += priv->aperture_size;
215 while (start_addr_highbits != end_addr_highbits) {
216 pci_write_config_dword ( priv->dev,
217 PMC551_PCI_MEM_MAP0,
218 (priv->mem_map0_base_val
219 | start_addr_highbits));
220 memcpy(copyto,
221 priv->start,
222 priv->aperture_size);
223 copyto += priv->aperture_size;
224 start_addr_highbits += priv->aperture_size;
225 if (start_addr_highbits >= mtd->size) {
226 /* Make sure we have the right value here. */
227 priv->curr_mem_map0_val
228 = (priv->mem_map0_base_val
229 | start_addr_highbits);
230 goto out;
233 priv->curr_mem_map0_val = (priv->mem_map0_base_val
234 | start_addr_highbits);
235 pci_write_config_dword ( priv->dev,
236 PMC551_PCI_MEM_MAP0,
237 priv->curr_mem_map0_val);
238 memcpy(copyto,
239 priv->start,
240 end_addr_lowbits);
241 copyto += end_addr_lowbits;
244 out:
245 *retlen = copyto - buf;
246 return 0;
249 static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
251 struct mypriv *priv = (struct mypriv *)mtd->priv;
252 u32 start_addr_highbits;
253 u32 end_addr_highbits;
254 u32 start_addr_lowbits;
255 u32 end_addr_lowbits;
256 unsigned long end;
257 const u_char *copyfrom = buf;
260 /* Is it past the end? */
261 if (to > mtd->size) {
262 return -EINVAL;
265 end = to + len;
266 start_addr_highbits = to & PMC551_ADDR_HIGH_MASK;
267 end_addr_highbits = end & PMC551_ADDR_HIGH_MASK;
268 start_addr_lowbits = to & PMC551_ADDR_LOW_MASK;
269 end_addr_lowbits = end & PMC551_ADDR_LOW_MASK;
272 /* Only rewrite the first value if it doesn't match our current
273 values. Most operations are on the same page as the previous
274 value, so this is a pretty good optimization. */
275 if (priv->curr_mem_map0_val !=
276 (priv->mem_map0_base_val | start_addr_highbits)) {
277 priv->curr_mem_map0_val = (priv->mem_map0_base_val
278 | start_addr_highbits);
279 pci_write_config_dword ( priv->dev,
280 PMC551_PCI_MEM_MAP0,
281 priv->curr_mem_map0_val);
284 if (start_addr_highbits == end_addr_highbits) {
285 /* The whole thing fits within one access, so just one shot
286 will do it. */
287 memcpy(priv->start + start_addr_lowbits,
288 copyfrom,
289 len);
290 copyfrom += len;
291 } else {
292 /* We have to do multiple writes to get all the data
293 written. */
294 memcpy(priv->start + start_addr_lowbits,
295 copyfrom,
296 priv->aperture_size - start_addr_lowbits);
297 copyfrom += priv->aperture_size - start_addr_lowbits;
298 start_addr_highbits += priv->aperture_size;
299 while (start_addr_highbits != end_addr_highbits) {
300 pci_write_config_dword ( priv->dev,
301 PMC551_PCI_MEM_MAP0,
302 (priv->mem_map0_base_val
303 | start_addr_highbits));
304 memcpy(priv->start,
305 copyfrom,
306 priv->aperture_size);
307 copyfrom += priv->aperture_size;
308 start_addr_highbits += priv->aperture_size;
309 if (start_addr_highbits >= mtd->size) {
310 /* Make sure we have the right value here. */
311 priv->curr_mem_map0_val
312 = (priv->mem_map0_base_val
313 | start_addr_highbits);
314 goto out;
317 priv->curr_mem_map0_val = (priv->mem_map0_base_val
318 | start_addr_highbits);
319 pci_write_config_dword ( priv->dev,
320 PMC551_PCI_MEM_MAP0,
321 priv->curr_mem_map0_val);
322 memcpy(priv->start,
323 copyfrom,
324 end_addr_lowbits);
325 copyfrom += end_addr_lowbits;
328 out:
329 *retlen = copyfrom - buf;
330 return 0;
334 * Fixup routines for the V370PDC
335 * PCI device ID 0x020011b0
337 * This function basicly kick starts the DRAM oboard the card and gets it
338 * ready to be used. Before this is done the device reads VERY erratic, so
339 * much that it can crash the Linux 2.2.x series kernels when a user cat's
340 * /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL
341 * register. FIXME: stop spinning on registers .. must implement a timeout
342 * mechanism
343 * returns the size of the memory region found.
345 static u32 fixup_pmc551 (struct pci_dev *dev)
347 #ifdef CONFIG_MTD_PMC551_BUGFIX
348 u32 dram_data;
349 #endif
350 u32 size, dcmd;
351 u16 cmd, i;
353 /* Sanity Check */
354 if(!dev) {
355 return -ENODEV;
359 * Get the size of the memory by reading all the DRAM size values
360 * and adding them up.
362 * KLUDGE ALERT: the boards we are using have invalid column and
363 * row mux values. We fix them here, but this will break other
364 * memory configurations.
366 #ifdef CONFIG_MTD_PMC551_BUGFIX
367 pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data);
368 size = PMC551_DRAM_BLK_GET_SIZE(dram_data);
369 dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
370 dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
371 pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data);
373 pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data);
374 size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
375 dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
376 dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
377 pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data);
379 pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data);
380 size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
381 dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
382 dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
383 pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data);
385 pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data);
386 size += PMC551_DRAM_BLK_GET_SIZE(dram_data);
387 dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
388 dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
389 pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data);
390 #endif /* CONFIG_MTD_PMC551_BUGFIX */
393 * Oops .. something went wrong
395 if( (size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) {
396 return -ENODEV;
400 * Set to be prefetchable
402 pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dcmd );
403 dcmd |= 0x8;
406 * Put it back the way it was
408 pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dcmd );
409 pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dcmd );
412 * Some screen fun
414 printk(KERN_NOTICE "pmc551: %dM (0x%x) of %sprefetchable memory at 0x%lx\n",
415 size/1024/1024, size, ((dcmd&0x8) == 0)?"non-":"",
416 PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK );
419 * Turn on PCI memory and I/O bus access just for kicks
421 pci_write_config_word( dev, PCI_COMMAND,
422 PCI_COMMAND_MEMORY | PCI_COMMAND_IO );
425 * Config DRAM
427 pci_write_config_word( dev, PMC551_SDRAM_MA, 0x0400 );
428 pci_write_config_word( dev, PMC551_SDRAM_CMD, 0x00bf );
431 * Wait untill command has gone through
432 * FIXME: register spinning issue
434 do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd );
435 } while ( (PCI_COMMAND_IO) & cmd );
438 * Must be held high for some duration of time to take effect??
440 for ( i = 1; i<=8 ; i++) {
441 pci_write_config_word (dev, PMC551_SDRAM_CMD, 0x0df);
444 * Make certain command has gone through
445 * FIXME: register spinning issue
447 do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
448 } while ( (PCI_COMMAND_IO) & cmd );
451 pci_write_config_word ( dev, PMC551_SDRAM_MA, 0x0020);
452 pci_write_config_word ( dev, PMC551_SDRAM_CMD, 0x0ff);
455 * Wait until command completes
456 * FIXME: register spinning issue
458 do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd);
459 } while ( (PCI_COMMAND_IO) & cmd );
461 pci_read_config_dword ( dev, PMC551_DRAM_CFG, &dcmd);
462 dcmd |= 0x02000000;
463 pci_write_config_dword ( dev, PMC551_DRAM_CFG, dcmd);
466 * Check to make certain fast back-to-back, if not
467 * then set it so
469 pci_read_config_word( dev, PCI_STATUS, &cmd);
470 if((cmd&PCI_COMMAND_FAST_BACK) == 0) {
471 cmd |= PCI_COMMAND_FAST_BACK;
472 pci_write_config_word( dev, PCI_STATUS, cmd);
476 * Check to make certain the DEVSEL is set correctly, this device
477 * has a tendancy to assert DEVSEL and TRDY when a write is performed
478 * to the memory when memory is read-only
480 if((cmd&PCI_STATUS_DEVSEL_MASK) != 0x0) {
481 cmd &= ~PCI_STATUS_DEVSEL_MASK;
482 pci_write_config_word( dev, PCI_STATUS, cmd );
486 * Check to see the state of the memory
487 * FIXME: perhaps hide some of this around an #ifdef DEBUG as
488 * it doesn't effect or enhance cards functionality
490 pci_read_config_dword( dev, 0x74, &dcmd );
491 printk(KERN_NOTICE "pmc551: DRAM_BLK3 Flags: %s,%s\n",
492 ((0x2&dcmd) == 0)?"RW":"RO",
493 ((0x1&dcmd) == 0)?"Off":"On" );
495 pci_read_config_dword( dev, 0x70, &dcmd );
496 printk(KERN_NOTICE "pmc551: DRAM_BLK2 Flags: %s,%s\n",
497 ((0x2&dcmd) == 0)?"RW":"RO",
498 ((0x1&dcmd) == 0)?"Off":"On" );
500 pci_read_config_dword( dev, 0x6C, &dcmd );
501 printk(KERN_NOTICE "pmc551: DRAM_BLK1 Flags: %s,%s\n",
502 ((0x2&dcmd) == 0)?"RW":"RO",
503 ((0x1&dcmd) == 0)?"Off":"On" );
505 pci_read_config_dword( dev, 0x68, &dcmd );
506 printk(KERN_NOTICE "pmc551: DRAM_BLK0 Flags: %s,%s\n",
507 ((0x2&dcmd) == 0)?"RW":"RO",
508 ((0x1&dcmd) == 0)?"Off":"On" );
510 pci_read_config_word( dev, 0x4, &cmd );
511 printk( KERN_NOTICE "pmc551: Memory Access %s\n",
512 ((0x2&cmd) == 0)?"off":"on" );
513 printk( KERN_NOTICE "pmc551: I/O Access %s\n",
514 ((0x1&cmd) == 0)?"off":"on" );
516 pci_read_config_word( dev, 0x6, &cmd );
517 printk( KERN_NOTICE "pmc551: Devsel %s\n",
518 ((PCI_STATUS_DEVSEL_MASK&cmd)==0x000)?"Fast":
519 ((PCI_STATUS_DEVSEL_MASK&cmd)==0x200)?"Medium":
520 ((PCI_STATUS_DEVSEL_MASK&cmd)==0x400)?"Slow":"Invalid" );
522 printk( KERN_NOTICE "pmc551: %sFast Back-to-Back\n",
523 ((PCI_COMMAND_FAST_BACK&cmd) == 0)?"Not ":"" );
525 return size;
529 * Kernel version specific module stuffages
531 #if LINUX_VERSION_CODE < 0x20300
532 #ifdef MODULE
533 #define init_pmc551 init_module
534 #define cleanup_pmc551 cleanup_module
535 #endif
536 #define __exit
537 #endif
541 * PMC551 Card Initialization
543 //static int __init init_pmc551(void)
544 int __init init_pmc551(void)
546 struct pci_dev *PCI_Device = NULL;
547 struct mypriv *priv;
548 int count, found=0;
549 struct mtd_info *mtd;
550 u32 length = 0;
553 printk(KERN_NOTICE "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n");
554 printk(KERN_INFO "$Id: pmc551.c,v 1.8 2000/07/14 07:53:31 dwmw2 Exp $\n");
556 if(!pci_present()) {
557 printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
558 return -ENODEV;
562 * PCU-bus chipset probe.
564 for( count = 0; count < MAX_MTD_DEVICES; count++ ) {
566 if ( (PCI_Device = pci_find_device( PCI_VENDOR_ID_V3_SEMI,
567 PCI_DEVICE_ID_V3_SEMI_V370PDC, PCI_Device ) ) == NULL) {
568 break;
571 printk(KERN_NOTICE "pmc551: Found PCI V370PDC IRQ:%d\n",
572 PCI_Device->irq);
575 * The PMC551 device acts VERY wierd if you don't init it
576 * first. i.e. it will not correctly report devsel. If for
577 * some reason the sdram is in a wrote-protected state the
578 * device will DEVSEL when it is written to causing problems
579 * with the oldproc.c driver in
580 * some kernels (2.2.*)
582 if((length = fixup_pmc551(PCI_Device)) <= 0) {
583 printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n");
584 break;
587 mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
588 if (!mtd) {
589 printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n");
590 break;
593 memset(mtd, 0, sizeof(struct mtd_info));
595 priv = kmalloc (sizeof(struct mypriv), GFP_KERNEL);
596 if (!priv) {
597 printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n");
598 kfree(mtd);
599 break;
601 memset(priv, 0, sizeof(*priv));
602 mtd->priv = priv;
604 priv->dev = PCI_Device;
605 priv->aperture_size = PMC551_APERTURE_SIZE;
606 priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device)
607 & PCI_BASE_ADDRESS_MEM_MASK),
608 priv->aperture_size);
609 priv->mem_map0_base_val = (PMC551_APERTURE_VAL
610 | PMC551_PCI_MEM_MAP_REG_EN
611 | PMC551_PCI_MEM_MAP_ENABLE);
612 priv->curr_mem_map0_val = priv->mem_map0_base_val;
614 pci_write_config_dword ( priv->dev,
615 PMC551_PCI_MEM_MAP0,
616 priv->curr_mem_map0_val);
618 mtd->size = length;
619 mtd->flags = (MTD_CLEAR_BITS
620 | MTD_SET_BITS
621 | MTD_WRITEB_WRITEABLE
622 | MTD_VOLATILE);
623 mtd->erase = pmc551_erase;
624 mtd->point = NULL;
625 mtd->unpoint = pmc551_unpoint;
626 mtd->read = pmc551_read;
627 mtd->write = pmc551_write;
628 mtd->module = THIS_MODULE;
629 mtd->type = MTD_RAM;
630 mtd->name = "PMC551 RAM board";
631 mtd->erasesize = 0x10000;
633 if (add_mtd_device(mtd)) {
634 printk(KERN_NOTICE "pmc551: Failed to register new device\n");
635 kfree(mtd->priv);
636 kfree(mtd);
637 break;
639 printk(KERN_NOTICE "Registered pmc551 memory device.\n");
640 printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
641 priv->aperture_size/1024/1024,
642 priv->start,
643 priv->start + priv->aperture_size);
644 printk(KERN_NOTICE "Total memory is %dM\n", length/1024/1024);
645 priv->nextpmc551 = pmc551list;
646 pmc551list = mtd;
647 found++;
650 if( !pmc551list ) {
651 printk(KERN_NOTICE "pmc551: not detected,\n");
652 return -ENODEV;
653 } else {
654 return 0;
655 printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found);
660 * PMC551 Card Cleanup
662 static void __exit cleanup_pmc551(void)
664 int found=0;
665 struct mtd_info *mtd;
666 struct mypriv *priv;
668 while((mtd=pmc551list)) {
669 priv = (struct mypriv *)mtd->priv;
670 pmc551list = priv->nextpmc551;
672 if(priv->start)
673 iounmap(((struct mypriv *)mtd->priv)->start);
675 kfree (mtd->priv);
676 del_mtd_device(mtd);
677 kfree(mtd);
678 found++;
681 printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found);
684 #if LINUX_VERSION_CODE > 0x20300
685 module_init(init_pmc551);
686 module_exit(cleanup_pmc551);
687 #endif