1190681Snwhitehorn/*-
2208149Snwhitehorn * Copyright (C) 2008-2010 Nathan Whitehorn
3190681Snwhitehorn * All rights reserved.
4190681Snwhitehorn *
5190681Snwhitehorn * Redistribution and use in source and binary forms, with or without
6190681Snwhitehorn * modification, are permitted provided that the following conditions
7190681Snwhitehorn * are met:
8190681Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9190681Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10190681Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11190681Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12190681Snwhitehorn *    documentation and/or other materials provided with the distribution.
13190681Snwhitehorn *
14190681Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15190681Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16190681Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17190681Snwhitehorn * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18190681Snwhitehorn * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19190681Snwhitehorn * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20190681Snwhitehorn * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21190681Snwhitehorn * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22190681Snwhitehorn * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23190681Snwhitehorn * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24190681Snwhitehorn */
25190681Snwhitehorn
26229093Shselasky#include <sys/cdefs.h>
27229093Shselasky__FBSDID("$FreeBSD$");
28229093Shselasky
29190681Snwhitehorn#include <sys/param.h>
30190681Snwhitehorn#include <sys/systm.h>
31190681Snwhitehorn#include <sys/module.h>
32190681Snwhitehorn#include <sys/bus.h>
33190681Snwhitehorn#include <sys/conf.h>
34190681Snwhitehorn#include <sys/kernel.h>
35209310Snwhitehorn#include <sys/pciio.h>
36209310Snwhitehorn#include <sys/rman.h>
37190681Snwhitehorn
38190681Snwhitehorn#include <dev/ofw/openfirm.h>
39190681Snwhitehorn#include <dev/ofw/ofw_pci.h>
40190681Snwhitehorn
41190681Snwhitehorn#include <dev/pci/pcivar.h>
42190681Snwhitehorn#include <dev/pci/pcireg.h>
43190681Snwhitehorn
44190681Snwhitehorn#include <machine/bus.h>
45208149Snwhitehorn#include <machine/intr_machdep.h>
46190681Snwhitehorn#include <machine/md_var.h>
47208149Snwhitehorn#include <machine/openpicvar.h>
48190681Snwhitehorn#include <machine/pio.h>
49190681Snwhitehorn#include <machine/resource.h>
50190681Snwhitehorn
51190681Snwhitehorn#include <dev/ofw/ofw_bus.h>
52190681Snwhitehorn#include <dev/ofw/ofw_bus_subr.h>
53235060Snwhitehorn#include <powerpc/ofw/ofw_pci.h>
54190681Snwhitehorn
55190681Snwhitehorn#include <vm/vm.h>
56190681Snwhitehorn#include <vm/pmap.h>
57190681Snwhitehorn
58190681Snwhitehorn#include "pcib_if.h"
59208149Snwhitehorn#include "pic_if.h"
60190681Snwhitehorn
61190681Snwhitehorn/*
62208149Snwhitehorn * IBM CPC9X5 Hypertransport Device interface.
63190681Snwhitehorn */
64190681Snwhitehornstatic int		cpcht_probe(device_t);
65190681Snwhitehornstatic int		cpcht_attach(device_t);
66190681Snwhitehorn
67208149Snwhitehornstatic void		cpcht_configure_htbridge(device_t, phandle_t);
68190681Snwhitehorn
69190681Snwhitehorn/*
70190681Snwhitehorn * pcib interface.
71190681Snwhitehorn */
72208149Snwhitehornstatic u_int32_t	cpcht_read_config(device_t, u_int, u_int, u_int,
73190681Snwhitehorn			    u_int, int);
74208149Snwhitehornstatic void		cpcht_write_config(device_t, u_int, u_int, u_int,
75190681Snwhitehorn			    u_int, u_int32_t, int);
76235060Snwhitehornstatic int		cpcht_route_interrupt(device_t, device_t, int);
77209310Snwhitehornstatic int		cpcht_alloc_msi(device_t dev, device_t child,
78209310Snwhitehorn			    int count, int maxcount, int *irqs);
79209310Snwhitehornstatic int		cpcht_release_msi(device_t dev, device_t child,
80209310Snwhitehorn			    int count, int *irqs);
81209310Snwhitehornstatic int		cpcht_alloc_msix(device_t dev, device_t child,
82209310Snwhitehorn			    int *irq);
83209310Snwhitehornstatic int		cpcht_release_msix(device_t dev, device_t child,
84209310Snwhitehorn			    int irq);
85209310Snwhitehornstatic int		cpcht_map_msi(device_t dev, device_t child,
86209310Snwhitehorn			    int irq, uint64_t *addr, uint32_t *data);
87190681Snwhitehorn
88190681Snwhitehorn/*
89190681Snwhitehorn * Driver methods.
90190681Snwhitehorn */
91208149Snwhitehornstatic device_method_t	cpcht_methods[] = {
92190681Snwhitehorn	/* Device interface */
93208149Snwhitehorn	DEVMETHOD(device_probe,		cpcht_probe),
94208149Snwhitehorn	DEVMETHOD(device_attach,	cpcht_attach),
95190681Snwhitehorn
96190681Snwhitehorn	/* pcib interface */
97208149Snwhitehorn	DEVMETHOD(pcib_read_config,	cpcht_read_config),
98208149Snwhitehorn	DEVMETHOD(pcib_write_config,	cpcht_write_config),
99208149Snwhitehorn	DEVMETHOD(pcib_route_interrupt,	cpcht_route_interrupt),
100209310Snwhitehorn	DEVMETHOD(pcib_alloc_msi,	cpcht_alloc_msi),
101209310Snwhitehorn	DEVMETHOD(pcib_release_msi,	cpcht_release_msi),
102209310Snwhitehorn	DEVMETHOD(pcib_alloc_msix,	cpcht_alloc_msix),
103209310Snwhitehorn	DEVMETHOD(pcib_release_msix,	cpcht_release_msix),
104209310Snwhitehorn	DEVMETHOD(pcib_map_msi,		cpcht_map_msi),
105190681Snwhitehorn
106229093Shselasky	DEVMETHOD_END
107190681Snwhitehorn};
108190681Snwhitehorn
109208149Snwhitehornstruct cpcht_irq {
110209310Snwhitehorn	enum {
111209310Snwhitehorn	    IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL
112209310Snwhitehorn	}		irq_type;
113209310Snwhitehorn
114208149Snwhitehorn	int		ht_source;
115208149Snwhitehorn
116208149Snwhitehorn	vm_offset_t	ht_base;
117208149Snwhitehorn	vm_offset_t	apple_eoi;
118208149Snwhitehorn	uint32_t	eoi_data;
119208149Snwhitehorn	int		edge;
120208149Snwhitehorn};
121208149Snwhitehorn
122208149Snwhitehornstatic struct cpcht_irq *cpcht_irqmap = NULL;
123209310Snwhitehornuint32_t cpcht_msipic = 0;
124208149Snwhitehorn
125208149Snwhitehornstruct cpcht_softc {
126235060Snwhitehorn	struct ofw_pci_softc	pci_sc;
127208149Snwhitehorn	vm_offset_t		sc_data;
128208149Snwhitehorn	uint64_t		sc_populated_slots;
129208149Snwhitehorn
130208149Snwhitehorn	struct cpcht_irq	htirq_map[128];
131209310Snwhitehorn	struct mtx		htirq_mtx;
132208149Snwhitehorn};
133208149Snwhitehorn
134208149Snwhitehornstatic devclass_t	cpcht_devclass;
135235060SnwhitehornDEFINE_CLASS_1(pcib, cpcht_driver, cpcht_methods, sizeof(struct cpcht_softc),
136235060Snwhitehorn    ofw_pci_driver);
137208149SnwhitehornDRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0);
138190681Snwhitehorn
139214575Snwhitehorn#define CPCHT_IOPORT_BASE	0xf4000000UL /* Hardwired */
140214575Snwhitehorn#define CPCHT_IOPORT_SIZE	0x00400000UL
141214575Snwhitehorn
142208149Snwhitehorn#define HTAPIC_REQUEST_EOI	0x20
143208149Snwhitehorn#define HTAPIC_TRIGGER_LEVEL	0x02
144208149Snwhitehorn#define HTAPIC_MASK		0x01
145208149Snwhitehorn
146190681Snwhitehornstatic int
147208149Snwhitehorncpcht_probe(device_t dev)
148190681Snwhitehorn{
149208149Snwhitehorn	const char	*type, *compatible;
150190681Snwhitehorn
151190681Snwhitehorn	type = ofw_bus_get_type(dev);
152208149Snwhitehorn	compatible = ofw_bus_get_compat(dev);
153190681Snwhitehorn
154208149Snwhitehorn	if (type == NULL || compatible == NULL)
155190681Snwhitehorn		return (ENXIO);
156190681Snwhitehorn
157208149Snwhitehorn	if (strcmp(type, "ht") != 0)
158190681Snwhitehorn		return (ENXIO);
159190681Snwhitehorn
160208149Snwhitehorn	if (strcmp(compatible, "u3-ht") != 0)
161208149Snwhitehorn		return (ENXIO);
162208149Snwhitehorn
163208149Snwhitehorn	device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel");
164190681Snwhitehorn	return (0);
165190681Snwhitehorn}
166190681Snwhitehorn
167190681Snwhitehornstatic int
168208149Snwhitehorncpcht_attach(device_t dev)
169190681Snwhitehorn{
170208149Snwhitehorn	struct		cpcht_softc *sc;
171208149Snwhitehorn	phandle_t	node, child;
172208149Snwhitehorn	u_int32_t	reg[3];
173235060Snwhitehorn	int		i;
174190681Snwhitehorn
175190681Snwhitehorn	node = ofw_bus_get_node(dev);
176190681Snwhitehorn	sc = device_get_softc(dev);
177190681Snwhitehorn
178208149Snwhitehorn	if (OF_getprop(node, "reg", reg, sizeof(reg)) < 12)
179190681Snwhitehorn		return (ENXIO);
180190681Snwhitehorn
181235060Snwhitehorn	if (OF_getproplen(node, "ranges") <= 0)
182235060Snwhitehorn		sc->pci_sc.sc_quirks = OFW_PCI_QUIRK_RANGES_ON_CHILDREN;
183208149Snwhitehorn	sc->sc_populated_slots = 0;
184208149Snwhitehorn	sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]);
185190681Snwhitehorn
186208149Snwhitehorn	/*
187208149Snwhitehorn	 * Set up the resource manager and the HT->MPIC mapping. For cpcht,
188208149Snwhitehorn	 * the ranges are properties of the child bridges, and this is also
189208149Snwhitehorn	 * where we get the HT interrupts properties.
190208149Snwhitehorn	 */
191190681Snwhitehorn
192235060Snwhitehorn#if 0
193214575Snwhitehorn	/* I/O port mappings are usually not in the device tree */
194235060Snwhitehorn	rman_manage_region(&sc->pci_sc.sc_io_rman, 0, CPCHT_IOPORT_SIZE - 1);
195235060Snwhitehorn#endif
196214575Snwhitehorn
197208149Snwhitehorn	bzero(sc->htirq_map, sizeof(sc->htirq_map));
198209310Snwhitehorn	mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF);
199209310Snwhitehorn	for (i = 0; i < 8; i++)
200209310Snwhitehorn		sc->htirq_map[i].irq_type = IRQ_INTERNAL;
201208149Snwhitehorn	for (child = OF_child(node); child != 0; child = OF_peer(child))
202208149Snwhitehorn		cpcht_configure_htbridge(dev, child);
203208149Snwhitehorn
204208149Snwhitehorn	/* Now make the mapping table available to the MPIC */
205208149Snwhitehorn	cpcht_irqmap = sc->htirq_map;
206208149Snwhitehorn
207235060Snwhitehorn	return (ofw_pci_attach(dev));
208208149Snwhitehorn}
209208149Snwhitehorn
210208149Snwhitehornstatic void
211208149Snwhitehorncpcht_configure_htbridge(device_t dev, phandle_t child)
212208149Snwhitehorn{
213208149Snwhitehorn	struct cpcht_softc *sc;
214208149Snwhitehorn	struct ofw_pci_register pcir;
215235060Snwhitehorn	int ptr, nextptr;
216208149Snwhitehorn	uint32_t vend, val;
217208149Snwhitehorn	int i, nirq, irq;
218235060Snwhitehorn	u_int b, f, s;
219208149Snwhitehorn
220208149Snwhitehorn	sc = device_get_softc(dev);
221208149Snwhitehorn	if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1)
222208149Snwhitehorn		return;
223208149Snwhitehorn
224235060Snwhitehorn	b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi);
225208149Snwhitehorn	s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
226208149Snwhitehorn	f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
227208149Snwhitehorn
228208149Snwhitehorn	/*
229208149Snwhitehorn	 * Mark this slot is populated. The remote south bridge does
230208149Snwhitehorn	 * not like us talking to unpopulated slots on the root bus.
231208149Snwhitehorn	 */
232208149Snwhitehorn	sc->sc_populated_slots |= (1 << s);
233208149Snwhitehorn
234208149Snwhitehorn	/*
235208149Snwhitehorn	 * Next build up any HT->MPIC mappings for this sub-bus. One would
236208149Snwhitehorn	 * naively hope that enabling, disabling, and EOIing interrupts would
237208149Snwhitehorn	 * cause the appropriate HT bus transactions to that effect. This is
238208149Snwhitehorn	 * not the case.
239208149Snwhitehorn	 *
240208149Snwhitehorn	 * Instead, we have to muck about on the HT peer's root PCI bridges,
241208149Snwhitehorn	 * figure out what interrupts they send, enable them, and cache
242208149Snwhitehorn	 * the location of their WaitForEOI registers so that we can
243208149Snwhitehorn	 * send EOIs later.
244208149Snwhitehorn	 */
245190681Snwhitehorn
246208149Snwhitehorn	/* All the devices we are interested in have caps */
247235060Snwhitehorn	if (!(PCIB_READ_CONFIG(dev, b, s, f, PCIR_STATUS, 2)
248208149Snwhitehorn	    & PCIM_STATUS_CAPPRESENT))
249208149Snwhitehorn		return;
250190681Snwhitehorn
251235060Snwhitehorn	nextptr = PCIB_READ_CONFIG(dev, b, s, f, PCIR_CAP_PTR, 1);
252208149Snwhitehorn	while (nextptr != 0) {
253208149Snwhitehorn		ptr = nextptr;
254235060Snwhitehorn		nextptr = PCIB_READ_CONFIG(dev, b, s, f,
255208149Snwhitehorn		    ptr + PCICAP_NEXTPTR, 1);
256190681Snwhitehorn
257208149Snwhitehorn		/* Find the HT IRQ capabilities */
258235060Snwhitehorn		if (PCIB_READ_CONFIG(dev, b, s, f,
259208149Snwhitehorn		    ptr + PCICAP_ID, 1) != PCIY_HT)
260208149Snwhitehorn			continue;
261190681Snwhitehorn
262235060Snwhitehorn		val = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 2);
263208149Snwhitehorn		if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT)
264208149Snwhitehorn			continue;
265190681Snwhitehorn
266208149Snwhitehorn		/* Ask for the IRQ count */
267235060Snwhitehorn		PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1);
268235060Snwhitehorn		nirq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4);
269208149Snwhitehorn		nirq = ((nirq >> 16) & 0xff) + 1;
270208149Snwhitehorn
271208149Snwhitehorn		device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f);
272208149Snwhitehorn
273208149Snwhitehorn		for (i = 0; i < nirq; i++) {
274235060Snwhitehorn			PCIB_WRITE_CONFIG(dev, b, s, f,
275208149Snwhitehorn			     ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1);
276235060Snwhitehorn			irq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4);
277208149Snwhitehorn
278208149Snwhitehorn			/*
279208149Snwhitehorn			 * Mask this interrupt for now.
280208149Snwhitehorn			 */
281235060Snwhitehorn			PCIB_WRITE_CONFIG(dev, b, s, f, ptr + 4,
282208149Snwhitehorn			    irq | HTAPIC_MASK, 4);
283208149Snwhitehorn			irq = (irq >> 16) & 0xff;
284208149Snwhitehorn
285209310Snwhitehorn			sc->htirq_map[irq].irq_type = IRQ_HT;
286208149Snwhitehorn			sc->htirq_map[irq].ht_source = i;
287208149Snwhitehorn			sc->htirq_map[irq].ht_base = sc->sc_data +
288208149Snwhitehorn			    (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr));
289208149Snwhitehorn
290235060Snwhitehorn			PCIB_WRITE_CONFIG(dev, b, s, f,
291208149Snwhitehorn			     ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1);
292208149Snwhitehorn			sc->htirq_map[irq].eoi_data =
293235060Snwhitehorn			    PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4) |
294208149Snwhitehorn			    0x80000000;
295208149Snwhitehorn
296208149Snwhitehorn			/*
297208149Snwhitehorn			 * Apple uses a non-compliant IO/APIC that differs
298208149Snwhitehorn			 * in how we signal EOIs. Check if this device was
299208149Snwhitehorn			 * made by Apple, and act accordingly.
300208149Snwhitehorn			 */
301235060Snwhitehorn			vend = PCIB_READ_CONFIG(dev, b, s, f,
302208149Snwhitehorn			    PCIR_DEVVENDOR, 4);
303208149Snwhitehorn			if ((vend & 0xffff) == 0x106b)
304208149Snwhitehorn				sc->htirq_map[irq].apple_eoi =
305208149Snwhitehorn				 (sc->htirq_map[irq].ht_base - ptr) + 0x60;
306208149Snwhitehorn		}
307208149Snwhitehorn	}
308190681Snwhitehorn}
309190681Snwhitehorn
310190681Snwhitehornstatic u_int32_t
311208149Snwhitehorncpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
312190681Snwhitehorn    int width)
313190681Snwhitehorn{
314208149Snwhitehorn	struct		cpcht_softc *sc;
315190681Snwhitehorn	vm_offset_t	caoff;
316190681Snwhitehorn
317190681Snwhitehorn	sc = device_get_softc(dev);
318190681Snwhitehorn	caoff = sc->sc_data +
319190681Snwhitehorn		(((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
320190681Snwhitehorn
321208149Snwhitehorn	if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
322208149Snwhitehorn		return (0xffffffff);
323208149Snwhitehorn
324208149Snwhitehorn	if (bus > 0)
325208149Snwhitehorn		caoff += 0x01000000UL + (bus << 16);
326208149Snwhitehorn
327190681Snwhitehorn	switch (width) {
328190681Snwhitehorn	case 1:
329190681Snwhitehorn		return (in8rb(caoff));
330190681Snwhitehorn		break;
331190681Snwhitehorn	case 2:
332190681Snwhitehorn		return (in16rb(caoff));
333190681Snwhitehorn		break;
334190681Snwhitehorn	case 4:
335190681Snwhitehorn		return (in32rb(caoff));
336190681Snwhitehorn		break;
337190681Snwhitehorn	}
338190681Snwhitehorn
339190681Snwhitehorn	return (0xffffffff);
340190681Snwhitehorn}
341190681Snwhitehorn
342190681Snwhitehornstatic void
343208149Snwhitehorncpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func,
344190681Snwhitehorn    u_int reg, u_int32_t val, int width)
345190681Snwhitehorn{
346208149Snwhitehorn	struct		cpcht_softc *sc;
347190681Snwhitehorn	vm_offset_t	caoff;
348190681Snwhitehorn
349190681Snwhitehorn	sc = device_get_softc(dev);
350190681Snwhitehorn	caoff = sc->sc_data +
351190681Snwhitehorn		(((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg);
352190681Snwhitehorn
353208149Snwhitehorn	if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0))
354208149Snwhitehorn		return;
355208149Snwhitehorn
356208149Snwhitehorn	if (bus > 0)
357208149Snwhitehorn		caoff += 0x01000000UL + (bus << 16);
358208149Snwhitehorn
359190681Snwhitehorn	switch (width) {
360190681Snwhitehorn	case 1:
361190681Snwhitehorn		out8rb(caoff, val);
362190681Snwhitehorn		break;
363190681Snwhitehorn	case 2:
364190681Snwhitehorn		out16rb(caoff, val);
365190681Snwhitehorn		break;
366190681Snwhitehorn	case 4:
367190681Snwhitehorn		out32rb(caoff, val);
368190681Snwhitehorn		break;
369190681Snwhitehorn	}
370190681Snwhitehorn}
371190681Snwhitehorn
372190681Snwhitehornstatic int
373208149Snwhitehorncpcht_route_interrupt(device_t bus, device_t dev, int pin)
374208149Snwhitehorn{
375208149Snwhitehorn	return (pin);
376208149Snwhitehorn}
377208149Snwhitehorn
378190681Snwhitehornstatic int
379209310Snwhitehorncpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount,
380209310Snwhitehorn    int *irqs)
381209310Snwhitehorn{
382209310Snwhitehorn	struct cpcht_softc *sc;
383209310Snwhitehorn	int i, j;
384209310Snwhitehorn
385209310Snwhitehorn	sc = device_get_softc(dev);
386209310Snwhitehorn	j = 0;
387209310Snwhitehorn
388209310Snwhitehorn	/* Bail if no MSI PIC yet */
389209310Snwhitehorn	if (cpcht_msipic == 0)
390209310Snwhitehorn		return (ENXIO);
391209310Snwhitehorn
392209310Snwhitehorn	mtx_lock(&sc->htirq_mtx);
393209310Snwhitehorn	for (i = 8; i < 124 - count; i++) {
394209310Snwhitehorn		for (j = 0; j < count; j++) {
395209310Snwhitehorn			if (sc->htirq_map[i+j].irq_type != IRQ_NONE)
396209310Snwhitehorn				break;
397209310Snwhitehorn		}
398209310Snwhitehorn		if (j == count)
399209310Snwhitehorn			break;
400209310Snwhitehorn
401209310Snwhitehorn		i += j; /* We know there isn't a large enough run */
402209310Snwhitehorn	}
403209310Snwhitehorn
404209310Snwhitehorn	if (j != count) {
405209310Snwhitehorn		mtx_unlock(&sc->htirq_mtx);
406209310Snwhitehorn		return (ENXIO);
407209310Snwhitehorn	}
408209310Snwhitehorn
409209310Snwhitehorn	for (j = 0; j < count; j++) {
410218184Smarcel		irqs[j] = MAP_IRQ(cpcht_msipic, i+j);
411209310Snwhitehorn		sc->htirq_map[i+j].irq_type = IRQ_MSI;
412209310Snwhitehorn	}
413209310Snwhitehorn	mtx_unlock(&sc->htirq_mtx);
414209310Snwhitehorn
415209310Snwhitehorn	return (0);
416209310Snwhitehorn}
417209310Snwhitehorn
418209310Snwhitehornstatic int
419209310Snwhitehorncpcht_release_msi(device_t dev, device_t child, int count, int *irqs)
420209310Snwhitehorn{
421209310Snwhitehorn	struct cpcht_softc *sc;
422209310Snwhitehorn	int i;
423209310Snwhitehorn
424209310Snwhitehorn	sc = device_get_softc(dev);
425209310Snwhitehorn
426209310Snwhitehorn	mtx_lock(&sc->htirq_mtx);
427209310Snwhitehorn	for (i = 0; i < count; i++)
428209310Snwhitehorn		sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE;
429209310Snwhitehorn	mtx_unlock(&sc->htirq_mtx);
430209310Snwhitehorn
431209310Snwhitehorn	return (0);
432209310Snwhitehorn}
433209310Snwhitehorn
434209310Snwhitehornstatic int
435209310Snwhitehorncpcht_alloc_msix(device_t dev, device_t child, int *irq)
436209310Snwhitehorn{
437209310Snwhitehorn	struct cpcht_softc *sc;
438209310Snwhitehorn	int i;
439209310Snwhitehorn
440209310Snwhitehorn	sc = device_get_softc(dev);
441209310Snwhitehorn
442209310Snwhitehorn	/* Bail if no MSI PIC yet */
443209310Snwhitehorn	if (cpcht_msipic == 0)
444209310Snwhitehorn		return (ENXIO);
445209310Snwhitehorn
446209310Snwhitehorn	mtx_lock(&sc->htirq_mtx);
447209310Snwhitehorn	for (i = 8; i < 124; i++) {
448209310Snwhitehorn		if (sc->htirq_map[i].irq_type == IRQ_NONE) {
449209310Snwhitehorn			sc->htirq_map[i].irq_type = IRQ_MSI;
450218184Smarcel			*irq = MAP_IRQ(cpcht_msipic, i);
451209310Snwhitehorn
452209310Snwhitehorn			mtx_unlock(&sc->htirq_mtx);
453209310Snwhitehorn			return (0);
454209310Snwhitehorn		}
455209310Snwhitehorn	}
456209310Snwhitehorn	mtx_unlock(&sc->htirq_mtx);
457209310Snwhitehorn
458209310Snwhitehorn	return (ENXIO);
459209310Snwhitehorn}
460209310Snwhitehorn
461209310Snwhitehornstatic int
462209310Snwhitehorncpcht_release_msix(device_t dev, device_t child, int irq)
463209310Snwhitehorn{
464209310Snwhitehorn	struct cpcht_softc *sc;
465209310Snwhitehorn
466209310Snwhitehorn	sc = device_get_softc(dev);
467209310Snwhitehorn
468209310Snwhitehorn	mtx_lock(&sc->htirq_mtx);
469209310Snwhitehorn	sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE;
470209310Snwhitehorn	mtx_unlock(&sc->htirq_mtx);
471209310Snwhitehorn
472209310Snwhitehorn	return (0);
473209310Snwhitehorn}
474209310Snwhitehorn
475209310Snwhitehornstatic int
476209310Snwhitehorncpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr,
477209310Snwhitehorn    uint32_t *data)
478209310Snwhitehorn{
479209310Snwhitehorn	device_t pcib;
480209310Snwhitehorn	struct pci_devinfo *dinfo;
481209310Snwhitehorn	struct pcicfg_ht *ht = NULL;
482209310Snwhitehorn
483209310Snwhitehorn	for (pcib = child; pcib != dev; pcib =
484209310Snwhitehorn	    device_get_parent(device_get_parent(pcib))) {
485209310Snwhitehorn		dinfo = device_get_ivars(pcib);
486209310Snwhitehorn		ht = &dinfo->cfg.ht;
487209310Snwhitehorn
488209310Snwhitehorn		if (ht == NULL)
489209310Snwhitehorn			continue;
490209310Snwhitehorn	}
491209310Snwhitehorn
492209310Snwhitehorn	if (ht == NULL)
493209310Snwhitehorn		return (ENXIO);
494209310Snwhitehorn
495209310Snwhitehorn	*addr = ht->ht_msiaddr;
496209310Snwhitehorn	*data = irq & 0xff;
497209310Snwhitehorn
498209310Snwhitehorn	return (0);
499209310Snwhitehorn}
500209310Snwhitehorn
501208149Snwhitehorn/*
502208149Snwhitehorn * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945)
503208149Snwhitehorn */
504208149Snwhitehorn
505208149Snwhitehornstatic int	openpic_cpcht_probe(device_t);
506208149Snwhitehornstatic int	openpic_cpcht_attach(device_t);
507208149Snwhitehornstatic void	openpic_cpcht_config(device_t, u_int irq,
508208149Snwhitehorn		    enum intr_trigger trig, enum intr_polarity pol);
509208149Snwhitehornstatic void	openpic_cpcht_enable(device_t, u_int irq, u_int vector);
510208149Snwhitehornstatic void	openpic_cpcht_unmask(device_t, u_int irq);
511208149Snwhitehornstatic void	openpic_cpcht_eoi(device_t, u_int irq);
512208149Snwhitehorn
513208149Snwhitehornstatic device_method_t  openpic_cpcht_methods[] = {
514208149Snwhitehorn	/* Device interface */
515208149Snwhitehorn	DEVMETHOD(device_probe,		openpic_cpcht_probe),
516208149Snwhitehorn	DEVMETHOD(device_attach,	openpic_cpcht_attach),
517208149Snwhitehorn
518208149Snwhitehorn	/* PIC interface */
519209486Snwhitehorn	DEVMETHOD(pic_bind,		openpic_bind),
520208149Snwhitehorn	DEVMETHOD(pic_config,		openpic_cpcht_config),
521208149Snwhitehorn	DEVMETHOD(pic_dispatch,		openpic_dispatch),
522208149Snwhitehorn	DEVMETHOD(pic_enable,		openpic_cpcht_enable),
523208149Snwhitehorn	DEVMETHOD(pic_eoi,		openpic_cpcht_eoi),
524208149Snwhitehorn	DEVMETHOD(pic_ipi,		openpic_ipi),
525208149Snwhitehorn	DEVMETHOD(pic_mask,		openpic_mask),
526208149Snwhitehorn	DEVMETHOD(pic_unmask,		openpic_cpcht_unmask),
527208149Snwhitehorn
528208149Snwhitehorn	{ 0, 0 },
529208149Snwhitehorn};
530208149Snwhitehorn
531208149Snwhitehornstruct openpic_cpcht_softc {
532208149Snwhitehorn	struct openpic_softc sc_openpic;
533208149Snwhitehorn
534208149Snwhitehorn	struct mtx sc_ht_mtx;
535208149Snwhitehorn};
536208149Snwhitehorn
537208149Snwhitehornstatic driver_t openpic_cpcht_driver = {
538208149Snwhitehorn	"htpic",
539208149Snwhitehorn	openpic_cpcht_methods,
540208285Snwhitehorn	sizeof(struct openpic_cpcht_softc),
541208149Snwhitehorn};
542208149Snwhitehorn
543208149SnwhitehornDRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0);
544208149Snwhitehorn
545208149Snwhitehornstatic int
546208149Snwhitehornopenpic_cpcht_probe(device_t dev)
547208149Snwhitehorn{
548208149Snwhitehorn	const char *type = ofw_bus_get_type(dev);
549208149Snwhitehorn
550208149Snwhitehorn	if (strcmp(type, "open-pic") != 0)
551208149Snwhitehorn                return (ENXIO);
552208149Snwhitehorn
553208149Snwhitehorn	device_set_desc(dev, OPENPIC_DEVSTR);
554208149Snwhitehorn	return (0);
555208149Snwhitehorn}
556208149Snwhitehorn
557208149Snwhitehornstatic int
558208149Snwhitehornopenpic_cpcht_attach(device_t dev)
559208149Snwhitehorn{
560208149Snwhitehorn	struct openpic_cpcht_softc *sc;
561218075Smarcel	phandle_t node;
562208149Snwhitehorn	int err, irq;
563208149Snwhitehorn
564218075Smarcel	node = ofw_bus_get_node(dev);
565218075Smarcel	err = openpic_common_attach(dev, node);
566208149Snwhitehorn	if (err != 0)
567208149Snwhitehorn		return (err);
568208149Snwhitehorn
569208149Snwhitehorn	/*
570208149Snwhitehorn	 * The HT APIC stuff is not thread-safe, so we need a mutex to
571208149Snwhitehorn	 * protect it.
572208149Snwhitehorn	 */
573208149Snwhitehorn	sc = device_get_softc(dev);
574208149Snwhitehorn	mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN);
575208149Snwhitehorn
576208149Snwhitehorn	/*
577208149Snwhitehorn	 * Interrupts 0-3 are internally sourced and are level triggered
578208149Snwhitehorn	 * active low. Interrupts 4-123 are connected to a pulse generator
579208149Snwhitehorn	 * and should be programmed as edge triggered low-to-high.
580208149Snwhitehorn	 *
581208149Snwhitehorn	 * IBM CPC945 Manual, Section 9.3.
582208149Snwhitehorn	 */
583208149Snwhitehorn
584208149Snwhitehorn	for (irq = 0; irq < 4; irq++)
585208149Snwhitehorn		openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW);
586208149Snwhitehorn	for (irq = 4; irq < 124; irq++)
587208149Snwhitehorn		openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW);
588208149Snwhitehorn
589209310Snwhitehorn	/*
590209310Snwhitehorn	 * Use this PIC for MSI only if it is the root PIC. This may not
591209310Snwhitehorn	 * be necessary, but Linux does it, and I cannot find any U3 machines
592209310Snwhitehorn	 * with MSI devices to test.
593209310Snwhitehorn	 */
594209310Snwhitehorn	if (dev == root_pic)
595218075Smarcel		cpcht_msipic = node;
596209310Snwhitehorn
597208149Snwhitehorn	return (0);
598208149Snwhitehorn}
599208149Snwhitehorn
600208149Snwhitehornstatic void
601208149Snwhitehornopenpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig,
602208149Snwhitehorn    enum intr_polarity pol)
603208149Snwhitehorn{
604208149Snwhitehorn	struct openpic_cpcht_softc *sc;
605208149Snwhitehorn	uint32_t ht_irq;
606208149Snwhitehorn
607208149Snwhitehorn	/*
608208149Snwhitehorn	 * The interrupt settings for the MPIC are completely determined
609208149Snwhitehorn	 * by the internal wiring in the northbridge. Real changes to these
610208149Snwhitehorn	 * settings need to be negotiated with the remote IO-APIC on the HT
611208149Snwhitehorn	 * link.
612208149Snwhitehorn	 */
613208149Snwhitehorn
614208149Snwhitehorn	sc = device_get_softc(dev);
615208149Snwhitehorn
616208149Snwhitehorn	if (cpcht_irqmap != NULL && irq < 128 &&
617208149Snwhitehorn	    cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
618208149Snwhitehorn		mtx_lock_spin(&sc->sc_ht_mtx);
619208149Snwhitehorn
620208149Snwhitehorn		/* Program the data port */
621208149Snwhitehorn		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
622208149Snwhitehorn		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
623208149Snwhitehorn
624208149Snwhitehorn		/* Grab the IRQ config register */
625208149Snwhitehorn		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
626208149Snwhitehorn
627208149Snwhitehorn		/* Mask the IRQ while we fiddle settings */
628208149Snwhitehorn		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK);
629208149Snwhitehorn
630208149Snwhitehorn		/* Program the interrupt sense */
631208149Snwhitehorn		ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI);
632208149Snwhitehorn		if (trig == INTR_TRIGGER_EDGE) {
633208149Snwhitehorn			cpcht_irqmap[irq].edge = 1;
634208149Snwhitehorn		} else {
635208149Snwhitehorn			cpcht_irqmap[irq].edge = 0;
636208149Snwhitehorn			ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI;
637208149Snwhitehorn		}
638208149Snwhitehorn		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
639208149Snwhitehorn
640208149Snwhitehorn		mtx_unlock_spin(&sc->sc_ht_mtx);
641208149Snwhitehorn	}
642208149Snwhitehorn}
643208149Snwhitehorn
644208149Snwhitehornstatic void
645208149Snwhitehornopenpic_cpcht_enable(device_t dev, u_int irq, u_int vec)
646208149Snwhitehorn{
647208149Snwhitehorn	struct openpic_cpcht_softc *sc;
648208149Snwhitehorn	uint32_t ht_irq;
649208149Snwhitehorn
650208149Snwhitehorn	openpic_enable(dev, irq, vec);
651208149Snwhitehorn
652208149Snwhitehorn	sc = device_get_softc(dev);
653208149Snwhitehorn
654208149Snwhitehorn	if (cpcht_irqmap != NULL && irq < 128 &&
655208149Snwhitehorn	    cpcht_irqmap[irq].ht_base > 0) {
656208149Snwhitehorn		mtx_lock_spin(&sc->sc_ht_mtx);
657208149Snwhitehorn
658208149Snwhitehorn		/* Program the data port */
659208149Snwhitehorn		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
660208149Snwhitehorn		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
661208149Snwhitehorn
662208149Snwhitehorn		/* Unmask the interrupt */
663208149Snwhitehorn		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
664208149Snwhitehorn		ht_irq &= ~HTAPIC_MASK;
665208149Snwhitehorn		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
666208149Snwhitehorn
667208149Snwhitehorn		mtx_unlock_spin(&sc->sc_ht_mtx);
668208149Snwhitehorn	}
669208149Snwhitehorn
670208149Snwhitehorn	openpic_cpcht_eoi(dev, irq);
671208149Snwhitehorn}
672208149Snwhitehorn
673208149Snwhitehornstatic void
674208149Snwhitehornopenpic_cpcht_unmask(device_t dev, u_int irq)
675208149Snwhitehorn{
676208149Snwhitehorn	struct openpic_cpcht_softc *sc;
677208149Snwhitehorn	uint32_t ht_irq;
678208149Snwhitehorn
679208149Snwhitehorn	openpic_unmask(dev, irq);
680208149Snwhitehorn
681208149Snwhitehorn	sc = device_get_softc(dev);
682208149Snwhitehorn
683208149Snwhitehorn	if (cpcht_irqmap != NULL && irq < 128 &&
684208149Snwhitehorn	    cpcht_irqmap[irq].ht_base > 0) {
685208149Snwhitehorn		mtx_lock_spin(&sc->sc_ht_mtx);
686208149Snwhitehorn
687208149Snwhitehorn		/* Program the data port */
688208149Snwhitehorn		out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
689208149Snwhitehorn		    0x10 + (cpcht_irqmap[irq].ht_source << 1));
690208149Snwhitehorn
691208149Snwhitehorn		/* Unmask the interrupt */
692208149Snwhitehorn		ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4);
693208149Snwhitehorn		ht_irq &= ~HTAPIC_MASK;
694208149Snwhitehorn		out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq);
695208149Snwhitehorn
696208149Snwhitehorn		mtx_unlock_spin(&sc->sc_ht_mtx);
697208149Snwhitehorn	}
698208149Snwhitehorn
699208149Snwhitehorn	openpic_cpcht_eoi(dev, irq);
700208149Snwhitehorn}
701208149Snwhitehorn
702208149Snwhitehornstatic void
703208149Snwhitehornopenpic_cpcht_eoi(device_t dev, u_int irq)
704208149Snwhitehorn{
705208149Snwhitehorn	struct openpic_cpcht_softc *sc;
706208149Snwhitehorn	uint32_t off, mask;
707208149Snwhitehorn
708208149Snwhitehorn	if (irq == 255)
709208149Snwhitehorn		return;
710208149Snwhitehorn
711208149Snwhitehorn	sc = device_get_softc(dev);
712208149Snwhitehorn
713208149Snwhitehorn	if (cpcht_irqmap != NULL && irq < 128 &&
714208149Snwhitehorn	    cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) {
715208149Snwhitehorn		/* If this is an HT IRQ, acknowledge it at the remote APIC */
716208149Snwhitehorn
717208149Snwhitehorn		if (cpcht_irqmap[irq].apple_eoi) {
718208149Snwhitehorn			off = (cpcht_irqmap[irq].ht_source >> 3) & ~3;
719208149Snwhitehorn			mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f);
720208149Snwhitehorn			out32rb(cpcht_irqmap[irq].apple_eoi + off, mask);
721208149Snwhitehorn		} else {
722208149Snwhitehorn			mtx_lock_spin(&sc->sc_ht_mtx);
723208149Snwhitehorn
724208149Snwhitehorn			out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND,
725208149Snwhitehorn			    0x11 + (cpcht_irqmap[irq].ht_source << 1));
726208149Snwhitehorn			out32rb(cpcht_irqmap[irq].ht_base + 4,
727208149Snwhitehorn			    cpcht_irqmap[irq].eoi_data);
728208149Snwhitehorn
729208149Snwhitehorn			mtx_unlock_spin(&sc->sc_ht_mtx);
730208149Snwhitehorn		}
731208149Snwhitehorn	}
732208149Snwhitehorn
733208149Snwhitehorn	openpic_eoi(dev, irq);
734208149Snwhitehorn}
735