blob: c1c255f2f60f46b2fedf215e72c8a701f37b3e44 [file] [log] [blame]
Corey Tabakab0bd1832009-04-03 01:17:52 -04001/*
2 * Copyright (c) 2009 Corey Tabaka
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
24#include <err.h>
25#include <stdlib.h>
26#include <string.h>
27#include <kernel/thread.h>
28#include <arch/x86/descriptor.h>
29#include <dev/pci.h>
30
31static int last_bus = 0;
32
33typedef struct {
34 uint16_t size;
35 void *offset;
36 uint16_t selector;
37} __PACKED irq_routing_options_t;
38
39static int pci_type1_detect(void);
40static int pci_bios_detect(void);
41
42int pci_get_last_bus(void)
43{
44 return last_bus;
45}
46
47/*
48 * pointers to installed PCI routines
49 */
50int (*g_pci_find_pci_device)(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
51int (*g_pci_find_pci_class_code)(pci_location_t *state, uint32_t class_code, uint16_t index);
52
53int (*g_pci_read_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t *value);
54int (*g_pci_read_config_half)(const pci_location_t *state, uint32_t reg, uint16_t *value);
55int (*g_pci_read_config_word)(const pci_location_t *state, uint32_t reg, uint32_t *value);
56
57int (*g_pci_write_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t value);
58int (*g_pci_write_config_half)(const pci_location_t *state, uint32_t reg, uint16_t value);
59int (*g_pci_write_config_word)(const pci_location_t *state, uint32_t reg, uint32_t value);
60
61int (*g_pci_get_irq_routing_options)(irq_routing_options_t *options, uint16_t *pci_irqs);
62int (*g_pci_set_irq_hw_int)(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
63
64
65int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
66{
67 enter_critical_section();
68
69 int res = g_pci_find_pci_device(state, device_id, vendor_id, index);
70
71 exit_critical_section();
72
73 return res;
74}
75
76int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
77{
78 enter_critical_section();
79
80 int res = g_pci_find_pci_class_code(state, class_code, index);
81
82 exit_critical_section();
83
84 return res;
85}
86
87int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
88{
89 enter_critical_section();
90
91 int res = g_pci_read_config_byte(state, reg, value);
92
93 exit_critical_section();
94
95 return res;
96}
97int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
98{
99 enter_critical_section();
100
101 int res = g_pci_read_config_half(state, reg, value);
102
103 exit_critical_section();
104
105 return res;
106}
107
108int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
109{
110 enter_critical_section();
111
112 int res = g_pci_read_config_word(state, reg, value);
113
114 exit_critical_section();
115
116 return res;
117}
118
119int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
120{
121 enter_critical_section();
122
123 int res = g_pci_write_config_byte(state, reg, value);
124
125 exit_critical_section();
126
127 return res;
128}
129
130int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
131{
132 enter_critical_section();
133
134 int res = g_pci_write_config_half(state, reg, value);
135
136 exit_critical_section();
137
138 return res;
139}
140
141int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
142{
143 enter_critical_section();
144
145 int res = g_pci_write_config_word(state, reg, value);
146
147 exit_critical_section();
148
149 return res;
150}
151
152
153int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs)
154{
155 enter_critical_section();
156
157 irq_routing_options_t options;
158 options.size = sizeof(irq_routing_entry) * *count;
159 options.selector = DATA_SELECTOR;
160 options.offset = entries;
161
162 int res = g_pci_get_irq_routing_options(&options, pci_irqs);
163
164 *count = options.size / sizeof(irq_routing_entry);
165
166 exit_critical_section();
167
168 return res;
169}
170
171int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
172{
173 enter_critical_section();
174
175 int res = g_pci_set_irq_hw_int(state, int_pin, irq);
176
177 exit_critical_section();
178
179 return res;
180}
181
182void pci_init(void)
183{
184 if (!pci_bios_detect()) {
185 dprintf(INFO, "pci bios functions installed\n");
186 dprintf(INFO, "last pci bus is %d\n", last_bus);
187 }
188}
189
190#define PCIBIOS_PRESENT 0xB101
191#define PCIBIOS_FIND_PCI_DEVICE 0xB102
192#define PCIBIOS_FIND_PCI_CLASS_CODE 0xB103
193#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xB106
194#define PCIBIOS_READ_CONFIG_BYTE 0xB108
195#define PCIBIOS_READ_CONFIG_WORD 0xB109
196#define PCIBIOS_READ_CONFIG_DWORD 0xB10A
197#define PCIBIOS_WRITE_CONFIG_BYTE 0xB10B
198#define PCIBIOS_WRITE_CONFIG_WORD 0xB10C
199#define PCIBIOS_WRITE_CONFIG_DWORD 0xB10D
200#define PCIBIOS_GET_IRQ_ROUTING_OPTIONS 0xB10E
201#define PCIBIOS_PCI_SET_IRQ_HW_INT 0xB10F
202
203#define PCIBIOS_SUCCESSFUL 0x00
204#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
205#define PCIBIOS_BAD_VENDOR_ID 0x83
206#define PCIBIOS_DEVICE_NOT_FOUND 0x86
207#define PCIBIOS_BAD_REGISTER_NUMBER 0x87
208#define PCIBIOS_SET_FAILED 0x88
209#define PCIBIOS_BUFFER_TOO_SMALL 0x89
210
211/*
212 * far call structure used by BIOS32 routines
213 */
214static struct {
215 uint32_t offset;
216 uint16_t selector;
217} __PACKED bios32_entry;
218
219/*
220 * BIOS32 entry header
221 */
222typedef struct {
223 uint8_t magic[4]; // "_32_"
224 void * entry; // entry point
225 uint8_t revision;
226 uint8_t length;
227 uint8_t checksum;
228 uint8_t reserved[5];
229} __PACKED pci_bios_info;
230
231/*
232 * scan for pci bios
233 */
234static const char * pci_bios_magic = "_32_";
235static pci_bios_info *find_pci_bios_info(void) {
236 uint32_t *head = (uint32_t *) 0x000e0000;
237 int8_t sum, *b;
238 uint i;
239
240 while (head < (uint32_t *) 0x000ffff0) {
241 if (*head == *(uint32_t *) pci_bios_magic) {
242 // perform the checksum
243 sum = 0;
244 b = (int8_t *) head;
245 for (i=0; i < sizeof(pci_bios_info); i++) {
246 sum += b[i];
247 }
248
249 if (sum == 0) {
250 return (pci_bios_info *) head;
251 }
252 }
253
254 head += 4;
255 }
256
257 return NULL;
258}
259
260/*
261 * local BIOS32 PCI routines
262 */
263static int bios_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
264{
265 uint32_t bx, ret;
266
267 __asm__(
268 "lcall *(%%edi) \n\t"
269 "jc 1f \n\t"
270 "xor %%ah,%%ah \n"
271 "1:"
272 : "=b"(bx),
273 "=a"(ret)
274 : "1"(PCIBIOS_FIND_PCI_DEVICE),
275 "c"(device_id),
276 "d"(vendor_id),
277 "S"(index),
278 "D"(&bios32_entry));
279
280 state->bus = bx >> 8;
281 state->dev_fn = bx & 0xFF;
282
283 ret >>= 8;
284 return ret & 0xFF;
285}
286
287static int bios_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
288{
289 uint32_t bx, ret;
290
291 __asm__(
292 "lcall *(%%edi) \n\t"
293 "jc 1f \n\t"
294 "xor %%ah,%%ah \n"
295 "1:"
296 : "=b"(bx),
297 "=a"(ret)
298 : "1"(PCIBIOS_FIND_PCI_CLASS_CODE),
299 "c"(class_code),
300 "S"(index),
301 "D"(&bios32_entry));
302
303 state->bus = bx >> 8;
304 state->dev_fn = bx & 0xFF;
305
306 ret >>= 8;
307 return ret & 0xFF;
308}
309
310
311static int bios_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
312{
313 uint32_t bx, ret;
314
315 bx = state->bus;
316 bx <<= 8;
317 bx |= state->dev_fn;
318 __asm__(
319 "lcall *(%%esi) \n\t"
320 "jc 1f \n\t"
321 "xor %%ah,%%ah \n"
322 "1:"
323 : "=c"(*value),
324 "=a"(ret)
325 : "1"(PCIBIOS_READ_CONFIG_BYTE),
326 "b"(bx),
327 "D"(reg),
328 "S"(&bios32_entry));
329 ret >>= 8;
330 return ret & 0xFF;
331}
332
333static int bios_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
334{
335 uint32_t bx, ret;
336
337 bx = state->bus;
338 bx <<= 8;
339 bx |= state->dev_fn;
340 __asm__(
341 "lcall *(%%esi) \n\t"
342 "jc 1f \n\t"
343 "xor %%ah,%%ah \n"
344 "1:"
345 : "=c"(*value),
346 "=a"(ret)
347 : "1"(PCIBIOS_READ_CONFIG_WORD),
348 "b"(bx),
349 "D"(reg),
350 "S"(&bios32_entry));
351 ret >>= 8;
352 return ret & 0xFF;
353}
354
355static int bios_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
356{
357 uint32_t bx, ret;
358
359 bx = state->bus;
360 bx <<= 8;
361 bx |= state->dev_fn;
362 __asm__(
363 "lcall *(%%esi) \n\t"
364 "jc 1f \n\t"
365 "xor %%ah,%%ah \n"
366 "1:"
367 : "=c"(*value),
368 "=a"(ret)
369 : "1"(PCIBIOS_READ_CONFIG_DWORD),
370 "b"(bx),
371 "D"(reg),
372 "S"(&bios32_entry));
373 ret >>= 8;
374 return ret & 0xFF;
375}
376
377static int bios_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
378{
379 uint32_t bx, ret;
380
381 bx = state->bus;
382 bx <<= 8;
383 bx |= state->dev_fn;
384 __asm__(
385 "lcall *(%%esi) \n\t"
386 "jc 1f \n\t"
387 "xor %%ah,%%ah \n"
388 "1:"
389 : "=a"(ret)
390 : "0"(PCIBIOS_WRITE_CONFIG_BYTE),
391 "c"(value),
392 "b"(bx),
393 "D"(reg),
394 "S"(&bios32_entry));
395 ret >>= 8;
396 return ret & 0xFF;
397}
398
399static int bios_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
400{
401 uint32_t bx, ret;
402
403 bx = state->bus;
404 bx <<= 8;
405 bx |= state->dev_fn;
406 __asm__(
407 "lcall *(%%esi) \n\t"
408 "jc 1f \n\t"
409 "xor %%ah,%%ah \n"
410 "1:"
411 : "=a"(ret)
412 : "0"(PCIBIOS_WRITE_CONFIG_WORD),
413 "c"(value),
414 "b"(bx),
415 "D"(reg),
416 "S"(&bios32_entry));
417 ret >>= 8;
418 return ret & 0xFF;
419}
420
421static int bios_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
422{
423 uint32_t bx, ret;
424
425 bx = state->bus;
426 bx <<= 8;
427 bx |= state->dev_fn;
428 __asm__(
429 "lcall *(%%esi) \n\t"
430 "jc 1f \n\t"
431 "xor %%ah,%%ah \n"
432 "1:"
433 : "=a"(ret)
434 : "0"(PCIBIOS_WRITE_CONFIG_DWORD),
435 "c"(value),
436 "b"(bx),
437 "D"(reg),
438 "S"(&bios32_entry));
439 ret >>= 8;
440 return ret & 0xFF;
441}
442
443static int bios_get_irq_routing_options(irq_routing_options_t *route_buffer, uint16_t *pciIrqs)
444{
445 uint32_t ret;
446
447 __asm__(
448 "lcall *(%%esi) \n\t"
449 "jc 1f \n\t"
450 "xor %%ah,%%ah \n"
451 "1:"
452 : "=b"(*pciIrqs),
453 "=a"(ret)
454 : "1"(PCIBIOS_GET_IRQ_ROUTING_OPTIONS),
455 "b"(0),
456 "D"(route_buffer),
457 "S"(&bios32_entry));
458 ret >>= 8;
459 return ret & 0xff;
460}
461
462static int bios_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
463{
464 uint32_t bx, cx, ret;
465
466 bx = state->bus;
467 bx <<= 8;
468 bx |= state->dev_fn;
469 cx = irq;
470 cx <<= 8;
471 cx |= int_pin;
472 __asm__(
473 "lcall *(%%esi) \n\t"
474 "jc 1f \n\t"
475 "xor %%ah,%%ah \n"
476 "1:"
477 : "=a"(ret)
478 : "0"(PCIBIOS_PCI_SET_IRQ_HW_INT),
479 "b"(bx),
480 "c"(cx),
481 "S"(&bios32_entry));
482 ret >>= 8;
483 return ret & 0xFF;
484}
485
486static const char *pci_signature = "PCI ";
487static int pci_bios_detect(void) {
488 pci_bios_info * pci = find_pci_bios_info();
489
490 if (pci != NULL) {
491 /*printf("Found PCI structure at %08x\n", (uint32_t) pci);
492
493 printf("\nPCI header info:\n");
494 printf("%c%c%c%c\n", pci->magic[0], pci->magic[1], pci->magic[2],
495 pci->magic[3]);
496 printf("%08x\n", (uint32_t) pci->entry);
497 printf("%d\n", pci->length * 16);
498 printf("%d\n", pci->checksum);*/
499
500 uint32_t adr, temp, len;
501 uint8_t err;
502
503 bios32_entry.offset = (uint32_t) pci->entry;
504 bios32_entry.selector = CODE_SELECTOR;
505
506 __asm__(
507 "lcall *(%%edi)"
508 : "=a"(err), /* AL out=status */
509 "=b"(adr), /* EBX out=code segment base adr */
510 "=c"(len), /* ECX out=code segment size */
511 "=d"(temp) /* EDX out=entry pt offset in code */
512 : "0"(0x49435024),/* EAX in=service="$PCI" */
513 "1"(0), /* EBX in=0=get service entry pt */
514 "D"(&bios32_entry)
515 );
516
517 if (err == 0x80) {
518 dprintf(INFO, "BIOS32 found, but no PCI BIOS\n");
519 return -1;
520 }
521
522 if (err != 0) {
523 dprintf(INFO, "BIOS32 call to locate PCI BIOS returned %x\n", err);
524 return -1;
525 }
526
527 bios32_entry.offset = adr + temp;
528
529 // now call PCI_BIOS_PRESENT to get version, hw mechanism, and last bus
530 uint16_t present, version, busses;
531 uint32_t signature;
532 __asm__(
533 "lcall *(%%edi) \n\t"
534 "jc 1f \n\t"
535 "xor %%ah,%%ah \n"
536 "1:"
537 : "=a"(present),
538 "=b"(version),
539 "=c"(busses),
540 "=d"(signature)
541 : "0"(PCIBIOS_PRESENT),
Corey Tabaka40301502009-04-03 16:46:34 -0400542 "D"(&bios32_entry)
Corey Tabakab0bd1832009-04-03 01:17:52 -0400543 );
544
545 if (present & 0xff00) {
546 dprintf(INFO, "PCI_BIOS_PRESENT call returned ah=%02x\n", present >> 8);
547 return -1;
548 }
549
550 if (signature != *(uint32_t *)pci_signature) {
551 dprintf(INFO, "PCI_BIOS_PRESENT call returned edx=%08x\n", signature);
552 return -1;
553 }
554
555 //dprintf(DEBUG, "busses=%04x\n", busses);
556 last_bus = busses & 0xff;
557
558 g_pci_find_pci_device = bios_find_pci_device;
559 g_pci_find_pci_class_code = bios_find_pci_class_code;
560
561 g_pci_read_config_word = bios_read_config_word;
562 g_pci_read_config_half = bios_read_config_half;
563 g_pci_read_config_byte = bios_read_config_byte;
564
565 g_pci_write_config_word = bios_write_config_word;
566 g_pci_write_config_half = bios_write_config_half;
567 g_pci_write_config_byte = bios_write_config_byte;
568
569 g_pci_get_irq_routing_options = bios_get_irq_routing_options;
570 g_pci_set_irq_hw_int = bios_set_irq_hw_int;
571
572 return 0;
573 }
574
575 return -1;
576}