xref: /openbsd/sys/dev/acpi/acpiprt.c (revision 471aeecf)
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