2 * $Id: pmc551.c,v 1.8 2000/07/14 07:53:31 dwmw2 Exp $
4 * PMC551 PCI Mezzanine Ram Device
8 * Copyright 1999,2000 Nortel Networks
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
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.
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.
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>
69 #include <linux/ioctl.h>
71 #include <asm/system.h>
72 #include <asm/segment.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)
83 #define PCI_BASE_ADDRESS(dev) (dev->base_address[0])
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
;
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
)) {
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
,
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
117 memset(priv
->start
+ start_addr_lowbits
,
121 /* We have to do multiple writes to get all the data
123 memset(priv
->start
+ start_addr_lowbits
,
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
,
130 (priv
->mem_map0_base_val
131 | start_addr_highbits
));
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
,
141 priv
->curr_mem_map0_val
);
147 instr
->state
= MTD_ERASE_DONE
;
149 if (instr
->callback
) {
150 (*(instr
->callback
))(instr
);
157 static void pmc551_unpoint (struct mtd_info
*mtd
, u_char
*addr
)
161 static int pmc551_read (struct mtd_info
*mtd
,
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
;
173 u_char
*copyto
= buf
;
176 /* Is it past the end? */
177 if (from
> mtd
->size
) {
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
,
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
204 priv
->start
+ start_addr_lowbits
,
208 /* We have to do multiple writes to get all the data
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
,
218 (priv
->mem_map0_base_val
219 | start_addr_highbits
));
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
);
233 priv
->curr_mem_map0_val
= (priv
->mem_map0_base_val
234 | start_addr_highbits
);
235 pci_write_config_dword ( priv
->dev
,
237 priv
->curr_mem_map0_val
);
241 copyto
+= end_addr_lowbits
;
245 *retlen
= copyto
- buf
;
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
;
257 const u_char
*copyfrom
= buf
;
260 /* Is it past the end? */
261 if (to
> mtd
->size
) {
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
,
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
287 memcpy(priv
->start
+ start_addr_lowbits
,
292 /* We have to do multiple writes to get all the data
294 memcpy(priv
->start
+ start_addr_lowbits
,
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
,
302 (priv
->mem_map0_base_val
303 | start_addr_highbits
));
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
);
317 priv
->curr_mem_map0_val
= (priv
->mem_map0_base_val
318 | start_addr_highbits
);
319 pci_write_config_dword ( priv
->dev
,
321 priv
->curr_mem_map0_val
);
325 copyfrom
+= end_addr_lowbits
;
329 *retlen
= copyfrom
- buf
;
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
343 * returns the size of the memory region found.
345 static u32
fixup_pmc551 (struct pci_dev
*dev
)
347 #ifdef CONFIG_MTD_PMC551_BUGFIX
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) {
400 * Set to be prefetchable
402 pci_read_config_dword(dev
, PCI_BASE_ADDRESS_0
, &dcmd
);
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
);
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
);
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
);
463 pci_write_config_dword ( dev
, PMC551_DRAM_CFG
, dcmd
);
466 * Check to make certain fast back-to-back, if not
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 ":"" );
529 * Kernel version specific module stuffages
531 #if LINUX_VERSION_CODE < 0x20300
533 #define init_pmc551 init_module
534 #define cleanup_pmc551 cleanup_module
541 * PMC551 Card Initialization
543 //static int __init init_pmc551(void)
544 int __init
init_pmc551(void)
546 struct pci_dev
*PCI_Device
= NULL
;
549 struct mtd_info
*mtd
;
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");
557 printk(KERN_NOTICE
"pmc551: PCI not enabled.\n");
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
) {
571 printk(KERN_NOTICE
"pmc551: Found PCI V370PDC IRQ:%d\n",
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");
587 mtd
= kmalloc(sizeof(struct mtd_info
), GFP_KERNEL
);
589 printk(KERN_NOTICE
"pmc551: Cannot allocate new MTD device.\n");
593 memset(mtd
, 0, sizeof(struct mtd_info
));
595 priv
= kmalloc (sizeof(struct mypriv
), GFP_KERNEL
);
597 printk(KERN_NOTICE
"pmc551: Cannot allocate new MTD device.\n");
601 memset(priv
, 0, sizeof(*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
,
616 priv
->curr_mem_map0_val
);
619 mtd
->flags
= (MTD_CLEAR_BITS
621 | MTD_WRITEB_WRITEABLE
623 mtd
->erase
= pmc551_erase
;
625 mtd
->unpoint
= pmc551_unpoint
;
626 mtd
->read
= pmc551_read
;
627 mtd
->write
= pmc551_write
;
628 mtd
->module
= THIS_MODULE
;
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");
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,
643 priv
->start
+ priv
->aperture_size
);
644 printk(KERN_NOTICE
"Total memory is %dM\n", length
/1024/1024);
645 priv
->nextpmc551
= pmc551list
;
651 printk(KERN_NOTICE
"pmc551: not detected,\n");
655 printk(KERN_NOTICE
"pmc551: %d pmc551 devices loaded\n", found
);
660 * PMC551 Card Cleanup
662 static void __exit
cleanup_pmc551(void)
665 struct mtd_info
*mtd
;
668 while((mtd
=pmc551list
)) {
669 priv
= (struct mypriv
*)mtd
->priv
;
670 pmc551list
= priv
->nextpmc551
;
673 iounmap(((struct mypriv
*)mtd
->priv
)->start
);
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
);