xref: /freebsd/sys/arm/nvidia/tegra_lic.c (revision ef2ee5d07af6ab1f4c33d67b23c0d7fbabb45c70)
1*ef2ee5d0SMichal Meloun /*-
2*ef2ee5d0SMichal Meloun  * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3*ef2ee5d0SMichal Meloun  * All rights reserved.
4*ef2ee5d0SMichal Meloun  *
5*ef2ee5d0SMichal Meloun  * Redistribution and use in source and binary forms, with or without
6*ef2ee5d0SMichal Meloun  * modification, are permitted provided that the following conditions
7*ef2ee5d0SMichal Meloun  * are met:
8*ef2ee5d0SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
9*ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
10*ef2ee5d0SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
11*ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
12*ef2ee5d0SMichal Meloun  *    documentation and/or other materials provided with the distribution.
13*ef2ee5d0SMichal Meloun  *
14*ef2ee5d0SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*ef2ee5d0SMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*ef2ee5d0SMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*ef2ee5d0SMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*ef2ee5d0SMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*ef2ee5d0SMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*ef2ee5d0SMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*ef2ee5d0SMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*ef2ee5d0SMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*ef2ee5d0SMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*ef2ee5d0SMichal Meloun  * SUCH DAMAGE.
25*ef2ee5d0SMichal Meloun  */
26*ef2ee5d0SMichal Meloun 
27*ef2ee5d0SMichal Meloun #include <sys/cdefs.h>
28*ef2ee5d0SMichal Meloun __FBSDID("$FreeBSD$");
29*ef2ee5d0SMichal Meloun 
30*ef2ee5d0SMichal Meloun /*
31*ef2ee5d0SMichal Meloun  * Local interrupt controller driver for Tegra SoCs.
32*ef2ee5d0SMichal Meloun  */
33*ef2ee5d0SMichal Meloun #include <sys/param.h>
34*ef2ee5d0SMichal Meloun #include <sys/module.h>
35*ef2ee5d0SMichal Meloun #include <sys/systm.h>
36*ef2ee5d0SMichal Meloun #include <sys/bus.h>
37*ef2ee5d0SMichal Meloun #include <sys/conf.h>
38*ef2ee5d0SMichal Meloun #include <sys/kernel.h>
39*ef2ee5d0SMichal Meloun #include <sys/rman.h>
40*ef2ee5d0SMichal Meloun 
41*ef2ee5d0SMichal Meloun #include <machine/fdt.h>
42*ef2ee5d0SMichal Meloun #include <machine/intr.h>
43*ef2ee5d0SMichal Meloun #include <machine/resource.h>
44*ef2ee5d0SMichal Meloun 
45*ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h>
46*ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
47*ef2ee5d0SMichal Meloun 
48*ef2ee5d0SMichal Meloun #include "pic_if.h"
49*ef2ee5d0SMichal Meloun 
50*ef2ee5d0SMichal Meloun #define	LIC_VIRQ_CPU		0x00
51*ef2ee5d0SMichal Meloun #define	LIC_VIRQ_COP		0x04
52*ef2ee5d0SMichal Meloun #define	LIC_VFRQ_CPU		0x08
53*ef2ee5d0SMichal Meloun #define	LIC_VFRQ_COP		0x0c
54*ef2ee5d0SMichal Meloun #define	LIC_ISR			0x10
55*ef2ee5d0SMichal Meloun #define	LIC_FIR			0x14
56*ef2ee5d0SMichal Meloun #define	LIC_FIR_SET		0x18
57*ef2ee5d0SMichal Meloun #define	LIC_FIR_CLR		0x1c
58*ef2ee5d0SMichal Meloun #define	LIC_CPU_IER		0x20
59*ef2ee5d0SMichal Meloun #define	LIC_CPU_IER_SET		0x24
60*ef2ee5d0SMichal Meloun #define	LIC_CPU_IER_CLR		0x28
61*ef2ee5d0SMichal Meloun #define	LIC_CPU_IEP_CLASS	0x2C
62*ef2ee5d0SMichal Meloun #define	LIC_COP_IER		0x30
63*ef2ee5d0SMichal Meloun #define	LIC_COP_IER_SET		0x34
64*ef2ee5d0SMichal Meloun #define	LIC_COP_IER_CLR		0x38
65*ef2ee5d0SMichal Meloun #define	LIC_COP_IEP_CLASS	0x3c
66*ef2ee5d0SMichal Meloun 
67*ef2ee5d0SMichal Meloun #define	WR4(_sc, _b, _r, _v)	bus_write_4((_sc)->mem_res[_b], (_r), (_v))
68*ef2ee5d0SMichal Meloun #define	RD4(_sc, _b, _r)	bus_read_4((_sc)->mem_res[_b], (_r))
69*ef2ee5d0SMichal Meloun 
70*ef2ee5d0SMichal Meloun static struct resource_spec lic_spec[] = {
71*ef2ee5d0SMichal Meloun 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
72*ef2ee5d0SMichal Meloun 	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },
73*ef2ee5d0SMichal Meloun 	{ SYS_RES_MEMORY,	2,	RF_ACTIVE },
74*ef2ee5d0SMichal Meloun 	{ SYS_RES_MEMORY,	3,	RF_ACTIVE },
75*ef2ee5d0SMichal Meloun 	{ SYS_RES_MEMORY,	4,	RF_ACTIVE },
76*ef2ee5d0SMichal Meloun 	{ -1, 0 }
77*ef2ee5d0SMichal Meloun };
78*ef2ee5d0SMichal Meloun 
79*ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = {
80*ef2ee5d0SMichal Meloun 	{"nvidia,tegra124-ictlr", 	1},
81*ef2ee5d0SMichal Meloun 	{NULL,				0}
82*ef2ee5d0SMichal Meloun };
83*ef2ee5d0SMichal Meloun 
84*ef2ee5d0SMichal Meloun struct tegra_lic_sc {
85*ef2ee5d0SMichal Meloun 	device_t		dev;
86*ef2ee5d0SMichal Meloun 	struct resource		*mem_res[nitems(lic_spec)];
87*ef2ee5d0SMichal Meloun 	device_t		parent;
88*ef2ee5d0SMichal Meloun };
89*ef2ee5d0SMichal Meloun 
90*ef2ee5d0SMichal Meloun static int
91*ef2ee5d0SMichal Meloun tegra_lic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu)
92*ef2ee5d0SMichal Meloun {
93*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
94*ef2ee5d0SMichal Meloun 
95*ef2ee5d0SMichal Meloun 	return (PIC_REGISTER(sc->parent, isrc, is_percpu));
96*ef2ee5d0SMichal Meloun }
97*ef2ee5d0SMichal Meloun 
98*ef2ee5d0SMichal Meloun static int
99*ef2ee5d0SMichal Meloun tegra_lic_unregister(device_t dev, struct intr_irqsrc *isrc)
100*ef2ee5d0SMichal Meloun {
101*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
102*ef2ee5d0SMichal Meloun 
103*ef2ee5d0SMichal Meloun 	return (PIC_UNREGISTER(sc->parent, isrc));
104*ef2ee5d0SMichal Meloun }
105*ef2ee5d0SMichal Meloun 
106*ef2ee5d0SMichal Meloun static void
107*ef2ee5d0SMichal Meloun tegra_lic_enable_source(device_t dev, struct intr_irqsrc *isrc)
108*ef2ee5d0SMichal Meloun {
109*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
110*ef2ee5d0SMichal Meloun 
111*ef2ee5d0SMichal Meloun 	PIC_ENABLE_SOURCE(sc->parent, isrc);
112*ef2ee5d0SMichal Meloun }
113*ef2ee5d0SMichal Meloun 
114*ef2ee5d0SMichal Meloun static void
115*ef2ee5d0SMichal Meloun tegra_lic_disable_source(device_t dev, struct intr_irqsrc *isrc)
116*ef2ee5d0SMichal Meloun {
117*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
118*ef2ee5d0SMichal Meloun 
119*ef2ee5d0SMichal Meloun 	PIC_DISABLE_SOURCE(sc->parent, isrc);
120*ef2ee5d0SMichal Meloun }
121*ef2ee5d0SMichal Meloun 
122*ef2ee5d0SMichal Meloun static void
123*ef2ee5d0SMichal Meloun tegra_lic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
124*ef2ee5d0SMichal Meloun {
125*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
126*ef2ee5d0SMichal Meloun 
127*ef2ee5d0SMichal Meloun 	PIC_ENABLE_INTR(sc->parent, isrc);
128*ef2ee5d0SMichal Meloun }
129*ef2ee5d0SMichal Meloun 
130*ef2ee5d0SMichal Meloun static void
131*ef2ee5d0SMichal Meloun tegra_lic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
132*ef2ee5d0SMichal Meloun {
133*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
134*ef2ee5d0SMichal Meloun 
135*ef2ee5d0SMichal Meloun 	PIC_PRE_ITHREAD(sc->parent, isrc);
136*ef2ee5d0SMichal Meloun }
137*ef2ee5d0SMichal Meloun 
138*ef2ee5d0SMichal Meloun 
139*ef2ee5d0SMichal Meloun static void
140*ef2ee5d0SMichal Meloun tegra_lic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
141*ef2ee5d0SMichal Meloun {
142*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
143*ef2ee5d0SMichal Meloun 
144*ef2ee5d0SMichal Meloun 	PIC_POST_ITHREAD(sc->parent, isrc);
145*ef2ee5d0SMichal Meloun }
146*ef2ee5d0SMichal Meloun 
147*ef2ee5d0SMichal Meloun static void
148*ef2ee5d0SMichal Meloun tegra_lic_post_filter(device_t dev, struct intr_irqsrc *isrc)
149*ef2ee5d0SMichal Meloun {
150*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
151*ef2ee5d0SMichal Meloun 
152*ef2ee5d0SMichal Meloun 	PIC_POST_FILTER(sc->parent, isrc);
153*ef2ee5d0SMichal Meloun }
154*ef2ee5d0SMichal Meloun 
155*ef2ee5d0SMichal Meloun #ifdef SMP
156*ef2ee5d0SMichal Meloun static int
157*ef2ee5d0SMichal Meloun tegra_lic_bind(device_t dev, struct intr_irqsrc *isrc)
158*ef2ee5d0SMichal Meloun {
159*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc = device_get_softc(dev);
160*ef2ee5d0SMichal Meloun 
161*ef2ee5d0SMichal Meloun 	return (PIC_BIND(sc->parent, isrc));
162*ef2ee5d0SMichal Meloun }
163*ef2ee5d0SMichal Meloun #endif
164*ef2ee5d0SMichal Meloun 
165*ef2ee5d0SMichal Meloun static int
166*ef2ee5d0SMichal Meloun tegra_lic_probe(device_t dev)
167*ef2ee5d0SMichal Meloun {
168*ef2ee5d0SMichal Meloun 	if (!ofw_bus_status_okay(dev))
169*ef2ee5d0SMichal Meloun 		return (ENXIO);
170*ef2ee5d0SMichal Meloun 
171*ef2ee5d0SMichal Meloun 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
172*ef2ee5d0SMichal Meloun 		return (ENXIO);
173*ef2ee5d0SMichal Meloun 
174*ef2ee5d0SMichal Meloun 	return (BUS_PROBE_DEFAULT);
175*ef2ee5d0SMichal Meloun }
176*ef2ee5d0SMichal Meloun 
177*ef2ee5d0SMichal Meloun static int
178*ef2ee5d0SMichal Meloun tegra_lic_attach(device_t dev)
179*ef2ee5d0SMichal Meloun {
180*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc;
181*ef2ee5d0SMichal Meloun 	phandle_t node;
182*ef2ee5d0SMichal Meloun 	phandle_t parent_xref;
183*ef2ee5d0SMichal Meloun 	int i, rv;
184*ef2ee5d0SMichal Meloun 
185*ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
186*ef2ee5d0SMichal Meloun 	sc->dev = dev;
187*ef2ee5d0SMichal Meloun 	node = ofw_bus_get_node(dev);
188*ef2ee5d0SMichal Meloun 
189*ef2ee5d0SMichal Meloun 	rv = OF_getencprop(node, "interrupt-parent", &parent_xref,
190*ef2ee5d0SMichal Meloun 	    sizeof(parent_xref));
191*ef2ee5d0SMichal Meloun 	if (rv <= 0) {
192*ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot read parent node property\n");
193*ef2ee5d0SMichal Meloun 		goto fail;
194*ef2ee5d0SMichal Meloun 	}
195*ef2ee5d0SMichal Meloun 	sc->parent = OF_device_from_xref(parent_xref);
196*ef2ee5d0SMichal Meloun 	if (sc->parent == NULL) {
197*ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannott find parent controller\n");
198*ef2ee5d0SMichal Meloun 		goto fail;
199*ef2ee5d0SMichal Meloun 	}
200*ef2ee5d0SMichal Meloun 
201*ef2ee5d0SMichal Meloun 	if (bus_alloc_resources(dev, lic_spec, sc->mem_res)) {
202*ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannott allocate resources\n");
203*ef2ee5d0SMichal Meloun 		goto fail;
204*ef2ee5d0SMichal Meloun 	}
205*ef2ee5d0SMichal Meloun 
206*ef2ee5d0SMichal Meloun 	/* Disable all interrupts, route all to irq */
207*ef2ee5d0SMichal Meloun 	for (i = 0; i < nitems(lic_spec); i++) {
208*ef2ee5d0SMichal Meloun 		if (sc->mem_res[i] == NULL)
209*ef2ee5d0SMichal Meloun 			continue;
210*ef2ee5d0SMichal Meloun 		WR4(sc, i, LIC_CPU_IER_CLR, 0xFFFFFFFF);
211*ef2ee5d0SMichal Meloun 		WR4(sc, i, LIC_CPU_IEP_CLASS, 0);
212*ef2ee5d0SMichal Meloun 	}
213*ef2ee5d0SMichal Meloun 
214*ef2ee5d0SMichal Meloun 
215*ef2ee5d0SMichal Meloun 	if (intr_pic_register(dev, OF_xref_from_node(node)) != 0) {
216*ef2ee5d0SMichal Meloun 		device_printf(dev, "Cannot register PIC\n");
217*ef2ee5d0SMichal Meloun 		goto fail;
218*ef2ee5d0SMichal Meloun 	}
219*ef2ee5d0SMichal Meloun 	return (0);
220*ef2ee5d0SMichal Meloun 
221*ef2ee5d0SMichal Meloun fail:
222*ef2ee5d0SMichal Meloun 	bus_release_resources(dev, lic_spec, sc->mem_res);
223*ef2ee5d0SMichal Meloun 	return (ENXIO);
224*ef2ee5d0SMichal Meloun }
225*ef2ee5d0SMichal Meloun 
226*ef2ee5d0SMichal Meloun static int
227*ef2ee5d0SMichal Meloun tegra_lic_detach(device_t dev)
228*ef2ee5d0SMichal Meloun {
229*ef2ee5d0SMichal Meloun 	struct tegra_lic_sc *sc;
230*ef2ee5d0SMichal Meloun 	int i;
231*ef2ee5d0SMichal Meloun 
232*ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
233*ef2ee5d0SMichal Meloun 	for (i = 0; i < nitems(lic_spec); i++) {
234*ef2ee5d0SMichal Meloun 		if (sc->mem_res[i] == NULL)
235*ef2ee5d0SMichal Meloun 			continue;
236*ef2ee5d0SMichal Meloun 		bus_release_resource(dev, SYS_RES_MEMORY, i,
237*ef2ee5d0SMichal Meloun 		    sc->mem_res[i]);
238*ef2ee5d0SMichal Meloun 	}
239*ef2ee5d0SMichal Meloun 	return (0);
240*ef2ee5d0SMichal Meloun }
241*ef2ee5d0SMichal Meloun 
242*ef2ee5d0SMichal Meloun static device_method_t tegra_lic_methods[] = {
243*ef2ee5d0SMichal Meloun 	DEVMETHOD(device_probe,		tegra_lic_probe),
244*ef2ee5d0SMichal Meloun 	DEVMETHOD(device_attach,	tegra_lic_attach),
245*ef2ee5d0SMichal Meloun 	DEVMETHOD(device_detach,	tegra_lic_detach),
246*ef2ee5d0SMichal Meloun 
247*ef2ee5d0SMichal Meloun 	/* Interrupt controller interface */
248*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_register,		tegra_lic_register),
249*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_unregister,	tegra_lic_unregister),
250*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_enable_source,	tegra_lic_enable_source),
251*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_disable_source,	tegra_lic_disable_source),
252*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_enable_intr,	tegra_lic_enable_intr),
253*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_pre_ithread,	tegra_lic_pre_ithread),
254*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_post_ithread,	tegra_lic_post_ithread),
255*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_post_filter,	tegra_lic_post_filter),
256*ef2ee5d0SMichal Meloun #ifdef SMP
257*ef2ee5d0SMichal Meloun 	DEVMETHOD(pic_bind,		tegra_lic_bind),
258*ef2ee5d0SMichal Meloun #endif
259*ef2ee5d0SMichal Meloun 	DEVMETHOD_END
260*ef2ee5d0SMichal Meloun };
261*ef2ee5d0SMichal Meloun devclass_t tegra_lic_devclass;
262*ef2ee5d0SMichal Meloun DEFINE_CLASS_0(tegra_lic, tegra_lic_driver, tegra_lic_methods,
263*ef2ee5d0SMichal Meloun     sizeof(struct tegra_lic_sc));
264*ef2ee5d0SMichal Meloun EARLY_DRIVER_MODULE(tegra_lic, simplebus, tegra_lic_driver, tegra_lic_devclass,
265*ef2ee5d0SMichal Meloun     NULL, NULL, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE + 1);
266