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