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