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