Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/i386/pci/pci_pir.c
39586 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 1997, Stefan Esser <[email protected]>
5
* Copyright (c) 2000, Michael Smith <[email protected]>
6
* Copyright (c) 2000, BSDi
7
* All rights reserved.
8
* Copyright (c) 2004, John Baldwin <[email protected]>
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice unmodified, this list of conditions, and the following
15
* disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
*/
31
32
#include <sys/param.h>
33
#include <sys/systm.h>
34
#include <sys/bus.h>
35
#include <sys/kernel.h>
36
#include <sys/malloc.h>
37
#include <sys/module.h>
38
#include <sys/sysctl.h>
39
#include <vm/vm.h>
40
#include <vm/pmap.h>
41
#include <vm/vm_param.h>
42
#include <machine/md_var.h>
43
#include <dev/pci/pcivar.h>
44
#include <dev/pci/pcireg.h>
45
#include <machine/pci_cfgreg.h>
46
#include <machine/segments.h>
47
#include <machine/pc/bios.h>
48
49
#define NUM_ISA_INTERRUPTS 16
50
51
/*
52
* A link device. Loosely based on the ACPI PCI link device. This doesn't
53
* try to support priorities for different ISA interrupts.
54
*/
55
struct pci_link {
56
TAILQ_ENTRY(pci_link) pl_links;
57
uint8_t pl_id;
58
uint8_t pl_irq;
59
uint16_t pl_irqmask;
60
int pl_references;
61
int pl_routed;
62
};
63
64
struct pci_link_lookup {
65
struct pci_link **pci_link_ptr;
66
int bus;
67
int device;
68
int pin;
69
};
70
71
struct pci_dev_lookup {
72
uint8_t link;
73
int bus;
74
int device;
75
int pin;
76
};
77
78
typedef void pir_entry_handler(struct PIR_entry *entry,
79
struct PIR_intpin* intpin, void *arg);
80
81
static void pci_print_irqmask(u_int16_t irqs);
82
static int pci_pir_biosroute(int bus, int device, int func, int pin,
83
int irq);
84
static int pci_pir_choose_irq(struct pci_link *pci_link, int irqmask);
85
static void pci_pir_create_links(struct PIR_entry *entry,
86
struct PIR_intpin *intpin, void *arg);
87
static void pci_pir_dump_links(void);
88
static struct pci_link *pci_pir_find_link(uint8_t link_id);
89
static void pci_pir_find_link_handler(struct PIR_entry *entry,
90
struct PIR_intpin *intpin, void *arg);
91
static void pci_pir_initial_irqs(struct PIR_entry *entry,
92
struct PIR_intpin *intpin, void *arg);
93
static void pci_pir_parse(void);
94
static uint8_t pci_pir_search_irq(int bus, int device, int pin);
95
static int pci_pir_valid_irq(struct pci_link *pci_link, int irq);
96
static void pci_pir_walk_table(pir_entry_handler *handler, void *arg);
97
98
static MALLOC_DEFINE(M_PIR, "$PIR", "$PIR structures");
99
100
static struct PIR_table *pci_route_table;
101
static device_t pir_device;
102
static int pci_route_count, pir_bios_irqs, pir_parsed;
103
static TAILQ_HEAD(, pci_link) pci_links;
104
static int pir_interrupt_weight[NUM_ISA_INTERRUPTS];
105
106
/* sysctl vars */
107
SYSCTL_DECL(_hw_pci);
108
109
/* XXX this likely should live in a header file */
110
/* IRQs 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15 */
111
#define PCI_IRQ_OVERRIDE_MASK 0xdef8
112
113
static uint32_t pci_irq_override_mask = PCI_IRQ_OVERRIDE_MASK;
114
SYSCTL_INT(_hw_pci, OID_AUTO, irq_override_mask, CTLFLAG_RDTUN,
115
&pci_irq_override_mask, PCI_IRQ_OVERRIDE_MASK,
116
"Mask of allowed irqs to try to route when it has no good clue about\n"
117
"which irqs it should use.");
118
119
/*
120
* Look for the interrupt routing table.
121
*
122
* We use PCI BIOS's PIR table if it's available. $PIR is the standard way
123
* to do this. Sadly, some machines are not standards conforming and have
124
* _PIR instead. We shrug and cope by looking for both.
125
*/
126
void
127
pci_pir_open(void)
128
{
129
struct PIR_table *pt;
130
uint32_t sigaddr;
131
int i;
132
uint8_t ck, *cv;
133
134
/* Don't try if we've already found a table. */
135
if (pci_route_table != NULL)
136
return;
137
138
/* Look for $PIR and then _PIR. */
139
sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0);
140
if (sigaddr == 0)
141
sigaddr = bios_sigsearch(0, "_PIR", 4, 16, 0);
142
if (sigaddr == 0)
143
return;
144
145
/* If we found something, check the checksum and length. */
146
/* XXX - Use pmap_mapdev()? */
147
pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
148
if (pt->pt_header.ph_length <= sizeof(struct PIR_header))
149
return;
150
for (cv = (u_int8_t *)pt, ck = 0, i = 0;
151
i < (pt->pt_header.ph_length); i++)
152
ck += cv[i];
153
if (ck != 0)
154
return;
155
156
/* Ok, we've got a valid table. */
157
pci_route_table = pt;
158
pci_route_count = (pt->pt_header.ph_length -
159
sizeof(struct PIR_header)) /
160
sizeof(struct PIR_entry);
161
}
162
163
/*
164
* Find the pci_link structure for a given link ID.
165
*/
166
static struct pci_link *
167
pci_pir_find_link(uint8_t link_id)
168
{
169
struct pci_link *pci_link;
170
171
TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
172
if (pci_link->pl_id == link_id)
173
return (pci_link);
174
}
175
return (NULL);
176
}
177
178
/*
179
* Find the link device associated with a PCI device in the table.
180
*/
181
static void
182
pci_pir_find_link_handler(struct PIR_entry *entry, struct PIR_intpin *intpin,
183
void *arg)
184
{
185
struct pci_link_lookup *lookup;
186
187
lookup = (struct pci_link_lookup *)arg;
188
if (entry->pe_bus == lookup->bus &&
189
entry->pe_device == lookup->device &&
190
intpin - entry->pe_intpin == lookup->pin)
191
*lookup->pci_link_ptr = pci_pir_find_link(intpin->link);
192
}
193
194
/*
195
* Check to see if a possible IRQ setting is valid.
196
*/
197
static int
198
pci_pir_valid_irq(struct pci_link *pci_link, int irq)
199
{
200
201
if (!PCI_INTERRUPT_VALID(irq))
202
return (0);
203
return (pci_link->pl_irqmask & (1 << irq));
204
}
205
206
/*
207
* Walk the $PIR executing the worker function for each valid intpin entry
208
* in the table. The handler is passed a pointer to both the entry and
209
* the intpin in the entry.
210
*/
211
static void
212
pci_pir_walk_table(pir_entry_handler *handler, void *arg)
213
{
214
struct PIR_entry *entry;
215
struct PIR_intpin *intpin;
216
int i, pin;
217
218
entry = &pci_route_table->pt_entry[0];
219
for (i = 0; i < pci_route_count; i++, entry++) {
220
intpin = &entry->pe_intpin[0];
221
for (pin = 0; pin < 4; pin++, intpin++)
222
if (intpin->link != 0)
223
handler(entry, intpin, arg);
224
}
225
}
226
227
static void
228
pci_pir_create_links(struct PIR_entry *entry, struct PIR_intpin *intpin,
229
void *arg)
230
{
231
struct pci_link *pci_link;
232
233
pci_link = pci_pir_find_link(intpin->link);
234
if (pci_link != NULL) {
235
pci_link->pl_references++;
236
if (intpin->irqs != pci_link->pl_irqmask) {
237
if (bootverbose)
238
printf(
239
"$PIR: Entry %d.%d.INT%c has different mask for link %#x, merging\n",
240
entry->pe_bus, entry->pe_device,
241
(intpin - entry->pe_intpin) + 'A',
242
pci_link->pl_id);
243
pci_link->pl_irqmask &= intpin->irqs;
244
}
245
} else {
246
pci_link = malloc(sizeof(struct pci_link), M_PIR, M_WAITOK);
247
pci_link->pl_id = intpin->link;
248
pci_link->pl_irqmask = intpin->irqs;
249
pci_link->pl_irq = PCI_INVALID_IRQ;
250
pci_link->pl_references = 1;
251
pci_link->pl_routed = 0;
252
TAILQ_INSERT_TAIL(&pci_links, pci_link, pl_links);
253
}
254
}
255
256
/*
257
* Look to see if any of the functions on the PCI device at bus/device
258
* have an interrupt routed to intpin 'pin' by the BIOS.
259
*/
260
static uint8_t
261
pci_pir_search_irq(int bus, int device, int pin)
262
{
263
uint32_t value;
264
uint8_t func, maxfunc;
265
266
/* See if we have a valid device at function 0. */
267
value = pci_cfgregread(0, bus, device, 0, PCIR_VENDOR, 2);
268
if (value == PCIV_INVALID)
269
return (PCI_INVALID_IRQ);
270
value = pci_cfgregread(0, bus, device, 0, PCIR_HDRTYPE, 1);
271
if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
272
return (PCI_INVALID_IRQ);
273
if (value & PCIM_MFDEV)
274
maxfunc = PCI_FUNCMAX;
275
else
276
maxfunc = 0;
277
278
/* Scan all possible functions at this device. */
279
for (func = 0; func <= maxfunc; func++) {
280
value = pci_cfgregread(0, bus, device, func, PCIR_VENDOR, 2);
281
if (value == PCIV_INVALID)
282
continue;
283
value = pci_cfgregread(0, bus, device, func, PCIR_INTPIN, 1);
284
285
/*
286
* See if it uses the pin in question. Note that the passed
287
* in pin uses 0 for A, .. 3 for D whereas the intpin
288
* register uses 0 for no interrupt, 1 for A, .. 4 for D.
289
*/
290
if (value != pin + 1)
291
continue;
292
value = pci_cfgregread(0, bus, device, func, PCIR_INTLINE, 1);
293
if (bootverbose)
294
printf(
295
"$PIR: Found matching pin for %d.%d.INT%c at func %d: %d\n",
296
bus, device, pin + 'A', func, value);
297
if (value != PCI_INVALID_IRQ)
298
return (value);
299
}
300
return (PCI_INVALID_IRQ);
301
}
302
303
/*
304
* Try to initialize IRQ based on this device's IRQ.
305
*/
306
static void
307
pci_pir_initial_irqs(struct PIR_entry *entry, struct PIR_intpin *intpin,
308
void *arg)
309
{
310
struct pci_link *pci_link;
311
uint8_t irq, pin;
312
313
pin = intpin - entry->pe_intpin;
314
pci_link = pci_pir_find_link(intpin->link);
315
irq = pci_pir_search_irq(entry->pe_bus, entry->pe_device, pin);
316
if (irq == PCI_INVALID_IRQ || irq == pci_link->pl_irq)
317
return;
318
319
/* Don't trust any BIOS IRQs greater than 15. */
320
if (irq >= NUM_ISA_INTERRUPTS) {
321
printf(
322
"$PIR: Ignoring invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n",
323
irq, entry->pe_bus, entry->pe_device, pin + 'A',
324
pci_link->pl_id);
325
return;
326
}
327
328
/*
329
* If we don't have an IRQ for this link yet, then we trust the
330
* BIOS, even if it seems invalid from the $PIR entries.
331
*/
332
if (pci_link->pl_irq == PCI_INVALID_IRQ) {
333
if (!pci_pir_valid_irq(pci_link, irq))
334
printf(
335
"$PIR: Using invalid BIOS IRQ %d from %d.%d.INT%c for link %#x\n",
336
irq, entry->pe_bus, entry->pe_device, pin + 'A',
337
pci_link->pl_id);
338
pci_link->pl_irq = irq;
339
pci_link->pl_routed = 1;
340
return;
341
}
342
343
/*
344
* We have an IRQ and it doesn't match the current IRQ for this
345
* link. If the new IRQ is invalid, then warn about it and ignore
346
* it. If the old IRQ is invalid and the new IRQ is valid, then
347
* prefer the new IRQ instead. If both IRQs are valid, then just
348
* use the first one. Note that if we ever get into this situation
349
* we are having to guess which setting the BIOS actually routed.
350
* Perhaps we should just give up instead.
351
*/
352
if (!pci_pir_valid_irq(pci_link, irq)) {
353
printf(
354
"$PIR: BIOS IRQ %d for %d.%d.INT%c is not valid for link %#x\n",
355
irq, entry->pe_bus, entry->pe_device, pin + 'A',
356
pci_link->pl_id);
357
} else if (!pci_pir_valid_irq(pci_link, pci_link->pl_irq)) {
358
printf(
359
"$PIR: Preferring valid BIOS IRQ %d from %d.%d.INT%c for link %#x to IRQ %d\n",
360
irq, entry->pe_bus, entry->pe_device, pin + 'A',
361
pci_link->pl_id, pci_link->pl_irq);
362
pci_link->pl_irq = irq;
363
pci_link->pl_routed = 1;
364
} else
365
printf(
366
"$PIR: BIOS IRQ %d for %d.%d.INT%c does not match link %#x irq %d\n",
367
irq, entry->pe_bus, entry->pe_device, pin + 'A',
368
pci_link->pl_id, pci_link->pl_irq);
369
}
370
371
/*
372
* Parse $PIR to enumerate link devices and attempt to determine their
373
* initial state. This could perhaps be cleaner if we had drivers for the
374
* various interrupt routers as they could read the initial IRQ for each
375
* link.
376
*/
377
static void
378
pci_pir_parse(void)
379
{
380
char tunable_buffer[64];
381
struct pci_link *pci_link;
382
int i, irq;
383
384
/* Only parse once. */
385
if (pir_parsed)
386
return;
387
pir_parsed = 1;
388
389
/* Enumerate link devices. */
390
TAILQ_INIT(&pci_links);
391
pci_pir_walk_table(pci_pir_create_links, NULL);
392
if (bootverbose) {
393
printf("$PIR: Links after initial probe:\n");
394
pci_pir_dump_links();
395
}
396
397
/*
398
* Check to see if the BIOS has already routed any of the links by
399
* checking each device connected to each link to see if it has a
400
* valid IRQ.
401
*/
402
pci_pir_walk_table(pci_pir_initial_irqs, NULL);
403
if (bootverbose) {
404
printf("$PIR: Links after initial IRQ discovery:\n");
405
pci_pir_dump_links();
406
}
407
408
/*
409
* Allow the user to override the IRQ for a given link device. We
410
* allow invalid IRQs to be specified but warn about them. An IRQ
411
* of 255 or 0 clears any preset IRQ.
412
*/
413
i = 0;
414
TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
415
snprintf(tunable_buffer, sizeof(tunable_buffer),
416
"hw.pci.link.%#x.irq", pci_link->pl_id);
417
if (getenv_int(tunable_buffer, &irq) == 0)
418
continue;
419
if (irq == 0)
420
irq = PCI_INVALID_IRQ;
421
if (irq != PCI_INVALID_IRQ &&
422
!pci_pir_valid_irq(pci_link, irq) && bootverbose)
423
printf(
424
"$PIR: Warning, IRQ %d for link %#x is not listed as valid\n",
425
irq, pci_link->pl_id);
426
pci_link->pl_routed = 0;
427
pci_link->pl_irq = irq;
428
i = 1;
429
}
430
if (bootverbose && i) {
431
printf("$PIR: Links after tunable overrides:\n");
432
pci_pir_dump_links();
433
}
434
435
/*
436
* Build initial interrupt weights as well as bitmap of "known-good"
437
* IRQs that the BIOS has already used for PCI link devices.
438
*/
439
TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
440
if (!PCI_INTERRUPT_VALID(pci_link->pl_irq))
441
continue;
442
pir_bios_irqs |= 1 << pci_link->pl_irq;
443
pir_interrupt_weight[pci_link->pl_irq] +=
444
pci_link->pl_references;
445
}
446
if (bootverbose) {
447
printf("$PIR: IRQs used by BIOS: ");
448
pci_print_irqmask(pir_bios_irqs);
449
printf("\n");
450
printf("$PIR: Interrupt Weights:\n[ ");
451
for (i = 0; i < NUM_ISA_INTERRUPTS; i++)
452
printf(" %3d", i);
453
printf(" ]\n[ ");
454
for (i = 0; i < NUM_ISA_INTERRUPTS; i++)
455
printf(" %3d", pir_interrupt_weight[i]);
456
printf(" ]\n");
457
}
458
}
459
460
/*
461
* Use the PCI BIOS to route an interrupt for a given device.
462
*
463
* Input:
464
* AX = PCIBIOS_ROUTE_INTERRUPT
465
* BH = bus
466
* BL = device [7:3] / function [2:0]
467
* CH = IRQ
468
* CL = Interrupt Pin (0x0A = A, ... 0x0D = D)
469
*/
470
static int
471
pci_pir_biosroute(int bus, int device, int func, int pin, int irq)
472
{
473
struct bios_regs args;
474
475
args.eax = PCIBIOS_ROUTE_INTERRUPT;
476
args.ebx = (bus << 8) | (device << 3) | func;
477
args.ecx = (irq << 8) | (0xa + pin);
478
return (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)));
479
}
480
481
/*
482
* Route a PCI interrupt using a link device from the $PIR.
483
*/
484
int
485
pci_pir_route_interrupt(int bus, int device, int func, int pin)
486
{
487
struct pci_link_lookup lookup;
488
struct pci_link *pci_link;
489
int error, irq;
490
491
if (pci_route_table == NULL)
492
return (PCI_INVALID_IRQ);
493
494
/* Lookup link device for this PCI device/pin. */
495
pci_link = NULL;
496
lookup.bus = bus;
497
lookup.device = device;
498
lookup.pin = pin - 1;
499
lookup.pci_link_ptr = &pci_link;
500
pci_pir_walk_table(pci_pir_find_link_handler, &lookup);
501
if (pci_link == NULL) {
502
printf("$PIR: No matching entry for %d.%d.INT%c\n", bus,
503
device, pin - 1 + 'A');
504
return (PCI_INVALID_IRQ);
505
}
506
507
/*
508
* Pick a new interrupt if we don't have one already. We look
509
* for an interrupt from several different sets. First, if
510
* this link only has one valid IRQ, use that. Second, we
511
* check the set of PCI only interrupts from the $PIR. Third,
512
* we check the set of known-good interrupts that the BIOS has
513
* already used. Lastly, we check the "all possible valid
514
* IRQs" set.
515
*/
516
if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) {
517
if (pci_link->pl_irqmask != 0 && powerof2(pci_link->pl_irqmask))
518
irq = ffs(pci_link->pl_irqmask) - 1;
519
else
520
irq = pci_pir_choose_irq(pci_link,
521
pci_route_table->pt_header.ph_pci_irqs);
522
if (!PCI_INTERRUPT_VALID(irq))
523
irq = pci_pir_choose_irq(pci_link, pir_bios_irqs);
524
if (!PCI_INTERRUPT_VALID(irq))
525
irq = pci_pir_choose_irq(pci_link,
526
pci_irq_override_mask);
527
if (!PCI_INTERRUPT_VALID(irq)) {
528
if (bootverbose)
529
printf(
530
"$PIR: Failed to route interrupt for %d:%d INT%c\n",
531
bus, device, pin - 1 + 'A');
532
return (PCI_INVALID_IRQ);
533
}
534
pci_link->pl_irq = irq;
535
}
536
537
/* Ask the BIOS to route this IRQ if we haven't done so already. */
538
if (!pci_link->pl_routed) {
539
error = pci_pir_biosroute(bus, device, func, pin - 1,
540
pci_link->pl_irq);
541
542
/* Ignore errors when routing a unique interrupt. */
543
if (error && !powerof2(pci_link->pl_irqmask)) {
544
printf("$PIR: ROUTE_INTERRUPT failed.\n");
545
return (PCI_INVALID_IRQ);
546
}
547
pci_link->pl_routed = 1;
548
549
/* Ensure the interrupt is set to level/low trigger. */
550
KASSERT(pir_device != NULL, ("missing pir device"));
551
BUS_CONFIG_INTR(pir_device, pci_link->pl_irq,
552
INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
553
}
554
if (bootverbose)
555
printf("$PIR: %d:%d INT%c routed to irq %d\n", bus, device,
556
pin - 1 + 'A', pci_link->pl_irq);
557
return (pci_link->pl_irq);
558
}
559
560
/*
561
* Try to pick an interrupt for the specified link from the interrupts
562
* set in the mask.
563
*/
564
static int
565
pci_pir_choose_irq(struct pci_link *pci_link, int irqmask)
566
{
567
int i, irq, realmask;
568
569
/* XXX: Need to have a #define of known bad IRQs to also mask out? */
570
realmask = pci_link->pl_irqmask & irqmask;
571
if (realmask == 0)
572
return (PCI_INVALID_IRQ);
573
574
/* Find IRQ with lowest weight. */
575
irq = PCI_INVALID_IRQ;
576
for (i = 0; i < NUM_ISA_INTERRUPTS; i++) {
577
if (!(realmask & 1 << i))
578
continue;
579
if (irq == PCI_INVALID_IRQ ||
580
pir_interrupt_weight[i] < pir_interrupt_weight[irq])
581
irq = i;
582
}
583
if (bootverbose && PCI_INTERRUPT_VALID(irq)) {
584
printf("$PIR: Found IRQ %d for link %#x from ", irq,
585
pci_link->pl_id);
586
pci_print_irqmask(realmask);
587
printf("\n");
588
}
589
return (irq);
590
}
591
592
static void
593
pci_print_irqmask(u_int16_t irqs)
594
{
595
int i, first;
596
597
if (irqs == 0) {
598
printf("none");
599
return;
600
}
601
first = 1;
602
for (i = 0; i < 16; i++, irqs >>= 1)
603
if (irqs & 1) {
604
if (!first)
605
printf(" ");
606
else
607
first = 0;
608
printf("%d", i);
609
}
610
}
611
612
/*
613
* Display link devices.
614
*/
615
static void
616
pci_pir_dump_links(void)
617
{
618
struct pci_link *pci_link;
619
620
printf("Link IRQ Rtd Ref IRQs\n");
621
TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
622
printf("%#4x %3d %c %3d ", pci_link->pl_id,
623
pci_link->pl_irq, pci_link->pl_routed ? 'Y' : 'N',
624
pci_link->pl_references);
625
pci_print_irqmask(pci_link->pl_irqmask);
626
printf("\n");
627
}
628
}
629
630
/*
631
* See if any interrupts for a given PCI bus are routed in the PIR. Don't
632
* even bother looking if the BIOS doesn't support routing anyways. If we
633
* are probing a PCI-PCI bridge, then require_parse will be true and we should
634
* only succeed if a host-PCI bridge has already attached and parsed the PIR.
635
*/
636
int
637
pci_pir_probe(int bus, int require_parse)
638
{
639
int i;
640
641
if (pci_route_table == NULL || (require_parse && !pir_parsed))
642
return (0);
643
for (i = 0; i < pci_route_count; i++)
644
if (pci_route_table->pt_entry[i].pe_bus == bus)
645
return (1);
646
return (0);
647
}
648
649
/*
650
* The driver for the new-bus pseudo device pir0 for the $PIR table.
651
*/
652
653
static int
654
pir_probe(device_t dev)
655
{
656
device_set_descf(dev, "PCI Interrupt Routing Table: %d Entries",
657
pci_route_count);
658
return (0);
659
}
660
661
static int
662
pir_attach(device_t dev)
663
{
664
665
pci_pir_parse();
666
KASSERT(pir_device == NULL, ("Multiple pir devices"));
667
pir_device = dev;
668
return (0);
669
}
670
671
static void
672
pir_resume_find_device(struct PIR_entry *entry, struct PIR_intpin *intpin,
673
void *arg)
674
{
675
struct pci_dev_lookup *pd;
676
677
pd = (struct pci_dev_lookup *)arg;
678
if (intpin->link != pd->link || pd->bus != -1)
679
return;
680
pd->bus = entry->pe_bus;
681
pd->device = entry->pe_device;
682
pd->pin = intpin - entry->pe_intpin;
683
}
684
685
static int
686
pir_resume(device_t dev)
687
{
688
struct pci_dev_lookup pd;
689
struct pci_link *pci_link;
690
int error;
691
692
/* Ask the BIOS to re-route each link that was already routed. */
693
TAILQ_FOREACH(pci_link, &pci_links, pl_links) {
694
if (!PCI_INTERRUPT_VALID(pci_link->pl_irq)) {
695
KASSERT(!pci_link->pl_routed,
696
("link %#x is routed but has invalid PCI IRQ",
697
pci_link->pl_id));
698
continue;
699
}
700
if (pci_link->pl_routed) {
701
pd.bus = -1;
702
pd.link = pci_link->pl_id;
703
pci_pir_walk_table(pir_resume_find_device, &pd);
704
KASSERT(pd.bus != -1,
705
("did not find matching entry for link %#x in the $PIR table",
706
pci_link->pl_id));
707
if (bootverbose)
708
device_printf(dev,
709
"Using %d.%d.INT%c to route link %#x to IRQ %d\n",
710
pd.bus, pd.device, pd.pin + 'A',
711
pci_link->pl_id, pci_link->pl_irq);
712
error = pci_pir_biosroute(pd.bus, pd.device, 0, pd.pin,
713
pci_link->pl_irq);
714
if (error)
715
device_printf(dev,
716
"ROUTE_INTERRUPT on resume for link %#x failed.\n",
717
pci_link->pl_id);
718
}
719
}
720
return (0);
721
}
722
723
static device_method_t pir_methods[] = {
724
/* Device interface */
725
DEVMETHOD(device_probe, pir_probe),
726
DEVMETHOD(device_attach, pir_attach),
727
DEVMETHOD(device_resume, pir_resume),
728
{ 0, 0 }
729
};
730
731
static driver_t pir_driver = {
732
"pir",
733
pir_methods,
734
1,
735
};
736
737
DRIVER_MODULE(pir, legacy, pir_driver, 0, 0);
738
739