xref: /openbsd-src/sys/dev/acpi/acpiprt.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
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 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
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
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
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
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_STRING) {
276 		node = aml_searchrel(sc->sc_devnode, pp->v_string);
277 		if (node == NULL) {
278 			printf("Invalid device\n");
279 			return;
280 		}
281 		pp = node->value;
282 	}
283 	if (pp->type == AML_OBJTYPE_NAMEREF) {
284 		node = aml_searchrel(sc->sc_devnode, pp->v_nameref);
285 		if (node == NULL) {
286 			printf("Invalid device\n");
287 			return;
288 		}
289 		pp = node->value;
290 	}
291 	if (pp->type == AML_OBJTYPE_OBJREF) {
292 		pp = pp->v_objref.ref;
293 	}
294 	if (pp->type == AML_OBJTYPE_DEVICE) {
295 		node = pp->node;
296 		if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) {
297 			printf("no _STA method\n");
298 			return;
299 		}
300 
301 		if ((sta & STA_PRESENT) == 0)
302 			return;
303 
304 		if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
305 			printf("no _CRS method\n");
306 			return;
307 		}
308 
309 		if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
310 			printf("invalid _CRS object\n");
311 			aml_freevalue(&res);
312 			return;
313 		}
314 		aml_parse_resource(&res, acpiprt_getirq, &irq);
315 		aml_freevalue(&res);
316 
317 		/* Pick a new IRQ if necessary. */
318 		if ((irq._int == 0 || irq._int == 2 || irq._int == 13) &&
319 		    !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){
320 			aml_parse_resource(&res, acpiprt_chooseirq, &irq);
321 			aml_freevalue(&res);
322 		}
323 
324 		if ((p = malloc(sizeof(*p), M_ACPI, M_NOWAIT)) == NULL)
325 			return;
326 		p->bus = sc->sc_bus;
327 		p->dev = ACPI_PCI_DEV(addr << 16);
328 		p->pin = pin;
329 		p->irq = irq._int;
330 		p->sc = sc;
331 		p->node = node;
332 		SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list);
333 	} else {
334 		irq._int = aml_val2int(v->v_package[3]);
335 		irq._shr = 1;
336 		irq._ll = 1;
337 		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 	if (nioapics > 0) {
347 		apic = ioapic_find_bybase(irq._int);
348 		if (apic == NULL) {
349 			printf("%s: no apic found for irq %d\n",
350 			    DEVNAME(sc), irq._int);
351 			return;
352 		}
353 
354 		map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
355 		if (map == NULL)
356 			return;
357 
358 		map->ioapic = apic;
359 		map->ioapic_pin = irq._int - apic->sc_apic_vecbase;
360 		map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3);
361 		if (irq._ll)
362 			map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT);
363 		else
364 			map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT);
365 		if (irq._he)
366 			map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT);
367 		else
368 			map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT);
369 
370 		map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
371 		switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) {
372 		case MPS_INTPO_DEF:
373 		case MPS_INTPO_ACTLO:
374 			map->redir |= IOAPIC_REDLO_ACTLO;
375 			break;
376 		}
377 		switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) {
378 		case MPS_INTTR_DEF:
379 		case MPS_INTTR_LEVEL:
380 			map->redir |= IOAPIC_REDLO_LEVEL;
381 			break;
382 		}
383 
384 		map->ioapic_ih = APIC_INT_VIA_APIC |
385 		    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
386 		    (map->ioapic_pin << APIC_INT_PIN_SHIFT));
387 
388 		apic->sc_pins[map->ioapic_pin].ip_map = map;
389 
390 		map->next = mp_busses[sc->sc_bus].mb_intrs;
391 		mp_busses[sc->sc_bus].mb_intrs = map;
392 
393 		return;
394 	}
395 #endif
396 
397 	bus = sc->sc_bus;
398 	dev = ACPI_PCI_DEV(addr << 16);
399 	tag = pci_make_tag(pc, bus, dev, 0);
400 
401 	reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
402 	if (PCI_HDRTYPE_MULTIFN(reg))
403 		nfuncs = 8;
404 	else
405 		nfuncs = 1;
406 
407 	for (func = 0; func < nfuncs; func++) {
408 		tag = pci_make_tag(pc, bus, dev, func);
409 		reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
410 		if (PCI_INTERRUPT_PIN(reg) == pin + 1) {
411 			reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
412 			reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT;
413 			pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg);
414 		}
415 	}
416 }
417 
418 int
419 acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node)
420 {
421 	/* Check if parent device has PCI mapping */
422 	return (node->parent && node->parent->pci) ?
423 		node->parent->pci->sub : -1;
424 }
425 
426 void
427 acpiprt_route_interrupt(int bus, int dev, int pin)
428 {
429 	struct acpiprt_softc *sc;
430 	struct acpiprt_map *p;
431 	struct acpiprt_irq irq;
432 	struct aml_node *node = NULL;
433 	struct aml_value res, res2;
434 	union acpi_resource *crs;
435 	int newirq;
436 	int64_t sta;
437 
438 	SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) {
439 		if (p->bus == bus && p->dev == dev && p->pin == (pin - 1)) {
440 			newirq = p->irq;
441 			sc = p->sc;
442 			node = p->node;
443 			break;
444 		}
445 	}
446 	if (node == NULL)
447 		return;
448 
449 	if (aml_evalinteger(sc->sc_acpi, node, "_STA", 0, NULL, &sta)) {
450 		printf("no _STA method\n");
451 		return;
452 	}
453 
454 	KASSERT(sta & STA_PRESENT);
455 
456 	if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
457 		printf("no _CRS method\n");
458 		return;
459 	}
460 	if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
461 		printf("invalid _CRS object\n");
462 		aml_freevalue(&res);
463 		return;
464 	}
465 	aml_parse_resource(&res, acpiprt_getirq, &irq);
466 
467 	/* Only re-route interrupts when necessary. */
468 	if ((sta & STA_ENABLED) && irq._int == newirq) {
469 		aml_freevalue(&res);
470 		return;
471 	}
472 
473 	crs = (union acpi_resource *)res.v_buffer;
474 	switch (AML_CRSTYPE(crs)) {
475 	case SR_IRQ:
476 		crs->sr_irq.irq_mask = htole16(1 << newirq);
477 		break;
478 	case LR_EXTIRQ:
479 		crs->lr_extirq.irq[0] = htole32(newirq);
480 		break;
481 	}
482 
483 	if (aml_evalname(sc->sc_acpi, node, "_SRS", 1, &res, &res2)) {
484 		printf("no _SRS method\n");
485 		aml_freevalue(&res);
486 		return;
487 	}
488 	aml_freevalue(&res);
489 	aml_freevalue(&res2);
490 }
491