xref: /freebsd/sys/arm/allwinner/aw_nmi.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1 /*-
2  * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 #include "opt_platform.h"
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/proc.h>
35 #include <sys/rman.h>
36 #include <machine/bus.h>
37 #include <machine/intr.h>
38 
39 #include <dev/fdt/fdt_intr.h>
40 #include <dev/ofw/openfirm.h>
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43 
44 #include "pic_if.h"
45 
46 #define	NMI_IRQ_CTRL_REG	0x0
47 #define	 NMI_IRQ_LOW_LEVEL	0x0
48 #define	 NMI_IRQ_LOW_EDGE	0x1
49 #define	 NMI_IRQ_HIGH_LEVEL	0x2
50 #define	 NMI_IRQ_HIGH_EDGE	0x3
51 #define	NMI_IRQ_PENDING_REG	0x4
52 #define	 NMI_IRQ_ACK		(1U << 0)
53 #define	A20_NMI_IRQ_ENABLE_REG	0x8
54 #define	A31_NMI_IRQ_ENABLE_REG	0x34
55 #define	 NMI_IRQ_ENABLE		(1U << 0)
56 
57 #define	R_NMI_IRQ_CTRL_REG	0x0c
58 #define	R_NMI_IRQ_PENDING_REG	0x10
59 #define	R_NMI_IRQ_ENABLE_REG	0x40
60 
61 #define	SC_NMI_READ(_sc, _reg)		bus_read_4(_sc->res[0], _reg)
62 #define	SC_NMI_WRITE(_sc, _reg, _val)	bus_write_4(_sc->res[0], _reg, _val)
63 
64 static struct resource_spec aw_nmi_res_spec[] = {
65 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
66 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
67 	{ -1,			0,	0 }
68 };
69 
70 struct aw_nmi_intr {
71 	struct intr_irqsrc	isrc;
72 	u_int			irq;
73 	enum intr_polarity	pol;
74 	enum intr_trigger	tri;
75 };
76 
77 struct aw_nmi_reg_cfg {
78 	uint8_t			ctrl_reg;
79 	uint8_t			pending_reg;
80 	uint8_t			enable_reg;
81 };
82 
83 struct aw_nmi_softc {
84 	device_t		dev;
85 	struct resource *	res[2];
86 	void *			intrcookie;
87 	struct aw_nmi_intr	intr;
88 	struct aw_nmi_reg_cfg *	cfg;
89 };
90 
91 static struct aw_nmi_reg_cfg a20_nmi_cfg = {
92 	.ctrl_reg =	NMI_IRQ_CTRL_REG,
93 	.pending_reg =	NMI_IRQ_PENDING_REG,
94 	.enable_reg =	A20_NMI_IRQ_ENABLE_REG,
95 };
96 
97 static struct aw_nmi_reg_cfg a31_nmi_cfg = {
98 	.ctrl_reg =	NMI_IRQ_CTRL_REG,
99 	.pending_reg =	NMI_IRQ_PENDING_REG,
100 	.enable_reg =	A31_NMI_IRQ_ENABLE_REG,
101 };
102 
103 static struct aw_nmi_reg_cfg a83t_r_nmi_cfg = {
104 	.ctrl_reg =	R_NMI_IRQ_CTRL_REG,
105 	.pending_reg =	R_NMI_IRQ_PENDING_REG,
106 	.enable_reg =	R_NMI_IRQ_ENABLE_REG,
107 };
108 
109 static struct ofw_compat_data compat_data[] = {
110 	{"allwinner,sun7i-a20-sc-nmi", (uintptr_t)&a20_nmi_cfg},
111 	{"allwinner,sun6i-a31-sc-nmi", (uintptr_t)&a31_nmi_cfg},
112 	{"allwinner,sun6i-a31-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
113 	{"allwinner,sun8i-a83t-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
114 	{NULL, 0},
115 };
116 
117 static int
aw_nmi_intr(void * arg)118 aw_nmi_intr(void *arg)
119 {
120 	struct aw_nmi_softc *sc;
121 
122 	sc = arg;
123 
124 	if (SC_NMI_READ(sc, sc->cfg->pending_reg) == 0) {
125 		device_printf(sc->dev, "Spurious interrupt\n");
126 		return (FILTER_HANDLED);
127 	}
128 
129 	if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) {
130 		SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
131 		device_printf(sc->dev, "Stray interrupt, NMI disabled\n");
132 	}
133 
134 	return (FILTER_HANDLED);
135 }
136 
137 static void
aw_nmi_enable_intr(device_t dev,struct intr_irqsrc * isrc)138 aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
139 {
140 	struct aw_nmi_softc *sc;
141 
142 	sc = device_get_softc(dev);
143 
144 	SC_NMI_WRITE(sc, sc->cfg->enable_reg, NMI_IRQ_ENABLE);
145 }
146 
147 static void
aw_nmi_disable_intr(device_t dev,struct intr_irqsrc * isrc)148 aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
149 {
150 	struct aw_nmi_softc *sc;
151 
152 	sc = device_get_softc(dev);
153 
154 	SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
155 }
156 
157 static int
aw_nmi_map_fdt(device_t dev,u_int ncells,pcell_t * cells,u_int * irqp,enum intr_polarity * polp,enum intr_trigger * trigp)158 aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
159     enum intr_polarity *polp, enum intr_trigger *trigp)
160 {
161 	u_int irq, tripol;
162 	enum intr_polarity pol;
163 	enum intr_trigger trig;
164 
165 	if (ncells != 2) {
166 		device_printf(dev, "Invalid #interrupt-cells\n");
167 		return (EINVAL);
168 	}
169 
170 	irq = cells[0];
171 	if (irq != 0) {
172 		device_printf(dev, "Controller only support irq 0\n");
173 		return (EINVAL);
174 	}
175 
176 	tripol = cells[1];
177 
178 	switch (tripol) {
179 	case FDT_INTR_EDGE_RISING:
180 		trig = INTR_TRIGGER_EDGE;
181 		pol  = INTR_POLARITY_HIGH;
182 		break;
183 	case FDT_INTR_EDGE_FALLING:
184 		trig = INTR_TRIGGER_EDGE;
185 		pol  = INTR_POLARITY_LOW;
186 		break;
187 	case FDT_INTR_LEVEL_HIGH:
188 		trig = INTR_TRIGGER_LEVEL;
189 		pol  = INTR_POLARITY_HIGH;
190 		break;
191 	case FDT_INTR_LEVEL_LOW:
192 		trig = INTR_TRIGGER_LEVEL;
193 		pol  = INTR_POLARITY_LOW;
194 		break;
195 	default:
196 		device_printf(dev, "unsupported trigger/polarity 0x%2x\n",
197 		    tripol);
198 		return (ENOTSUP);
199 	}
200 
201 	*irqp = irq;
202 	if (polp != NULL)
203 		*polp = pol;
204 	if (trigp != NULL)
205 		*trigp = trig;
206 	return (0);
207 }
208 
209 static int
aw_nmi_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)210 aw_nmi_map_intr(device_t dev, struct intr_map_data *data,
211     struct intr_irqsrc **isrcp)
212 {
213 	struct intr_map_data_fdt *daf;
214 	struct aw_nmi_softc *sc;
215 	int error;
216 	u_int irq;
217 
218 	if (data->type != INTR_MAP_DATA_FDT)
219 		return (ENOTSUP);
220 
221 	sc = device_get_softc(dev);
222 	daf = (struct intr_map_data_fdt *)data;
223 
224 	error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL);
225 	if (error == 0)
226 		*isrcp = &sc->intr.isrc;
227 
228 	return (error);
229 }
230 
231 static int
aw_nmi_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)232 aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
233     struct resource *res, struct intr_map_data *data)
234 {
235 	struct intr_map_data_fdt *daf;
236 	struct aw_nmi_softc *sc;
237 	struct aw_nmi_intr *nmi_intr;
238 	int error, icfg;
239 	u_int irq;
240 	enum intr_trigger trig;
241 	enum intr_polarity pol;
242 
243 	/* Get config for interrupt. */
244 	if (data == NULL || data->type != INTR_MAP_DATA_FDT)
245 		return (ENOTSUP);
246 
247 	sc = device_get_softc(dev);
248 	nmi_intr = (struct aw_nmi_intr *)isrc;
249 	daf = (struct intr_map_data_fdt *)data;
250 
251 	error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig);
252 	if (error != 0)
253 		return (error);
254 	if (nmi_intr->irq != irq)
255 		return (EINVAL);
256 
257 	/* Compare config if this is not first setup. */
258 	if (isrc->isrc_handlers != 0) {
259 		if (pol != nmi_intr->pol || trig != nmi_intr->tri)
260 			return (EINVAL);
261 		else
262 			return (0);
263 	}
264 
265 	nmi_intr->pol = pol;
266 	nmi_intr->tri = trig;
267 
268 	if (trig == INTR_TRIGGER_LEVEL) {
269 		if (pol == INTR_POLARITY_LOW)
270 			icfg = NMI_IRQ_LOW_LEVEL;
271 		else
272 			icfg = NMI_IRQ_HIGH_LEVEL;
273 	} else {
274 		if (pol == INTR_POLARITY_HIGH)
275 			icfg = NMI_IRQ_HIGH_EDGE;
276 		else
277 			icfg = NMI_IRQ_LOW_EDGE;
278 	}
279 
280 	SC_NMI_WRITE(sc, sc->cfg->ctrl_reg, icfg);
281 
282 	return (0);
283 }
284 
285 static int
aw_nmi_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)286 aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
287     struct resource *res, struct intr_map_data *data)
288 {
289 	struct aw_nmi_softc *sc;
290 
291 	sc = device_get_softc(dev);
292 
293 	if (isrc->isrc_handlers == 0) {
294 		sc->intr.pol = INTR_POLARITY_CONFORM;
295 		sc->intr.tri = INTR_TRIGGER_CONFORM;
296 
297 		SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
298 	}
299 
300 	return (0);
301 }
302 
303 static void
aw_nmi_pre_ithread(device_t dev,struct intr_irqsrc * isrc)304 aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
305 {
306 	struct aw_nmi_softc *sc;
307 
308 	sc = device_get_softc(dev);
309 	aw_nmi_disable_intr(dev, isrc);
310 	SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
311 }
312 
313 static void
aw_nmi_post_ithread(device_t dev,struct intr_irqsrc * isrc)314 aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
315 {
316 
317 	arm_irq_memory_barrier(0);
318 	aw_nmi_enable_intr(dev, isrc);
319 }
320 
321 static void
aw_nmi_post_filter(device_t dev,struct intr_irqsrc * isrc)322 aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc)
323 {
324 	struct aw_nmi_softc *sc;
325 
326 	sc = device_get_softc(dev);
327 
328 	arm_irq_memory_barrier(0);
329 	SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
330 }
331 
332 static int
aw_nmi_probe(device_t dev)333 aw_nmi_probe(device_t dev)
334 {
335 
336 	if (!ofw_bus_status_okay(dev))
337 		return (ENXIO);
338 
339 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
340 		return (ENXIO);
341 	device_set_desc(dev, "Allwinner NMI Controller");
342 	return (BUS_PROBE_DEFAULT);
343 }
344 
345 static int
aw_nmi_attach(device_t dev)346 aw_nmi_attach(device_t dev)
347 {
348 	struct aw_nmi_softc *sc;
349 	phandle_t xref;
350 
351 	sc = device_get_softc(dev);
352 	sc->dev = dev;
353 	sc->cfg = (struct aw_nmi_reg_cfg *)
354 	    ofw_bus_search_compatible(dev, compat_data)->ocd_data;
355 
356 	if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) {
357 		device_printf(dev, "can't allocate device resources\n");
358 		return (ENXIO);
359 	}
360 	if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC,
361 	    aw_nmi_intr, NULL, sc, &sc->intrcookie))) {
362 		device_printf(dev, "unable to register interrupt handler\n");
363 		bus_release_resources(dev, aw_nmi_res_spec, sc->res);
364 		return (ENXIO);
365 	}
366 
367 	/* Disable and clear interrupts */
368 	SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
369 	SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
370 
371 	xref = OF_xref_from_node(ofw_bus_get_node(dev));
372 	/* Register our isrc */
373 	sc->intr.irq = 0;
374 	sc->intr.pol = INTR_POLARITY_CONFORM;
375 	sc->intr.tri = INTR_TRIGGER_CONFORM;
376 	if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u",
377 	      device_get_nameunit(sc->dev), sc->intr.irq) != 0)
378 		goto error;
379 
380 	if (intr_pic_register(dev, (intptr_t)xref) == NULL) {
381 		device_printf(dev, "could not register pic\n");
382 		goto error;
383 	}
384 	return (0);
385 
386 error:
387 	bus_teardown_intr(dev, sc->res[1], sc->intrcookie);
388 	bus_release_resources(dev, aw_nmi_res_spec, sc->res);
389 	return (ENXIO);
390 }
391 
392 static device_method_t aw_nmi_methods[] = {
393 	DEVMETHOD(device_probe,		aw_nmi_probe),
394 	DEVMETHOD(device_attach,	aw_nmi_attach),
395 
396 	/* Interrupt controller interface */
397 	DEVMETHOD(pic_disable_intr,	aw_nmi_disable_intr),
398 	DEVMETHOD(pic_enable_intr,	aw_nmi_enable_intr),
399 	DEVMETHOD(pic_map_intr,		aw_nmi_map_intr),
400 	DEVMETHOD(pic_setup_intr,	aw_nmi_setup_intr),
401 	DEVMETHOD(pic_teardown_intr,	aw_nmi_teardown_intr),
402 	DEVMETHOD(pic_post_filter,	aw_nmi_post_filter),
403 	DEVMETHOD(pic_post_ithread,	aw_nmi_post_ithread),
404 	DEVMETHOD(pic_pre_ithread,	aw_nmi_pre_ithread),
405 
406 	{0, 0},
407 };
408 
409 static driver_t aw_nmi_driver = {
410 	"aw_nmi",
411 	aw_nmi_methods,
412 	sizeof(struct aw_nmi_softc),
413 };
414 
415 EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver, 0, 0,
416     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
417