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