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
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD$");
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/module.h>
32#include <sys/bus.h>
33#include <sys/conf.h>
34#include <sys/kernel.h>
35#include <sys/pciio.h>
36#include <sys/rman.h>
37
38#include <dev/ofw/openfirm.h>
39#include <dev/ofw/ofw_pci.h>
40
41#include <dev/pci/pcivar.h>
42#include <dev/pci/pcireg.h>
43
44#include <machine/bus.h>
45#include <machine/intr_machdep.h>
46#include <machine/md_var.h>
47#include <machine/openpicvar.h>
48#include <machine/pio.h>
49#include <machine/resource.h>
50
51#include <dev/ofw/ofw_bus.h>
52#include <dev/ofw/ofw_bus_subr.h>
53#include <powerpc/ofw/ofw_pci.h>
54
55#include <vm/vm.h>
56#include <vm/pmap.h>
57
58#include "pcib_if.h"
59#include "pic_if.h"
60
61/*
62 * IBM CPC9X5 Hypertransport Device interface.
63 */
64static int		cpcht_probe(device_t);
65static int		cpcht_attach(device_t);
66
67static void		cpcht_configure_htbridge(device_t, phandle_t);
68
69/*
70 * pcib interface.
71 */
72static u_int32_t	cpcht_read_config(device_t, u_int, u_int, u_int,
73			    u_int, int);
74static void		cpcht_write_config(device_t, u_int, u_int, u_int,
75			    u_int, u_int32_t, int);
76static int		cpcht_route_interrupt(device_t, device_t, int);
77static int		cpcht_alloc_msi(device_t dev, device_t child,
78			    int count, int maxcount, int *irqs);
79static int		cpcht_release_msi(device_t dev, device_t child,
80			    int count, int *irqs);
81static int		cpcht_alloc_msix(device_t dev, device_t child,
82			    int *irq);
83static int		cpcht_release_msix(device_t dev, device_t child,
84			    int irq);
85static int		cpcht_map_msi(device_t dev, device_t child,
86			    int irq, uint64_t *addr, uint32_t *data);
87
88/*
89 * Driver methods.
90 */
91static device_method_t	cpcht_methods[] = {
92	/* Device interface */
93	DEVMETHOD(device_probe,		cpcht_probe),
94	DEVMETHOD(device_attach,	cpcht_attach),
95
96	/* pcib interface */
97	DEVMETHOD(pcib_read_config,	cpcht_read_config),
98	DEVMETHOD(pcib_write_config,	cpcht_write_config),
99	DEVMETHOD(pcib_route_interrupt,	cpcht_route_interrupt),
100	DEVMETHOD(pcib_alloc_msi,	cpcht_alloc_msi),
101	DEVMETHOD(pcib_release_msi,	cpcht_release_msi),
102	DEVMETHOD(pcib_alloc_msix,	cpcht_alloc_msix),
103	DEVMETHOD(pcib_release_msix,	cpcht_release_msix),
104	DEVMETHOD(pcib_map_msi,		cpcht_map_msi),
105
106	DEVMETHOD_END
107};
108
109struct cpcht_irq {
110	enum {
111	    IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL
112	}		irq_type;
113
114	int		ht_source;
115
116	vm_offset_t	ht_base;
117	vm_offset_t	apple_eoi;
118	uint32_t	eoi_data;
119	int		edge;
120};
121
122static struct cpcht_irq *cpcht_irqmap = NULL;
123uint32_t cpcht_msipic = 0;
124
125struct cpcht_softc {
126	struct ofw_pci_softc	pci_sc;
127	vm_offset_t		sc_data;
128	uint64_t		sc_populated_slots;
129
130	struct cpcht_irq	htirq_map[128];
131	struct mtx		htirq_mtx;
132};
133
134static devclass_t	cpcht_devclass;
135DEFINE_CLASS_1(pcib, cpcht_driver, cpcht_methods, sizeof(struct cpcht_softc),
136    ofw_pci_driver);
137DRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0);
138
139#define CPCHT_IOPORT_BASE	0xf4000000UL /* Hardwired */
140#define CPCHT_IOPORT_SIZE	0x00400000UL
141
142#define HTAPIC_REQUEST_EOI	0x20
143#define HTAPIC_TRIGGER_LEVEL	0x02
144#define HTAPIC_MASK		0x01
145
146static int
147cpcht_probe(device_t dev)
148{
149	const char	*type, *compatible;
150
151	type = ofw_bus_get_type(dev);
152	compatible = ofw_bus_get_compat(dev);
153
154	if (type == NULL || compatible == NULL)
155		return (ENXIO);
156
157	if (strcmp(type, "ht") != 0)
158		return (ENXIO);
159
160	if (strcmp(compatible, "u3-ht") != 0)
161		return (ENXIO);
162
163	device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel");
164	return (0);
165}
166
167static int
168cpcht_attach(device_t dev)
169{
170	struct		cpcht_softc *sc;
171	phandle_t	node, child;
172	u_int32_t	reg[3];
173	int		i;
174
175	node = ofw_bus_get_node(dev);
176	sc = device_get_softc(dev);
177
178	if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12)
179		return (ENXIO);
180
181	if (OF_getproplen(node, "ranges") <= 0)
182		sc->pci_sc.sc_quirks = OFW_PCI_QUIRK_RANGES_ON_CHILDREN;
183	sc->sc_populated_slots = 0;
184	sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]);
185
186	/*
187	 * Set up the resource manager and the HT->MPIC mapping. For cpcht,
188	 * the ranges are properties of the child bridges, and this is also
189	 * where we get the HT interrupts properties.
190	 */
191
192#if 0
193	/* I/O port mappings are usually not in the device tree */
194	rman_manage_region(&sc->pci_sc.sc_io_rman, 0, CPCHT_IOPORT_SIZE - 1);
195#endif
196
197	bzero(sc->htirq_map, sizeof(sc->htirq_map));
198	mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF);
199	for (i = 0; i < 8; i++)
200		sc->htirq_map[i].irq_type = IRQ_INTERNAL;
201	for (child = OF_child(node); child != 0; child = OF_peer(child))
202		cpcht_configure_htbridge(dev, child);
203
204	/* Now make the mapping table available to the MPIC */
205	cpcht_irqmap = sc->htirq_map;
206
207	return (ofw_pci_attach(dev));
208}
209
210static void
211cpcht_configure_htbridge(device_t dev, phandle_t child)
212{
213	struct cpcht_softc *sc;
214	struct ofw_pci_register pcir;
215	int ptr, nextptr;
216	uint32_t vend, val;
217	int i, nirq, irq;
218	u_int b, f, s;
219
220	sc = device_get_softc(dev);
221	if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1)
222		return;
223
224	b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi);
225	s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
226	f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
227
228	/*
229	 * Mark this slot is populated. The remote south bridge does
230	 * not like us talking to unpopulated slots on the root bus.
231	 */
232	sc->sc_populated_slots |= (1 << s);
233
234	/*
235	 * Next build up any HT->MPIC mappings for this sub-bus. One would
236	 * naively hope that enabling, disabling, and EOIing interrupts would
237	 * cause the appropriate HT bus transactions to that effect. This is
238	 * not the case.
239	 *
240	 * Instead, we have to muck about on the HT peer's root PCI bridges,
241	 * figure out what interrupts they send, enable them, and cache
242	 * the location of their WaitForEOI registers so that we can
243	 * send EOIs later.
244	 */
245
246	/* All the devices we are interested in have caps */
247	if (!(PCIB_READ_CONFIG(dev, b, s, f, PCIR_STATUS, 2)
248	    & PCIM_STATUS_CAPPRESENT))
249		return;
250
251	nextptr = PCIB_READ_CONFIG(dev, b, s, f, PCIR_CAP_PTR, 1);
252	while (nextptr != 0) {
253		ptr = nextptr;
254		nextptr = PCIB_READ_CONFIG(dev, b, s, f,
255		    ptr + PCICAP_NEXTPTR, 1);
256
257		/* Find the HT IRQ capabilities */
258		if (PCIB_READ_CONFIG(dev, b, s, f,
259		    ptr + PCICAP_ID, 1) != PCIY_HT)
260			continue;
261
262		val = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 2);
263		if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT)
264			continue;
265
266		/* Ask for the IRQ count */
267		PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1);
268		nirq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4);
269		nirq = ((nirq >> 16) & 0xff) + 1;
270
271		device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f);
272
273		for (i = 0; i < nirq; i++) {
274			PCIB_WRITE_CONFIG(dev, b, s, f,
275			     ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1);
276			irq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4);
277
278			/*
279			 * Mask this interrupt for now.
280			 */
281			PCIB_WRITE_CONFIG(dev, b, s, f, ptr + 4,
282			    irq | HTAPIC_MASK, 4);
283			irq = (irq >> 16) & 0xff;
284
285			sc->htirq_map[irq].irq_type = IRQ_HT;
286			sc->htirq_map[irq].ht_source = i;
287			sc->htirq_map[irq].ht_base = sc->sc_data +
288			    (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr));
289
290			PCIB_WRITE_CONFIG(dev, b, s, f,
291			     ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1);
292			sc->htirq_map[irq].eoi_data =
293			    PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4) |
294			    0x80000000;
295
296			/*
297			 * Apple uses a non-compliant IO/APIC that differs
298			 * in how we signal EOIs. Check if this device was
299			 * made by Apple, and act accordingly.
300			 */
301			vend = PCIB_READ_CONFIG(dev, b, s, f,
302			    PCIR_DEVVENDOR, 4);
303			if ((vend & 0xffff) == 0x106b)
304				sc->htirq_map[irq].apple_eoi =
305				 (sc->htirq_map[irq].ht_base - ptr) + 0x60;
306		}
307	}
308}
309
310static u_int32_t
311cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
312    int width)
313{
314	struct		cpcht_softc *sc;
315	vm_offset_t	caoff;
316
317	sc = device_get_softc(dev);
318	caoff = sc->sc_data +
319		(((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
320
321	if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
322		return (0xffffffff);
323
324	if (bus > 0)
325		caoff += 0x01000000UL + (bus << 16);
326
327	switch (width) {
328	case 1:
329		return (in8rb(caoff));
330		break;
331	case 2:
332		return (in16rb(caoff));
333		break;
334	case 4:
335		return (in32rb(caoff));
336		break;
337	}
338
339	return (0xffffffff);
340}
341
342static void
343cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func,
344    u_int reg, u_int32_t val, int width)
345{
346	struct		cpcht_softc *sc;
347	vm_offset_t	caoff;
348
349	sc = device_get_softc(dev);
350	caoff = sc->sc_data +
351		(((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
352
353	if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
354		return;
355
356	if (bus > 0)
357		caoff += 0x01000000UL + (bus << 16);
358
359	switch (width) {
360	case 1:
361		out8rb(caoff, val);
362		break;
363	case 2:
364		out16rb(caoff, val);
365		break;
366	case 4:
367		out32rb(caoff, val);
368		break;
369	}
370}
371
372static int
373cpcht_route_interrupt(device_t bus, device_t dev, int pin)
374{
375	return (pin);
376}
377
378static int
379cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount,
380    int *irqs)
381{
382	struct cpcht_softc *sc;
383	int i, j;
384
385	sc = device_get_softc(dev);
386	j = 0;
387
388	/* Bail if no MSI PIC yet */
389	if (cpcht_msipic == 0)
390		return (ENXIO);
391
392	mtx_lock(&sc->htirq_mtx);
393	for (i = 8; i < 124 - count; i++) {
394		for (j = 0; j < count; j++) {
395			if (sc->htirq_map[i+j].irq_type != IRQ_NONE)
396				break;
397		}
398		if (j == count)
399			break;
400
401		i += j; /* We know there isn't a large enough run */
402	}
403
404	if (j != count) {
405		mtx_unlock(&sc->htirq_mtx);
406		return (ENXIO);
407	}
408
409	for (j = 0; j < count; j++) {
410		irqs[j] = MAP_IRQ(cpcht_msipic, i+j);
411		sc->htirq_map[i+j].irq_type = IRQ_MSI;
412	}
413	mtx_unlock(&sc->htirq_mtx);
414
415	return (0);
416}
417
418static int
419cpcht_release_msi(device_t dev, device_t child, int count, int *irqs)
420{
421	struct cpcht_softc *sc;
422	int i;
423
424	sc = device_get_softc(dev);
425
426	mtx_lock(&sc->htirq_mtx);
427	for (i = 0; i < count; i++)
428		sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE;
429	mtx_unlock(&sc->htirq_mtx);
430
431	return (0);
432}
433
434static int
435cpcht_alloc_msix(device_t dev, device_t child, int *irq)
436{
437	struct cpcht_softc *sc;
438	int i;
439
440	sc = device_get_softc(dev);
441
442	/* Bail if no MSI PIC yet */
443	if (cpcht_msipic == 0)
444		return (ENXIO);
445
446	mtx_lock(&sc->htirq_mtx);
447	for (i = 8; i < 124; i++) {
448		if (sc->htirq_map[i].irq_type == IRQ_NONE) {
449			sc->htirq_map[i].irq_type = IRQ_MSI;
450			*irq = MAP_IRQ(cpcht_msipic, i);
451
452			mtx_unlock(&sc->htirq_mtx);
453			return (0);
454		}
455	}
456	mtx_unlock(&sc->htirq_mtx);
457
458	return (ENXIO);
459}
460
461static int
462cpcht_release_msix(device_t dev, device_t child, int irq)
463{
464	struct cpcht_softc *sc;
465
466	sc = device_get_softc(dev);
467
468	mtx_lock(&sc->htirq_mtx);
469	sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE;
470	mtx_unlock(&sc->htirq_mtx);
471
472	return (0);
473}
474
475static int
476cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr,
477    uint32_t *data)
478{
479	device_t pcib;
480	struct pci_devinfo *dinfo;
481	struct pcicfg_ht *ht = NULL;
482
483	for (pcib = child; pcib != dev; pcib =
484	    device_get_parent(device_get_parent(pcib))) {
485		dinfo = device_get_ivars(pcib);
486		ht = &dinfo->cfg.ht;
487
488		if (ht == NULL)
489			continue;
490	}
491
492	if (ht == NULL)
493		return (ENXIO);
494
495	*addr = ht->ht_msiaddr;
496	*data = irq & 0xff;
497
498	return (0);
499}
500
501/*
502 * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945)
503 */
504
505static int	openpic_cpcht_probe(device_t);
506static int	openpic_cpcht_attach(device_t);
507static void	openpic_cpcht_config(device_t, u_int irq,
508		    enum intr_trigger trig, enum intr_polarity pol);
509static void	openpic_cpcht_enable(device_t, u_int irq, u_int vector);
510static void	openpic_cpcht_unmask(device_t, u_int irq);
511static void	openpic_cpcht_eoi(device_t, u_int irq);
512
513static device_method_t  openpic_cpcht_methods[] = {
514	/* Device interface */
515	DEVMETHOD(device_probe,		openpic_cpcht_probe),
516	DEVMETHOD(device_attach,	openpic_cpcht_attach),
517
518	/* PIC interface */
519	DEVMETHOD(pic_bind,		openpic_bind),
520	DEVMETHOD(pic_config,		openpic_cpcht_config),
521	DEVMETHOD(pic_dispatch,		openpic_dispatch),
522	DEVMETHOD(pic_enable,		openpic_cpcht_enable),
523	DEVMETHOD(pic_eoi,		openpic_cpcht_eoi),
524	DEVMETHOD(pic_ipi,		openpic_ipi),
525	DEVMETHOD(pic_mask,		openpic_mask),
526	DEVMETHOD(pic_unmask,		openpic_cpcht_unmask),
527
528	{ 0, 0 },
529};
530
531struct openpic_cpcht_softc {
532	struct openpic_softc sc_openpic;
533
534	struct mtx sc_ht_mtx;
535};
536
537static driver_t openpic_cpcht_driver = {
538	"htpic",
539	openpic_cpcht_methods,
540	sizeof(struct openpic_cpcht_softc),
541};
542
543DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0);
544
545static int
546openpic_cpcht_probe(device_t dev)
547{
548	const char *type = ofw_bus_get_type(dev);
549
550	if (strcmp(type, "open-pic") != 0)
551                return (ENXIO);
552
553	device_set_desc(dev, OPENPIC_DEVSTR);
554	return (0);
555}
556
557static int
558openpic_cpcht_attach(device_t dev)
559{
560	struct openpic_cpcht_softc *sc;
561	phandle_t node;
562	int err, irq;
563
564	node = ofw_bus_get_node(dev);
565	err = openpic_common_attach(dev, node);
566	if (err != 0)
567		return (err);
568
569	/*
570	 * The HT APIC stuff is not thread-safe, so we need a mutex to
571	 * protect it.
572	 */
573	sc = device_get_softc(dev);
574	mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN);
575
576	/*
577	 * Interrupts 0-3 are internally sourced and are level triggered
578	 * active low. Interrupts 4-123 are connected to a pulse generator
579	 * and should be programmed as edge triggered low-to-high.
580	 *
581	 * IBM CPC945 Manual, Section 9.3.
582	 */
583
584	for (irq = 0; irq < 4; irq++)
585		openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
586	for (irq = 4; irq < 124; irq++)
587		openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
588
589	/*
590	 * Use this PIC for MSI only if it is the root PIC. This may not
591	 * be necessary, but Linux does it, and I cannot find any U3 machines
592	 * with MSI devices to test.
593	 */
594	if (dev == root_pic)
595		cpcht_msipic = node;
596
597	return (0);
598}
599
600static void
601openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig,
602    enum intr_polarity pol)
603{
604	struct openpic_cpcht_softc *sc;
605	uint32_t ht_irq;
606
607	/*
608	 * The interrupt settings for the MPIC are completely determined
609	 * by the internal wiring in the northbridge. Real changes to these
610	 * settings need to be negotiated with the remote IO-APIC on the HT
611	 * link.
612	 */
613
614	sc = device_get_softc(dev);
615
616	if (cpcht_irqmap != NULL && irq < 128 &&
617	    cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
618		mtx_lock_spin(&sc->sc_ht_mtx);
619
620		/* Program the data port */
621		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
622		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
623
624		/* Grab the IRQ config register */
625		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
626
627		/* Mask the IRQ while we fiddle settings */
628		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK);
629
630		/* Program the interrupt sense */
631		ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI);
632		if (trig == INTR_TRIGGER_EDGE) {
633			cpcht_irqmap[irq].edge = 1;
634		} else {
635			cpcht_irqmap[irq].edge = 0;
636			ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI;
637		}
638		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
639
640		mtx_unlock_spin(&sc->sc_ht_mtx);
641	}
642}
643
644static void
645openpic_cpcht_enable(device_t dev, u_int irq, u_int vec)
646{
647	struct openpic_cpcht_softc *sc;
648	uint32_t ht_irq;
649
650	openpic_enable(dev, irq, vec);
651
652	sc = device_get_softc(dev);
653
654	if (cpcht_irqmap != NULL && irq < 128 &&
655	    cpcht_irqmap[irq].ht_base > 0) {
656		mtx_lock_spin(&sc->sc_ht_mtx);
657
658		/* Program the data port */
659		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
660		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
661
662		/* Unmask the interrupt */
663		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
664		ht_irq &= ~HTAPIC_MASK;
665		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
666
667		mtx_unlock_spin(&sc->sc_ht_mtx);
668	}
669
670	openpic_cpcht_eoi(dev, irq);
671}
672
673static void
674openpic_cpcht_unmask(device_t dev, u_int irq)
675{
676	struct openpic_cpcht_softc *sc;
677	uint32_t ht_irq;
678
679	openpic_unmask(dev, irq);
680
681	sc = device_get_softc(dev);
682
683	if (cpcht_irqmap != NULL && irq < 128 &&
684	    cpcht_irqmap[irq].ht_base > 0) {
685		mtx_lock_spin(&sc->sc_ht_mtx);
686
687		/* Program the data port */
688		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
689		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
690
691		/* Unmask the interrupt */
692		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
693		ht_irq &= ~HTAPIC_MASK;
694		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
695
696		mtx_unlock_spin(&sc->sc_ht_mtx);
697	}
698
699	openpic_cpcht_eoi(dev, irq);
700}
701
702static void
703openpic_cpcht_eoi(device_t dev, u_int irq)
704{
705	struct openpic_cpcht_softc *sc;
706	uint32_t off, mask;
707
708	if (irq == 255)
709		return;
710
711	sc = device_get_softc(dev);
712
713	if (cpcht_irqmap != NULL && irq < 128 &&
714	    cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
715		/* If this is an HT IRQ, acknowledge it at the remote APIC */
716
717		if (cpcht_irqmap[irq].apple_eoi) {
718			off = (cpcht_irqmap[irq].ht_source >> 3) & ~3;
719			mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f);
720			out32rb(cpcht_irqmap[irq].apple_eoi + off, mask);
721		} else {
722			mtx_lock_spin(&sc->sc_ht_mtx);
723
724			out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
725			    0x11 + (cpcht_irqmap[irq].ht_source << 1));
726			out32rb(cpcht_irqmap[irq].ht_base + 4,
727			    cpcht_irqmap[irq].eoi_data);
728
729			mtx_unlock_spin(&sc->sc_ht_mtx);
730		}
731	}
732
733	openpic_eoi(dev, irq);
734}
735