Rev 587 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
587 | giacomo | 1 | /* |
2 | * BIOS32 and PCI BIOS handling. |
||
3 | */ |
||
4 | |||
5 | #include <linuxcomp.h> |
||
6 | |||
7 | #include <linux/pci.h> |
||
8 | #include <linux/init.h> |
||
9 | #include "pci2.h" |
||
10 | #include "pci-functions.h" |
||
11 | |||
12 | |||
13 | /* BIOS32 signature: "_32_" */ |
||
14 | #define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) |
||
15 | |||
16 | /* PCI signature: "PCI " */ |
||
17 | #define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) |
||
18 | |||
19 | /* PCI service signature: "$PCI" */ |
||
20 | #define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) |
||
21 | |||
22 | /* PCI BIOS hardware mechanism flags */ |
||
23 | #define PCIBIOS_HW_TYPE1 0x01 |
||
24 | #define PCIBIOS_HW_TYPE2 0x02 |
||
25 | #define PCIBIOS_HW_TYPE1_SPEC 0x10 |
||
26 | #define PCIBIOS_HW_TYPE2_SPEC 0x20 |
||
27 | |||
28 | /* |
||
29 | * This is the standard structure used to identify the entry point |
||
30 | * to the BIOS32 Service Directory, as documented in |
||
31 | * Standard BIOS 32-bit Service Directory Proposal |
||
32 | * Revision 0.4 May 24, 1993 |
||
33 | * Phoenix Technologies Ltd. |
||
34 | * Norwood, MA |
||
35 | * and the PCI BIOS specification. |
||
36 | */ |
||
37 | |||
38 | union bios32 { |
||
39 | struct { |
||
40 | unsigned long signature; /* _32_ */ |
||
41 | unsigned long entry; /* 32 bit physical address */ |
||
42 | unsigned char revision; /* Revision level, 0 */ |
||
43 | unsigned char length; /* Length in paragraphs should be 01 */ |
||
44 | unsigned char checksum; /* All bytes must add up to zero */ |
||
45 | unsigned char reserved[5]; /* Must be zero */ |
||
46 | } fields; |
||
47 | char chars[16]; |
||
48 | }; |
||
49 | |||
50 | /* |
||
51 | * Physical address of the service directory. I don't know if we're |
||
52 | * allowed to have more than one of these or not, so just in case |
||
53 | * we'll make pcibios_present() take a memory start parameter and store |
||
54 | * the array there. |
||
55 | */ |
||
56 | |||
57 | static struct { |
||
58 | unsigned long address; |
||
59 | unsigned short segment; |
||
60 | } bios32_indirect = { 0, __KERNEL_CS }; |
||
61 | |||
62 | /* |
||
63 | * Returns the entry point for the given service, NULL on error |
||
64 | */ |
||
65 | |||
66 | static unsigned long bios32_service(unsigned long service) |
||
67 | { |
||
68 | unsigned char return_code; /* %al */ |
||
69 | unsigned long address; /* %ebx */ |
||
70 | unsigned long length; /* %ecx */ |
||
71 | unsigned long entry; /* %edx */ |
||
72 | unsigned long flags; |
||
73 | |||
74 | local_irq_save(flags); |
||
75 | __asm__("lcall *(%%edi); cld" |
||
76 | : "=a" (return_code), |
||
77 | "=b" (address), |
||
78 | "=c" (length), |
||
79 | "=d" (entry) |
||
80 | : "0" (service), |
||
81 | "1" (PCIBIOS_PCI_BIOS_PRESENT), |
||
82 | "D" (&bios32_indirect)); |
||
83 | local_irq_restore(flags); |
||
84 | |||
85 | printk("Breakpoint\n"); |
||
86 | |||
87 | switch (return_code) { |
||
88 | case 0: |
||
89 | return address + entry; |
||
90 | case 0x80: /* Not present */ |
||
91 | printk(KERN_WARNING "bios32_service(0x%lx): not present\n", service); |
||
92 | return 0; |
||
93 | default: /* Shouldn't happen */ |
||
94 | printk(KERN_WARNING "bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n", |
||
95 | service, return_code); |
||
96 | return 0; |
||
97 | } |
||
98 | } |
||
99 | |||
100 | static struct { |
||
101 | unsigned long address; |
||
102 | unsigned short segment; |
||
103 | } pci_indirect = { 0, 0 /*__KERNEL_CS*/ }; |
||
104 | |||
105 | static int pci_bios_present; |
||
106 | |||
107 | static int __devinit check_pcibios(void) |
||
108 | { |
||
109 | u32 signature, eax, ebx, ecx; |
||
110 | u8 status, major_ver, minor_ver, hw_mech; |
||
111 | unsigned long flags, pcibios_entry; |
||
112 | |||
113 | if ((pcibios_entry = bios32_service(PCI_SERVICE))) { |
||
114 | pci_indirect.address = pcibios_entry + PAGE_OFFSET; |
||
115 | |||
116 | local_irq_save(flags); |
||
117 | __asm__( |
||
118 | "lcall *(%%edi); cld\n\t" |
||
119 | "jc 1f\n\t" |
||
120 | "xor %%ah, %%ah\n" |
||
121 | "1:" |
||
122 | : "=d" (signature), |
||
123 | "=a" (eax), |
||
124 | "=b" (ebx), |
||
125 | "=c" (ecx) |
||
126 | : "1" (PCIBIOS_PCI_BIOS_PRESENT), |
||
127 | "D" (&pci_indirect) |
||
128 | : "memory"); |
||
129 | local_irq_restore(flags); |
||
130 | |||
131 | status = (eax >> 8) & 0xff; |
||
132 | hw_mech = eax & 0xff; |
||
133 | major_ver = (ebx >> 8) & 0xff; |
||
134 | minor_ver = ebx & 0xff; |
||
135 | if (pcibios_last_bus < 0) |
||
136 | pcibios_last_bus = ecx & 0xff; |
||
137 | DBG("PCI: BIOS probe returned s=%02x hw=%02x ver=%02x.%02x l=%02x\n", |
||
138 | status, hw_mech, major_ver, minor_ver, pcibios_last_bus); |
||
139 | if (status || signature != PCI_SIGNATURE) { |
||
140 | printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found\n", |
||
141 | status, signature); |
||
142 | return 0; |
||
143 | } |
||
144 | printk(KERN_INFO "PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n", |
||
145 | major_ver, minor_ver, pcibios_entry, pcibios_last_bus); |
||
146 | #ifdef CONFIG_PCI_DIRECT |
||
147 | if (!(hw_mech & PCIBIOS_HW_TYPE1)) |
||
148 | pci_probe &= ~PCI_PROBE_CONF1; |
||
149 | if (!(hw_mech & PCIBIOS_HW_TYPE2)) |
||
150 | pci_probe &= ~PCI_PROBE_CONF2; |
||
151 | #endif |
||
152 | return 1; |
||
153 | } |
||
154 | return 0; |
||
155 | } |
||
156 | |||
157 | static int __devinit pci_bios_find_device (unsigned short vendor, unsigned short device_id, |
||
158 | unsigned short index, unsigned char *bus, unsigned char *device_fn) |
||
159 | { |
||
160 | unsigned short bx; |
||
161 | unsigned short ret; |
||
162 | |||
163 | __asm__("lcall *(%%edi); cld\n\t" |
||
164 | "jc 1f\n\t" |
||
165 | "xor %%ah, %%ah\n" |
||
166 | "1:" |
||
167 | : "=b" (bx), |
||
168 | "=a" (ret) |
||
169 | : "1" (PCIBIOS_FIND_PCI_DEVICE), |
||
170 | "c" (device_id), |
||
171 | "d" (vendor), |
||
172 | "S" ((int) index), |
||
173 | "D" (&pci_indirect)); |
||
174 | *bus = (bx >> 8) & 0xff; |
||
175 | *device_fn = bx & 0xff; |
||
176 | return (int) (ret & 0xff00) >> 8; |
||
177 | } |
||
178 | |||
179 | static int pci_bios_read (int seg, int bus, int devfn, int reg, int len, u32 *value) |
||
180 | { |
||
181 | unsigned long result = 0; |
||
182 | unsigned long flags; |
||
183 | unsigned long bx = (bus << 8) | devfn; |
||
184 | |||
185 | if (!value || (bus > 255) || (devfn > 255) || (reg > 255)) |
||
186 | return -EINVAL; |
||
187 | |||
188 | spin_lock_irqsave(&pci_config_lock, flags); |
||
189 | |||
190 | switch (len) { |
||
191 | case 1: |
||
192 | __asm__("lcall *(%%esi); cld\n\t" |
||
193 | "jc 1f\n\t" |
||
194 | "xor %%ah, %%ah\n" |
||
195 | "1:" |
||
196 | : "=c" (*value), |
||
197 | "=a" (result) |
||
198 | : "1" (PCIBIOS_READ_CONFIG_BYTE), |
||
199 | "b" (bx), |
||
200 | "D" ((long)reg), |
||
201 | "S" (&pci_indirect)); |
||
202 | break; |
||
203 | case 2: |
||
204 | __asm__("lcall *(%%esi); cld\n\t" |
||
205 | "jc 1f\n\t" |
||
206 | "xor %%ah, %%ah\n" |
||
207 | "1:" |
||
208 | : "=c" (*value), |
||
209 | "=a" (result) |
||
210 | : "1" (PCIBIOS_READ_CONFIG_WORD), |
||
211 | "b" (bx), |
||
212 | "D" ((long)reg), |
||
213 | "S" (&pci_indirect)); |
||
214 | break; |
||
215 | case 4: |
||
216 | __asm__("lcall *(%%esi); cld\n\t" |
||
217 | "jc 1f\n\t" |
||
218 | "xor %%ah, %%ah\n" |
||
219 | "1:" |
||
220 | : "=c" (*value), |
||
221 | "=a" (result) |
||
222 | : "1" (PCIBIOS_READ_CONFIG_DWORD), |
||
223 | "b" (bx), |
||
224 | "D" ((long)reg), |
||
225 | "S" (&pci_indirect)); |
||
226 | break; |
||
227 | } |
||
228 | |||
229 | spin_unlock_irqrestore(&pci_config_lock, flags); |
||
230 | |||
231 | return (int)((result & 0xff00) >> 8); |
||
232 | } |
||
233 | |||
234 | static int pci_bios_write (int seg, int bus, int devfn, int reg, int len, u32 value) |
||
235 | { |
||
236 | unsigned long result = 0; |
||
237 | unsigned long flags; |
||
238 | unsigned long bx = (bus << 8) | devfn; |
||
239 | |||
240 | if ((bus > 255) || (devfn > 255) || (reg > 255)) |
||
241 | return -EINVAL; |
||
242 | |||
243 | spin_lock_irqsave(&pci_config_lock, flags); |
||
244 | |||
245 | switch (len) { |
||
246 | case 1: |
||
247 | __asm__("lcall *(%%esi); cld\n\t" |
||
248 | "jc 1f\n\t" |
||
249 | "xor %%ah, %%ah\n" |
||
250 | "1:" |
||
251 | : "=a" (result) |
||
252 | : "0" (PCIBIOS_WRITE_CONFIG_BYTE), |
||
253 | "c" (value), |
||
254 | "b" (bx), |
||
255 | "D" ((long)reg), |
||
256 | "S" (&pci_indirect)); |
||
257 | break; |
||
258 | case 2: |
||
259 | __asm__("lcall *(%%esi); cld\n\t" |
||
260 | "jc 1f\n\t" |
||
261 | "xor %%ah, %%ah\n" |
||
262 | "1:" |
||
263 | : "=a" (result) |
||
264 | : "0" (PCIBIOS_WRITE_CONFIG_WORD), |
||
265 | "c" (value), |
||
266 | "b" (bx), |
||
267 | "D" ((long)reg), |
||
268 | "S" (&pci_indirect)); |
||
269 | break; |
||
270 | case 4: |
||
271 | __asm__("lcall *(%%esi); cld\n\t" |
||
272 | "jc 1f\n\t" |
||
273 | "xor %%ah, %%ah\n" |
||
274 | "1:" |
||
275 | : "=a" (result) |
||
276 | : "0" (PCIBIOS_WRITE_CONFIG_DWORD), |
||
277 | "c" (value), |
||
278 | "b" (bx), |
||
279 | "D" ((long)reg), |
||
280 | "S" (&pci_indirect)); |
||
281 | break; |
||
282 | } |
||
283 | |||
284 | spin_unlock_irqrestore(&pci_config_lock, flags); |
||
285 | |||
286 | return (int)((result & 0xff00) >> 8); |
||
287 | } |
||
288 | |||
289 | |||
290 | /* |
||
291 | * Function table for BIOS32 access |
||
292 | */ |
||
293 | |||
294 | static struct pci_raw_ops pci_bios_access = { |
||
295 | .read = pci_bios_read, |
||
296 | .write = pci_bios_write |
||
297 | }; |
||
298 | |||
299 | /* |
||
300 | * Try to find PCI BIOS. |
||
301 | */ |
||
302 | |||
303 | static struct pci_raw_ops * __devinit pci_find_bios(void) |
||
304 | { |
||
305 | union bios32 *check; |
||
306 | unsigned char sum; |
||
307 | int i, length; |
||
308 | |||
309 | /* |
||
310 | * Follow the standard procedure for locating the BIOS32 Service |
||
311 | * directory by scanning the permissible address range from |
||
312 | * 0xe0000 through 0xfffff for a valid BIOS32 structure. |
||
313 | */ |
||
314 | |||
315 | for (check = (union bios32 *) __va(0xe0000); |
||
316 | check <= (union bios32 *) __va(0xffff0); |
||
317 | ++check) { |
||
318 | if (check->fields.signature != BIOS32_SIGNATURE) |
||
319 | continue; |
||
320 | length = check->fields.length * 16; |
||
321 | if (!length) |
||
322 | continue; |
||
323 | sum = 0; |
||
324 | for (i = 0; i < length ; ++i) |
||
325 | sum += check->chars[i]; |
||
326 | if (sum != 0) |
||
327 | continue; |
||
328 | if (check->fields.revision != 0) { |
||
329 | printk("PCI: unsupported BIOS32 revision %d at 0x%p\n", |
||
330 | check->fields.revision, check); |
||
331 | continue; |
||
332 | } |
||
333 | DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check); |
||
334 | if (check->fields.entry >= 0x100000) { |
||
335 | printk("PCI: BIOS32 entry (0x%p) in high memory, cannot use.\n", check); |
||
336 | return NULL; |
||
337 | } else { |
||
338 | unsigned long bios32_entry = check->fields.entry; |
||
339 | DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", bios32_entry); |
||
340 | bios32_indirect.address = bios32_entry + PAGE_OFFSET; |
||
341 | if (check_pcibios()) |
||
342 | return &pci_bios_access; |
||
343 | } |
||
344 | break; /* Hopefully more than one BIOS32 cannot happen... */ |
||
345 | } |
||
346 | |||
347 | return NULL; |
||
348 | } |
||
349 | |||
350 | /* |
||
351 | * Sort the device list according to PCI BIOS. Nasty hack, but since some |
||
352 | * fool forgot to define the `correct' device order in the PCI BIOS specs |
||
353 | * and we want to be (possibly bug-to-bug ;-]) compatible with older kernels |
||
354 | * which used BIOS ordering, we are bound to do this... |
||
355 | */ |
||
356 | |||
357 | void __devinit pcibios_sort(void) |
||
358 | { |
||
359 | LIST_HEAD(sorted_devices); |
||
360 | struct list_head *ln; |
||
361 | struct pci_dev *dev, *d; |
||
362 | int idx, found; |
||
363 | unsigned char bus, devfn; |
||
364 | |||
365 | DBG("PCI: Sorting device list...\n"); |
||
366 | while (!list_empty(&pci_devices)) { |
||
367 | ln = pci_devices.next; |
||
368 | dev = pci_dev_g(ln); |
||
369 | idx = found = 0; |
||
370 | while (pci_bios_find_device(dev->vendor, dev->device, idx, &bus, &devfn) == PCIBIOS_SUCCESSFUL) { |
||
371 | idx++; |
||
372 | for (ln=pci_devices.next; ln != &pci_devices; ln=ln->next) { |
||
373 | d = pci_dev_g(ln); |
||
374 | if (d->bus->number == bus && d->devfn == devfn) { |
||
375 | list_del(&d->global_list); |
||
376 | list_add_tail(&d->global_list, &sorted_devices); |
||
377 | if (d == dev) |
||
378 | found = 1; |
||
379 | break; |
||
380 | } |
||
381 | } |
||
382 | if (ln == &pci_devices) { |
||
383 | printk(KERN_WARNING "PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn); |
||
384 | /* |
||
385 | * We must not continue scanning as several buggy BIOSes |
||
386 | * return garbage after the last device. Grr. |
||
387 | */ |
||
388 | break; |
||
389 | } |
||
390 | } |
||
391 | if (!found) { |
||
392 | printk(KERN_WARNING "PCI: Device %02x:%02x not found by BIOS\n", |
||
393 | dev->bus->number, dev->devfn); |
||
394 | list_del(&dev->global_list); |
||
395 | list_add_tail(&dev->global_list, &sorted_devices); |
||
396 | } |
||
397 | } |
||
398 | list_splice(&sorted_devices, &pci_devices); |
||
399 | } |
||
400 | |||
401 | /* |
||
402 | * BIOS Functions for IRQ Routing |
||
403 | */ |
||
404 | |||
405 | struct irq_routing_options { |
||
406 | u16 size; |
||
407 | struct irq_info *table; |
||
408 | u16 segment; |
||
409 | } __attribute__((packed)); |
||
410 | |||
411 | struct irq_routing_table * __devinit pcibios_get_irq_routing_table(void) |
||
412 | { |
||
413 | struct irq_routing_options opt; |
||
414 | struct irq_routing_table *rt = NULL; |
||
415 | int ret, map; |
||
416 | unsigned long page; |
||
417 | |||
418 | if (!pci_bios_present) |
||
419 | return NULL; |
||
420 | page = (long)kmalloc(PAGE_SIZE,GFP_KERNEL); |
||
421 | if (!page) |
||
422 | return NULL; |
||
423 | opt.table = (struct irq_info *) page; |
||
424 | opt.size = PAGE_SIZE; |
||
425 | opt.segment = __KERNEL_DS; |
||
426 | |||
427 | DBG("PCI: Fetching IRQ routing table... "); |
||
428 | __asm__("push %%es\n\t" |
||
429 | "push %%ds\n\t" |
||
430 | "pop %%es\n\t" |
||
431 | "lcall *(%%esi); cld\n\t" |
||
432 | "pop %%es\n\t" |
||
433 | "jc 1f\n\t" |
||
434 | "xor %%ah, %%ah\n" |
||
435 | "1:" |
||
436 | : "=a" (ret), |
||
437 | "=b" (map), |
||
438 | "+m" (opt) |
||
439 | : "0" (PCIBIOS_GET_ROUTING_OPTIONS), |
||
440 | "1" (0), |
||
441 | "D" ((long) &opt), |
||
442 | "S" (&pci_indirect)); |
||
443 | DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map); |
||
444 | if (ret & 0xff00) |
||
445 | printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff); |
||
446 | else if (opt.size) { |
||
447 | rt = kmalloc(sizeof(struct irq_routing_table) + opt.size, GFP_KERNEL); |
||
448 | if (rt) { |
||
449 | memset(rt, 0, sizeof(struct irq_routing_table)); |
||
450 | rt->size = opt.size + sizeof(struct irq_routing_table); |
||
451 | rt->exclusive_irqs = map; |
||
452 | memcpy(rt->slots, (void *) page, opt.size); |
||
453 | printk(KERN_INFO "PCI: Using BIOS Interrupt Routing Table\n"); |
||
454 | } |
||
455 | } |
||
456 | kfree((void *)page); |
||
457 | return rt; |
||
458 | } |
||
459 | |||
460 | |||
461 | int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq) |
||
462 | { |
||
463 | int ret; |
||
464 | |||
465 | __asm__("lcall *(%%esi); cld\n\t" |
||
466 | "jc 1f\n\t" |
||
467 | "xor %%ah, %%ah\n" |
||
468 | "1:" |
||
469 | : "=a" (ret) |
||
470 | : "0" (PCIBIOS_SET_PCI_HW_INT), |
||
471 | "b" ((dev->bus->number << 8) | dev->devfn), |
||
472 | "c" ((irq << 8) | (pin + 10)), |
||
473 | "S" (&pci_indirect)); |
||
474 | return !(ret & 0xff00); |
||
475 | } |
||
476 | |||
477 | int __init pci_pcbios_init(void) |
||
478 | { |
||
479 | if ((pci_probe & PCI_PROBE_BIOS) |
||
480 | && ((raw_pci_ops = pci_find_bios()))) { |
||
481 | pci_probe |= PCI_BIOS_SORT; |
||
482 | pci_bios_present = 1; |
||
483 | } |
||
484 | return 0; |
||
485 | } |
||
486 | |||
487 | arch_initcall(pci_pcbios_init); |