Line data Source code
1 : /* $OpenBSD: acpiprt.c,v 1.48 2016/10/25 06:48:58 pirofti 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 : 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
88 0 : acpiprt_match(struct device *parent, void *match, void *aux)
89 : {
90 0 : struct acpi_attach_args *aa = aux;
91 0 : struct cfdata *cf = match;
92 :
93 : /* sanity */
94 0 : if (aa->aaa_name == NULL ||
95 0 : strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
96 0 : aa->aaa_table != NULL)
97 0 : return (0);
98 :
99 0 : return (1);
100 0 : }
101 :
102 : void
103 0 : acpiprt_attach(struct device *parent, struct device *self, void *aux)
104 : {
105 0 : struct acpiprt_softc *sc = (struct acpiprt_softc *)self;
106 0 : struct acpi_attach_args *aa = aux;
107 0 : struct aml_value res;
108 : int i;
109 :
110 0 : sc->sc_acpi = (struct acpi_softc *)parent;
111 0 : sc->sc_devnode = aa->aaa_node;
112 0 : sc->sc_bus = acpiprt_getpcibus(sc, sc->sc_devnode);
113 0 : printf(": bus %d (%s)", sc->sc_bus, sc->sc_devnode->parent->name);
114 :
115 0 : if (sc->sc_bus == -1) {
116 0 : printf("\n");
117 0 : return;
118 : }
119 :
120 0 : if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res)) {
121 0 : printf(": no PCI interrupt routing table\n");
122 0 : return;
123 : }
124 :
125 0 : if (res.type != AML_OBJTYPE_PACKAGE) {
126 0 : printf(": _PRT is not a package\n");
127 0 : aml_freevalue(&res);
128 0 : return;
129 : }
130 :
131 0 : printf("\n");
132 :
133 0 : for (i = 0; i < res.length; i++)
134 0 : acpiprt_prt_add(sc, res.v_package[i]);
135 :
136 0 : aml_freevalue(&res);
137 0 : }
138 :
139 : int
140 0 : acpiprt_getirq(int crsidx, union acpi_resource *crs, void *arg)
141 : {
142 0 : struct acpiprt_irq *irq = arg;
143 : int typ, len;
144 :
145 0 : irq->_shr = 0;
146 0 : irq->_ll = 0;
147 0 : irq->_he = 1;
148 :
149 0 : typ = AML_CRSTYPE(crs);
150 0 : len = AML_CRSLEN(crs);
151 0 : switch (typ) {
152 : case SR_IRQ:
153 0 : irq->_int= ffs(letoh16(crs->sr_irq.irq_mask)) - 1;
154 0 : if (len > 2) {
155 0 : irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
156 0 : irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
157 0 : irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
158 0 : }
159 : break;
160 : case LR_EXTIRQ:
161 0 : irq->_int = letoh32(crs->lr_extirq.irq[0]);
162 0 : irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
163 0 : irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
164 0 : irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
165 0 : break;
166 : default:
167 0 : printf("unknown interrupt: %x\n", typ);
168 0 : }
169 0 : 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
193 0 : acpiprt_chooseirq(int crsidx, union acpi_resource *crs, void *arg)
194 : {
195 0 : struct acpiprt_irq *irq = arg;
196 : int typ, len, i, pri = -1;
197 :
198 0 : irq->_shr = 0;
199 0 : irq->_ll = 0;
200 0 : irq->_he = 1;
201 :
202 0 : typ = AML_CRSTYPE(crs);
203 0 : len = AML_CRSLEN(crs);
204 0 : switch (typ) {
205 : case SR_IRQ:
206 0 : for (i = 0; i < sizeof(crs->sr_irq.irq_mask) * 8; i++) {
207 0 : if (crs->sr_irq.irq_mask & (1 << i) &&
208 0 : acpiprt_pri[i] > pri) {
209 0 : irq->_int = i;
210 0 : pri = acpiprt_pri[irq->_int];
211 0 : }
212 : }
213 0 : if (len > 2) {
214 0 : irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
215 0 : irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
216 0 : irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
217 0 : }
218 : break;
219 : case LR_EXTIRQ:
220 : /* First try non-8259 interrupts. */
221 0 : for (i = 0; i < crs->lr_extirq.irq_count; i++) {
222 0 : if (crs->lr_extirq.irq[i] > 15) {
223 0 : irq->_int = crs->lr_extirq.irq[i];
224 0 : return (0);
225 : }
226 : }
227 :
228 0 : for (i = 0; i < crs->lr_extirq.irq_count; i++) {
229 0 : if (acpiprt_pri[crs->lr_extirq.irq[i]] > pri) {
230 0 : irq->_int = crs->lr_extirq.irq[i];
231 0 : pri = acpiprt_pri[irq->_int];
232 0 : }
233 : }
234 0 : irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
235 0 : irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
236 0 : irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
237 0 : break;
238 : default:
239 0 : printf("unknown interrupt: %x\n", typ);
240 0 : }
241 0 : return (0);
242 0 : }
243 :
244 : void
245 0 : acpiprt_prt_add(struct acpiprt_softc *sc, struct aml_value *v)
246 : {
247 : struct aml_node *node;
248 0 : struct aml_value res, *pp;
249 0 : struct acpiprt_irq irq;
250 : u_int64_t addr;
251 : int pin;
252 0 : 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 0 : if (v->type != AML_OBJTYPE_PACKAGE || v->length != 4) {
264 0 : printf("invalid mapping object\n");
265 0 : return;
266 : }
267 :
268 0 : addr = aml_val2int(v->v_package[0]);
269 0 : pin = aml_val2int(v->v_package[1]);
270 0 : if (pin > 3) {
271 0 : return;
272 : }
273 :
274 0 : pp = v->v_package[2];
275 0 : if (pp->type == AML_OBJTYPE_STRING) {
276 0 : node = aml_searchrel(sc->sc_devnode, pp->v_string);
277 0 : if (node == NULL) {
278 0 : printf("Invalid device\n");
279 0 : return;
280 : }
281 0 : pp = node->value;
282 0 : }
283 0 : if (pp->type == AML_OBJTYPE_NAMEREF) {
284 0 : node = aml_searchrel(sc->sc_devnode, pp->v_nameref);
285 0 : if (node == NULL) {
286 0 : printf("Invalid device\n");
287 0 : return;
288 : }
289 0 : pp = node->value;
290 0 : }
291 0 : if (pp->type == AML_OBJTYPE_OBJREF) {
292 0 : pp = pp->v_objref.ref;
293 0 : }
294 0 : if (pp->type == AML_OBJTYPE_DEVICE) {
295 0 : node = pp->node;
296 0 : if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) {
297 0 : printf("no _STA method\n");
298 0 : return;
299 : }
300 :
301 0 : if ((sta & STA_PRESENT) == 0)
302 0 : return;
303 :
304 0 : if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
305 0 : printf("no _CRS method\n");
306 0 : return;
307 : }
308 :
309 0 : if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
310 0 : printf("invalid _CRS object\n");
311 0 : aml_freevalue(&res);
312 0 : return;
313 : }
314 0 : aml_parse_resource(&res, acpiprt_getirq, &irq);
315 0 : aml_freevalue(&res);
316 :
317 : /* Pick a new IRQ if necessary. */
318 0 : if ((irq._int == 0 || irq._int == 2 || irq._int == 13) &&
319 0 : !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){
320 0 : aml_parse_resource(&res, acpiprt_chooseirq, &irq);
321 0 : aml_freevalue(&res);
322 0 : }
323 :
324 0 : if ((p = malloc(sizeof(*p), M_ACPI, M_NOWAIT)) == NULL)
325 0 : return;
326 0 : p->bus = sc->sc_bus;
327 0 : p->dev = ACPI_PCI_DEV(addr << 16);
328 0 : p->pin = pin;
329 0 : p->irq = irq._int;
330 0 : p->sc = sc;
331 0 : p->node = node;
332 0 : SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list);
333 0 : } else {
334 0 : irq._int = aml_val2int(v->v_package[3]);
335 0 : irq._shr = 1;
336 0 : irq._ll = 1;
337 0 : irq._he = 0;
338 : }
339 :
340 : #ifdef ACPI_DEBUG
341 : printf("%s: %s addr 0x%llx pin %d irq %d\n",
342 : DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq._int);
343 : #endif
344 :
345 : #if NIOAPIC > 0
346 0 : if (nioapics > 0) {
347 0 : apic = ioapic_find_bybase(irq._int);
348 0 : if (apic == NULL) {
349 0 : printf("%s: no apic found for irq %d\n",
350 0 : DEVNAME(sc), irq._int);
351 0 : return;
352 : }
353 :
354 0 : map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
355 0 : if (map == NULL)
356 0 : return;
357 :
358 0 : map->ioapic = apic;
359 0 : map->ioapic_pin = irq._int - apic->sc_apic_vecbase;
360 0 : map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3);
361 0 : if (irq._ll)
362 0 : map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT);
363 : else
364 0 : map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT);
365 0 : if (irq._he)
366 0 : map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT);
367 : else
368 0 : map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT);
369 :
370 0 : map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
371 0 : switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) {
372 : case MPS_INTPO_DEF:
373 : case MPS_INTPO_ACTLO:
374 0 : map->redir |= IOAPIC_REDLO_ACTLO;
375 0 : break;
376 : }
377 0 : switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) {
378 : case MPS_INTTR_DEF:
379 : case MPS_INTTR_LEVEL:
380 0 : map->redir |= IOAPIC_REDLO_LEVEL;
381 0 : break;
382 : }
383 :
384 0 : map->ioapic_ih = APIC_INT_VIA_APIC |
385 0 : ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
386 0 : (map->ioapic_pin << APIC_INT_PIN_SHIFT));
387 :
388 0 : apic->sc_pins[map->ioapic_pin].ip_map = map;
389 :
390 0 : map->next = mp_busses[sc->sc_bus].mb_intrs;
391 0 : mp_busses[sc->sc_bus].mb_intrs = map;
392 :
393 0 : return;
394 : }
395 : #endif
396 :
397 0 : bus = sc->sc_bus;
398 0 : dev = ACPI_PCI_DEV(addr << 16);
399 0 : tag = pci_make_tag(pc, bus, dev, 0);
400 :
401 0 : reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
402 0 : if (PCI_HDRTYPE_MULTIFN(reg))
403 0 : nfuncs = 8;
404 : else
405 : nfuncs = 1;
406 :
407 0 : for (func = 0; func < nfuncs; func++) {
408 0 : tag = pci_make_tag(pc, bus, dev, func);
409 0 : reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
410 0 : if (PCI_INTERRUPT_PIN(reg) == pin + 1) {
411 0 : reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
412 0 : reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT;
413 0 : pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg);
414 0 : }
415 : }
416 0 : }
417 :
418 : int
419 0 : acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node)
420 : {
421 : /* Check if parent device has PCI mapping */
422 0 : return (node->parent && node->parent->pci) ?
423 0 : node->parent->pci->sub : -1;
424 : }
425 :
426 : void
427 0 : acpiprt_route_interrupt(int bus, int dev, int pin)
428 : {
429 : struct acpiprt_softc *sc;
430 : struct acpiprt_map *p;
431 0 : struct acpiprt_irq irq;
432 : struct aml_node *node = NULL;
433 0 : struct aml_value res, res2;
434 : union acpi_resource *crs;
435 : int newirq;
436 0 : int64_t sta;
437 :
438 0 : SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) {
439 0 : if (p->bus == bus && p->dev == dev && p->pin == (pin - 1)) {
440 0 : newirq = p->irq;
441 0 : sc = p->sc;
442 0 : node = p->node;
443 0 : break;
444 : }
445 : }
446 0 : if (node == NULL)
447 0 : return;
448 :
449 0 : if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) {
450 0 : printf("no _STA method\n");
451 0 : return;
452 : }
453 :
454 0 : KASSERT(sta & STA_PRESENT);
455 :
456 0 : if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
457 0 : printf("no _CRS method\n");
458 0 : return;
459 : }
460 0 : if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
461 0 : printf("invalid _CRS object\n");
462 0 : aml_freevalue(&res);
463 0 : return;
464 : }
465 0 : aml_parse_resource(&res, acpiprt_getirq, &irq);
466 :
467 : /* Only re-route interrupts when necessary. */
468 0 : if ((sta & STA_ENABLED) && irq._int == newirq) {
469 0 : aml_freevalue(&res);
470 0 : return;
471 : }
472 :
473 0 : crs = (union acpi_resource *)res.v_buffer;
474 0 : switch (AML_CRSTYPE(crs)) {
475 : case SR_IRQ:
476 0 : crs->sr_irq.irq_mask = htole16(1 << newirq);
477 0 : break;
478 : case LR_EXTIRQ:
479 0 : crs->lr_extirq.irq[0] = htole32(newirq);
480 0 : break;
481 : }
482 :
483 0 : if (aml_evalname(sc->sc_acpi, node, "_SRS", 1, &res, &res2)) {
484 0 : printf("no _SRS method\n");
485 0 : aml_freevalue(&res);
486 0 : return;
487 : }
488 0 : aml_freevalue(&res);
489 0 : aml_freevalue(&res2);
490 0 : }
|