]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/aw_nmi.c
Return the struct intr_pic pointer from intr_pic_register. This will be
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / aw_nmi.c
1 /*-
2  * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * 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$");
29
30 #include "opt_platform.h"
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/proc.h>
38 #include <machine/bus.h>
39 #include <machine/intr.h>
40
41 #include <dev/fdt/fdt_common.h>
42 #include <dev/ofw/openfirm.h>
43 #include <dev/ofw/ofw_bus.h>
44 #include <dev/ofw/ofw_bus_subr.h>
45
46 #include <dt-bindings/interrupt-controller/irq.h>
47
48 #include "pic_if.h"
49
50 #define NMI_IRQ_CTRL_REG        0x0
51 #define  NMI_IRQ_LOW_LEVEL      0x0
52 #define  NMI_IRQ_LOW_EDGE       0x1
53 #define  NMI_IRQ_HIGH_LEVEL     0x2
54 #define  NMI_IRQ_HIGH_EDGE      0x3
55 #define NMI_IRQ_PENDING_REG     0x4
56 #define  NMI_IRQ_ACK            (1U << 0)
57 #define A20_NMI_IRQ_ENABLE_REG  0x8
58 #define A31_NMI_IRQ_ENABLE_REG  0x34
59 #define  NMI_IRQ_ENABLE         (1U << 0)
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_softc {
78         device_t                dev;
79         struct resource *       res[2];
80         void *                  intrcookie;
81         struct aw_nmi_intr      intr;
82         uint8_t                 enable_reg;
83 };
84
85 #define A20_NMI 1
86 #define A31_NMI 2
87
88 static struct ofw_compat_data compat_data[] = {
89         {"allwinner,sun7i-a20-sc-nmi", A20_NMI},
90         {"allwinner,sun6i-a31-sc-nmi", A31_NMI},
91
92         {NULL, 0},
93 };
94
95 static int
96 aw_nmi_intr(void *arg)
97 {
98         struct aw_nmi_softc *sc;
99
100         sc = arg;
101
102         if (SC_NMI_READ(sc, NMI_IRQ_PENDING_REG) == 0) {
103                 device_printf(sc->dev, "Spurious interrupt\n");
104                 return (FILTER_HANDLED);
105         }
106
107         if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) {
108                 SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE);
109                 device_printf(sc->dev, "Stray interrupt, NMI disabled\n");
110         }
111
112         return (FILTER_HANDLED);
113 }
114
115 static void
116 aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
117 {
118         struct aw_nmi_softc *sc;
119
120         sc = device_get_softc(dev);
121
122         SC_NMI_WRITE(sc, sc->enable_reg, NMI_IRQ_ENABLE);
123 }
124
125 static void
126 aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
127 {
128         struct aw_nmi_softc *sc;
129
130         sc = device_get_softc(dev);
131
132         SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE);
133 }
134
135 static int
136 aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
137     enum intr_polarity *polp, enum intr_trigger *trigp)
138 {
139         u_int irq, tripol;
140         enum intr_polarity pol;
141         enum intr_trigger trig;
142
143         if (ncells != 2) {
144                 device_printf(dev, "Invalid #interrupt-cells\n");
145                 return (EINVAL);
146         }
147
148         irq = cells[0];
149         if (irq != 0) {
150                 device_printf(dev, "Controller only support irq 0\n");
151                 return (EINVAL);
152         }
153
154         tripol = cells[1];
155
156         switch (tripol) {
157         case IRQ_TYPE_EDGE_RISING:
158                 trig = INTR_TRIGGER_EDGE;
159                 pol  = INTR_POLARITY_HIGH;
160                 break;
161         case IRQ_TYPE_EDGE_FALLING:
162                 trig = INTR_TRIGGER_EDGE;
163                 pol  = INTR_POLARITY_LOW;
164                 break;
165         case IRQ_TYPE_LEVEL_HIGH:
166                 trig = INTR_TRIGGER_LEVEL;
167                 pol  = INTR_POLARITY_HIGH;
168                 break;
169         case IRQ_TYPE_LEVEL_LOW:
170                 trig = INTR_TRIGGER_LEVEL;
171                 pol  = INTR_POLARITY_LOW;
172                 break;
173         default:
174                 device_printf(dev, "unsupported trigger/polarity 0x%2x\n",
175                     tripol);
176                 return (ENOTSUP);
177         }
178
179         *irqp = irq;
180         if (polp != NULL)
181                 *polp = pol;
182         if (trigp != NULL)
183                 *trigp = trig;
184         return (0);
185 }
186
187 static int
188 aw_nmi_map_intr(device_t dev, struct intr_map_data *data,
189     struct intr_irqsrc **isrcp)
190 {
191         struct intr_map_data_fdt *daf;
192         struct aw_nmi_softc *sc;
193         int error;
194         u_int irq;
195
196         if (data->type != INTR_MAP_DATA_FDT)
197                 return (ENOTSUP);
198
199         sc = device_get_softc(dev);
200         daf = (struct intr_map_data_fdt *)data;
201
202         error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL);
203         if (error == 0)
204                 *isrcp = &sc->intr.isrc;
205
206         return (error);
207 }
208
209 static int
210 aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
211     struct resource *res, struct intr_map_data *data)
212 {
213         struct intr_map_data_fdt *daf;
214         struct aw_nmi_softc *sc;
215         struct aw_nmi_intr *nmi_intr;
216         int error, icfg;
217         u_int irq;
218         enum intr_trigger trig;
219         enum intr_polarity pol;
220
221         /* Get config for interrupt. */
222         if (data == NULL || data->type != INTR_MAP_DATA_FDT)
223                 return (ENOTSUP);
224
225         sc = device_get_softc(dev);
226         nmi_intr = (struct aw_nmi_intr *)isrc;
227         daf = (struct intr_map_data_fdt *)data;
228
229         error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig);
230         if (error != 0)
231                 return (error);
232         if (nmi_intr->irq != irq)
233                 return (EINVAL);
234
235         /* Compare config if this is not first setup. */
236         if (isrc->isrc_handlers != 0) {
237                 if (pol != nmi_intr->pol || trig != nmi_intr->tri)
238                         return (EINVAL);
239                 else
240                         return (0);
241         }
242
243         nmi_intr->pol = pol;
244         nmi_intr->tri = trig;
245
246         if (trig == INTR_TRIGGER_LEVEL) {
247                 if (pol == INTR_POLARITY_LOW)
248                         icfg = NMI_IRQ_LOW_LEVEL;
249                 else
250                         icfg = NMI_IRQ_HIGH_LEVEL;
251         } else {
252                 if (pol == INTR_POLARITY_HIGH)
253                         icfg = NMI_IRQ_HIGH_EDGE;
254                 else
255                         icfg = NMI_IRQ_LOW_EDGE;
256         }
257
258         SC_NMI_WRITE(sc, NMI_IRQ_CTRL_REG, icfg);
259
260         return (0);
261 }
262
263 static int
264 aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
265     struct resource *res, struct intr_map_data *data)
266 {
267         struct aw_nmi_softc *sc;
268
269         sc = device_get_softc(dev);
270
271         if (isrc->isrc_handlers == 0) {
272                 sc->intr.pol = INTR_POLARITY_CONFORM;
273                 sc->intr.tri = INTR_TRIGGER_CONFORM;
274
275                 SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE);
276         }
277
278         return (0);
279 }
280
281 static void
282 aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
283 {
284         struct aw_nmi_softc *sc;
285
286         sc = device_get_softc(dev);
287         aw_nmi_disable_intr(dev, isrc);
288         SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK);
289 }
290
291 static void
292 aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
293 {
294
295         arm_irq_memory_barrier(0);
296         aw_nmi_enable_intr(dev, isrc);
297 }
298
299 static void
300 aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc)
301 {
302         struct aw_nmi_softc *sc;
303
304         sc = device_get_softc(dev);
305
306         arm_irq_memory_barrier(0);
307         SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK);
308 }
309
310 static int
311 aw_nmi_probe(device_t dev)
312 {
313
314         if (!ofw_bus_status_okay(dev))
315                 return (ENXIO);
316
317         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
318                 return (ENXIO);
319         device_set_desc(dev, "Allwinner NMI Controller");
320         return (BUS_PROBE_DEFAULT);
321 }
322
323 static int
324 aw_nmi_attach(device_t dev)
325 {
326         struct aw_nmi_softc *sc;
327         phandle_t xref;
328
329         sc = device_get_softc(dev);
330         sc->dev = dev;
331
332         if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) {
333                 device_printf(dev, "can't allocate device resources\n");
334                 return (ENXIO);
335         }
336         if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC,
337             aw_nmi_intr, NULL, sc, &sc->intrcookie))) {
338                 device_printf(dev, "unable to register interrupt handler\n");
339                 bus_release_resources(dev, aw_nmi_res_spec, sc->res);
340                 return (ENXIO);
341         }
342
343         switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) {
344         case A20_NMI:
345                 sc->enable_reg = A20_NMI_IRQ_ENABLE_REG;
346                 break;
347         case A31_NMI:
348                 sc->enable_reg = A31_NMI_IRQ_ENABLE_REG;
349                 break;
350         }
351
352         /* Disable and clear interrupts */
353         SC_NMI_WRITE(sc, sc->enable_reg, !NMI_IRQ_ENABLE);
354         SC_NMI_WRITE(sc, NMI_IRQ_PENDING_REG, NMI_IRQ_ACK);
355
356         xref = OF_xref_from_node(ofw_bus_get_node(dev));
357         /* Register our isrc */
358         sc->intr.irq = 0;
359         sc->intr.pol = INTR_POLARITY_CONFORM;
360         sc->intr.tri = INTR_TRIGGER_CONFORM;
361         if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u",
362               device_get_nameunit(sc->dev), sc->intr.irq) != 0)
363                 goto error;
364
365         if (intr_pic_register(dev, (intptr_t)xref) == NULL) {
366                 device_printf(dev, "could not register pic\n");
367                 goto error;
368         }
369         return (0);
370
371 error:
372         bus_teardown_intr(dev, sc->res[1], sc->intrcookie);
373         bus_release_resources(dev, aw_nmi_res_spec, sc->res);
374         return (ENXIO);
375 }
376
377 static device_method_t aw_nmi_methods[] = {
378         DEVMETHOD(device_probe,         aw_nmi_probe),
379         DEVMETHOD(device_attach,        aw_nmi_attach),
380
381         /* Interrupt controller interface */
382         DEVMETHOD(pic_disable_intr,     aw_nmi_disable_intr),
383         DEVMETHOD(pic_enable_intr,      aw_nmi_enable_intr),
384         DEVMETHOD(pic_map_intr,         aw_nmi_map_intr),
385         DEVMETHOD(pic_setup_intr,       aw_nmi_setup_intr),
386         DEVMETHOD(pic_teardown_intr,    aw_nmi_teardown_intr),
387         DEVMETHOD(pic_post_filter,      aw_nmi_post_filter),
388         DEVMETHOD(pic_post_ithread,     aw_nmi_post_ithread),
389         DEVMETHOD(pic_pre_ithread,      aw_nmi_pre_ithread),
390
391         {0, 0},
392 };
393
394 static driver_t aw_nmi_driver = {
395         "aw_nmi",
396         aw_nmi_methods,
397         sizeof(struct aw_nmi_softc),
398 };
399
400 static devclass_t aw_nmi_devclass;
401
402 EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver,
403     aw_nmi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);