xref: /openbsd-src/sys/dev/acpi/acpiprt.c (revision 471aeecfc619bc9b69519928152daf993376c2a1)
1*471aeecfSnaddy /* $OpenBSD: acpiprt.c,v 1.52 2022/04/06 18:59:27 naddy Exp $ */
22d9ec9b4Skettenis /*
32d9ec9b4Skettenis  * Copyright (c) 2006 Mark Kettenis <kettenis@openbsd.org>
42d9ec9b4Skettenis  *
52d9ec9b4Skettenis  * Permission to use, copy, modify, and distribute this software for any
62d9ec9b4Skettenis  * purpose with or without fee is hereby granted, provided that the above
72d9ec9b4Skettenis  * copyright notice and this permission notice appear in all copies.
82d9ec9b4Skettenis  *
92d9ec9b4Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
102d9ec9b4Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
112d9ec9b4Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
122d9ec9b4Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
132d9ec9b4Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
142d9ec9b4Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
152d9ec9b4Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
162d9ec9b4Skettenis  */
172d9ec9b4Skettenis 
182d9ec9b4Skettenis #include <sys/param.h>
192d9ec9b4Skettenis #include <sys/signalvar.h>
202d9ec9b4Skettenis #include <sys/systm.h>
212d9ec9b4Skettenis #include <sys/device.h>
222d9ec9b4Skettenis #include <sys/malloc.h>
232d9ec9b4Skettenis 
242d9ec9b4Skettenis #include <machine/bus.h>
252d9ec9b4Skettenis 
262d9ec9b4Skettenis #include <dev/acpi/acpireg.h>
272d9ec9b4Skettenis #include <dev/acpi/acpivar.h>
282d9ec9b4Skettenis #include <dev/acpi/acpidev.h>
292d9ec9b4Skettenis #include <dev/acpi/amltypes.h>
302d9ec9b4Skettenis #include <dev/acpi/dsdt.h>
312d9ec9b4Skettenis 
322d9ec9b4Skettenis #include <dev/pci/pcivar.h>
332d9ec9b4Skettenis #include <dev/pci/ppbreg.h>
342d9ec9b4Skettenis 
352d9ec9b4Skettenis #include <machine/i82093reg.h>
362d9ec9b4Skettenis #include <machine/i82093var.h>
372d9ec9b4Skettenis 
382d9ec9b4Skettenis #include <machine/mpbiosvar.h>
392d9ec9b4Skettenis 
400b1e0221Skettenis #include "ioapic.h"
410b1e0221Skettenis 
428d56ca36Skettenis struct acpiprt_irq {
438d56ca36Skettenis 	int _int;
448d56ca36Skettenis 	int _shr;
458d56ca36Skettenis 	int _ll;
468d56ca36Skettenis 	int _he;
478d56ca36Skettenis };
488d56ca36Skettenis 
4949bbc65bSkettenis struct acpiprt_map {
5049bbc65bSkettenis 	int bus, dev;
5149bbc65bSkettenis 	int pin;
5249bbc65bSkettenis 	int irq;
5349bbc65bSkettenis 	struct acpiprt_softc *sc;
5449bbc65bSkettenis 	struct aml_node *node;
5549bbc65bSkettenis 	SIMPLEQ_ENTRY(acpiprt_map) list;
5649bbc65bSkettenis };
5749bbc65bSkettenis 
5849bbc65bSkettenis SIMPLEQ_HEAD(, acpiprt_map) acpiprt_map_list =
5949bbc65bSkettenis     SIMPLEQ_HEAD_INITIALIZER(acpiprt_map_list);
6049bbc65bSkettenis 
612d9ec9b4Skettenis int	acpiprt_match(struct device *, void *, void *);
622d9ec9b4Skettenis void	acpiprt_attach(struct device *, struct device *, void *);
63dd9a9f1cSpirofti int	acpiprt_getirq(int, union acpi_resource *, void *);
64dd9a9f1cSpirofti int	acpiprt_chooseirq(int, union acpi_resource *, void *);
652d9ec9b4Skettenis 
662d9ec9b4Skettenis struct acpiprt_softc {
672d9ec9b4Skettenis 	struct device		sc_dev;
682d9ec9b4Skettenis 
692d9ec9b4Skettenis 	struct acpi_softc	*sc_acpi;
702d9ec9b4Skettenis 	struct aml_node		*sc_devnode;
712d9ec9b4Skettenis 
722d9ec9b4Skettenis 	int			sc_bus;
732d9ec9b4Skettenis };
742d9ec9b4Skettenis 
75*471aeecfSnaddy const struct cfattach acpiprt_ca = {
762d9ec9b4Skettenis 	sizeof(struct acpiprt_softc), acpiprt_match, acpiprt_attach
772d9ec9b4Skettenis };
782d9ec9b4Skettenis 
792d9ec9b4Skettenis struct cfdriver acpiprt_cd = {
802d9ec9b4Skettenis 	NULL, "acpiprt", DV_DULL
812d9ec9b4Skettenis };
822d9ec9b4Skettenis 
832d9ec9b4Skettenis void	acpiprt_prt_add(struct acpiprt_softc *, struct aml_value *);
842d9ec9b4Skettenis int	acpiprt_getpcibus(struct acpiprt_softc *, struct aml_node *);
8549bbc65bSkettenis void	acpiprt_route_interrupt(int bus, int dev, int pin);
862d9ec9b4Skettenis 
872d9ec9b4Skettenis int
acpiprt_match(struct device * parent,void * match,void * aux)882d9ec9b4Skettenis acpiprt_match(struct device *parent, void *match, void *aux)
892d9ec9b4Skettenis {
902d9ec9b4Skettenis 	struct acpi_attach_args	*aa = aux;
912d9ec9b4Skettenis 	struct cfdata  *cf = match;
922d9ec9b4Skettenis 
932d9ec9b4Skettenis 	/* sanity */
942d9ec9b4Skettenis 	if (aa->aaa_name == NULL ||
952d9ec9b4Skettenis 	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
962d9ec9b4Skettenis 	    aa->aaa_table != NULL)
972d9ec9b4Skettenis 		return (0);
982d9ec9b4Skettenis 
992d9ec9b4Skettenis 	return (1);
1002d9ec9b4Skettenis }
1012d9ec9b4Skettenis 
1022d9ec9b4Skettenis void
acpiprt_attach(struct device * parent,struct device * self,void * aux)1032d9ec9b4Skettenis acpiprt_attach(struct device *parent, struct device *self, void *aux)
1042d9ec9b4Skettenis {
1052d9ec9b4Skettenis 	struct acpiprt_softc *sc = (struct acpiprt_softc *)self;
1062d9ec9b4Skettenis 	struct acpi_attach_args *aa = aux;
1072d9ec9b4Skettenis 	struct aml_value res;
108bd2a7025Skettenis 	int i;
1092d9ec9b4Skettenis 
1102d9ec9b4Skettenis 	sc->sc_acpi = (struct acpi_softc *)parent;
111e9d02a87Sjordan 	sc->sc_devnode = aa->aaa_node;
1122d9ec9b4Skettenis 	sc->sc_bus = acpiprt_getpcibus(sc, sc->sc_devnode);
1132d9ec9b4Skettenis 	printf(": bus %d (%s)", sc->sc_bus, sc->sc_devnode->parent->name);
1142d9ec9b4Skettenis 
115e0f3578aSkettenis 	if (sc->sc_bus == -1) {
116e0f3578aSkettenis 		printf("\n");
117e0f3578aSkettenis 		return;
118e0f3578aSkettenis 	}
119e0f3578aSkettenis 
12059ce1b19Skettenis 	if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res)) {
1212d9ec9b4Skettenis 		printf(": no PCI interrupt routing table\n");
1222d9ec9b4Skettenis 		return;
1232d9ec9b4Skettenis 	}
1242d9ec9b4Skettenis 
1252d9ec9b4Skettenis 	if (res.type != AML_OBJTYPE_PACKAGE) {
1262d9ec9b4Skettenis 		printf(": _PRT is not a package\n");
12704045085Smarco 		aml_freevalue(&res);
1282d9ec9b4Skettenis 		return;
1292d9ec9b4Skettenis 	}
1302d9ec9b4Skettenis 
1312d9ec9b4Skettenis 	printf("\n");
1322d9ec9b4Skettenis 
1332d9ec9b4Skettenis 	for (i = 0; i < res.length; i++)
1342d9ec9b4Skettenis 		acpiprt_prt_add(sc, res.v_package[i]);
13504045085Smarco 
13604045085Smarco 	aml_freevalue(&res);
1372d9ec9b4Skettenis }
1382d9ec9b4Skettenis 
1393b455a03Smarco int
acpiprt_getirq(int crsidx,union acpi_resource * crs,void * arg)140dd9a9f1cSpirofti acpiprt_getirq(int crsidx, union acpi_resource *crs, void *arg)
141e9d02a87Sjordan {
1428d56ca36Skettenis 	struct acpiprt_irq *irq = arg;
1438d56ca36Skettenis 	int typ, len;
1448d56ca36Skettenis 
1458d56ca36Skettenis 	irq->_shr = 0;
1468d56ca36Skettenis 	irq->_ll = 0;
1478d56ca36Skettenis 	irq->_he = 1;
148e9d02a87Sjordan 
149e9d02a87Sjordan 	typ = AML_CRSTYPE(crs);
1508d56ca36Skettenis 	len = AML_CRSLEN(crs);
151e9d02a87Sjordan 	switch (typ) {
152e9d02a87Sjordan 	case SR_IRQ:
1538d56ca36Skettenis 		irq->_int= ffs(letoh16(crs->sr_irq.irq_mask)) - 1;
1548d56ca36Skettenis 		if (len > 2) {
1558d56ca36Skettenis 			irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
1568d56ca36Skettenis 			irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
1578d56ca36Skettenis 			irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
1588d56ca36Skettenis 		}
159e9d02a87Sjordan 		break;
160e9d02a87Sjordan 	case LR_EXTIRQ:
1618d56ca36Skettenis 		irq->_int = letoh32(crs->lr_extirq.irq[0]);
1628d56ca36Skettenis 		irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
1638d56ca36Skettenis 		irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
1648d56ca36Skettenis 		irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
165e9d02a87Sjordan 		break;
166e9d02a87Sjordan 	default:
16749bbc65bSkettenis 		printf("unknown interrupt: %x\n", typ);
168e9d02a87Sjordan 	}
169e9d02a87Sjordan 	return (0);
170e9d02a87Sjordan }
171e9d02a87Sjordan 
17201e588f2Skettenis int
17301e588f2Skettenis acpiprt_pri[16] = {
17401e588f2Skettenis 	0,			/* 8254 Counter 0 */
17501e588f2Skettenis 	1,			/* Keyboard */
17601e588f2Skettenis 	0,			/* 8259 Slave */
17701e588f2Skettenis 	2,			/* Serial Port A */
17801e588f2Skettenis 	2,			/* Serial Port B */
17901e588f2Skettenis 	5,			/* Parallel Port / Generic */
18001e588f2Skettenis 	2,			/* Floppy Disk */
18101e588f2Skettenis 	4,			/* Parallel Port / Generic */
18201e588f2Skettenis 	1,			/* RTC */
18301e588f2Skettenis 	6,			/* Generic */
18401e588f2Skettenis 	7,			/* Generic */
18501e588f2Skettenis 	7,			/* Generic */
18601e588f2Skettenis 	1,			/* Mouse */
18701e588f2Skettenis 	0,			/* FPU */
18801e588f2Skettenis 	2,			/* Primary IDE */
18901e588f2Skettenis 	3			/* Secondary IDE */
19001e588f2Skettenis };
19101e588f2Skettenis 
19201e588f2Skettenis int
acpiprt_chooseirq(int crsidx,union acpi_resource * crs,void * arg)193dd9a9f1cSpirofti acpiprt_chooseirq(int crsidx, union acpi_resource *crs, void *arg)
19401e588f2Skettenis {
1958d56ca36Skettenis 	struct acpiprt_irq *irq = arg;
1968d56ca36Skettenis 	int typ, len, i, pri = -1;
1978d56ca36Skettenis 
1988d56ca36Skettenis 	irq->_shr = 0;
1998d56ca36Skettenis 	irq->_ll = 0;
2008d56ca36Skettenis 	irq->_he = 1;
20101e588f2Skettenis 
20201e588f2Skettenis 	typ = AML_CRSTYPE(crs);
2038d56ca36Skettenis 	len = AML_CRSLEN(crs);
20401e588f2Skettenis 	switch (typ) {
20501e588f2Skettenis 	case SR_IRQ:
20601e588f2Skettenis 		for (i = 0; i < sizeof(crs->sr_irq.irq_mask) * 8; i++) {
20701e588f2Skettenis 			if (crs->sr_irq.irq_mask & (1 << i) &&
20801e588f2Skettenis 			    acpiprt_pri[i] > pri) {
2098d56ca36Skettenis 				irq->_int = i;
2108d56ca36Skettenis 				pri = acpiprt_pri[irq->_int];
21101e588f2Skettenis 			}
21201e588f2Skettenis 		}
2138d56ca36Skettenis 		if (len > 2) {
2148d56ca36Skettenis 			irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
2158d56ca36Skettenis 			irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
2168d56ca36Skettenis 			irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
2178d56ca36Skettenis 		}
21801e588f2Skettenis 		break;
21901e588f2Skettenis 	case LR_EXTIRQ:
22001e588f2Skettenis 		/* First try non-8259 interrupts. */
22101e588f2Skettenis 		for (i = 0; i < crs->lr_extirq.irq_count; i++) {
22201e588f2Skettenis 			if (crs->lr_extirq.irq[i] > 15) {
2238d56ca36Skettenis 				irq->_int = crs->lr_extirq.irq[i];
22401e588f2Skettenis 				return (0);
22501e588f2Skettenis 			}
22601e588f2Skettenis 		}
22701e588f2Skettenis 
22801e588f2Skettenis 		for (i = 0; i < crs->lr_extirq.irq_count; i++) {
22901e588f2Skettenis 			if (acpiprt_pri[crs->lr_extirq.irq[i]] > pri) {
2308d56ca36Skettenis 				irq->_int = crs->lr_extirq.irq[i];
2318d56ca36Skettenis 				pri = acpiprt_pri[irq->_int];
23201e588f2Skettenis 			}
23301e588f2Skettenis 		}
2348d56ca36Skettenis 		irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
2358d56ca36Skettenis 		irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
2368d56ca36Skettenis 		irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
23701e588f2Skettenis 		break;
23801e588f2Skettenis 	default:
23901e588f2Skettenis 		printf("unknown interrupt: %x\n", typ);
24001e588f2Skettenis 	}
24101e588f2Skettenis 	return (0);
24201e588f2Skettenis }
24301e588f2Skettenis 
2442d9ec9b4Skettenis void
acpiprt_prt_add(struct acpiprt_softc * sc,struct aml_value * v)2452d9ec9b4Skettenis acpiprt_prt_add(struct acpiprt_softc *sc, struct aml_value *v)
2462d9ec9b4Skettenis {
2472d9ec9b4Skettenis 	struct aml_node	*node;
248e9d02a87Sjordan 	struct aml_value res, *pp;
2498d56ca36Skettenis 	struct acpiprt_irq irq;
2500b1e0221Skettenis 	u_int64_t addr;
2518d56ca36Skettenis 	int pin;
252a95d1b72Sjordan 	int64_t sta;
2530b1e0221Skettenis #if NIOAPIC > 0
2542d9ec9b4Skettenis 	struct mp_intr_map *map;
2552d9ec9b4Skettenis 	struct ioapic_softc *apic;
256ad5960baSkettenis #endif
2570b1e0221Skettenis 	pci_chipset_tag_t pc = NULL;
2580b1e0221Skettenis 	pcitag_t tag;
2590b1e0221Skettenis 	pcireg_t reg;
2600b1e0221Skettenis 	int bus, dev, func, nfuncs;
26149bbc65bSkettenis 	struct acpiprt_map *p;
2622d9ec9b4Skettenis 
2632d9ec9b4Skettenis 	if (v->type != AML_OBJTYPE_PACKAGE || v->length != 4) {
2642d9ec9b4Skettenis 		printf("invalid mapping object\n");
2652d9ec9b4Skettenis 		return;
2662d9ec9b4Skettenis 	}
2672d9ec9b4Skettenis 
2682d9ec9b4Skettenis 	addr = aml_val2int(v->v_package[0]);
2692d9ec9b4Skettenis 	pin = aml_val2int(v->v_package[1]);
270e9d02a87Sjordan 	if (pin > 3) {
2712d9ec9b4Skettenis 		return;
2722d9ec9b4Skettenis 	}
2732d9ec9b4Skettenis 
274e9d02a87Sjordan 	pp = v->v_package[2];
275e9d02a87Sjordan 	if (pp->type == AML_OBJTYPE_NAMEREF) {
276abe2aaccSpatrick 		node = aml_searchrel(sc->sc_devnode,
277abe2aaccSpatrick 		    aml_getname(pp->v_nameref));
278e9d02a87Sjordan 		if (node == NULL) {
2797c54c5b5Sderaadt 			printf("Invalid device\n");
280e9d02a87Sjordan 			return;
281e9d02a87Sjordan 		}
282e9d02a87Sjordan 		pp = node->value;
283e9d02a87Sjordan 	}
284e9d02a87Sjordan 	if (pp->type == AML_OBJTYPE_OBJREF) {
285e9d02a87Sjordan 		pp = pp->v_objref.ref;
286e9d02a87Sjordan 	}
287e9d02a87Sjordan 	if (pp->type == AML_OBJTYPE_DEVICE) {
288e9d02a87Sjordan 		node = pp->node;
28904045085Smarco 
290248de57fSkettenis 		sta = acpi_getsta(sc->sc_acpi, node);
2913861348dSmarco 		if ((sta & STA_PRESENT) == 0)
2921fc7e64aSkettenis 			return;
2932d9ec9b4Skettenis 
2943861348dSmarco 		if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
2952d9ec9b4Skettenis 			printf("no _CRS method\n");
2963861348dSmarco 			return;
2973861348dSmarco 		}
2982d9ec9b4Skettenis 
299396e7b9cSkettenis 		if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
3002d9ec9b4Skettenis 			printf("invalid _CRS object\n");
30104045085Smarco 			aml_freevalue(&res);
3022d9ec9b4Skettenis 			return;
3032d9ec9b4Skettenis 		}
304311c37bcSjordan 		aml_parse_resource(&res, acpiprt_getirq, &irq);
30504045085Smarco 		aml_freevalue(&res);
3063b455a03Smarco 
30766f49704Skettenis 		/* Pick a new IRQ if necessary. */
3088d56ca36Skettenis 		if ((irq._int == 0 || irq._int == 2 || irq._int == 13) &&
30966f49704Skettenis 		    !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){
310311c37bcSjordan 			aml_parse_resource(&res, acpiprt_chooseirq, &irq);
3113b455a03Smarco 			aml_freevalue(&res);
3123b455a03Smarco 		}
31349bbc65bSkettenis 
31449bbc65bSkettenis 		if ((p = malloc(sizeof(*p), M_ACPI, M_NOWAIT)) == NULL)
31549bbc65bSkettenis 			return;
31649bbc65bSkettenis 		p->bus = sc->sc_bus;
317fb31a265Sjordan 		p->dev = ACPI_PCI_DEV(addr << 16);
31849bbc65bSkettenis 		p->pin = pin;
3198d56ca36Skettenis 		p->irq = irq._int;
32049bbc65bSkettenis 		p->sc = sc;
32149bbc65bSkettenis 		p->node = node;
32249bbc65bSkettenis 		SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list);
323a835314aSkettenis 	} else {
3248d56ca36Skettenis 		irq._int = aml_val2int(v->v_package[3]);
3258d56ca36Skettenis 		irq._shr = 1;
3268d56ca36Skettenis 		irq._ll = 1;
3278d56ca36Skettenis 		irq._he = 0;
328e9d02a87Sjordan 	}
3290b1e0221Skettenis 
3300b1e0221Skettenis #ifdef ACPI_DEBUG
3311fc7e64aSkettenis 	printf("%s: %s addr 0x%llx pin %d irq %d\n",
3328d56ca36Skettenis 	    DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq._int);
3330b1e0221Skettenis #endif
3340b1e0221Skettenis 
3350b1e0221Skettenis #if NIOAPIC > 0
336ad5960baSkettenis 	if (nioapics > 0) {
3378d56ca36Skettenis 		apic = ioapic_find_bybase(irq._int);
33847a6fcc9Smarco 		if (apic == NULL) {
3398d56ca36Skettenis 			printf("%s: no apic found for irq %d\n",
3408d56ca36Skettenis 			    DEVNAME(sc), irq._int);
34147a6fcc9Smarco 			return;
34247a6fcc9Smarco 		}
3432d9ec9b4Skettenis 
3448b13d1b4Skrw 		map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
3452d9ec9b4Skettenis 		if (map == NULL)
3462d9ec9b4Skettenis 			return;
3472d9ec9b4Skettenis 
348b51539b6Sniklas 		map->ioapic = apic;
3498d56ca36Skettenis 		map->ioapic_pin = irq._int - apic->sc_apic_vecbase;
3502d9ec9b4Skettenis 		map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3);
3518d56ca36Skettenis 		if (irq._ll)
3528d56ca36Skettenis 			map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT);
3538d56ca36Skettenis 		else
3548d56ca36Skettenis 			map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT);
3558d56ca36Skettenis 		if (irq._he)
3568d56ca36Skettenis 			map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT);
3578d56ca36Skettenis 		else
3588d56ca36Skettenis 			map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT);
3598d56ca36Skettenis 
3608d56ca36Skettenis 		map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
3618d56ca36Skettenis 		switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) {
3628d56ca36Skettenis 		case MPS_INTPO_DEF:
3638d56ca36Skettenis 		case MPS_INTPO_ACTLO:
3648d56ca36Skettenis 			map->redir |= IOAPIC_REDLO_ACTLO;
3658d56ca36Skettenis 			break;
3668d56ca36Skettenis 		}
3678d56ca36Skettenis 		switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) {
3688d56ca36Skettenis 		case MPS_INTTR_DEF:
3698d56ca36Skettenis 		case MPS_INTTR_LEVEL:
3708d56ca36Skettenis 			map->redir |= IOAPIC_REDLO_LEVEL;
3718d56ca36Skettenis 			break;
3728d56ca36Skettenis 		}
3732d9ec9b4Skettenis 
3742d9ec9b4Skettenis 		map->ioapic_ih = APIC_INT_VIA_APIC |
375b51539b6Sniklas 		    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
376b51539b6Sniklas 		    (map->ioapic_pin << APIC_INT_PIN_SHIFT));
3772d9ec9b4Skettenis 
378b51539b6Sniklas 		apic->sc_pins[map->ioapic_pin].ip_map = map;
3792d9ec9b4Skettenis 
3802d9ec9b4Skettenis 		map->next = mp_busses[sc->sc_bus].mb_intrs;
3812d9ec9b4Skettenis 		mp_busses[sc->sc_bus].mb_intrs = map;
382ad5960baSkettenis 
383ad5960baSkettenis 		return;
384ad5960baSkettenis 	}
385ad5960baSkettenis #endif
386ad5960baSkettenis 
3870b1e0221Skettenis 	bus = sc->sc_bus;
3880b1e0221Skettenis 	dev = ACPI_PCI_DEV(addr << 16);
3890b1e0221Skettenis 	tag = pci_make_tag(pc, bus, dev, 0);
3900b1e0221Skettenis 
3910b1e0221Skettenis 	reg = pci_conf_read(pc, tag, PCI_BHLC_REG);
3920b1e0221Skettenis 	if (PCI_HDRTYPE_MULTIFN(reg))
3930b1e0221Skettenis 		nfuncs = 8;
3940b1e0221Skettenis 	else
3950b1e0221Skettenis 		nfuncs = 1;
3960b1e0221Skettenis 
3970b1e0221Skettenis 	for (func = 0; func < nfuncs; func++) {
3980b1e0221Skettenis 		tag = pci_make_tag(pc, bus, dev, func);
3990b1e0221Skettenis 		reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
4000b1e0221Skettenis 		if (PCI_INTERRUPT_PIN(reg) == pin + 1) {
4010b1e0221Skettenis 			reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
4028d56ca36Skettenis 			reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT;
4030b1e0221Skettenis 			pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg);
4040b1e0221Skettenis 		}
4050b1e0221Skettenis 	}
4062d9ec9b4Skettenis }
4072d9ec9b4Skettenis 
4082d9ec9b4Skettenis int
acpiprt_getpcibus(struct acpiprt_softc * sc,struct aml_node * node)4092d9ec9b4Skettenis acpiprt_getpcibus(struct acpiprt_softc *sc, struct aml_node *node)
4102d9ec9b4Skettenis {
411bd2a7025Skettenis 	/* Check if parent device has PCI mapping */
412bd2a7025Skettenis 	return (node->parent && node->parent->pci) ?
413bd2a7025Skettenis 		node->parent->pci->sub : -1;
4142d9ec9b4Skettenis }
4153861348dSmarco 
4163861348dSmarco void
acpiprt_route_interrupt(int bus,int dev,int pin)41749bbc65bSkettenis acpiprt_route_interrupt(int bus, int dev, int pin)
4183861348dSmarco {
41949bbc65bSkettenis 	struct acpiprt_softc *sc;
42049bbc65bSkettenis 	struct acpiprt_map *p;
4218d56ca36Skettenis 	struct acpiprt_irq irq;
42249bbc65bSkettenis 	struct aml_node *node = NULL;
4233861348dSmarco 	struct aml_value res, res2;
4243861348dSmarco 	union acpi_resource *crs;
4258d56ca36Skettenis 	int newirq;
42613627354Sjordan 	int64_t sta;
4273861348dSmarco 
42849bbc65bSkettenis 	SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) {
42949bbc65bSkettenis 		if (p->bus == bus && p->dev == dev && p->pin == (pin - 1)) {
43049bbc65bSkettenis 			newirq = p->irq;
43149bbc65bSkettenis 			sc = p->sc;
43249bbc65bSkettenis 			node = p->node;
43349bbc65bSkettenis 			break;
43449bbc65bSkettenis 		}
43549bbc65bSkettenis 	}
43649bbc65bSkettenis 	if (node == NULL)
43749bbc65bSkettenis 		return;
43849bbc65bSkettenis 
439248de57fSkettenis 	sta = acpi_getsta(sc->sc_acpi, node);
44049bbc65bSkettenis 	KASSERT(sta & STA_PRESENT);
44149bbc65bSkettenis 
44249bbc65bSkettenis 	if (aml_evalname(sc->sc_acpi, node, "_CRS", 0, NULL, &res)) {
44349bbc65bSkettenis 		printf("no _CRS method\n");
44449bbc65bSkettenis 		return;
44549bbc65bSkettenis 	}
446396e7b9cSkettenis 	if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) {
44749bbc65bSkettenis 		printf("invalid _CRS object\n");
4483861348dSmarco 		aml_freevalue(&res);
4493861348dSmarco 		return;
4503861348dSmarco 	}
451311c37bcSjordan 	aml_parse_resource(&res, acpiprt_getirq, &irq);
4523861348dSmarco 
4534e9b0919Skettenis 	/* Only re-route interrupts when necessary. */
4548d56ca36Skettenis 	if ((sta & STA_ENABLED) && irq._int == newirq) {
4554e9b0919Skettenis 		aml_freevalue(&res);
4564e9b0919Skettenis 		return;
4574e9b0919Skettenis 	}
4584e9b0919Skettenis 
4593861348dSmarco 	crs = (union acpi_resource *)res.v_buffer;
4603861348dSmarco 	switch (AML_CRSTYPE(crs)) {
4613861348dSmarco 	case SR_IRQ:
46249bbc65bSkettenis 		crs->sr_irq.irq_mask = htole16(1 << newirq);
4633861348dSmarco 		break;
4643861348dSmarco 	case LR_EXTIRQ:
46549bbc65bSkettenis 		crs->lr_extirq.irq[0] = htole32(newirq);
4663861348dSmarco 		break;
4673861348dSmarco 	}
4683861348dSmarco 
4693861348dSmarco 	if (aml_evalname(sc->sc_acpi, node, "_SRS", 1, &res, &res2)) {
4703861348dSmarco 		printf("no _SRS method\n");
4713861348dSmarco 		aml_freevalue(&res);
4723861348dSmarco 		return;
4733861348dSmarco 	}
4743861348dSmarco 	aml_freevalue(&res);
4753861348dSmarco 	aml_freevalue(&res2);
4763861348dSmarco }
477