]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/allwinner/a10/a10_intc.c
dts: Import DTS for arm64
[FreeBSD/FreeBSD.git] / sys / arm / allwinner / a10 / a10_intc.c
1 /*-
2  * Copyright (c) 2012 Ganbold Tsagaankhuu <ganbold@freebsd.org>
3  * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include "opt_platform.h"
32
33 #include <sys/types.h>
34 #include <sys/bus.h>
35 #include <sys/cpuset.h>
36 #include <sys/kernel.h>
37 #include <sys/ktr.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40 #include <sys/param.h>
41 #include <sys/pcpu.h>
42 #include <sys/proc.h>
43 #include <sys/rman.h>
44 #include <sys/smp.h>
45 #include <sys/systm.h>
46 #include <sys/sched.h>
47 #include <machine/bus.h>
48 #include <machine/intr.h>
49
50 #include <dev/ofw/openfirm.h>
51 #include <dev/ofw/ofw_bus.h>
52 #include <dev/ofw/ofw_bus_subr.h>
53
54 #include "pic_if.h"
55
56 /**
57  * Interrupt controller registers
58  *
59  */
60 #define SW_INT_VECTOR_REG               0x00
61 #define SW_INT_BASE_ADR_REG             0x04
62 #define SW_INT_PROTECTION_REG           0x08
63 #define SW_INT_NMI_CTRL_REG             0x0c
64
65 #define SW_INT_IRQ_PENDING_REG0         0x10
66 #define SW_INT_IRQ_PENDING_REG1         0x14
67 #define SW_INT_IRQ_PENDING_REG2         0x18
68
69 #define SW_INT_FIQ_PENDING_REG0         0x20
70 #define SW_INT_FIQ_PENDING_REG1         0x24
71 #define SW_INT_FIQ_PENDING_REG2         0x28
72
73 #define SW_INT_SELECT_REG0              0x30
74 #define SW_INT_SELECT_REG1              0x34
75 #define SW_INT_SELECT_REG2              0x38
76
77 #define SW_INT_ENABLE_REG0              0x40
78 #define SW_INT_ENABLE_REG1              0x44
79 #define SW_INT_ENABLE_REG2              0x48
80
81 #define SW_INT_MASK_REG0                0x50
82 #define SW_INT_MASK_REG1                0x54
83 #define SW_INT_MASK_REG2                0x58
84
85 #define SW_INT_IRQNO_ENMI               0
86
87 #define A10_INTR_MAX_NIRQS              81
88
89 #define SW_INT_IRQ_PENDING_REG(_b)      (0x10 + ((_b) * 4))
90 #define SW_INT_FIQ_PENDING_REG(_b)      (0x20 + ((_b) * 4))
91 #define SW_INT_SELECT_REG(_b)           (0x30 + ((_b) * 4))
92 #define SW_INT_ENABLE_REG(_b)           (0x40 + ((_b) * 4))
93 #define SW_INT_MASK_REG(_b)             (0x50 + ((_b) * 4))
94
95 struct a10_intr_irqsrc {
96         struct intr_irqsrc      isrc;
97         u_int                   irq;
98 };
99
100 struct a10_aintc_softc {
101         device_t                sc_dev;
102         struct resource *       aintc_res;
103         bus_space_tag_t         aintc_bst;
104         bus_space_handle_t      aintc_bsh;
105         struct mtx              mtx;
106         struct a10_intr_irqsrc  isrcs[A10_INTR_MAX_NIRQS];
107 };
108
109 #define aintc_read_4(sc, reg)                                           \
110         bus_space_read_4(sc->aintc_bst, sc->aintc_bsh, reg)
111 #define aintc_write_4(sc, reg, val)                                     \
112         bus_space_write_4(sc->aintc_bst, sc->aintc_bsh, reg, val)
113
114 static __inline void
115 a10_intr_eoi(struct a10_aintc_softc *sc, u_int irq)
116 {
117
118         if (irq != SW_INT_IRQNO_ENMI)
119                 return;
120         mtx_lock_spin(&sc->mtx);
121         aintc_write_4(sc, SW_INT_IRQ_PENDING_REG(0),
122             (1 << SW_INT_IRQNO_ENMI));
123         mtx_unlock_spin(&sc->mtx);
124 }
125
126 static void
127 a10_intr_unmask(struct a10_aintc_softc *sc, u_int irq)
128 {
129         uint32_t bit, block, value;
130
131         bit = (irq % 32);
132         block = (irq / 32);
133
134         mtx_lock_spin(&sc->mtx);
135         value = aintc_read_4(sc, SW_INT_ENABLE_REG(block));
136         value |= (1 << bit);
137         aintc_write_4(sc, SW_INT_ENABLE_REG(block), value);
138
139         value = aintc_read_4(sc, SW_INT_MASK_REG(block));
140         value &= ~(1 << bit);
141         aintc_write_4(sc, SW_INT_MASK_REG(block), value);
142         mtx_unlock_spin(&sc->mtx);
143 }
144
145 static void
146 a10_intr_mask(struct a10_aintc_softc *sc, u_int irq)
147 {
148         uint32_t bit, block, value;
149
150         bit = (irq % 32);
151         block = (irq / 32);
152
153         mtx_lock_spin(&sc->mtx);
154         value = aintc_read_4(sc, SW_INT_ENABLE_REG(block));
155         value &= ~(1 << bit);
156         aintc_write_4(sc, SW_INT_ENABLE_REG(block), value);
157
158         value = aintc_read_4(sc, SW_INT_MASK_REG(block));
159         value |= (1 << bit);
160         aintc_write_4(sc, SW_INT_MASK_REG(block), value);
161         mtx_unlock_spin(&sc->mtx);
162 }
163
164 static int
165 a10_pending_irq(struct a10_aintc_softc *sc)
166 {
167         uint32_t value;
168         int i, b;
169
170         for (i = 0; i < 3; i++) {
171                 value = aintc_read_4(sc, SW_INT_IRQ_PENDING_REG(i));
172                 if (value == 0)
173                         continue;
174                 for (b = 0; b < 32; b++)
175                         if (value & (1 << b)) {
176                                 return (i * 32 + b);
177                         }
178         }
179
180         return (-1);
181 }
182
183 static int
184 a10_intr(void *arg)
185 {
186         struct a10_aintc_softc *sc = arg;
187         u_int irq;
188
189         irq = a10_pending_irq(sc);
190         if (irq == -1 || irq > A10_INTR_MAX_NIRQS) {
191                 device_printf(sc->sc_dev, "Spurious interrupt %d\n", irq);
192                 return (FILTER_HANDLED);
193         }
194
195         while (irq != -1) {
196                 if (irq > A10_INTR_MAX_NIRQS) {
197                         device_printf(sc->sc_dev, "Spurious interrupt %d\n",
198                             irq);
199                         return (FILTER_HANDLED);
200                 }
201                 if (intr_isrc_dispatch(&sc->isrcs[irq].isrc,
202                     curthread->td_intr_frame) != 0) {
203                         a10_intr_mask(sc, irq);
204                         a10_intr_eoi(sc, irq);
205                         device_printf(sc->sc_dev,
206                             "Stray interrupt %d disabled\n", irq);
207                 }
208
209                 arm_irq_memory_barrier(irq);
210                 irq = a10_pending_irq(sc);
211         }
212
213         return (FILTER_HANDLED);
214 }
215
216 static int
217 a10_intr_pic_attach(struct a10_aintc_softc *sc)
218 {
219         struct intr_pic *pic;
220         int error;
221         uint32_t irq;
222         const char *name;
223         intptr_t xref;
224
225         name = device_get_nameunit(sc->sc_dev);
226         for (irq = 0; irq < A10_INTR_MAX_NIRQS; irq++) {
227                 sc->isrcs[irq].irq = irq;
228
229                 error = intr_isrc_register(&sc->isrcs[irq].isrc,
230                     sc->sc_dev, 0, "%s,%u", name, irq);
231                 if (error != 0)
232                         return (error);
233         }
234
235         xref = OF_xref_from_node(ofw_bus_get_node(sc->sc_dev));
236         pic = intr_pic_register(sc->sc_dev, xref);
237         if (pic == NULL)
238                 return (ENXIO);
239
240         return (intr_pic_claim_root(sc->sc_dev, xref, a10_intr, sc, 0));
241 }
242
243 static void
244 a10_intr_enable_intr(device_t dev, struct intr_irqsrc *isrc)
245 {
246         struct a10_aintc_softc *sc;
247         u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
248
249         sc = device_get_softc(dev);
250         arm_irq_memory_barrier(irq);
251         a10_intr_unmask(sc, irq);
252 }
253
254 static void
255 a10_intr_disable_intr(device_t dev, struct intr_irqsrc *isrc)
256 {
257         struct a10_aintc_softc *sc;
258         u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
259
260         sc = device_get_softc(dev);
261         a10_intr_mask(sc, irq);
262 }
263
264 static int
265 a10_intr_map_intr(device_t dev, struct intr_map_data *data,
266     struct intr_irqsrc **isrcp)
267 {
268         struct intr_map_data_fdt *daf;
269         struct a10_aintc_softc *sc;
270
271         if (data->type != INTR_MAP_DATA_FDT)
272                 return (ENOTSUP);
273
274         daf = (struct intr_map_data_fdt *)data;
275         if (daf->ncells != 1 || daf->cells[0] >= A10_INTR_MAX_NIRQS)
276                 return (EINVAL);
277
278         sc = device_get_softc(dev);
279         *isrcp = &sc->isrcs[daf->cells[0]].isrc;
280         return (0);
281 }
282
283 static void
284 a10_intr_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
285 {
286         struct a10_aintc_softc *sc = device_get_softc(dev);
287         u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
288
289         a10_intr_mask(sc, irq);
290         a10_intr_eoi(sc, irq);
291 }
292
293 static void
294 a10_intr_post_ithread(device_t dev, struct intr_irqsrc *isrc)
295 {
296
297         a10_intr_enable_intr(dev, isrc);
298 }
299
300 static void
301 a10_intr_post_filter(device_t dev, struct intr_irqsrc *isrc)
302 {
303         struct a10_aintc_softc *sc = device_get_softc(dev);
304         u_int irq = ((struct a10_intr_irqsrc *)isrc)->irq;
305
306         a10_intr_eoi(sc, irq);
307 }
308
309 static int
310 a10_aintc_probe(device_t dev)
311 {
312
313         if (!ofw_bus_status_okay(dev))
314                 return (ENXIO);
315
316         if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-a10-ic"))
317                 return (ENXIO);
318         device_set_desc(dev, "A10 AINTC Interrupt Controller");
319         return (BUS_PROBE_DEFAULT);
320 }
321
322 static int
323 a10_aintc_attach(device_t dev)
324 {
325         struct a10_aintc_softc *sc = device_get_softc(dev);
326         int rid = 0;
327         int i;
328         sc->sc_dev = dev;
329
330         sc->aintc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
331             &rid, RF_ACTIVE);
332         if (!sc->aintc_res) {
333                 device_printf(dev, "could not allocate resource\n");
334                 goto error;
335         }
336
337         sc->aintc_bst = rman_get_bustag(sc->aintc_res);
338         sc->aintc_bsh = rman_get_bushandle(sc->aintc_res);
339
340         mtx_init(&sc->mtx, "A10 AINTC lock", "", MTX_SPIN);
341
342         /* Disable & clear all interrupts */
343         for (i = 0; i < 3; i++) {
344                 aintc_write_4(sc, SW_INT_ENABLE_REG(i), 0);
345                 aintc_write_4(sc, SW_INT_MASK_REG(i), 0xffffffff);
346         }
347         /* enable protection mode*/
348         aintc_write_4(sc, SW_INT_PROTECTION_REG, 0x01);
349
350         /* config the external interrupt source type*/
351         aintc_write_4(sc, SW_INT_NMI_CTRL_REG, 0x00);
352
353         if (a10_intr_pic_attach(sc) != 0) {
354                 device_printf(dev, "could not attach PIC\n");
355                 return (ENXIO);
356         }
357
358         return (0);
359
360 error:
361         bus_release_resource(dev, SYS_RES_MEMORY, rid,
362             sc->aintc_res);
363         return (ENXIO);
364 }
365
366 static device_method_t a10_aintc_methods[] = {
367         DEVMETHOD(device_probe,         a10_aintc_probe),
368         DEVMETHOD(device_attach,        a10_aintc_attach),
369
370         /* Interrupt controller interface */
371         DEVMETHOD(pic_disable_intr,     a10_intr_disable_intr),
372         DEVMETHOD(pic_enable_intr,      a10_intr_enable_intr),
373         DEVMETHOD(pic_map_intr,         a10_intr_map_intr),
374         DEVMETHOD(pic_post_filter,      a10_intr_post_filter),
375         DEVMETHOD(pic_post_ithread,     a10_intr_post_ithread),
376         DEVMETHOD(pic_pre_ithread,      a10_intr_pre_ithread),
377
378         { 0, 0 }
379 };
380
381 static driver_t a10_aintc_driver = {
382         "aintc",
383         a10_aintc_methods,
384         sizeof(struct a10_aintc_softc),
385 };
386
387 static devclass_t a10_aintc_devclass;
388
389 EARLY_DRIVER_MODULE(aintc, simplebus, a10_aintc_driver, a10_aintc_devclass, 0, 0,
390     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_FIRST);