1 /* $OpenBSD: acpiprt.c,v 1.52 2022/04/06 18:59:27 naddy Exp $ */
2 /*
3 * Copyright (c) 2006 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/signalvar.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23
24 #include <machine/bus.h>
25
26 #include <dev/acpi/acpireg.h>
27 #include <dev/acpi/acpivar.h>
28 #include <dev/acpi/acpidev.h>
29 #include <dev/acpi/amltypes.h>
30 #include <dev/acpi/dsdt.h>
31
32 #include <dev/pci/pcivar.h>
33 #include <dev/pci/ppbreg.h>
34
35 #include <machine/i82093reg.h>
36 #include <machine/i82093var.h>
37
38 #include <machine/mpbiosvar.h>
39
40 #include "ioapic.h"
41
42 struct acpiprt_irq {
43 int _int;
44 int _shr;
45 int _ll;
46 int _he;
47 };
48
49 struct acpiprt_map {
50 int bus, dev;
51 int pin;
52 int irq;
53 struct acpiprt_softc *sc;
54 struct aml_node *node;
55 SIMPLEQ_ENTRY(acpiprt_map) list;
56 };
57
58 SIMPLEQ_HEAD(, acpiprt_map) acpiprt_map_list =
59 SIMPLEQ_HEAD_INITIALIZER(acpiprt_map_list);
60
61 int acpiprt_match(struct device *, void *, void *);
62 void acpiprt_attach(struct device *, struct device *, void *);
63 int acpiprt_getirq(int, union acpi_resource *, void *);
64 int acpiprt_chooseirq(int, union acpi_resource *, void *);
65
66 struct acpiprt_softc {
67 struct device sc_dev;
68
69 struct acpi_softc *sc_acpi;
70 struct aml_node *sc_devnode;
71
72 int sc_bus;
73 };
74
75 const struct cfattach acpiprt_ca = {
76 sizeof(struct acpiprt_softc), acpiprt_match, acpiprt_attach
77 };
78
79 struct cfdriver acpiprt_cd = {
80 NULL, "acpiprt", DV_DULL
81 };
82
83 void acpiprt_prt_add(struct acpiprt_softc *, struct aml_value *);
84 int acpiprt_getpcibus(struct acpiprt_softc *, struct aml_node *);
85 void acpiprt_route_interrupt(int bus, int dev, int pin);
86
87 int
acpiprt_match(struct device * parent,void * match,void * aux)88 acpiprt_match(struct device *parent, void *match, void *aux)
89 {
90 struct acpi_attach_args *aa = aux;
91 struct cfdata *cf = match;
92
93 /* sanity */
94 if (aa->aaa_name == NULL ||
95 strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
96 aa->aaa_table != NULL)
97 return (0);
98
99 return (1);
100 }
101
102 void
acpiprt_attach(struct device * parent,struct device * self,void * aux)103 acpiprt_attach(struct device *parent, struct device *self, void *aux)
104 {
105 struct acpiprt_softc *sc = (struct acpiprt_softc *)self;
106 struct acpi_attach_args *aa = aux;
107 struct aml_value res;
108 int i;
109
110 sc->sc_acpi = (struct acpi_softc *)parent;
111 sc->sc_devnode = aa->aaa_node;
112 sc->sc_bus = acpiprt_getpcibus(sc, sc->sc_devnode);
113 printf(": bus %d (%s)", sc->sc_bus, sc->sc_devnode->parent->name);
114
115 if (sc->sc_bus == -1) {
116 printf("\n");
117 return;
118 }
119
120 if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res)) {
121 printf(": no PCI interrupt routing table\n");
122 return;
123 }
124
125 if (res.type != AML_OBJTYPE_PACKAGE) {
126 printf(": _PRT is not a package\n");
127 aml_freevalue(&res);
128 return;
129 }
130
131 printf("\n");
132
133 for (i = 0; i < res.length; i++)
134 acpiprt_prt_add(sc, res.v_package[i]);
135
136 aml_freevalue(&res);
137 }
138
139 int
acpiprt_getirq(int crsidx,union acpi_resource * crs,void * arg)140 acpiprt_getirq(int crsidx, union acpi_resource *crs, void *arg)
141 {
142 struct acpiprt_irq *irq = arg;
143 int typ, len;
144
145 irq->_shr = 0;
146 irq->_ll = 0;
147 irq->_he = 1;
148
149 typ = AML_CRSTYPE(crs);
150 len = AML_CRSLEN(crs);
151 switch (typ) {
152 case SR_IRQ:
153 irq->_int= ffs(letoh16(crs->sr_irq.irq_mask)) - 1;
154 if (len > 2) {
155 irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
156 irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
157 irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
158 }
159 break;
160 case LR_EXTIRQ:
161 irq->_int = letoh32(crs->lr_extirq.irq[0]);
162 irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
163 irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
164 irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
165 break;
166 default:
167 printf("unknown interrupt: %x\n", typ);
168 }
169 return (0);
170 }
171
172 int
173 acpiprt_pri[16] = {
174 0, /* 8254 Counter 0 */
175 1, /* Keyboard */
176 0, /* 8259 Slave */
177 2, /* Serial Port A */
178 2, /* Serial Port B */
179 5, /* Parallel Port / Generic */
180 2, /* Floppy Disk */
181 4, /* Parallel Port / Generic */
182 1, /* RTC */
183 6, /* Generic */
184 7, /* Generic */
185 7, /* Generic */
186 1, /* Mouse */
187 0, /* FPU */
188 2, /* Primary IDE */
189 3 /* Secondary IDE */
190 };
191
192 int
acpiprt_chooseirq(int crsidx,union acpi_resource * crs,void * arg)193 acpiprt_chooseirq(int crsidx, union acpi_resource *crs, void *arg)
194 {
195 struct acpiprt_irq *irq = arg;
196 int typ, len, i, pri = -1;
197
198 irq->_shr = 0;
199 irq->_ll = 0;
200 irq->_he = 1;
201
202 typ = AML_CRSTYPE(crs);
203 len = AML_CRSLEN(crs);
204 switch (typ) {
205 case SR_IRQ:
206 for (i = 0; i < sizeof(crs->sr_irq.irq_mask) * 8; i++) {
207 if (crs->sr_irq.irq_mask & (1 << i) &&
208 acpiprt_pri[i] > pri) {
209 irq->_int = i;
210 pri = acpiprt_pri[irq->_int];
211 }
212 }
213 if (len > 2) {
214 irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
215 irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
216 irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
217 }
218 break;
219 case LR_EXTIRQ:
220 /* First try non-8259 interrupts. */
221 for (i = 0; i < crs->lr_extirq.irq_count; i++) {
222 if (crs->lr_extirq.irq[i] > 15) {
223 irq->_int = crs->lr_extirq.irq[i];
224 return (0);
225 }
226 }
227
228 for (i = 0; i < crs->lr_extirq.irq_count; i++) {
229 if (acpiprt_pri[crs->lr_extirq.irq[i]] > pri) {
230 irq->_int = crs->lr_extirq.irq[i];
231 pri = acpiprt_pri[irq->_int];
232 }
233 }
234 irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
235 irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
236 irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
237 break;
238 default:
239 printf("unknown interrupt: %x\n", typ);
240 }
241 return (0);
242 }
243
244 void
acpiprt_prt_add(struct acpiprt_softc * sc,struct aml_value * v)245 acpiprt_prt_add(struct acpiprt_softc *sc, struct aml_value *v)
246 {
247 struct aml_node *node;
248 struct aml_value res, *pp;
249 struct acpiprt_irq irq;
250 u_int64_t addr;
251 int pin;
252 int64_t sta;
253 #if NIOAPIC > 0
254 struct mp_intr_map *map;
255 struct ioapic_softc *apic;
256 #endif
257 pci_chipset_tag_t pc = NULL;
258 pcitag_t tag;
259 pcireg_t reg;
260 int bus, dev, func, nfuncs;
261 struct acpiprt_map *p;
262
263 if (v->type != AML_OBJTYPE_PACKAGE || v->length != 4) {
264 printf("invalid mapping object\n");
265 return;
266 }
267
268 addr = aml_val2int(v->v_package[0]);
269 pin = aml_val2int(v->v_package[1]);
270 if (pin > 3) {
271 return;
272 }
273
274 pp = v->v_package[2];
275 if (pp->type == AML_OBJTYPE_NAMEREF) {
276 node = aml_searchrel(sc->sc_devnode,
277 aml_getname(pp->v_nameref));
278 if (node == NULL) {
279 printf("Invalid device\n");
280 return;
281 }
282 pp = node->value;
283 }
284 if (pp->type == AML_OBJTYPE_OBJREF) {
285 pp = pp->v_objref.ref;
286 }
287 if (pp->type == AML_OBJTYPE_DEVICE) {
288 node = pp->node;
289
290 sta = acpi_getsta(sc->sc_acpi, node);
291 if ((sta & STA_PRESENT) == 0)
292 return;
293
294 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
295 printf("no _CRS method\n");
296 return;
297 }
298
299 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
300 printf("invalid _CRS object\n");
301 aml_freevalue(&res);
302 return;
303 }
304 aml_parse_resource(&res, acpiprt_getirq, &irq);
305 aml_freevalue(&res);
306
307 /* Pick a new IRQ if necessary. */
308 if ((irq._int == 0 || irq._int == 2 || irq._int == 13) &&
309 !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){
310 aml_parse_resource(&res, acpiprt_chooseirq, &irq);
311 aml_freevalue(&res);
312 }
313
314 if ((p = malloc(sizeof(*p), M_ACPI, M_NOWAIT)) == NULL)
315 return;
316 p->bus = sc->sc_bus;
317 p->dev = ACPI_PCI_DEV(addr << 16);
318 p->pin = pin;
319 p->irq = irq._int;
320 p->sc = sc;
321 p->node = node;
322 SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list);
323 } else {
324 irq._int = aml_val2int(v->v_package[3]);
325 irq._shr = 1;
326 irq._ll = 1;
327 irq._he = 0;
328 }
329
330 #ifdef ACPI_DEBUG
331 printf("%s: %s addr 0x%llx pin %d irq %d\n",
332 DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq._int);
333 #endif
334
335 #if NIOAPIC > 0
336 if (nioapics > 0) {
337 apic = ioapic_find_bybase(irq._int);
338 if (apic == NULL) {
339 printf("%s: no apic found for irq %d\n",
340 DEVNAME(sc), irq._int);
341 return;
342 }
343
344 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
345 if (map == NULL)
346 return;
347
348 map->ioapic = apic;
349 map->ioapic_pin = irq._int - apic->sc_apic_vecbase;
350 map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3);
351 if (irq._ll)
352 map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT);
353 else
354 map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT);
355 if (irq._he)
356 map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT);
357 else
358 map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT);
359
360 map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
361 switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) {
362 case MPS_INTPO_DEF:
363 case MPS_INTPO_ACTLO:
364 map->redir |= IOAPIC_REDLO_ACTLO;
365 break;
366 }
367 switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) {
368 case MPS_INTTR_DEF:
369 case MPS_INTTR_LEVEL:
370 map->redir |= IOAPIC_REDLO_LEVEL;
371 break;
372 }
373
374 map->ioapic_ih = APIC_INT_VIA_APIC |
375 ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
376 (map->ioapic_pin << APIC_INT_PIN_SHIFT));
377
378 apic->sc_pins[map->ioapic_pin].ip_map = map;
379
380 map->next = mp_busses[sc->sc_bus].mb_intrs;
381 mp_busses[sc->sc_bus].mb_intrs = map;
382
383 return;
384 }
385 #endif
386
387 bus = sc->sc_bus;
388 dev = ACPI_PCI_DEV(addr << 16);
389 tag = pci_make_tag(pc, bus, dev, 0);
390
391 reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
392 if (PCI_HDRTYPE_MULTIFN(reg))
393 nfuncs = 8;
394 else
395 nfuncs = 1;
396
397 for (func = 0; func < nfuncs; func++) {
398 tag = pci_make_tag(pc, bus, dev, func);
399 reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
400 if (PCI_INTERRUPT_PIN(reg) == pin + 1) {
401 reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
402 reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT;
403 pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg);
404 }
405 }
406 }
407
408 int
acpiprt_getpcibus(struct acpiprt_softc * sc,struct aml_node * node)409 acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node)
410 {
411 /* Check if parent device has PCI mapping */
412 return (node->parent && node->parent->pci) ?
413 node->parent->pci->sub : -1;
414 }
415
416 void
acpiprt_route_interrupt(int bus,int dev,int pin)417 acpiprt_route_interrupt(int bus, int dev, int pin)
418 {
419 struct acpiprt_softc *sc;
420 struct acpiprt_map *p;
421 struct acpiprt_irq irq;
422 struct aml_node *node = NULL;
423 struct aml_value res, res2;
424 union acpi_resource *crs;
425 int newirq;
426 int64_t sta;
427
428 SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) {
429 if (p->bus == bus && p->dev == dev && p->pin == (pin - 1)) {
430 newirq = p->irq;
431 sc = p->sc;
432 node = p->node;
433 break;
434 }
435 }
436 if (node == NULL)
437 return;
438
439 sta = acpi_getsta(sc->sc_acpi, node);
440 KASSERT(sta & STA_PRESENT);
441
442 if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
443 printf("no _CRS method\n");
444 return;
445 }
446 if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
447 printf("invalid _CRS object\n");
448 aml_freevalue(&res);
449 return;
450 }
451 aml_parse_resource(&res, acpiprt_getirq, &irq);
452
453 /* Only re-route interrupts when necessary. */
454 if ((sta & STA_ENABLED) && irq._int == newirq) {
455 aml_freevalue(&res);
456 return;
457 }
458
459 crs = (union acpi_resource *)res.v_buffer;
460 switch (AML_CRSTYPE(crs)) {
461 case SR_IRQ:
462 crs->sr_irq.irq_mask = htole16(1 << newirq);
463 break;
464 case LR_EXTIRQ:
465 crs->lr_extirq.irq[0] = htole32(newirq);
466 break;
467 }
468
469 if (aml_evalname(sc->sc_acpi, node, "_SRS", 1, &res, &res2)) {
470 printf("no _SRS method\n");
471 aml_freevalue(&res);
472 return;
473 }
474 aml_freevalue(&res);
475 aml_freevalue(&res2);
476 }
477