1255643Snwhitehorn/*-
2255643Snwhitehorn * Copyright 2011 Nathan Whitehorn
3255643Snwhitehorn *
4255643Snwhitehorn * Redistribution and use in source and binary forms, with or without
5255643Snwhitehorn * modification, are permitted provided that the following conditions
6255643Snwhitehorn * are met:
7255643Snwhitehorn * 1. Redistributions of source code must retain the above copyright
8255643Snwhitehorn *    notice, this list of conditions and the following disclaimer.
9255643Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
10255643Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
11255643Snwhitehorn *    documentation and/or other materials provided with the distribution.
12255643Snwhitehorn *
13255643Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14255643Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15255643Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16255643Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17255643Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18255643Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19255643Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20255643Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21255643Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22255643Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23255643Snwhitehorn * SUCH DAMAGE.
24255643Snwhitehorn */
25255643Snwhitehorn
26255643Snwhitehorn#include <sys/cdefs.h>
27255643Snwhitehorn__FBSDID("$FreeBSD: releng/10.3/sys/powerpc/pseries/xics.c 266160 2014-05-15 17:30:16Z ian $");
28255643Snwhitehorn
29255643Snwhitehorn#include <sys/param.h>
30255643Snwhitehorn#include <sys/systm.h>
31255643Snwhitehorn#include <sys/module.h>
32255643Snwhitehorn#include <sys/bus.h>
33255643Snwhitehorn#include <sys/conf.h>
34255643Snwhitehorn#include <sys/kernel.h>
35255643Snwhitehorn#include <sys/malloc.h>
36255643Snwhitehorn#include <sys/smp.h>
37255643Snwhitehorn
38255643Snwhitehorn#include <vm/vm.h>
39255643Snwhitehorn#include <vm/pmap.h>
40255643Snwhitehorn
41255643Snwhitehorn#include <machine/bus.h>
42255643Snwhitehorn#include <machine/intr_machdep.h>
43255643Snwhitehorn#include <machine/md_var.h>
44255643Snwhitehorn#include <machine/rtas.h>
45255643Snwhitehorn
46255643Snwhitehorn#include <dev/ofw/ofw_bus.h>
47255643Snwhitehorn#include <dev/ofw/ofw_bus_subr.h>
48255643Snwhitehorn
49255643Snwhitehorn#include "phyp-hvcall.h"
50255643Snwhitehorn#include "pic_if.h"
51255643Snwhitehorn
52255643Snwhitehorn#define XICP_PRIORITY	5	/* Random non-zero number */
53255643Snwhitehorn#define XICP_IPI	2
54255643Snwhitehorn#define MAX_XICP_IRQS	(1<<24)	/* 24-bit XIRR field */
55255643Snwhitehorn
56255643Snwhitehornstatic int	xicp_probe(device_t);
57255643Snwhitehornstatic int	xicp_attach(device_t);
58255643Snwhitehornstatic int	xics_probe(device_t);
59255643Snwhitehornstatic int	xics_attach(device_t);
60255643Snwhitehorn
61255643Snwhitehornstatic void	xicp_bind(device_t dev, u_int irq, cpuset_t cpumask);
62255643Snwhitehornstatic void	xicp_dispatch(device_t, struct trapframe *);
63255643Snwhitehornstatic void	xicp_enable(device_t, u_int, u_int);
64255643Snwhitehornstatic void	xicp_eoi(device_t, u_int);
65255643Snwhitehornstatic void	xicp_ipi(device_t, u_int);
66255643Snwhitehornstatic void	xicp_mask(device_t, u_int);
67255643Snwhitehornstatic void	xicp_unmask(device_t, u_int);
68255643Snwhitehorn
69255643Snwhitehornstatic device_method_t  xicp_methods[] = {
70255643Snwhitehorn	/* Device interface */
71255643Snwhitehorn	DEVMETHOD(device_probe,		xicp_probe),
72255643Snwhitehorn	DEVMETHOD(device_attach,	xicp_attach),
73255643Snwhitehorn
74255643Snwhitehorn	/* PIC interface */
75255643Snwhitehorn	DEVMETHOD(pic_bind,		xicp_bind),
76255643Snwhitehorn	DEVMETHOD(pic_dispatch,		xicp_dispatch),
77255643Snwhitehorn	DEVMETHOD(pic_enable,		xicp_enable),
78255643Snwhitehorn	DEVMETHOD(pic_eoi,		xicp_eoi),
79255643Snwhitehorn	DEVMETHOD(pic_ipi,		xicp_ipi),
80255643Snwhitehorn	DEVMETHOD(pic_mask,		xicp_mask),
81255643Snwhitehorn	DEVMETHOD(pic_unmask,		xicp_unmask),
82255643Snwhitehorn
83255643Snwhitehorn	{ 0, 0 },
84255643Snwhitehorn};
85255643Snwhitehorn
86255643Snwhitehornstatic device_method_t  xics_methods[] = {
87255643Snwhitehorn	/* Device interface */
88255643Snwhitehorn	DEVMETHOD(device_probe,		xics_probe),
89255643Snwhitehorn	DEVMETHOD(device_attach,	xics_attach),
90255643Snwhitehorn
91255643Snwhitehorn	{ 0, 0 },
92255643Snwhitehorn};
93255643Snwhitehorn
94255643Snwhitehornstruct xicp_softc {
95255643Snwhitehorn	struct mtx sc_mtx;
96255643Snwhitehorn
97255643Snwhitehorn	int ibm_int_on;
98255643Snwhitehorn	int ibm_int_off;
99255643Snwhitehorn	int ibm_get_xive;
100255643Snwhitehorn	int ibm_set_xive;
101255643Snwhitehorn
102255643Snwhitehorn	/* XXX: inefficient -- hash table? tree? */
103255643Snwhitehorn	struct {
104255643Snwhitehorn		int irq;
105255643Snwhitehorn		int vector;
106255643Snwhitehorn	} intvecs[256];
107255643Snwhitehorn	int nintvecs;
108255643Snwhitehorn};
109255643Snwhitehorn
110255643Snwhitehornstatic driver_t xicp_driver = {
111255643Snwhitehorn	"xicp",
112255643Snwhitehorn	xicp_methods,
113255643Snwhitehorn	sizeof(struct xicp_softc)
114255643Snwhitehorn};
115255643Snwhitehorn
116255643Snwhitehornstatic driver_t xics_driver = {
117255643Snwhitehorn	"xics",
118255643Snwhitehorn	xics_methods,
119255643Snwhitehorn	0
120255643Snwhitehorn};
121255643Snwhitehorn
122255643Snwhitehornstatic devclass_t xicp_devclass;
123255643Snwhitehornstatic devclass_t xics_devclass;
124255643Snwhitehorn
125266160SianEARLY_DRIVER_MODULE(xicp, ofwbus, xicp_driver, xicp_devclass, 0, 0,
126255643Snwhitehorn    BUS_PASS_INTERRUPT-1);
127266160SianEARLY_DRIVER_MODULE(xics, ofwbus, xics_driver, xics_devclass, 0, 0,
128255643Snwhitehorn    BUS_PASS_INTERRUPT);
129255643Snwhitehorn
130255643Snwhitehornstatic int
131255643Snwhitehornxicp_probe(device_t dev)
132255643Snwhitehorn{
133255643Snwhitehorn	if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev),
134255643Snwhitehorn	    "interrupt-controller") != 0)
135255643Snwhitehorn		return (ENXIO);
136255643Snwhitehorn
137255643Snwhitehorn	if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp"))
138255643Snwhitehorn		return (ENXIO);
139255643Snwhitehorn
140255643Snwhitehorn	device_set_desc(dev, "PAPR virtual interrupt controller");
141255643Snwhitehorn	return (BUS_PROBE_GENERIC);
142255643Snwhitehorn}
143255643Snwhitehorn
144255643Snwhitehornstatic int
145255643Snwhitehornxics_probe(device_t dev)
146255643Snwhitehorn{
147255643Snwhitehorn	if (ofw_bus_get_name(dev) == NULL || strcmp(ofw_bus_get_name(dev),
148255643Snwhitehorn	    "interrupt-controller") != 0)
149255643Snwhitehorn		return (ENXIO);
150255643Snwhitehorn
151255643Snwhitehorn	if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics"))
152255643Snwhitehorn		return (ENXIO);
153255643Snwhitehorn
154255643Snwhitehorn	device_set_desc(dev, "PAPR virtual interrupt source");
155255643Snwhitehorn	return (BUS_PROBE_GENERIC);
156255643Snwhitehorn}
157255643Snwhitehorn
158255643Snwhitehornstatic int
159255643Snwhitehornxicp_attach(device_t dev)
160255643Snwhitehorn{
161255643Snwhitehorn	struct xicp_softc *sc = device_get_softc(dev);
162255643Snwhitehorn	phandle_t phandle = ofw_bus_get_node(dev);
163255643Snwhitehorn
164255643Snwhitehorn	mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF);
165255643Snwhitehorn	sc->nintvecs = 0;
166255643Snwhitehorn
167255643Snwhitehorn	sc->ibm_int_on = rtas_token_lookup("ibm,int-on");
168255643Snwhitehorn	sc->ibm_int_off = rtas_token_lookup("ibm,int-off");
169255643Snwhitehorn	sc->ibm_set_xive = rtas_token_lookup("ibm,set-xive");
170255643Snwhitehorn	sc->ibm_get_xive = rtas_token_lookup("ibm,get-xive");
171255643Snwhitehorn
172255643Snwhitehorn	if (OF_getproplen(phandle, "ibm,phandle") > 0)
173255643Snwhitehorn		OF_getprop(phandle, "ibm,phandle", &phandle, sizeof(phandle));
174255643Snwhitehorn
175255643Snwhitehorn	powerpc_register_pic(dev, phandle, MAX_XICP_IRQS,
176255643Snwhitehorn	    1 /* Number of IPIs */, FALSE);
177255643Snwhitehorn	root_pic = dev;
178255643Snwhitehorn
179255643Snwhitehorn	return (0);
180255643Snwhitehorn}
181255643Snwhitehorn
182255643Snwhitehornstatic int
183255643Snwhitehornxics_attach(device_t dev)
184255643Snwhitehorn{
185255643Snwhitehorn	phandle_t phandle = ofw_bus_get_node(dev);
186255643Snwhitehorn
187255643Snwhitehorn	if (OF_getproplen(phandle, "ibm,phandle") > 0)
188255643Snwhitehorn		OF_getprop(phandle, "ibm,phandle", &phandle, sizeof(phandle));
189255643Snwhitehorn
190255643Snwhitehorn	/* The XICP (root PIC) will handle all our interrupts */
191255643Snwhitehorn	powerpc_register_pic(root_pic, phandle, MAX_XICP_IRQS,
192255643Snwhitehorn	    1 /* Number of IPIs */, FALSE);
193255643Snwhitehorn
194255643Snwhitehorn	return (0);
195255643Snwhitehorn}
196255643Snwhitehorn
197255643Snwhitehorn/*
198255643Snwhitehorn * PIC I/F methods.
199255643Snwhitehorn */
200255643Snwhitehorn
201255643Snwhitehornstatic void
202255643Snwhitehornxicp_bind(device_t dev, u_int irq, cpuset_t cpumask)
203255643Snwhitehorn{
204255643Snwhitehorn	struct xicp_softc *sc = device_get_softc(dev);
205255643Snwhitehorn	cell_t status, cpu;
206255643Snwhitehorn
207255643Snwhitehorn	/*
208255643Snwhitehorn	 * This doesn't appear to actually support affinity groups, so just
209255643Snwhitehorn	 * use the first CPU.
210255643Snwhitehorn	 */
211255643Snwhitehorn	CPU_FOREACH(cpu)
212255643Snwhitehorn		if (CPU_ISSET(cpu, &cpumask)) break;
213255643Snwhitehorn
214255643Snwhitehorn	rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY,
215255643Snwhitehorn	    &status);
216255643Snwhitehorn}
217255643Snwhitehorn
218255643Snwhitehornstatic void
219255643Snwhitehornxicp_dispatch(device_t dev, struct trapframe *tf)
220255643Snwhitehorn{
221255643Snwhitehorn	struct xicp_softc *sc;
222255643Snwhitehorn	uint64_t xirr, junk;
223255643Snwhitehorn	int i;
224255643Snwhitehorn
225255643Snwhitehorn	sc = device_get_softc(dev);
226255643Snwhitehorn	for (;;) {
227255643Snwhitehorn		/* Return value in R4, use the PFT call */
228255643Snwhitehorn		phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk);
229255643Snwhitehorn		xirr &= 0x00ffffff;
230255643Snwhitehorn
231255643Snwhitehorn		if (xirr == 0) { /* No more pending interrupts? */
232255643Snwhitehorn			phyp_hcall(H_CPPR, (uint64_t)0xff);
233255643Snwhitehorn			break;
234255643Snwhitehorn		}
235255643Snwhitehorn		if (xirr == XICP_IPI) {		/* Magic number for IPIs */
236255643Snwhitehorn			xirr = MAX_XICP_IRQS;	/* Map to FreeBSD magic */
237255643Snwhitehorn			phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(cpuid)),
238255643Snwhitehorn			    0xff); /* Clear IPI */
239255643Snwhitehorn		}
240255643Snwhitehorn
241255643Snwhitehorn		/* XXX: super inefficient */
242255643Snwhitehorn		for (i = 0; i < sc->nintvecs; i++) {
243255643Snwhitehorn			if (sc->intvecs[i].irq == xirr)
244255643Snwhitehorn				break;
245255643Snwhitehorn		}
246255643Snwhitehorn
247255643Snwhitehorn		KASSERT(i < sc->nintvecs, ("Unmapped XIRR"));
248255643Snwhitehorn		powerpc_dispatch_intr(sc->intvecs[i].vector, tf);
249255643Snwhitehorn	}
250255643Snwhitehorn}
251255643Snwhitehorn
252255643Snwhitehornstatic void
253255643Snwhitehornxicp_enable(device_t dev, u_int irq, u_int vector)
254255643Snwhitehorn{
255255643Snwhitehorn	struct xicp_softc *sc;
256255643Snwhitehorn	cell_t status, cpu;
257255643Snwhitehorn
258255643Snwhitehorn	sc = device_get_softc(dev);
259255643Snwhitehorn
260255643Snwhitehorn	KASSERT(sc->nintvecs + 1 < sizeof(sc->intvecs)/sizeof(sc->intvecs[0]),
261255643Snwhitehorn	    ("Too many XICP interrupts"));
262255643Snwhitehorn
263255643Snwhitehorn	mtx_lock(&sc->sc_mtx);
264255643Snwhitehorn	sc->intvecs[sc->nintvecs].irq = irq;
265255643Snwhitehorn	sc->intvecs[sc->nintvecs].vector = vector;
266255643Snwhitehorn	mb();
267255643Snwhitehorn	sc->nintvecs++;
268255643Snwhitehorn	mtx_unlock(&sc->sc_mtx);
269255643Snwhitehorn
270255643Snwhitehorn	/* IPIs are also enabled */
271255643Snwhitehorn	if (irq == MAX_XICP_IRQS)
272255643Snwhitehorn		return;
273255643Snwhitehorn
274255643Snwhitehorn	/* Bind to this CPU to start: distrib. ID is last entry in gserver# */
275255643Snwhitehorn	cpu = PCPU_GET(cpuid);
276255643Snwhitehorn	rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY,
277255643Snwhitehorn	    &status);
278255643Snwhitehorn	xicp_unmask(dev, irq);
279255643Snwhitehorn}
280255643Snwhitehorn
281255643Snwhitehornstatic void
282255643Snwhitehornxicp_eoi(device_t dev, u_int irq)
283255643Snwhitehorn{
284255643Snwhitehorn	uint64_t xirr;
285255643Snwhitehorn
286255643Snwhitehorn	if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */
287255643Snwhitehorn		irq = XICP_IPI;
288255643Snwhitehorn	xirr = irq | (XICP_PRIORITY << 24);
289255643Snwhitehorn
290255643Snwhitehorn	phyp_hcall(H_EOI, xirr);
291255643Snwhitehorn}
292255643Snwhitehorn
293255643Snwhitehornstatic void
294255643Snwhitehornxicp_ipi(device_t dev, u_int cpu)
295255643Snwhitehorn{
296255643Snwhitehorn
297255643Snwhitehorn	phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY);
298255643Snwhitehorn}
299255643Snwhitehorn
300255643Snwhitehornstatic void
301255643Snwhitehornxicp_mask(device_t dev, u_int irq)
302255643Snwhitehorn{
303255643Snwhitehorn	struct xicp_softc *sc = device_get_softc(dev);
304255643Snwhitehorn	cell_t status;
305255643Snwhitehorn
306255643Snwhitehorn	if (irq == MAX_XICP_IRQS)
307255643Snwhitehorn		return;
308255643Snwhitehorn
309255643Snwhitehorn	rtas_call_method(sc->ibm_int_off, 1, 1, irq, &status);
310255643Snwhitehorn}
311255643Snwhitehorn
312255643Snwhitehornstatic void
313255643Snwhitehornxicp_unmask(device_t dev, u_int irq)
314255643Snwhitehorn{
315255643Snwhitehorn	struct xicp_softc *sc = device_get_softc(dev);
316255643Snwhitehorn	cell_t status;
317255643Snwhitehorn
318255643Snwhitehorn	if (irq == MAX_XICP_IRQS)
319255643Snwhitehorn		return;
320255643Snwhitehorn
321255643Snwhitehorn	rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status);
322255643Snwhitehorn}
323255643Snwhitehorn
324