diff options
Diffstat (limited to 'drivers/mtd/devices/pmc551.c')
-rw-r--r-- | drivers/mtd/devices/pmc551.c | 843 |
1 files changed, 843 insertions, 0 deletions
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c new file mode 100644 index 000000000000..5b3defadf884 --- /dev/null +++ b/drivers/mtd/devices/pmc551.c | |||
@@ -0,0 +1,843 @@ | |||
1 | /* | ||
2 | * $Id: pmc551.c,v 1.30 2005/01/05 18:05:13 dwmw2 Exp $ | ||
3 | * | ||
4 | * PMC551 PCI Mezzanine Ram Device | ||
5 | * | ||
6 | * Author: | ||
7 | * Mark Ferrell <mferrell@mvista.com> | ||
8 | * Copyright 1999,2000 Nortel Networks | ||
9 | * | ||
10 | * License: | ||
11 | * As part of this driver was derived from the slram.c driver it | ||
12 | * falls under the same license, which is GNU General Public | ||
13 | * License v2 | ||
14 | * | ||
15 | * Description: | ||
16 | * This driver is intended to support the PMC551 PCI Ram device | ||
17 | * from Ramix Inc. The PMC551 is a PMC Mezzanine module for | ||
18 | * cPCI embedded systems. The device contains a single SROM | ||
19 | * that initially programs the V370PDC chipset onboard the | ||
20 | * device, and various banks of DRAM/SDRAM onboard. This driver | ||
21 | * implements this PCI Ram device as an MTD (Memory Technology | ||
22 | * Device) so that it can be used to hold a file system, or for | ||
23 | * added swap space in embedded systems. Since the memory on | ||
24 | * this board isn't as fast as main memory we do not try to hook | ||
25 | * it into main memory as that would simply reduce performance | ||
26 | * on the system. Using it as a block device allows us to use | ||
27 | * it as high speed swap or for a high speed disk device of some | ||
28 | * sort. Which becomes very useful on diskless systems in the | ||
29 | * embedded market I might add. | ||
30 | * | ||
31 | * Notes: | ||
32 | * Due to what I assume is more buggy SROM, the 64M PMC551 I | ||
33 | * have available claims that all 4 of it's DRAM banks have 64M | ||
34 | * of ram configured (making a grand total of 256M onboard). | ||
35 | * This is slightly annoying since the BAR0 size reflects the | ||
36 | * aperture size, not the dram size, and the V370PDC supplies no | ||
37 | * other method for memory size discovery. This problem is | ||
38 | * mostly only relevant when compiled as a module, as the | ||
39 | * unloading of the module with an aperture size smaller then | ||
40 | * the ram will cause the driver to detect the onboard memory | ||
41 | * size to be equal to the aperture size when the module is | ||
42 | * reloaded. Soooo, to help, the module supports an msize | ||
43 | * option to allow the specification of the onboard memory, and | ||
44 | * an asize option, to allow the specification of the aperture | ||
45 | * size. The aperture must be equal to or less then the memory | ||
46 | * size, the driver will correct this if you screw it up. This | ||
47 | * problem is not relevant for compiled in drivers as compiled | ||
48 | * in drivers only init once. | ||
49 | * | ||
50 | * Credits: | ||
51 | * Saeed Karamooz <saeed@ramix.com> of Ramix INC. for the | ||
52 | * initial example code of how to initialize this device and for | ||
53 | * help with questions I had concerning operation of the device. | ||
54 | * | ||
55 | * Most of the MTD code for this driver was originally written | ||
56 | * for the slram.o module in the MTD drivers package which | ||
57 | * allows the mapping of system memory into an MTD device. | ||
58 | * Since the PMC551 memory module is accessed in the same | ||
59 | * fashion as system memory, the slram.c code became a very nice | ||
60 | * fit to the needs of this driver. All we added was PCI | ||
61 | * detection/initialization to the driver and automatically figure | ||
62 | * out the size via the PCI detection.o, later changes by Corey | ||
63 | * Minyard set up the card to utilize a 1M sliding apature. | ||
64 | * | ||
65 | * Corey Minyard <minyard@nortelnetworks.com> | ||
66 | * * Modified driver to utilize a sliding aperture instead of | ||
67 | * mapping all memory into kernel space which turned out to | ||
68 | * be very wasteful. | ||
69 | * * Located a bug in the SROM's initialization sequence that | ||
70 | * made the memory unusable, added a fix to code to touch up | ||
71 | * the DRAM some. | ||
72 | * | ||
73 | * Bugs/FIXME's: | ||
74 | * * MUST fix the init function to not spin on a register | ||
75 | * waiting for it to set .. this does not safely handle busted | ||
76 | * devices that never reset the register correctly which will | ||
77 | * cause the system to hang w/ a reboot being the only chance at | ||
78 | * recover. [sort of fixed, could be better] | ||
79 | * * Add I2C handling of the SROM so we can read the SROM's information | ||
80 | * about the aperture size. This should always accurately reflect the | ||
81 | * onboard memory size. | ||
82 | * * Comb the init routine. It's still a bit cludgy on a few things. | ||
83 | */ | ||
84 | |||
85 | #include <linux/version.h> | ||
86 | #include <linux/config.h> | ||
87 | #include <linux/kernel.h> | ||
88 | #include <linux/module.h> | ||
89 | #include <asm/uaccess.h> | ||
90 | #include <linux/types.h> | ||
91 | #include <linux/sched.h> | ||
92 | #include <linux/init.h> | ||
93 | #include <linux/ptrace.h> | ||
94 | #include <linux/slab.h> | ||
95 | #include <linux/string.h> | ||
96 | #include <linux/timer.h> | ||
97 | #include <linux/major.h> | ||
98 | #include <linux/fs.h> | ||
99 | #include <linux/ioctl.h> | ||
100 | #include <asm/io.h> | ||
101 | #include <asm/system.h> | ||
102 | #include <linux/pci.h> | ||
103 | |||
104 | #ifndef CONFIG_PCI | ||
105 | #error Enable PCI in your kernel config | ||
106 | #endif | ||
107 | |||
108 | #include <linux/mtd/mtd.h> | ||
109 | #include <linux/mtd/pmc551.h> | ||
110 | #include <linux/mtd/compatmac.h> | ||
111 | |||
112 | static struct mtd_info *pmc551list; | ||
113 | |||
114 | static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) | ||
115 | { | ||
116 | struct mypriv *priv = mtd->priv; | ||
117 | u32 soff_hi, soff_lo; /* start address offset hi/lo */ | ||
118 | u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ | ||
119 | unsigned long end; | ||
120 | u_char *ptr; | ||
121 | size_t retlen; | ||
122 | |||
123 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
124 | printk(KERN_DEBUG "pmc551_erase(pos:%ld, len:%ld)\n", (long)instr->addr, (long)instr->len); | ||
125 | #endif | ||
126 | |||
127 | end = instr->addr + instr->len - 1; | ||
128 | |||
129 | /* Is it past the end? */ | ||
130 | if ( end > mtd->size ) { | ||
131 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
132 | printk(KERN_DEBUG "pmc551_erase() out of bounds (%ld > %ld)\n", (long)end, (long)mtd->size); | ||
133 | #endif | ||
134 | return -EINVAL; | ||
135 | } | ||
136 | |||
137 | eoff_hi = end & ~(priv->asize - 1); | ||
138 | soff_hi = instr->addr & ~(priv->asize - 1); | ||
139 | eoff_lo = end & (priv->asize - 1); | ||
140 | soff_lo = instr->addr & (priv->asize - 1); | ||
141 | |||
142 | pmc551_point (mtd, instr->addr, instr->len, &retlen, &ptr); | ||
143 | |||
144 | if ( soff_hi == eoff_hi || mtd->size == priv->asize) { | ||
145 | /* The whole thing fits within one access, so just one shot | ||
146 | will do it. */ | ||
147 | memset(ptr, 0xff, instr->len); | ||
148 | } else { | ||
149 | /* We have to do multiple writes to get all the data | ||
150 | written. */ | ||
151 | while (soff_hi != eoff_hi) { | ||
152 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
153 | printk( KERN_DEBUG "pmc551_erase() soff_hi: %ld, eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); | ||
154 | #endif | ||
155 | memset(ptr, 0xff, priv->asize); | ||
156 | if (soff_hi + priv->asize >= mtd->size) { | ||
157 | goto out; | ||
158 | } | ||
159 | soff_hi += priv->asize; | ||
160 | pmc551_point (mtd,(priv->base_map0|soff_hi), | ||
161 | priv->asize, &retlen, &ptr); | ||
162 | } | ||
163 | memset (ptr, 0xff, eoff_lo); | ||
164 | } | ||
165 | |||
166 | out: | ||
167 | instr->state = MTD_ERASE_DONE; | ||
168 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
169 | printk(KERN_DEBUG "pmc551_erase() done\n"); | ||
170 | #endif | ||
171 | |||
172 | mtd_erase_callback(instr); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | |||
177 | static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) | ||
178 | { | ||
179 | struct mypriv *priv = mtd->priv; | ||
180 | u32 soff_hi; | ||
181 | u32 soff_lo; | ||
182 | |||
183 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
184 | printk(KERN_DEBUG "pmc551_point(%ld, %ld)\n", (long)from, (long)len); | ||
185 | #endif | ||
186 | |||
187 | if (from + len > mtd->size) { | ||
188 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
189 | printk(KERN_DEBUG "pmc551_point() out of bounds (%ld > %ld)\n", (long)from+len, (long)mtd->size); | ||
190 | #endif | ||
191 | return -EINVAL; | ||
192 | } | ||
193 | |||
194 | soff_hi = from & ~(priv->asize - 1); | ||
195 | soff_lo = from & (priv->asize - 1); | ||
196 | |||
197 | /* Cheap hack optimization */ | ||
198 | if( priv->curr_map0 != from ) { | ||
199 | pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, | ||
200 | (priv->base_map0 | soff_hi) ); | ||
201 | priv->curr_map0 = soff_hi; | ||
202 | } | ||
203 | |||
204 | *mtdbuf = priv->start + soff_lo; | ||
205 | *retlen = len; | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | |||
210 | static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) | ||
211 | { | ||
212 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
213 | printk(KERN_DEBUG "pmc551_unpoint()\n"); | ||
214 | #endif | ||
215 | } | ||
216 | |||
217 | |||
218 | static int pmc551_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) | ||
219 | { | ||
220 | struct mypriv *priv = mtd->priv; | ||
221 | u32 soff_hi, soff_lo; /* start address offset hi/lo */ | ||
222 | u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ | ||
223 | unsigned long end; | ||
224 | u_char *ptr; | ||
225 | u_char *copyto = buf; | ||
226 | |||
227 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
228 | printk(KERN_DEBUG "pmc551_read(pos:%ld, len:%ld) asize: %ld\n", (long)from, (long)len, (long)priv->asize); | ||
229 | #endif | ||
230 | |||
231 | end = from + len - 1; | ||
232 | |||
233 | /* Is it past the end? */ | ||
234 | if (end > mtd->size) { | ||
235 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
236 | printk(KERN_DEBUG "pmc551_read() out of bounds (%ld > %ld)\n", (long) end, (long)mtd->size); | ||
237 | #endif | ||
238 | return -EINVAL; | ||
239 | } | ||
240 | |||
241 | soff_hi = from & ~(priv->asize - 1); | ||
242 | eoff_hi = end & ~(priv->asize - 1); | ||
243 | soff_lo = from & (priv->asize - 1); | ||
244 | eoff_lo = end & (priv->asize - 1); | ||
245 | |||
246 | pmc551_point (mtd, from, len, retlen, &ptr); | ||
247 | |||
248 | if (soff_hi == eoff_hi) { | ||
249 | /* The whole thing fits within one access, so just one shot | ||
250 | will do it. */ | ||
251 | memcpy(copyto, ptr, len); | ||
252 | copyto += len; | ||
253 | } else { | ||
254 | /* We have to do multiple writes to get all the data | ||
255 | written. */ | ||
256 | while (soff_hi != eoff_hi) { | ||
257 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
258 | printk( KERN_DEBUG "pmc551_read() soff_hi: %ld, eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); | ||
259 | #endif | ||
260 | memcpy(copyto, ptr, priv->asize); | ||
261 | copyto += priv->asize; | ||
262 | if (soff_hi + priv->asize >= mtd->size) { | ||
263 | goto out; | ||
264 | } | ||
265 | soff_hi += priv->asize; | ||
266 | pmc551_point (mtd, soff_hi, priv->asize, retlen, &ptr); | ||
267 | } | ||
268 | memcpy(copyto, ptr, eoff_lo); | ||
269 | copyto += eoff_lo; | ||
270 | } | ||
271 | |||
272 | out: | ||
273 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
274 | printk(KERN_DEBUG "pmc551_read() done\n"); | ||
275 | #endif | ||
276 | *retlen = copyto - buf; | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) | ||
281 | { | ||
282 | struct mypriv *priv = mtd->priv; | ||
283 | u32 soff_hi, soff_lo; /* start address offset hi/lo */ | ||
284 | u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ | ||
285 | unsigned long end; | ||
286 | u_char *ptr; | ||
287 | const u_char *copyfrom = buf; | ||
288 | |||
289 | |||
290 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
291 | printk(KERN_DEBUG "pmc551_write(pos:%ld, len:%ld) asize:%ld\n", (long)to, (long)len, (long)priv->asize); | ||
292 | #endif | ||
293 | |||
294 | end = to + len - 1; | ||
295 | /* Is it past the end? or did the u32 wrap? */ | ||
296 | if (end > mtd->size ) { | ||
297 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
298 | printk(KERN_DEBUG "pmc551_write() out of bounds (end: %ld, size: %ld, to: %ld)\n", (long) end, (long)mtd->size, (long)to); | ||
299 | #endif | ||
300 | return -EINVAL; | ||
301 | } | ||
302 | |||
303 | soff_hi = to & ~(priv->asize - 1); | ||
304 | eoff_hi = end & ~(priv->asize - 1); | ||
305 | soff_lo = to & (priv->asize - 1); | ||
306 | eoff_lo = end & (priv->asize - 1); | ||
307 | |||
308 | pmc551_point (mtd, to, len, retlen, &ptr); | ||
309 | |||
310 | if (soff_hi == eoff_hi) { | ||
311 | /* The whole thing fits within one access, so just one shot | ||
312 | will do it. */ | ||
313 | memcpy(ptr, copyfrom, len); | ||
314 | copyfrom += len; | ||
315 | } else { | ||
316 | /* We have to do multiple writes to get all the data | ||
317 | written. */ | ||
318 | while (soff_hi != eoff_hi) { | ||
319 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
320 | printk( KERN_DEBUG "pmc551_write() soff_hi: %ld, eoff_hi: %ld\n", (long)soff_hi, (long)eoff_hi); | ||
321 | #endif | ||
322 | memcpy(ptr, copyfrom, priv->asize); | ||
323 | copyfrom += priv->asize; | ||
324 | if (soff_hi >= mtd->size) { | ||
325 | goto out; | ||
326 | } | ||
327 | soff_hi += priv->asize; | ||
328 | pmc551_point (mtd, soff_hi, priv->asize, retlen, &ptr); | ||
329 | } | ||
330 | memcpy(ptr, copyfrom, eoff_lo); | ||
331 | copyfrom += eoff_lo; | ||
332 | } | ||
333 | |||
334 | out: | ||
335 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
336 | printk(KERN_DEBUG "pmc551_write() done\n"); | ||
337 | #endif | ||
338 | *retlen = copyfrom - buf; | ||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | /* | ||
343 | * Fixup routines for the V370PDC | ||
344 | * PCI device ID 0x020011b0 | ||
345 | * | ||
346 | * This function basicly kick starts the DRAM oboard the card and gets it | ||
347 | * ready to be used. Before this is done the device reads VERY erratic, so | ||
348 | * much that it can crash the Linux 2.2.x series kernels when a user cat's | ||
349 | * /proc/pci .. though that is mainly a kernel bug in handling the PCI DEVSEL | ||
350 | * register. FIXME: stop spinning on registers .. must implement a timeout | ||
351 | * mechanism | ||
352 | * returns the size of the memory region found. | ||
353 | */ | ||
354 | static u32 fixup_pmc551 (struct pci_dev *dev) | ||
355 | { | ||
356 | #ifdef CONFIG_MTD_PMC551_BUGFIX | ||
357 | u32 dram_data; | ||
358 | #endif | ||
359 | u32 size, dcmd, cfg, dtmp; | ||
360 | u16 cmd, tmp, i; | ||
361 | u8 bcmd, counter; | ||
362 | |||
363 | /* Sanity Check */ | ||
364 | if(!dev) { | ||
365 | return -ENODEV; | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * Attempt to reset the card | ||
370 | * FIXME: Stop Spinning registers | ||
371 | */ | ||
372 | counter=0; | ||
373 | /* unlock registers */ | ||
374 | pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5 ); | ||
375 | /* read in old data */ | ||
376 | pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd ); | ||
377 | /* bang the reset line up and down for a few */ | ||
378 | for(i=0;i<10;i++) { | ||
379 | counter=0; | ||
380 | bcmd &= ~0x80; | ||
381 | while(counter++ < 100) { | ||
382 | pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd); | ||
383 | } | ||
384 | counter=0; | ||
385 | bcmd |= 0x80; | ||
386 | while(counter++ < 100) { | ||
387 | pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd); | ||
388 | } | ||
389 | } | ||
390 | bcmd |= (0x40|0x20); | ||
391 | pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd); | ||
392 | |||
393 | /* | ||
394 | * Take care and turn off the memory on the device while we | ||
395 | * tweak the configurations | ||
396 | */ | ||
397 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | ||
398 | tmp = cmd & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY); | ||
399 | pci_write_config_word(dev, PCI_COMMAND, tmp); | ||
400 | |||
401 | /* | ||
402 | * Disable existing aperture before probing memory size | ||
403 | */ | ||
404 | pci_read_config_dword(dev, PMC551_PCI_MEM_MAP0, &dcmd); | ||
405 | dtmp=(dcmd|PMC551_PCI_MEM_MAP_ENABLE|PMC551_PCI_MEM_MAP_REG_EN); | ||
406 | pci_write_config_dword(dev, PMC551_PCI_MEM_MAP0, dtmp); | ||
407 | /* | ||
408 | * Grab old BAR0 config so that we can figure out memory size | ||
409 | * This is another bit of kludge going on. The reason for the | ||
410 | * redundancy is I am hoping to retain the original configuration | ||
411 | * previously assigned to the card by the BIOS or some previous | ||
412 | * fixup routine in the kernel. So we read the old config into cfg, | ||
413 | * then write all 1's to the memory space, read back the result into | ||
414 | * "size", and then write back all the old config. | ||
415 | */ | ||
416 | pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &cfg ); | ||
417 | #ifndef CONFIG_MTD_PMC551_BUGFIX | ||
418 | pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, ~0 ); | ||
419 | pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &size ); | ||
420 | size = (size&PCI_BASE_ADDRESS_MEM_MASK); | ||
421 | size &= ~(size-1); | ||
422 | pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg ); | ||
423 | #else | ||
424 | /* | ||
425 | * Get the size of the memory by reading all the DRAM size values | ||
426 | * and adding them up. | ||
427 | * | ||
428 | * KLUDGE ALERT: the boards we are using have invalid column and | ||
429 | * row mux values. We fix them here, but this will break other | ||
430 | * memory configurations. | ||
431 | */ | ||
432 | pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data); | ||
433 | size = PMC551_DRAM_BLK_GET_SIZE(dram_data); | ||
434 | dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); | ||
435 | dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); | ||
436 | pci_write_config_dword(dev, PMC551_DRAM_BLK0, dram_data); | ||
437 | |||
438 | pci_read_config_dword(dev, PMC551_DRAM_BLK1, &dram_data); | ||
439 | size += PMC551_DRAM_BLK_GET_SIZE(dram_data); | ||
440 | dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); | ||
441 | dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); | ||
442 | pci_write_config_dword(dev, PMC551_DRAM_BLK1, dram_data); | ||
443 | |||
444 | pci_read_config_dword(dev, PMC551_DRAM_BLK2, &dram_data); | ||
445 | size += PMC551_DRAM_BLK_GET_SIZE(dram_data); | ||
446 | dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); | ||
447 | dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); | ||
448 | pci_write_config_dword(dev, PMC551_DRAM_BLK2, dram_data); | ||
449 | |||
450 | pci_read_config_dword(dev, PMC551_DRAM_BLK3, &dram_data); | ||
451 | size += PMC551_DRAM_BLK_GET_SIZE(dram_data); | ||
452 | dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5); | ||
453 | dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9); | ||
454 | pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data); | ||
455 | |||
456 | /* | ||
457 | * Oops .. something went wrong | ||
458 | */ | ||
459 | if( (size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) { | ||
460 | return -ENODEV; | ||
461 | } | ||
462 | #endif /* CONFIG_MTD_PMC551_BUGFIX */ | ||
463 | |||
464 | if ((cfg&PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) { | ||
465 | return -ENODEV; | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | * Precharge Dram | ||
470 | */ | ||
471 | pci_write_config_word( dev, PMC551_SDRAM_MA, 0x0400 ); | ||
472 | pci_write_config_word( dev, PMC551_SDRAM_CMD, 0x00bf ); | ||
473 | |||
474 | /* | ||
475 | * Wait until command has gone through | ||
476 | * FIXME: register spinning issue | ||
477 | */ | ||
478 | do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd ); | ||
479 | if(counter++ > 100)break; | ||
480 | } while ( (PCI_COMMAND_IO) & cmd ); | ||
481 | |||
482 | /* | ||
483 | * Turn on auto refresh | ||
484 | * The loop is taken directly from Ramix's example code. I assume that | ||
485 | * this must be held high for some duration of time, but I can find no | ||
486 | * documentation refrencing the reasons why. | ||
487 | */ | ||
488 | for ( i = 1; i<=8 ; i++) { | ||
489 | pci_write_config_word (dev, PMC551_SDRAM_CMD, 0x0df); | ||
490 | |||
491 | /* | ||
492 | * Make certain command has gone through | ||
493 | * FIXME: register spinning issue | ||
494 | */ | ||
495 | counter=0; | ||
496 | do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd); | ||
497 | if(counter++ > 100)break; | ||
498 | } while ( (PCI_COMMAND_IO) & cmd ); | ||
499 | } | ||
500 | |||
501 | pci_write_config_word ( dev, PMC551_SDRAM_MA, 0x0020); | ||
502 | pci_write_config_word ( dev, PMC551_SDRAM_CMD, 0x0ff); | ||
503 | |||
504 | /* | ||
505 | * Wait until command completes | ||
506 | * FIXME: register spinning issue | ||
507 | */ | ||
508 | counter=0; | ||
509 | do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd); | ||
510 | if(counter++ > 100)break; | ||
511 | } while ( (PCI_COMMAND_IO) & cmd ); | ||
512 | |||
513 | pci_read_config_dword ( dev, PMC551_DRAM_CFG, &dcmd); | ||
514 | dcmd |= 0x02000000; | ||
515 | pci_write_config_dword ( dev, PMC551_DRAM_CFG, dcmd); | ||
516 | |||
517 | /* | ||
518 | * Check to make certain fast back-to-back, if not | ||
519 | * then set it so | ||
520 | */ | ||
521 | pci_read_config_word( dev, PCI_STATUS, &cmd); | ||
522 | if((cmd&PCI_COMMAND_FAST_BACK) == 0) { | ||
523 | cmd |= PCI_COMMAND_FAST_BACK; | ||
524 | pci_write_config_word( dev, PCI_STATUS, cmd); | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * Check to make certain the DEVSEL is set correctly, this device | ||
529 | * has a tendancy to assert DEVSEL and TRDY when a write is performed | ||
530 | * to the memory when memory is read-only | ||
531 | */ | ||
532 | if((cmd&PCI_STATUS_DEVSEL_MASK) != 0x0) { | ||
533 | cmd &= ~PCI_STATUS_DEVSEL_MASK; | ||
534 | pci_write_config_word( dev, PCI_STATUS, cmd ); | ||
535 | } | ||
536 | /* | ||
537 | * Set to be prefetchable and put everything back based on old cfg. | ||
538 | * it's possible that the reset of the V370PDC nuked the original | ||
539 | * setup | ||
540 | */ | ||
541 | /* | ||
542 | cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH; | ||
543 | pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg ); | ||
544 | */ | ||
545 | |||
546 | /* | ||
547 | * Turn PCI memory and I/O bus access back on | ||
548 | */ | ||
549 | pci_write_config_word( dev, PCI_COMMAND, | ||
550 | PCI_COMMAND_MEMORY | PCI_COMMAND_IO ); | ||
551 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
552 | /* | ||
553 | * Some screen fun | ||
554 | */ | ||
555 | printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at 0x%lx\n", | ||
556 | (size<1024)?size:(size<1048576)?size>>10:size>>20, | ||
557 | (size<1024)?'B':(size<1048576)?'K':'M', | ||
558 | size, ((dcmd&(0x1<<3)) == 0)?"non-":"", | ||
559 | (dev->resource[0].start)&PCI_BASE_ADDRESS_MEM_MASK ); | ||
560 | |||
561 | /* | ||
562 | * Check to see the state of the memory | ||
563 | */ | ||
564 | pci_read_config_dword( dev, PMC551_DRAM_BLK0, &dcmd ); | ||
565 | printk(KERN_DEBUG "pmc551: DRAM_BLK0 Flags: %s,%s\n" | ||
566 | "pmc551: DRAM_BLK0 Size: %d at %d\n" | ||
567 | "pmc551: DRAM_BLK0 Row MUX: %d, Col MUX: %d\n", | ||
568 | (((0x1<<1)&dcmd) == 0)?"RW":"RO", | ||
569 | (((0x1<<0)&dcmd) == 0)?"Off":"On", | ||
570 | PMC551_DRAM_BLK_GET_SIZE(dcmd), | ||
571 | ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); | ||
572 | |||
573 | pci_read_config_dword( dev, PMC551_DRAM_BLK1, &dcmd ); | ||
574 | printk(KERN_DEBUG "pmc551: DRAM_BLK1 Flags: %s,%s\n" | ||
575 | "pmc551: DRAM_BLK1 Size: %d at %d\n" | ||
576 | "pmc551: DRAM_BLK1 Row MUX: %d, Col MUX: %d\n", | ||
577 | (((0x1<<1)&dcmd) == 0)?"RW":"RO", | ||
578 | (((0x1<<0)&dcmd) == 0)?"Off":"On", | ||
579 | PMC551_DRAM_BLK_GET_SIZE(dcmd), | ||
580 | ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); | ||
581 | |||
582 | pci_read_config_dword( dev, PMC551_DRAM_BLK2, &dcmd ); | ||
583 | printk(KERN_DEBUG "pmc551: DRAM_BLK2 Flags: %s,%s\n" | ||
584 | "pmc551: DRAM_BLK2 Size: %d at %d\n" | ||
585 | "pmc551: DRAM_BLK2 Row MUX: %d, Col MUX: %d\n", | ||
586 | (((0x1<<1)&dcmd) == 0)?"RW":"RO", | ||
587 | (((0x1<<0)&dcmd) == 0)?"Off":"On", | ||
588 | PMC551_DRAM_BLK_GET_SIZE(dcmd), | ||
589 | ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); | ||
590 | |||
591 | pci_read_config_dword( dev, PMC551_DRAM_BLK3, &dcmd ); | ||
592 | printk(KERN_DEBUG "pmc551: DRAM_BLK3 Flags: %s,%s\n" | ||
593 | "pmc551: DRAM_BLK3 Size: %d at %d\n" | ||
594 | "pmc551: DRAM_BLK3 Row MUX: %d, Col MUX: %d\n", | ||
595 | (((0x1<<1)&dcmd) == 0)?"RW":"RO", | ||
596 | (((0x1<<0)&dcmd) == 0)?"Off":"On", | ||
597 | PMC551_DRAM_BLK_GET_SIZE(dcmd), | ||
598 | ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) ); | ||
599 | |||
600 | pci_read_config_word( dev, PCI_COMMAND, &cmd ); | ||
601 | printk( KERN_DEBUG "pmc551: Memory Access %s\n", | ||
602 | (((0x1<<1)&cmd) == 0)?"off":"on" ); | ||
603 | printk( KERN_DEBUG "pmc551: I/O Access %s\n", | ||
604 | (((0x1<<0)&cmd) == 0)?"off":"on" ); | ||
605 | |||
606 | pci_read_config_word( dev, PCI_STATUS, &cmd ); | ||
607 | printk( KERN_DEBUG "pmc551: Devsel %s\n", | ||
608 | ((PCI_STATUS_DEVSEL_MASK&cmd)==0x000)?"Fast": | ||
609 | ((PCI_STATUS_DEVSEL_MASK&cmd)==0x200)?"Medium": | ||
610 | ((PCI_STATUS_DEVSEL_MASK&cmd)==0x400)?"Slow":"Invalid" ); | ||
611 | |||
612 | printk( KERN_DEBUG "pmc551: %sFast Back-to-Back\n", | ||
613 | ((PCI_COMMAND_FAST_BACK&cmd) == 0)?"Not ":"" ); | ||
614 | |||
615 | pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd ); | ||
616 | printk( KERN_DEBUG "pmc551: EEPROM is under %s control\n" | ||
617 | "pmc551: System Control Register is %slocked to PCI access\n" | ||
618 | "pmc551: System Control Register is %slocked to EEPROM access\n", | ||
619 | (bcmd&0x1)?"software":"hardware", | ||
620 | (bcmd&0x20)?"":"un", (bcmd&0x40)?"":"un"); | ||
621 | #endif | ||
622 | return size; | ||
623 | } | ||
624 | |||
625 | /* | ||
626 | * Kernel version specific module stuffages | ||
627 | */ | ||
628 | |||
629 | |||
630 | MODULE_LICENSE("GPL"); | ||
631 | MODULE_AUTHOR("Mark Ferrell <mferrell@mvista.com>"); | ||
632 | MODULE_DESCRIPTION(PMC551_VERSION); | ||
633 | |||
634 | /* | ||
635 | * Stuff these outside the ifdef so as to not bust compiled in driver support | ||
636 | */ | ||
637 | static int msize=0; | ||
638 | #if defined(CONFIG_MTD_PMC551_APERTURE_SIZE) | ||
639 | static int asize=CONFIG_MTD_PMC551_APERTURE_SIZE | ||
640 | #else | ||
641 | static int asize=0; | ||
642 | #endif | ||
643 | |||
644 | module_param(msize, int, 0); | ||
645 | MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]"); | ||
646 | module_param(asize, int, 0); | ||
647 | MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); | ||
648 | |||
649 | /* | ||
650 | * PMC551 Card Initialization | ||
651 | */ | ||
652 | static int __init init_pmc551(void) | ||
653 | { | ||
654 | struct pci_dev *PCI_Device = NULL; | ||
655 | struct mypriv *priv; | ||
656 | int count, found=0; | ||
657 | struct mtd_info *mtd; | ||
658 | u32 length = 0; | ||
659 | |||
660 | if(msize) { | ||
661 | msize = (1 << (ffs(msize) - 1))<<20; | ||
662 | if (msize > (1<<30)) { | ||
663 | printk(KERN_NOTICE "pmc551: Invalid memory size [%d]\n", msize); | ||
664 | return -EINVAL; | ||
665 | } | ||
666 | } | ||
667 | |||
668 | if(asize) { | ||
669 | asize = (1 << (ffs(asize) - 1))<<20; | ||
670 | if (asize > (1<<30) ) { | ||
671 | printk(KERN_NOTICE "pmc551: Invalid aperture size [%d]\n", asize); | ||
672 | return -EINVAL; | ||
673 | } | ||
674 | } | ||
675 | |||
676 | printk(KERN_INFO PMC551_VERSION); | ||
677 | |||
678 | /* | ||
679 | * PCU-bus chipset probe. | ||
680 | */ | ||
681 | for( count = 0; count < MAX_MTD_DEVICES; count++ ) { | ||
682 | |||
683 | if ((PCI_Device = pci_find_device(PCI_VENDOR_ID_V3_SEMI, | ||
684 | PCI_DEVICE_ID_V3_SEMI_V370PDC, | ||
685 | PCI_Device ) ) == NULL) { | ||
686 | break; | ||
687 | } | ||
688 | |||
689 | printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%lX\n", | ||
690 | PCI_Device->resource[0].start); | ||
691 | |||
692 | /* | ||
693 | * The PMC551 device acts VERY weird if you don't init it | ||
694 | * first. i.e. it will not correctly report devsel. If for | ||
695 | * some reason the sdram is in a wrote-protected state the | ||
696 | * device will DEVSEL when it is written to causing problems | ||
697 | * with the oldproc.c driver in | ||
698 | * some kernels (2.2.*) | ||
699 | */ | ||
700 | if((length = fixup_pmc551(PCI_Device)) <= 0) { | ||
701 | printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n"); | ||
702 | break; | ||
703 | } | ||
704 | |||
705 | /* | ||
706 | * This is needed until the driver is capable of reading the | ||
707 | * onboard I2C SROM to discover the "real" memory size. | ||
708 | */ | ||
709 | if(msize) { | ||
710 | length = msize; | ||
711 | printk(KERN_NOTICE "pmc551: Using specified memory size 0x%x\n", length); | ||
712 | } else { | ||
713 | msize = length; | ||
714 | } | ||
715 | |||
716 | mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); | ||
717 | if (!mtd) { | ||
718 | printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n"); | ||
719 | break; | ||
720 | } | ||
721 | |||
722 | memset(mtd, 0, sizeof(struct mtd_info)); | ||
723 | |||
724 | priv = kmalloc (sizeof(struct mypriv), GFP_KERNEL); | ||
725 | if (!priv) { | ||
726 | printk(KERN_NOTICE "pmc551: Cannot allocate new MTD device.\n"); | ||
727 | kfree(mtd); | ||
728 | break; | ||
729 | } | ||
730 | memset(priv, 0, sizeof(*priv)); | ||
731 | mtd->priv = priv; | ||
732 | priv->dev = PCI_Device; | ||
733 | |||
734 | if(asize > length) { | ||
735 | printk(KERN_NOTICE "pmc551: reducing aperture size to fit %dM\n",length>>20); | ||
736 | priv->asize = asize = length; | ||
737 | } else if (asize == 0 || asize == length) { | ||
738 | printk(KERN_NOTICE "pmc551: Using existing aperture size %dM\n", length>>20); | ||
739 | priv->asize = asize = length; | ||
740 | } else { | ||
741 | printk(KERN_NOTICE "pmc551: Using specified aperture size %dM\n", asize>>20); | ||
742 | priv->asize = asize; | ||
743 | } | ||
744 | priv->start = ioremap(((PCI_Device->resource[0].start) | ||
745 | & PCI_BASE_ADDRESS_MEM_MASK), | ||
746 | priv->asize); | ||
747 | |||
748 | if (!priv->start) { | ||
749 | printk(KERN_NOTICE "pmc551: Unable to map IO space\n"); | ||
750 | kfree(mtd->priv); | ||
751 | kfree(mtd); | ||
752 | break; | ||
753 | } | ||
754 | |||
755 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
756 | printk( KERN_DEBUG "pmc551: setting aperture to %d\n", | ||
757 | ffs(priv->asize>>20)-1); | ||
758 | #endif | ||
759 | |||
760 | priv->base_map0 = ( PMC551_PCI_MEM_MAP_REG_EN | ||
761 | | PMC551_PCI_MEM_MAP_ENABLE | ||
762 | | (ffs(priv->asize>>20)-1)<<4 ); | ||
763 | priv->curr_map0 = priv->base_map0; | ||
764 | pci_write_config_dword ( priv->dev, PMC551_PCI_MEM_MAP0, | ||
765 | priv->curr_map0 ); | ||
766 | |||
767 | #ifdef CONFIG_MTD_PMC551_DEBUG | ||
768 | printk( KERN_DEBUG "pmc551: aperture set to %d\n", | ||
769 | (priv->base_map0 & 0xF0)>>4 ); | ||
770 | #endif | ||
771 | |||
772 | mtd->size = msize; | ||
773 | mtd->flags = MTD_CAP_RAM; | ||
774 | mtd->erase = pmc551_erase; | ||
775 | mtd->read = pmc551_read; | ||
776 | mtd->write = pmc551_write; | ||
777 | mtd->point = pmc551_point; | ||
778 | mtd->unpoint = pmc551_unpoint; | ||
779 | mtd->type = MTD_RAM; | ||
780 | mtd->name = "PMC551 RAM board"; | ||
781 | mtd->erasesize = 0x10000; | ||
782 | mtd->owner = THIS_MODULE; | ||
783 | |||
784 | if (add_mtd_device(mtd)) { | ||
785 | printk(KERN_NOTICE "pmc551: Failed to register new device\n"); | ||
786 | iounmap(priv->start); | ||
787 | kfree(mtd->priv); | ||
788 | kfree(mtd); | ||
789 | break; | ||
790 | } | ||
791 | printk(KERN_NOTICE "Registered pmc551 memory device.\n"); | ||
792 | printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n", | ||
793 | priv->asize>>20, | ||
794 | priv->start, | ||
795 | priv->start + priv->asize); | ||
796 | printk(KERN_NOTICE "Total memory is %d%c\n", | ||
797 | (length<1024)?length: | ||
798 | (length<1048576)?length>>10:length>>20, | ||
799 | (length<1024)?'B':(length<1048576)?'K':'M'); | ||
800 | priv->nextpmc551 = pmc551list; | ||
801 | pmc551list = mtd; | ||
802 | found++; | ||
803 | } | ||
804 | |||
805 | if( !pmc551list ) { | ||
806 | printk(KERN_NOTICE "pmc551: not detected\n"); | ||
807 | return -ENODEV; | ||
808 | } else { | ||
809 | printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found); | ||
810 | return 0; | ||
811 | } | ||
812 | } | ||
813 | |||
814 | /* | ||
815 | * PMC551 Card Cleanup | ||
816 | */ | ||
817 | static void __exit cleanup_pmc551(void) | ||
818 | { | ||
819 | int found=0; | ||
820 | struct mtd_info *mtd; | ||
821 | struct mypriv *priv; | ||
822 | |||
823 | while((mtd=pmc551list)) { | ||
824 | priv = mtd->priv; | ||
825 | pmc551list = priv->nextpmc551; | ||
826 | |||
827 | if(priv->start) { | ||
828 | printk (KERN_DEBUG "pmc551: unmapping %dM starting at 0x%p\n", | ||
829 | priv->asize>>20, priv->start); | ||
830 | iounmap (priv->start); | ||
831 | } | ||
832 | |||
833 | kfree (mtd->priv); | ||
834 | del_mtd_device (mtd); | ||
835 | kfree (mtd); | ||
836 | found++; | ||
837 | } | ||
838 | |||
839 | printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found); | ||
840 | } | ||
841 | |||
842 | module_init(init_pmc551); | ||
843 | module_exit(cleanup_pmc551); | ||