Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/acpica/acpi_pci_link.c
39507 views
1
/*-
2
* Copyright (c) 2002 Mitsuru IWASAKI <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/cdefs.h>
28
#include "opt_acpi.h"
29
#include <sys/param.h>
30
#include <sys/bus.h>
31
#include <sys/kernel.h>
32
#include <sys/limits.h>
33
#include <sys/malloc.h>
34
#include <sys/module.h>
35
36
#include <contrib/dev/acpica/include/acpi.h>
37
38
#include <dev/acpica/acpivar.h>
39
#include <dev/acpica/acpi_pcibvar.h>
40
41
#include <machine/pci_cfgreg.h>
42
#include <dev/pci/pcireg.h>
43
#include <dev/pci/pcivar.h>
44
#include "pcib_if.h"
45
46
/* Hooks for the ACPI CA debugging infrastructure. */
47
#define _COMPONENT ACPI_BUS
48
ACPI_MODULE_NAME("PCI_LINK")
49
50
ACPI_SERIAL_DECL(pci_link, "ACPI PCI link");
51
52
#define NUM_ISA_INTERRUPTS 16
53
#define NUM_ACPI_INTERRUPTS 256
54
55
/*
56
* An ACPI PCI link device may contain multiple links. Each link has its
57
* own ACPI resource. _PRT entries specify which link is being used via
58
* the Source Index.
59
*
60
* XXX: A note about Source Indices and DPFs: Currently we assume that
61
* the DPF start and end tags are not counted towards the index that
62
* Source Index corresponds to. Also, we assume that when DPFs are in use
63
* they various sets overlap in terms of Indices. Here's an example
64
* resource list indicating these assumptions:
65
*
66
* Resource Index
67
* -------- -----
68
* I/O Port 0
69
* Start DPF -
70
* IRQ 1
71
* MemIO 2
72
* Start DPF -
73
* IRQ 1
74
* MemIO 2
75
* End DPF -
76
* DMA Channel 3
77
*
78
* The XXX is because I'm not sure if this is a valid assumption to make.
79
*/
80
81
/* States during DPF processing. */
82
#define DPF_OUTSIDE 0
83
#define DPF_FIRST 1
84
#define DPF_IGNORE 2
85
86
struct link;
87
88
struct acpi_pci_link_softc {
89
int pl_num_links;
90
int pl_crs_bad;
91
struct link *pl_links;
92
device_t pl_dev;
93
};
94
95
struct link {
96
struct acpi_pci_link_softc *l_sc;
97
uint8_t l_bios_irq;
98
uint8_t l_irq;
99
uint8_t l_initial_irq;
100
UINT32 l_crs_type;
101
int l_res_index;
102
int l_num_irqs;
103
int *l_irqs;
104
int l_references;
105
bool l_routed:1;
106
bool l_isa_irq:1;
107
ACPI_RESOURCE l_prs_template;
108
};
109
110
struct link_count_request {
111
int in_dpf;
112
int count;
113
};
114
115
struct link_res_request {
116
struct acpi_pci_link_softc *sc;
117
int in_dpf;
118
int res_index;
119
int link_index;
120
};
121
122
static MALLOC_DEFINE(M_PCI_LINK, "pci_link", "ACPI PCI Link structures");
123
124
static int pci_link_interrupt_weights[NUM_ACPI_INTERRUPTS];
125
static int pci_link_bios_isa_irqs;
126
127
static char *pci_link_ids[] = { "PNP0C0F", NULL };
128
129
/*
130
* Fetch the short name associated with an ACPI handle and save it in the
131
* passed in buffer.
132
*/
133
static ACPI_STATUS
134
acpi_short_name(ACPI_HANDLE handle, char *buffer, size_t buflen)
135
{
136
ACPI_BUFFER buf;
137
138
buf.Length = buflen;
139
buf.Pointer = buffer;
140
return (AcpiGetName(handle, ACPI_SINGLE_NAME, &buf));
141
}
142
143
static int
144
acpi_pci_link_probe(device_t dev)
145
{
146
char name[12];
147
int rv;
148
149
/*
150
* We explicitly do not check _STA since not all systems set it to
151
* sensible values.
152
*/
153
if (acpi_disabled("pci_link"))
154
return (ENXIO);
155
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, pci_link_ids, NULL);
156
if (rv > 0)
157
return (rv);
158
159
if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), name,
160
sizeof(name))))
161
device_set_descf(dev, "ACPI PCI Link %s", name);
162
else
163
device_set_desc(dev, "ACPI PCI Link");
164
device_quiet(dev);
165
return (rv);
166
}
167
168
static ACPI_STATUS
169
acpi_count_irq_resources(ACPI_RESOURCE *res, void *context)
170
{
171
struct link_count_request *req;
172
173
req = (struct link_count_request *)context;
174
switch (res->Type) {
175
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
176
switch (req->in_dpf) {
177
case DPF_OUTSIDE:
178
/* We've started the first DPF. */
179
req->in_dpf = DPF_FIRST;
180
break;
181
case DPF_FIRST:
182
/* We've started the second DPF. */
183
req->in_dpf = DPF_IGNORE;
184
break;
185
}
186
break;
187
case ACPI_RESOURCE_TYPE_END_DEPENDENT:
188
/* We are finished with DPF parsing. */
189
KASSERT(req->in_dpf != DPF_OUTSIDE,
190
("%s: end dpf when not parsing a dpf", __func__));
191
req->in_dpf = DPF_OUTSIDE;
192
break;
193
case ACPI_RESOURCE_TYPE_IRQ:
194
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
195
/*
196
* Don't count resources if we are in a DPF set that we are
197
* ignoring.
198
*/
199
if (req->in_dpf != DPF_IGNORE)
200
req->count++;
201
}
202
return (AE_OK);
203
}
204
205
static ACPI_STATUS
206
link_add_crs(ACPI_RESOURCE *res, void *context)
207
{
208
struct link_res_request *req;
209
struct link *link;
210
211
ACPI_SERIAL_ASSERT(pci_link);
212
req = (struct link_res_request *)context;
213
switch (res->Type) {
214
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
215
switch (req->in_dpf) {
216
case DPF_OUTSIDE:
217
/* We've started the first DPF. */
218
req->in_dpf = DPF_FIRST;
219
break;
220
case DPF_FIRST:
221
/* We've started the second DPF. */
222
panic(
223
"%s: Multiple dependent functions within a current resource",
224
__func__);
225
break;
226
}
227
break;
228
case ACPI_RESOURCE_TYPE_END_DEPENDENT:
229
/* We are finished with DPF parsing. */
230
KASSERT(req->in_dpf != DPF_OUTSIDE,
231
("%s: end dpf when not parsing a dpf", __func__));
232
req->in_dpf = DPF_OUTSIDE;
233
break;
234
case ACPI_RESOURCE_TYPE_IRQ:
235
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
236
KASSERT(req->link_index < req->sc->pl_num_links,
237
("%s: array boundary violation", __func__));
238
link = &req->sc->pl_links[req->link_index];
239
link->l_res_index = req->res_index;
240
link->l_crs_type = res->Type;
241
req->link_index++;
242
req->res_index++;
243
244
/*
245
* Only use the current value if there's one IRQ. Some
246
* systems return multiple IRQs (which is nonsense for _CRS)
247
* when the link hasn't been programmed.
248
*/
249
if (res->Type == ACPI_RESOURCE_TYPE_IRQ) {
250
if (res->Data.Irq.InterruptCount == 1)
251
link->l_irq = res->Data.Irq.Interrupts[0];
252
} else if (res->Data.ExtendedIrq.InterruptCount == 1)
253
link->l_irq = res->Data.ExtendedIrq.Interrupts[0];
254
255
/*
256
* An IRQ of zero means that the link isn't routed.
257
*/
258
if (link->l_irq == 0)
259
link->l_irq = PCI_INVALID_IRQ;
260
break;
261
default:
262
req->res_index++;
263
}
264
return (AE_OK);
265
}
266
267
/*
268
* Populate the set of possible IRQs for each device.
269
*/
270
static ACPI_STATUS
271
link_add_prs(ACPI_RESOURCE *res, void *context)
272
{
273
ACPI_RESOURCE *tmp;
274
struct link_res_request *req;
275
struct link *link;
276
UINT8 *irqs = NULL;
277
UINT32 *ext_irqs = NULL;
278
int i, is_ext_irq = 1;
279
280
ACPI_SERIAL_ASSERT(pci_link);
281
req = (struct link_res_request *)context;
282
switch (res->Type) {
283
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
284
switch (req->in_dpf) {
285
case DPF_OUTSIDE:
286
/* We've started the first DPF. */
287
req->in_dpf = DPF_FIRST;
288
break;
289
case DPF_FIRST:
290
/* We've started the second DPF. */
291
req->in_dpf = DPF_IGNORE;
292
break;
293
}
294
break;
295
case ACPI_RESOURCE_TYPE_END_DEPENDENT:
296
/* We are finished with DPF parsing. */
297
KASSERT(req->in_dpf != DPF_OUTSIDE,
298
("%s: end dpf when not parsing a dpf", __func__));
299
req->in_dpf = DPF_OUTSIDE;
300
break;
301
case ACPI_RESOURCE_TYPE_IRQ:
302
is_ext_irq = 0;
303
/* fall through */
304
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
305
/*
306
* Don't parse resources if we are in a DPF set that we are
307
* ignoring.
308
*/
309
if (req->in_dpf == DPF_IGNORE)
310
break;
311
312
KASSERT(req->link_index < req->sc->pl_num_links,
313
("%s: array boundary violation", __func__));
314
link = &req->sc->pl_links[req->link_index];
315
if (link->l_res_index == -1) {
316
KASSERT(req->sc->pl_crs_bad,
317
("res_index should be set"));
318
link->l_res_index = req->res_index;
319
}
320
req->link_index++;
321
req->res_index++;
322
323
/*
324
* Stash a copy of the resource for later use when doing
325
* _SRS.
326
*/
327
tmp = &link->l_prs_template;
328
if (is_ext_irq) {
329
bcopy(res, tmp, ACPI_RS_SIZE(tmp->Data.ExtendedIrq));
330
331
/*
332
* XXX acpi_AppendBufferResource() cannot handle
333
* optional data.
334
*/
335
bzero(&tmp->Data.ExtendedIrq.ResourceSource,
336
sizeof(tmp->Data.ExtendedIrq.ResourceSource));
337
tmp->Length = ACPI_RS_SIZE(tmp->Data.ExtendedIrq);
338
339
link->l_num_irqs =
340
res->Data.ExtendedIrq.InterruptCount;
341
ext_irqs = res->Data.ExtendedIrq.Interrupts;
342
} else {
343
bcopy(res, tmp, ACPI_RS_SIZE(tmp->Data.Irq));
344
link->l_num_irqs = res->Data.Irq.InterruptCount;
345
irqs = res->Data.Irq.Interrupts;
346
}
347
if (link->l_num_irqs == 0)
348
break;
349
350
/*
351
* Save a list of the valid IRQs. Also, if all of the
352
* valid IRQs are ISA IRQs, then mark this link as
353
* routed via an ISA interrupt.
354
*/
355
link->l_isa_irq = true;
356
link->l_irqs = malloc(sizeof(int) * link->l_num_irqs,
357
M_PCI_LINK, M_WAITOK | M_ZERO);
358
for (i = 0; i < link->l_num_irqs; i++) {
359
if (is_ext_irq) {
360
link->l_irqs[i] = ext_irqs[i];
361
if (ext_irqs[i] >= NUM_ISA_INTERRUPTS)
362
link->l_isa_irq = false;
363
} else {
364
link->l_irqs[i] = irqs[i];
365
if (irqs[i] >= NUM_ISA_INTERRUPTS)
366
link->l_isa_irq = false;
367
}
368
}
369
370
/*
371
* If this is not an ISA IRQ but _CRS used a non-extended
372
* IRQ descriptor, don't use _CRS as a template for _SRS.
373
*/
374
if (!req->sc->pl_crs_bad && !link->l_isa_irq &&
375
link->l_crs_type == ACPI_RESOURCE_TYPE_IRQ)
376
req->sc->pl_crs_bad = true;
377
break;
378
default:
379
if (req->in_dpf == DPF_IGNORE)
380
break;
381
if (req->sc->pl_crs_bad)
382
device_printf(req->sc->pl_dev,
383
"Warning: possible resource %d will be lost during _SRS\n",
384
req->res_index);
385
req->res_index++;
386
}
387
return (AE_OK);
388
}
389
390
static bool
391
link_valid_irq(struct link *link, int irq)
392
{
393
int i;
394
395
ACPI_SERIAL_ASSERT(pci_link);
396
397
/* Invalid interrupts are never valid. */
398
if (!PCI_INTERRUPT_VALID(irq))
399
return (false);
400
401
/* Any interrupt in the list of possible interrupts is valid. */
402
for (i = 0; i < link->l_num_irqs; i++)
403
if (link->l_irqs[i] == irq)
404
return (true);
405
406
/*
407
* For links routed via an ISA interrupt, if the SCI is routed via
408
* an ISA interrupt, the SCI is always treated as a valid IRQ.
409
*/
410
if (link->l_isa_irq && AcpiGbl_FADT.SciInterrupt == irq &&
411
irq < NUM_ISA_INTERRUPTS)
412
return (true);
413
414
/* If the interrupt wasn't found in the list it is not valid. */
415
return (false);
416
}
417
418
static void
419
acpi_pci_link_dump(struct acpi_pci_link_softc *sc, int header, const char *tag)
420
{
421
struct link *link;
422
char buf[16];
423
int i, j;
424
425
ACPI_SERIAL_ASSERT(pci_link);
426
if (header) {
427
snprintf(buf, sizeof(buf), "%s:",
428
device_get_nameunit(sc->pl_dev));
429
printf("%-16.16s Index IRQ Rtd Ref IRQs\n", buf);
430
}
431
for (i = 0; i < sc->pl_num_links; i++) {
432
link = &sc->pl_links[i];
433
printf(" %-14.14s %5d %3d %c %3d ", i == 0 ? tag : "", i,
434
link->l_irq, link->l_routed ? 'Y' : 'N',
435
link->l_references);
436
if (link->l_num_irqs == 0)
437
printf(" none");
438
else for (j = 0; j < link->l_num_irqs; j++)
439
printf(" %d", link->l_irqs[j]);
440
printf("\n");
441
}
442
}
443
444
static int
445
acpi_pci_link_attach(device_t dev)
446
{
447
struct acpi_pci_link_softc *sc;
448
struct link_count_request creq;
449
struct link_res_request rreq;
450
ACPI_STATUS status;
451
int i;
452
453
sc = device_get_softc(dev);
454
sc->pl_dev = dev;
455
ACPI_SERIAL_BEGIN(pci_link);
456
457
/*
458
* Count the number of current resources so we know how big of
459
* a link array to allocate. On some systems, _CRS is broken,
460
* so for those systems try to derive the count from _PRS instead.
461
*/
462
creq.in_dpf = DPF_OUTSIDE;
463
creq.count = 0;
464
status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
465
acpi_count_irq_resources, &creq);
466
sc->pl_crs_bad = ACPI_FAILURE(status);
467
if (sc->pl_crs_bad) {
468
creq.in_dpf = DPF_OUTSIDE;
469
creq.count = 0;
470
status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
471
acpi_count_irq_resources, &creq);
472
if (ACPI_FAILURE(status)) {
473
device_printf(dev,
474
"Unable to parse _CRS or _PRS: %s\n",
475
AcpiFormatException(status));
476
ACPI_SERIAL_END(pci_link);
477
return (ENXIO);
478
}
479
}
480
sc->pl_num_links = creq.count;
481
if (creq.count == 0) {
482
ACPI_SERIAL_END(pci_link);
483
return (0);
484
}
485
sc->pl_links = malloc(sizeof(struct link) * sc->pl_num_links,
486
M_PCI_LINK, M_WAITOK | M_ZERO);
487
488
/* Initialize the child links. */
489
for (i = 0; i < sc->pl_num_links; i++) {
490
sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
491
sc->pl_links[i].l_bios_irq = PCI_INVALID_IRQ;
492
sc->pl_links[i].l_sc = sc;
493
sc->pl_links[i].l_isa_irq = false;
494
sc->pl_links[i].l_res_index = -1;
495
}
496
497
/* Try to read the current settings from _CRS if it is valid. */
498
if (!sc->pl_crs_bad) {
499
rreq.in_dpf = DPF_OUTSIDE;
500
rreq.link_index = 0;
501
rreq.res_index = 0;
502
rreq.sc = sc;
503
status = AcpiWalkResources(acpi_get_handle(dev), "_CRS",
504
link_add_crs, &rreq);
505
if (ACPI_FAILURE(status)) {
506
device_printf(dev, "Unable to parse _CRS: %s\n",
507
AcpiFormatException(status));
508
goto fail;
509
}
510
}
511
512
/*
513
* Try to read the possible settings from _PRS. Note that if the
514
* _CRS is toast, we depend on having a working _PRS. However, if
515
* _CRS works, then it is ok for _PRS to be missing.
516
*/
517
rreq.in_dpf = DPF_OUTSIDE;
518
rreq.link_index = 0;
519
rreq.res_index = 0;
520
rreq.sc = sc;
521
status = AcpiWalkResources(acpi_get_handle(dev), "_PRS",
522
link_add_prs, &rreq);
523
if (ACPI_FAILURE(status) &&
524
(status != AE_NOT_FOUND || sc->pl_crs_bad)) {
525
device_printf(dev, "Unable to parse _PRS: %s\n",
526
AcpiFormatException(status));
527
goto fail;
528
}
529
if (bootverbose)
530
acpi_pci_link_dump(sc, 1, "Initial Probe");
531
532
/* Verify initial IRQs if we have _PRS. */
533
if (status != AE_NOT_FOUND)
534
for (i = 0; i < sc->pl_num_links; i++)
535
if (!link_valid_irq(&sc->pl_links[i],
536
sc->pl_links[i].l_irq))
537
sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
538
if (bootverbose)
539
acpi_pci_link_dump(sc, 0, "Validation");
540
541
/* Save initial IRQs. */
542
for (i = 0; i < sc->pl_num_links; i++)
543
sc->pl_links[i].l_initial_irq = sc->pl_links[i].l_irq;
544
545
/*
546
* Try to disable this link. If successful, set the current IRQ to
547
* zero and flags to indicate this link is not routed. If we can't
548
* run _DIS (i.e., the method doesn't exist), assume the initial
549
* IRQ was routed by the BIOS.
550
*/
551
if (ACPI_SUCCESS(AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL,
552
NULL)))
553
for (i = 0; i < sc->pl_num_links; i++)
554
sc->pl_links[i].l_irq = PCI_INVALID_IRQ;
555
else
556
for (i = 0; i < sc->pl_num_links; i++)
557
if (PCI_INTERRUPT_VALID(sc->pl_links[i].l_irq))
558
sc->pl_links[i].l_routed = true;
559
if (bootverbose)
560
acpi_pci_link_dump(sc, 0, "After Disable");
561
ACPI_SERIAL_END(pci_link);
562
return (0);
563
fail:
564
ACPI_SERIAL_END(pci_link);
565
for (i = 0; i < sc->pl_num_links; i++)
566
if (sc->pl_links[i].l_irqs != NULL)
567
free(sc->pl_links[i].l_irqs, M_PCI_LINK);
568
free(sc->pl_links, M_PCI_LINK);
569
return (ENXIO);
570
}
571
572
/* XXX: Note that this is identical to pci_pir_search_irq(). */
573
static uint8_t
574
acpi_pci_link_search_irq(int domain, int bus, int device, int pin)
575
{
576
uint32_t value;
577
uint8_t func, maxfunc;
578
579
/* See if we have a valid device at function 0. */
580
value = pci_cfgregread(domain, bus, device, 0, PCIR_VENDOR, 2);
581
if (value == PCIV_INVALID)
582
return (PCI_INVALID_IRQ);
583
value = pci_cfgregread(domain, bus, device, 0, PCIR_HDRTYPE, 1);
584
if ((value & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
585
return (PCI_INVALID_IRQ);
586
if (value & PCIM_MFDEV)
587
maxfunc = PCI_FUNCMAX;
588
else
589
maxfunc = 0;
590
591
/* Scan all possible functions at this device. */
592
for (func = 0; func <= maxfunc; func++) {
593
value = pci_cfgregread(domain, bus, device, func, PCIR_VENDOR,
594
2);
595
if (value == PCIV_INVALID)
596
continue;
597
value = pci_cfgregread(domain, bus, device, func, PCIR_INTPIN,
598
1);
599
600
/*
601
* See if it uses the pin in question. Note that the passed
602
* in pin uses 0 for A, .. 3 for D whereas the intpin
603
* register uses 0 for no interrupt, 1 for A, .. 4 for D.
604
*/
605
if (value != pin + 1)
606
continue;
607
value = pci_cfgregread(domain, bus, device, func, PCIR_INTLINE,
608
1);
609
if (bootverbose)
610
printf(
611
"ACPI: Found matching pin for %d.%d.INT%c at func %d: %d\n",
612
bus, device, pin + 'A', func, value);
613
if (value != PCI_INVALID_IRQ)
614
return (value);
615
}
616
return (PCI_INVALID_IRQ);
617
}
618
619
/*
620
* Find the link structure that corresponds to the resource index passed in
621
* via 'source_index'.
622
*/
623
static struct link *
624
acpi_pci_link_lookup(device_t dev, int source_index)
625
{
626
struct acpi_pci_link_softc *sc;
627
int i;
628
629
ACPI_SERIAL_ASSERT(pci_link);
630
sc = device_get_softc(dev);
631
for (i = 0; i < sc->pl_num_links; i++)
632
if (sc->pl_links[i].l_res_index == source_index)
633
return (&sc->pl_links[i]);
634
return (NULL);
635
}
636
637
void
638
acpi_pci_link_add_reference(device_t dev, int index, device_t pcib, int slot,
639
int pin)
640
{
641
struct link *link;
642
uint8_t bios_irq;
643
uintptr_t bus, domain;
644
645
/*
646
* Look up the PCI domain and bus for the specified PCI bridge
647
* device. Note that the PCI bridge device might not have any
648
* children yet. However, looking up these IVARs doesn't
649
* require a valid child device, so we just pass NULL.
650
*/
651
if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_BUS, &bus) != 0) {
652
device_printf(pcib, "Unable to read PCI bus number");
653
panic("PCI bridge without a bus number");
654
}
655
if (BUS_READ_IVAR(pcib, NULL, PCIB_IVAR_DOMAIN, &domain) != 0) {
656
device_printf(pcib, "Unable to read PCI domain number");
657
panic("PCI bridge without a domain number");
658
}
659
660
/* Bump the reference count. */
661
ACPI_SERIAL_BEGIN(pci_link);
662
link = acpi_pci_link_lookup(dev, index);
663
if (link == NULL) {
664
device_printf(dev, "apparently invalid index %d\n", index);
665
ACPI_SERIAL_END(pci_link);
666
return;
667
}
668
link->l_references++;
669
if (link->l_routed)
670
pci_link_interrupt_weights[link->l_irq]++;
671
672
/*
673
* The BIOS only routes interrupts via ISA IRQs using the ATPICs
674
* (8259As). Thus, if this link is routed via an ISA IRQ, go
675
* look to see if the BIOS routed an IRQ for this link at the
676
* indicated (domain, bus, slot, pin). If so, we prefer that IRQ for
677
* this link and add that IRQ to our list of known-good IRQs.
678
* This provides a good work-around for link devices whose _CRS
679
* method is either broken or bogus. We only use the value
680
* returned by _CRS if we can't find a valid IRQ via this method
681
* in fact.
682
*
683
* If this link is not routed via an ISA IRQ (because we are using
684
* APIC for example), then don't bother looking up the BIOS IRQ
685
* as if we find one it won't be valid anyway.
686
*/
687
if (!link->l_isa_irq) {
688
ACPI_SERIAL_END(pci_link);
689
return;
690
}
691
692
/* Try to find a BIOS IRQ setting from any matching devices. */
693
bios_irq = acpi_pci_link_search_irq(domain, bus, slot, pin);
694
if (!PCI_INTERRUPT_VALID(bios_irq)) {
695
ACPI_SERIAL_END(pci_link);
696
return;
697
}
698
699
/* Validate the BIOS IRQ. */
700
if (!link_valid_irq(link, bios_irq)) {
701
device_printf(dev, "BIOS IRQ %u for %d.%d.INT%c is invalid\n",
702
bios_irq, (int)bus, slot, pin + 'A');
703
} else if (!PCI_INTERRUPT_VALID(link->l_bios_irq)) {
704
link->l_bios_irq = bios_irq;
705
if (bios_irq < NUM_ISA_INTERRUPTS)
706
pci_link_bios_isa_irqs |= (1 << bios_irq);
707
if (bios_irq != link->l_initial_irq &&
708
PCI_INTERRUPT_VALID(link->l_initial_irq))
709
device_printf(dev,
710
"BIOS IRQ %u does not match initial IRQ %u\n",
711
bios_irq, link->l_initial_irq);
712
} else if (bios_irq != link->l_bios_irq)
713
device_printf(dev,
714
"BIOS IRQ %u for %d.%d.INT%c does not match previous BIOS IRQ %u\n",
715
bios_irq, (int)bus, slot, pin + 'A',
716
link->l_bios_irq);
717
ACPI_SERIAL_END(pci_link);
718
}
719
720
static ACPI_STATUS
721
acpi_pci_link_srs_from_crs(struct acpi_pci_link_softc *sc, ACPI_BUFFER *srsbuf)
722
{
723
ACPI_RESOURCE *end, *res;
724
ACPI_STATUS status;
725
struct link *link;
726
int i __diagused, in_dpf;
727
728
/* Fetch the _CRS. */
729
ACPI_SERIAL_ASSERT(pci_link);
730
srsbuf->Pointer = NULL;
731
srsbuf->Length = ACPI_ALLOCATE_BUFFER;
732
status = AcpiGetCurrentResources(acpi_get_handle(sc->pl_dev), srsbuf);
733
if (ACPI_SUCCESS(status) && srsbuf->Pointer == NULL)
734
status = AE_NO_MEMORY;
735
if (ACPI_FAILURE(status)) {
736
if (bootverbose)
737
device_printf(sc->pl_dev,
738
"Unable to fetch current resources: %s\n",
739
AcpiFormatException(status));
740
return (status);
741
}
742
743
/* Fill in IRQ resources via link structures. */
744
link = sc->pl_links;
745
i = 0;
746
in_dpf = DPF_OUTSIDE;
747
res = (ACPI_RESOURCE *)srsbuf->Pointer;
748
end = (ACPI_RESOURCE *)((char *)srsbuf->Pointer + srsbuf->Length);
749
for (;;) {
750
switch (res->Type) {
751
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
752
switch (in_dpf) {
753
case DPF_OUTSIDE:
754
/* We've started the first DPF. */
755
in_dpf = DPF_FIRST;
756
break;
757
case DPF_FIRST:
758
/* We've started the second DPF. */
759
panic(
760
"%s: Multiple dependent functions within a current resource",
761
__func__);
762
break;
763
}
764
break;
765
case ACPI_RESOURCE_TYPE_END_DEPENDENT:
766
/* We are finished with DPF parsing. */
767
KASSERT(in_dpf != DPF_OUTSIDE,
768
("%s: end dpf when not parsing a dpf", __func__));
769
in_dpf = DPF_OUTSIDE;
770
break;
771
case ACPI_RESOURCE_TYPE_IRQ:
772
MPASS(i < sc->pl_num_links);
773
res->Data.Irq.InterruptCount = 1;
774
if (PCI_INTERRUPT_VALID(link->l_irq)) {
775
KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
776
("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
777
__func__, link->l_irq));
778
res->Data.Irq.Interrupts[0] = link->l_irq;
779
} else
780
res->Data.Irq.Interrupts[0] = 0;
781
link++;
782
i++;
783
break;
784
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
785
MPASS(i < sc->pl_num_links);
786
res->Data.ExtendedIrq.InterruptCount = 1;
787
if (PCI_INTERRUPT_VALID(link->l_irq))
788
res->Data.ExtendedIrq.Interrupts[0] =
789
link->l_irq;
790
else
791
res->Data.ExtendedIrq.Interrupts[0] = 0;
792
link++;
793
i++;
794
break;
795
}
796
if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
797
break;
798
res = ACPI_NEXT_RESOURCE(res);
799
if (res >= end)
800
break;
801
}
802
return (AE_OK);
803
}
804
805
static ACPI_STATUS
806
acpi_pci_link_srs_from_links(struct acpi_pci_link_softc *sc,
807
ACPI_BUFFER *srsbuf)
808
{
809
ACPI_RESOURCE newres;
810
ACPI_STATUS status;
811
struct link *link;
812
int i;
813
814
/* Start off with an empty buffer. */
815
srsbuf->Pointer = NULL;
816
link = sc->pl_links;
817
for (i = 0; i < sc->pl_num_links; i++) {
818
/* Add a new IRQ resource from each link. */
819
link = &sc->pl_links[i];
820
if (link->l_prs_template.Type == ACPI_RESOURCE_TYPE_IRQ) {
821
/* Build an IRQ resource. */
822
bcopy(&link->l_prs_template, &newres,
823
ACPI_RS_SIZE(newres.Data.Irq));
824
newres.Data.Irq.InterruptCount = 1;
825
if (PCI_INTERRUPT_VALID(link->l_irq)) {
826
KASSERT(link->l_irq < NUM_ISA_INTERRUPTS,
827
("%s: can't put non-ISA IRQ %d in legacy IRQ resource type",
828
__func__, link->l_irq));
829
newres.Data.Irq.Interrupts[0] = link->l_irq;
830
} else
831
newres.Data.Irq.Interrupts[0] = 0;
832
} else {
833
/* Build an ExtIRQ resuorce. */
834
bcopy(&link->l_prs_template, &newres,
835
ACPI_RS_SIZE(newres.Data.ExtendedIrq));
836
newres.Data.ExtendedIrq.InterruptCount = 1;
837
if (PCI_INTERRUPT_VALID(link->l_irq))
838
newres.Data.ExtendedIrq.Interrupts[0] =
839
link->l_irq;
840
else
841
newres.Data.ExtendedIrq.Interrupts[0] = 0;
842
}
843
844
/* Add the new resource to the end of the _SRS buffer. */
845
status = acpi_AppendBufferResource(srsbuf, &newres);
846
if (ACPI_FAILURE(status)) {
847
device_printf(sc->pl_dev,
848
"Unable to build resources: %s\n",
849
AcpiFormatException(status));
850
if (srsbuf->Pointer != NULL) {
851
AcpiOsFree(srsbuf->Pointer);
852
srsbuf->Pointer = NULL;
853
}
854
return (status);
855
}
856
}
857
return (AE_OK);
858
}
859
860
static ACPI_STATUS
861
acpi_pci_link_route_irqs(device_t dev)
862
{
863
struct acpi_pci_link_softc *sc;
864
ACPI_RESOURCE *resource, *end;
865
ACPI_BUFFER srsbuf;
866
ACPI_STATUS status;
867
struct link *link;
868
int i __diagused;
869
870
ACPI_SERIAL_ASSERT(pci_link);
871
sc = device_get_softc(dev);
872
if (sc->pl_crs_bad)
873
status = acpi_pci_link_srs_from_links(sc, &srsbuf);
874
else
875
status = acpi_pci_link_srs_from_crs(sc, &srsbuf);
876
if (ACPI_FAILURE(status))
877
return (status);
878
879
/* Write out new resources via _SRS. */
880
status = AcpiSetCurrentResources(acpi_get_handle(dev), &srsbuf);
881
if (ACPI_FAILURE(status)) {
882
device_printf(dev, "Unable to route IRQs: %s\n",
883
AcpiFormatException(status));
884
AcpiOsFree(srsbuf.Pointer);
885
return (status);
886
}
887
888
/*
889
* Perform acpi_config_intr() on each IRQ resource if it was just
890
* routed for the first time.
891
*/
892
link = sc->pl_links;
893
i = 0;
894
resource = (ACPI_RESOURCE *)srsbuf.Pointer;
895
end = (ACPI_RESOURCE *)((char *)srsbuf.Pointer + srsbuf.Length);
896
for (;;) {
897
if (resource->Type == ACPI_RESOURCE_TYPE_END_TAG)
898
break;
899
switch (resource->Type) {
900
case ACPI_RESOURCE_TYPE_IRQ:
901
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
902
MPASS(i < sc->pl_num_links);
903
904
/*
905
* Only configure the interrupt and update the
906
* weights if this link has a valid IRQ and was
907
* previously unrouted.
908
*/
909
if (!link->l_routed &&
910
PCI_INTERRUPT_VALID(link->l_irq)) {
911
link->l_routed = true;
912
acpi_config_intr(dev, resource);
913
pci_link_interrupt_weights[link->l_irq] +=
914
link->l_references;
915
}
916
link++;
917
i++;
918
break;
919
}
920
resource = ACPI_NEXT_RESOURCE(resource);
921
if (resource >= end)
922
break;
923
}
924
AcpiOsFree(srsbuf.Pointer);
925
return (AE_OK);
926
}
927
928
static int
929
acpi_pci_link_resume(device_t dev)
930
{
931
struct acpi_pci_link_softc *sc;
932
ACPI_STATUS status;
933
int i, routed;
934
935
/*
936
* If all of our links are routed, then restore the link via _SRS,
937
* otherwise, disable the link via _DIS.
938
*/
939
ACPI_SERIAL_BEGIN(pci_link);
940
sc = device_get_softc(dev);
941
routed = 0;
942
for (i = 0; i < sc->pl_num_links; i++)
943
if (sc->pl_links[i].l_routed)
944
routed++;
945
if (routed == sc->pl_num_links)
946
status = acpi_pci_link_route_irqs(dev);
947
else {
948
AcpiEvaluateObject(acpi_get_handle(dev), "_DIS", NULL, NULL);
949
status = AE_OK;
950
}
951
ACPI_SERIAL_END(pci_link);
952
if (ACPI_FAILURE(status))
953
return (ENXIO);
954
else
955
return (0);
956
}
957
958
/*
959
* Pick an IRQ to use for this unrouted link.
960
*/
961
static uint8_t
962
acpi_pci_link_choose_irq(device_t dev, struct link *link)
963
{
964
char tunable_buffer[64], link_name[5];
965
u_int8_t best_irq, pos_irq;
966
int best_weight, pos_weight, i;
967
968
KASSERT(!link->l_routed, ("%s: link already routed", __func__));
969
KASSERT(!PCI_INTERRUPT_VALID(link->l_irq),
970
("%s: link already has an IRQ", __func__));
971
972
/* Check for a tunable override. */
973
if (ACPI_SUCCESS(acpi_short_name(acpi_get_handle(dev), link_name,
974
sizeof(link_name)))) {
975
snprintf(tunable_buffer, sizeof(tunable_buffer),
976
"hw.pci.link.%s.%d.irq", link_name, link->l_res_index);
977
if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
978
if (!link_valid_irq(link, i))
979
device_printf(dev,
980
"Warning, IRQ %d is not listed as valid\n",
981
i);
982
return (i);
983
}
984
snprintf(tunable_buffer, sizeof(tunable_buffer),
985
"hw.pci.link.%s.irq", link_name);
986
if (getenv_int(tunable_buffer, &i) && PCI_INTERRUPT_VALID(i)) {
987
if (!link_valid_irq(link, i))
988
device_printf(dev,
989
"Warning, IRQ %d is not listed as valid\n",
990
i);
991
return (i);
992
}
993
}
994
995
/*
996
* If we have a valid BIOS IRQ, use that. We trust what the BIOS
997
* says it routed over what _CRS says the link thinks is routed.
998
*/
999
if (PCI_INTERRUPT_VALID(link->l_bios_irq))
1000
return (link->l_bios_irq);
1001
1002
/*
1003
* If we don't have a BIOS IRQ but do have a valid IRQ from _CRS,
1004
* then use that.
1005
*/
1006
if (PCI_INTERRUPT_VALID(link->l_initial_irq))
1007
return (link->l_initial_irq);
1008
1009
/*
1010
* Ok, we have no useful hints, so we have to pick from the
1011
* possible IRQs. For ISA IRQs we only use interrupts that
1012
* have already been used by the BIOS.
1013
*/
1014
best_irq = PCI_INVALID_IRQ;
1015
best_weight = INT_MAX;
1016
for (i = 0; i < link->l_num_irqs; i++) {
1017
pos_irq = link->l_irqs[i];
1018
if (pos_irq < NUM_ISA_INTERRUPTS &&
1019
(pci_link_bios_isa_irqs & 1 << pos_irq) == 0)
1020
continue;
1021
pos_weight = pci_link_interrupt_weights[pos_irq];
1022
if (pos_weight < best_weight) {
1023
best_weight = pos_weight;
1024
best_irq = pos_irq;
1025
}
1026
}
1027
1028
/*
1029
* If this is an ISA IRQ, try using the SCI if it is also an ISA
1030
* interrupt as a fallback.
1031
*/
1032
if (link->l_isa_irq) {
1033
pos_irq = AcpiGbl_FADT.SciInterrupt;
1034
pos_weight = pci_link_interrupt_weights[pos_irq];
1035
if (pos_weight < best_weight) {
1036
best_weight = pos_weight;
1037
best_irq = pos_irq;
1038
}
1039
}
1040
1041
if (PCI_INTERRUPT_VALID(best_irq)) {
1042
if (bootverbose)
1043
device_printf(dev, "Picked IRQ %u with weight %d\n",
1044
best_irq, best_weight);
1045
} else
1046
device_printf(dev, "Unable to choose an IRQ\n");
1047
return (best_irq);
1048
}
1049
1050
int
1051
acpi_pci_link_route_interrupt(device_t dev, int index)
1052
{
1053
struct link *link;
1054
1055
if (acpi_disabled("pci_link"))
1056
return (PCI_INVALID_IRQ);
1057
1058
ACPI_SERIAL_BEGIN(pci_link);
1059
link = acpi_pci_link_lookup(dev, index);
1060
if (link == NULL)
1061
panic("%s: apparently invalid index %d", __func__, index);
1062
1063
/*
1064
* If this link device is already routed to an interrupt, just return
1065
* the interrupt it is routed to.
1066
*/
1067
if (link->l_routed) {
1068
KASSERT(PCI_INTERRUPT_VALID(link->l_irq),
1069
("%s: link is routed but has an invalid IRQ", __func__));
1070
ACPI_SERIAL_END(pci_link);
1071
return (link->l_irq);
1072
}
1073
1074
/* Choose an IRQ if we need one. */
1075
if (!PCI_INTERRUPT_VALID(link->l_irq)) {
1076
link->l_irq = acpi_pci_link_choose_irq(dev, link);
1077
1078
/*
1079
* Try to route the interrupt we picked. If it fails, then
1080
* assume the interrupt is not routed.
1081
*/
1082
if (PCI_INTERRUPT_VALID(link->l_irq)) {
1083
acpi_pci_link_route_irqs(dev);
1084
if (!link->l_routed)
1085
link->l_irq = PCI_INVALID_IRQ;
1086
}
1087
}
1088
ACPI_SERIAL_END(pci_link);
1089
1090
return (link->l_irq);
1091
}
1092
1093
/*
1094
* This is gross, but we abuse the identify routine to perform one-time
1095
* SYSINIT() style initialization for the driver.
1096
*/
1097
static void
1098
acpi_pci_link_identify(driver_t *driver, device_t parent)
1099
{
1100
1101
/*
1102
* If the SCI is an ISA IRQ, add it to the bitmask of known good
1103
* ISA IRQs.
1104
*
1105
* XXX: If we are using the APIC, the SCI might have been
1106
* rerouted to an APIC pin in which case this is invalid. However,
1107
* if we are using the APIC, we also shouldn't be having any PCI
1108
* interrupts routed via ISA IRQs, so this is probably ok.
1109
*/
1110
if (AcpiGbl_FADT.SciInterrupt < NUM_ISA_INTERRUPTS)
1111
pci_link_bios_isa_irqs |= (1 << AcpiGbl_FADT.SciInterrupt);
1112
}
1113
1114
static device_method_t acpi_pci_link_methods[] = {
1115
/* Device interface */
1116
DEVMETHOD(device_identify, acpi_pci_link_identify),
1117
DEVMETHOD(device_probe, acpi_pci_link_probe),
1118
DEVMETHOD(device_attach, acpi_pci_link_attach),
1119
DEVMETHOD(device_resume, acpi_pci_link_resume),
1120
1121
DEVMETHOD_END
1122
};
1123
1124
static driver_t acpi_pci_link_driver = {
1125
"pci_link",
1126
acpi_pci_link_methods,
1127
sizeof(struct acpi_pci_link_softc),
1128
};
1129
1130
DRIVER_MODULE(acpi_pci_link, acpi, acpi_pci_link_driver, 0, 0);
1131
MODULE_DEPEND(acpi_pci_link, acpi, 1, 1, 1);
1132
1133