xref: /freebsd/sys/powerpc/powermac/cpcht.c (revision aa64588d28258aef88cc33b8043112e8856948d0)
1 /*-
2  * Copyright (C) 2008-2010 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/module.h>
31 #include <sys/bus.h>
32 #include <sys/conf.h>
33 #include <sys/kernel.h>
34 
35 #include <dev/ofw/openfirm.h>
36 #include <dev/ofw/ofw_pci.h>
37 
38 #include <dev/pci/pcivar.h>
39 #include <dev/pci/pcireg.h>
40 
41 #include <machine/bus.h>
42 #include <machine/intr_machdep.h>
43 #include <machine/md_var.h>
44 #include <machine/openpicvar.h>
45 #include <machine/pio.h>
46 #include <machine/resource.h>
47 
48 #include <sys/rman.h>
49 
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52 
53 #include <vm/vm.h>
54 #include <vm/pmap.h>
55 
56 #include "pcib_if.h"
57 #include "pic_if.h"
58 
59 /*
60  * IBM CPC9X5 Hypertransport Device interface.
61  */
62 static int		cpcht_probe(device_t);
63 static int		cpcht_attach(device_t);
64 
65 static void		cpcht_configure_htbridge(device_t, phandle_t);
66 
67 /*
68  * Bus interface.
69  */
70 static int		cpcht_read_ivar(device_t, device_t, int,
71 			    uintptr_t *);
72 static struct resource *cpcht_alloc_resource(device_t bus, device_t child,
73 			    int type, int *rid, u_long start, u_long end,
74 			    u_long count, u_int flags);
75 static int		cpcht_activate_resource(device_t bus, device_t child,
76 			    int type, int rid, struct resource *res);
77 static int		cpcht_release_resource(device_t bus, device_t child,
78 			    int type, int rid, struct resource *res);
79 static int		cpcht_deactivate_resource(device_t bus, device_t child,
80 			    int type, int rid, struct resource *res);
81 
82 /*
83  * pcib interface.
84  */
85 static int		cpcht_maxslots(device_t);
86 static u_int32_t	cpcht_read_config(device_t, u_int, u_int, u_int,
87 			    u_int, int);
88 static void		cpcht_write_config(device_t, u_int, u_int, u_int,
89 			    u_int, u_int32_t, int);
90 static int		cpcht_route_interrupt(device_t bus, device_t dev,
91 			    int pin);
92 
93 /*
94  * ofw_bus interface
95  */
96 
97 static phandle_t	cpcht_get_node(device_t bus, device_t child);
98 
99 /*
100  * Driver methods.
101  */
102 static device_method_t	cpcht_methods[] = {
103 	/* Device interface */
104 	DEVMETHOD(device_probe,		cpcht_probe),
105 	DEVMETHOD(device_attach,	cpcht_attach),
106 
107 	/* Bus interface */
108 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
109 	DEVMETHOD(bus_read_ivar,	cpcht_read_ivar),
110 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
111 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
112 	DEVMETHOD(bus_alloc_resource,	cpcht_alloc_resource),
113 	DEVMETHOD(bus_release_resource,	cpcht_release_resource),
114 	DEVMETHOD(bus_activate_resource,	cpcht_activate_resource),
115 	DEVMETHOD(bus_deactivate_resource,	cpcht_deactivate_resource),
116 
117 	/* pcib interface */
118 	DEVMETHOD(pcib_maxslots,	cpcht_maxslots),
119 	DEVMETHOD(pcib_read_config,	cpcht_read_config),
120 	DEVMETHOD(pcib_write_config,	cpcht_write_config),
121 	DEVMETHOD(pcib_route_interrupt,	cpcht_route_interrupt),
122 
123 	/* ofw_bus interface */
124 	DEVMETHOD(ofw_bus_get_node,     cpcht_get_node),
125 	{ 0, 0 }
126 };
127 
128 struct cpcht_irq {
129 	int		ht_source;
130 
131 	vm_offset_t	ht_base;
132 	vm_offset_t	apple_eoi;
133 	uint32_t	eoi_data;
134 	int		edge;
135 };
136 
137 static struct cpcht_irq *cpcht_irqmap = NULL;
138 
139 struct cpcht_softc {
140 	device_t		sc_dev;
141 	phandle_t		sc_node;
142 	vm_offset_t		sc_data;
143 	uint64_t		sc_populated_slots;
144 	struct			rman sc_mem_rman;
145 
146 	struct cpcht_irq	htirq_map[128];
147 };
148 
149 static driver_t	cpcht_driver = {
150 	"pcib",
151 	cpcht_methods,
152 	sizeof(struct cpcht_softc)
153 };
154 
155 static devclass_t	cpcht_devclass;
156 
157 DRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0);
158 
159 #define HTAPIC_REQUEST_EOI	0x20
160 #define HTAPIC_TRIGGER_LEVEL	0x02
161 #define HTAPIC_MASK		0x01
162 
163 struct cpcht_range {
164 	u_int32_t       pci_hi;
165 	u_int32_t       pci_mid;
166 	u_int32_t       pci_lo;
167 	u_int32_t       junk;
168 	u_int32_t       host_hi;
169 	u_int32_t       host_lo;
170 	u_int32_t       size_hi;
171 	u_int32_t       size_lo;
172 };
173 
174 static int
175 cpcht_probe(device_t dev)
176 {
177 	const char	*type, *compatible;
178 
179 	type = ofw_bus_get_type(dev);
180 	compatible = ofw_bus_get_compat(dev);
181 
182 	if (type == NULL || compatible == NULL)
183 		return (ENXIO);
184 
185 	if (strcmp(type, "ht") != 0)
186 		return (ENXIO);
187 
188 	if (strcmp(compatible, "u3-ht") != 0)
189 		return (ENXIO);
190 
191 
192 	device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel");
193 	return (0);
194 }
195 
196 static int
197 cpcht_attach(device_t dev)
198 {
199 	struct		cpcht_softc *sc;
200 	phandle_t	node, child;
201 	u_int32_t	reg[3];
202 	int		error;
203 
204 	node = ofw_bus_get_node(dev);
205 	sc = device_get_softc(dev);
206 
207 	if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12)
208 		return (ENXIO);
209 
210 	sc->sc_dev = dev;
211 	sc->sc_node = node;
212 	sc->sc_populated_slots = 0;
213 	sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]);
214 
215 	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
216 	sc->sc_mem_rman.rm_descr = "CPCHT Device Memory";
217 	error = rman_init(&sc->sc_mem_rman);
218 
219 	if (error) {
220 		device_printf(dev, "rman_init() failed. error = %d\n", error);
221 		return (error);
222 	}
223 
224 	/*
225 	 * Set up the resource manager and the HT->MPIC mapping. For cpcht,
226 	 * the ranges are properties of the child bridges, and this is also
227 	 * where we get the HT interrupts properties.
228 	 */
229 
230 	bzero(sc->htirq_map, sizeof(sc->htirq_map));
231 	for (child = OF_child(node); child != 0; child = OF_peer(child))
232 		cpcht_configure_htbridge(dev, child);
233 
234 	/* Now make the mapping table available to the MPIC */
235 	cpcht_irqmap = sc->htirq_map;
236 
237 	device_add_child(dev, "pci", device_get_unit(dev));
238 
239 	return (bus_generic_attach(dev));
240 }
241 
242 static void
243 cpcht_configure_htbridge(device_t dev, phandle_t child)
244 {
245 	struct cpcht_softc *sc;
246 	struct ofw_pci_register pcir;
247 	struct cpcht_range ranges[6], *rp;
248 	int nranges, ptr, nextptr;
249 	uint32_t vend, val;
250 	int i, nirq, irq;
251 	u_int f, s;
252 
253 	sc = device_get_softc(dev);
254 	if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1)
255 		return;
256 
257 	s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
258 	f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
259 
260 	/*
261 	 * Mark this slot is populated. The remote south bridge does
262 	 * not like us talking to unpopulated slots on the root bus.
263 	 */
264 	sc->sc_populated_slots |= (1 << s);
265 
266 	/*
267 	 * Next grab this child bus's bus ranges.
268 	 */
269 	bzero(ranges, sizeof(ranges));
270 	nranges = OF_getprop(child, "ranges", ranges, sizeof(ranges));
271 
272 	ranges[6].pci_hi = 0;
273 	for (rp = ranges; rp->pci_hi != 0; rp++) {
274 		switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
275 		case OFW_PCI_PHYS_HI_SPACE_CONFIG:
276 			break;
277 		case OFW_PCI_PHYS_HI_SPACE_IO:
278 		case OFW_PCI_PHYS_HI_SPACE_MEM32:
279 			rman_manage_region(&sc->sc_mem_rman, rp->pci_lo,
280 			    rp->pci_lo + rp->size_lo - 1);
281 			break;
282 		case OFW_PCI_PHYS_HI_SPACE_MEM64:
283 			panic("64-bit CPCHT reserved memory!");
284 			break;
285 		}
286 	}
287 
288 	/*
289 	 * Next build up any HT->MPIC mappings for this sub-bus. One would
290 	 * naively hope that enabling, disabling, and EOIing interrupts would
291 	 * cause the appropriate HT bus transactions to that effect. This is
292 	 * not the case.
293 	 *
294 	 * Instead, we have to muck about on the HT peer's root PCI bridges,
295 	 * figure out what interrupts they send, enable them, and cache
296 	 * the location of their WaitForEOI registers so that we can
297 	 * send EOIs later.
298 	 */
299 
300 	/* All the devices we are interested in have caps */
301 	if (!(PCIB_READ_CONFIG(dev, 0, s, f, PCIR_STATUS, 2)
302 	    & PCIM_STATUS_CAPPRESENT))
303 		return;
304 
305 	nextptr = PCIB_READ_CONFIG(dev, 0, s, f, PCIR_CAP_PTR, 1);
306 	while (nextptr != 0) {
307 		ptr = nextptr;
308 		nextptr = PCIB_READ_CONFIG(dev, 0, s, f,
309 		    ptr + PCICAP_NEXTPTR, 1);
310 
311 		/* Find the HT IRQ capabilities */
312 		if (PCIB_READ_CONFIG(dev, 0, s, f,
313 		    ptr + PCICAP_ID, 1) != PCIY_HT)
314 			continue;
315 
316 		val = PCIB_READ_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 2);
317 		if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT)
318 			continue;
319 
320 		/* Ask for the IRQ count */
321 		PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1);
322 		nirq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4);
323 		nirq = ((nirq >> 16) & 0xff) + 1;
324 
325 		device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f);
326 
327 		for (i = 0; i < nirq; i++) {
328 			PCIB_WRITE_CONFIG(dev, 0, s, f,
329 			     ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1);
330 			irq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4);
331 
332 			/*
333 			 * Mask this interrupt for now.
334 			 */
335 			PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + 4,
336 			    irq | HTAPIC_MASK, 4);
337 			irq = (irq >> 16) & 0xff;
338 
339 			sc->htirq_map[irq].ht_source = i;
340 			sc->htirq_map[irq].ht_base = sc->sc_data +
341 			    (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr));
342 
343 			PCIB_WRITE_CONFIG(dev, 0, s, f,
344 			     ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1);
345 			sc->htirq_map[irq].eoi_data =
346 			    PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4) |
347 			    0x80000000;
348 
349 			/*
350 			 * Apple uses a non-compliant IO/APIC that differs
351 			 * in how we signal EOIs. Check if this device was
352 			 * made by Apple, and act accordingly.
353 			 */
354 			vend = PCIB_READ_CONFIG(dev, 0, s, f,
355 			    PCIR_DEVVENDOR, 4);
356 			if ((vend & 0xffff) == 0x106b)
357 				sc->htirq_map[irq].apple_eoi =
358 				 (sc->htirq_map[irq].ht_base - ptr) + 0x60;
359 		}
360 	}
361 }
362 
363 static int
364 cpcht_maxslots(device_t dev)
365 {
366 
367 	return (PCI_SLOTMAX);
368 }
369 
370 static u_int32_t
371 cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
372     int width)
373 {
374 	struct		cpcht_softc *sc;
375 	vm_offset_t	caoff;
376 
377 	sc = device_get_softc(dev);
378 	caoff = sc->sc_data +
379 		(((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
380 
381 	if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
382 		return (0xffffffff);
383 
384 	if (bus > 0)
385 		caoff += 0x01000000UL + (bus << 16);
386 
387 	switch (width) {
388 	case 1:
389 		return (in8rb(caoff));
390 		break;
391 	case 2:
392 		return (in16rb(caoff));
393 		break;
394 	case 4:
395 		return (in32rb(caoff));
396 		break;
397 	}
398 
399 	return (0xffffffff);
400 }
401 
402 static void
403 cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func,
404     u_int reg, u_int32_t val, int width)
405 {
406 	struct		cpcht_softc *sc;
407 	vm_offset_t	caoff;
408 
409 	sc = device_get_softc(dev);
410 	caoff = sc->sc_data +
411 		(((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
412 
413 	if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
414 		return;
415 
416 	if (bus > 0)
417 		caoff += 0x01000000UL + (bus << 16);
418 
419 	switch (width) {
420 	case 1:
421 		out8rb(caoff, val);
422 		break;
423 	case 2:
424 		out16rb(caoff, val);
425 		break;
426 	case 4:
427 		out32rb(caoff, val);
428 		break;
429 	}
430 }
431 
432 static int
433 cpcht_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
434 {
435 	struct	cpcht_softc *sc;
436 
437 	sc = device_get_softc(dev);
438 
439 	switch (which) {
440 	case PCIB_IVAR_DOMAIN:
441 		*result = device_get_unit(dev);
442 		return (0);
443 	case PCIB_IVAR_BUS:
444 		*result = 0;	/* Root bus */
445 		return (0);
446 	}
447 
448 	return (ENOENT);
449 }
450 
451 static phandle_t
452 cpcht_get_node(device_t bus, device_t dev)
453 {
454 	struct cpcht_softc *sc;
455 
456 	sc = device_get_softc(bus);
457 	/* We only have one child, the PCI bus, which needs our own node. */
458 	return (sc->sc_node);
459 }
460 
461 static int
462 cpcht_route_interrupt(device_t bus, device_t dev, int pin)
463 {
464 	return (pin);
465 }
466 
467 static struct resource *
468 cpcht_alloc_resource(device_t bus, device_t child, int type, int *rid,
469     u_long start, u_long end, u_long count, u_int flags)
470 {
471 	struct			cpcht_softc *sc;
472 	struct			resource *rv;
473 	struct			rman *rm;
474 	int			needactivate, err;
475 
476 	needactivate = flags & RF_ACTIVE;
477 	flags &= ~RF_ACTIVE;
478 
479 	sc = device_get_softc(bus);
480 	err = 0;
481 
482 	switch (type) {
483 	case SYS_RES_IOPORT:
484 		end = min(end, start + count);
485 
486 		/* FALLTHROUGH */
487 	case SYS_RES_MEMORY:
488 		rm = &sc->sc_mem_rman;
489 		break;
490 
491 	case SYS_RES_IRQ:
492 		return (bus_alloc_resource(bus, type, rid, start, end, count,
493 		    flags));
494 
495 	default:
496 		device_printf(bus, "unknown resource request from %s\n",
497 		    device_get_nameunit(child));
498 		return (NULL);
499 	}
500 
501 	rv = rman_reserve_resource(rm, start, end, count, flags, child);
502 	if (rv == NULL) {
503 		device_printf(bus, "failed to reserve resource for %s\n",
504 		    device_get_nameunit(child));
505 		return (NULL);
506 	}
507 
508 	rman_set_rid(rv, *rid);
509 
510 	if (needactivate) {
511 		if (bus_activate_resource(child, type, *rid, rv) != 0) {
512 			device_printf(bus,
513 			    "failed to activate resource for %s\n",
514 			    device_get_nameunit(child));
515 			rman_release_resource(rv);
516 			return (NULL);
517 		}
518 	}
519 
520 	return (rv);
521 }
522 
523 static int
524 cpcht_activate_resource(device_t bus, device_t child, int type, int rid,
525     struct resource *res)
526 {
527 	void	*p;
528 	struct	cpcht_softc *sc;
529 
530 	sc = device_get_softc(bus);
531 
532 	if (type == SYS_RES_IRQ)
533 		return (bus_activate_resource(bus, type, rid, res));
534 
535 	if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
536 		vm_offset_t start;
537 
538 		start = (vm_offset_t)rman_get_start(res);
539 
540 		if (bootverbose)
541 			printf("cpcht mapdev: start %zx, len %ld\n", start,
542 			    rman_get_size(res));
543 
544 		p = pmap_mapdev(start, (vm_size_t)rman_get_size(res));
545 		if (p == NULL)
546 			return (ENOMEM);
547 		rman_set_virtual(res, p);
548 		rman_set_bustag(res, &bs_le_tag);
549 		rman_set_bushandle(res, (u_long)p);
550 	}
551 
552 	return (rman_activate_resource(res));
553 }
554 
555 static int
556 cpcht_release_resource(device_t bus, device_t child, int type, int rid,
557     struct resource *res)
558 {
559 
560 	if (rman_get_flags(res) & RF_ACTIVE) {
561 		int error = bus_deactivate_resource(child, type, rid, res);
562 		if (error)
563 			return error;
564 	}
565 
566 	return (rman_release_resource(res));
567 }
568 
569 static int
570 cpcht_deactivate_resource(device_t bus, device_t child, int type, int rid,
571     struct resource *res)
572 {
573 
574 	/*
575 	 * If this is a memory resource, unmap it.
576 	 */
577 	if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
578 		u_int32_t psize;
579 
580 		psize = rman_get_size(res);
581 		pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
582 	}
583 
584 	return (rman_deactivate_resource(res));
585 }
586 
587 /*
588  * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945)
589  */
590 
591 static int	openpic_cpcht_probe(device_t);
592 static int	openpic_cpcht_attach(device_t);
593 static void	openpic_cpcht_config(device_t, u_int irq,
594 		    enum intr_trigger trig, enum intr_polarity pol);
595 static void	openpic_cpcht_enable(device_t, u_int irq, u_int vector);
596 static void	openpic_cpcht_unmask(device_t, u_int irq);
597 static void	openpic_cpcht_eoi(device_t, u_int irq);
598 
599 static device_method_t  openpic_cpcht_methods[] = {
600 	/* Device interface */
601 	DEVMETHOD(device_probe,		openpic_cpcht_probe),
602 	DEVMETHOD(device_attach,	openpic_cpcht_attach),
603 
604 	/* PIC interface */
605 	DEVMETHOD(pic_config,		openpic_cpcht_config),
606 	DEVMETHOD(pic_dispatch,		openpic_dispatch),
607 	DEVMETHOD(pic_enable,		openpic_cpcht_enable),
608 	DEVMETHOD(pic_eoi,		openpic_cpcht_eoi),
609 	DEVMETHOD(pic_ipi,		openpic_ipi),
610 	DEVMETHOD(pic_mask,		openpic_mask),
611 	DEVMETHOD(pic_unmask,		openpic_cpcht_unmask),
612 
613 	{ 0, 0 },
614 };
615 
616 struct openpic_cpcht_softc {
617 	struct openpic_softc sc_openpic;
618 
619 	struct mtx sc_ht_mtx;
620 };
621 
622 static driver_t openpic_cpcht_driver = {
623 	"htpic",
624 	openpic_cpcht_methods,
625 	sizeof(struct openpic_cpcht_softc),
626 };
627 
628 DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0);
629 
630 static int
631 openpic_cpcht_probe(device_t dev)
632 {
633 	const char *type = ofw_bus_get_type(dev);
634 
635 	if (strcmp(type, "open-pic") != 0)
636                 return (ENXIO);
637 
638 	device_set_desc(dev, OPENPIC_DEVSTR);
639 	return (0);
640 }
641 
642 static int
643 openpic_cpcht_attach(device_t dev)
644 {
645 	struct openpic_cpcht_softc *sc;
646 	int err, irq;
647 
648 	err = openpic_attach(dev);
649 	if (err != 0)
650 		return (err);
651 
652 	/*
653 	 * The HT APIC stuff is not thread-safe, so we need a mutex to
654 	 * protect it.
655 	 */
656 	sc = device_get_softc(dev);
657 	mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN);
658 
659 	/*
660 	 * Interrupts 0-3 are internally sourced and are level triggered
661 	 * active low. Interrupts 4-123 are connected to a pulse generator
662 	 * and should be programmed as edge triggered low-to-high.
663 	 *
664 	 * IBM CPC945 Manual, Section 9.3.
665 	 */
666 
667 	for (irq = 0; irq < 4; irq++)
668 		openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
669 	for (irq = 4; irq < 124; irq++)
670 		openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
671 
672 	return (0);
673 }
674 
675 static void
676 openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig,
677     enum intr_polarity pol)
678 {
679 	struct openpic_cpcht_softc *sc;
680 	uint32_t ht_irq;
681 
682 	/*
683 	 * The interrupt settings for the MPIC are completely determined
684 	 * by the internal wiring in the northbridge. Real changes to these
685 	 * settings need to be negotiated with the remote IO-APIC on the HT
686 	 * link.
687 	 */
688 
689 	sc = device_get_softc(dev);
690 
691 	if (cpcht_irqmap != NULL && irq < 128 &&
692 	    cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
693 		mtx_lock_spin(&sc->sc_ht_mtx);
694 
695 		/* Program the data port */
696 		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
697 		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
698 
699 		/* Grab the IRQ config register */
700 		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
701 
702 		/* Mask the IRQ while we fiddle settings */
703 		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK);
704 
705 		/* Program the interrupt sense */
706 		ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI);
707 		if (trig == INTR_TRIGGER_EDGE) {
708 			cpcht_irqmap[irq].edge = 1;
709 		} else {
710 			cpcht_irqmap[irq].edge = 0;
711 			ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI;
712 		}
713 		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
714 
715 		mtx_unlock_spin(&sc->sc_ht_mtx);
716 	}
717 }
718 
719 static void
720 openpic_cpcht_enable(device_t dev, u_int irq, u_int vec)
721 {
722 	struct openpic_cpcht_softc *sc;
723 	uint32_t ht_irq;
724 
725 	openpic_enable(dev, irq, vec);
726 
727 	sc = device_get_softc(dev);
728 
729 	if (cpcht_irqmap != NULL && irq < 128 &&
730 	    cpcht_irqmap[irq].ht_base > 0) {
731 		mtx_lock_spin(&sc->sc_ht_mtx);
732 
733 		/* Program the data port */
734 		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
735 		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
736 
737 		/* Unmask the interrupt */
738 		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
739 		ht_irq &= ~HTAPIC_MASK;
740 		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
741 
742 		mtx_unlock_spin(&sc->sc_ht_mtx);
743 	}
744 
745 	openpic_cpcht_eoi(dev, irq);
746 }
747 
748 static void
749 openpic_cpcht_unmask(device_t dev, u_int irq)
750 {
751 	struct openpic_cpcht_softc *sc;
752 	uint32_t ht_irq;
753 
754 	openpic_unmask(dev, irq);
755 
756 	sc = device_get_softc(dev);
757 
758 	if (cpcht_irqmap != NULL && irq < 128 &&
759 	    cpcht_irqmap[irq].ht_base > 0) {
760 		mtx_lock_spin(&sc->sc_ht_mtx);
761 
762 		/* Program the data port */
763 		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
764 		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
765 
766 		/* Unmask the interrupt */
767 		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
768 		ht_irq &= ~HTAPIC_MASK;
769 		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
770 
771 		mtx_unlock_spin(&sc->sc_ht_mtx);
772 	}
773 
774 	openpic_cpcht_eoi(dev, irq);
775 }
776 
777 static void
778 openpic_cpcht_eoi(device_t dev, u_int irq)
779 {
780 	struct openpic_cpcht_softc *sc;
781 	uint32_t off, mask;
782 
783 	if (irq == 255)
784 		return;
785 
786 	sc = device_get_softc(dev);
787 
788 	if (cpcht_irqmap != NULL && irq < 128 &&
789 	    cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
790 		/* If this is an HT IRQ, acknowledge it at the remote APIC */
791 
792 		if (cpcht_irqmap[irq].apple_eoi) {
793 			off = (cpcht_irqmap[irq].ht_source >> 3) & ~3;
794 			mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f);
795 			out32rb(cpcht_irqmap[irq].apple_eoi + off, mask);
796 		} else {
797 			mtx_lock_spin(&sc->sc_ht_mtx);
798 
799 			out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
800 			    0x11 + (cpcht_irqmap[irq].ht_source << 1));
801 			out32rb(cpcht_irqmap[irq].ht_base + 4,
802 			    cpcht_irqmap[irq].eoi_data);
803 
804 			mtx_unlock_spin(&sc->sc_ht_mtx);
805 		}
806 	}
807 
808 	openpic_eoi(dev, irq);
809 }
810 
811