]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/arm/pl190.c
Compile in VERBOSE_SYSINIT support by default, remain silent by default
[FreeBSD/FreeBSD.git] / sys / arm / arm / pl190.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@bluezbox.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/ktr.h>
37 #include <sys/module.h>
38 #include <sys/proc.h>
39 #include <sys/rman.h>
40 #include <machine/bus.h>
41 #include <machine/intr.h>
42
43 #include <dev/ofw/openfirm.h>
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46
47 #include "pic_if.h"
48
49 #ifdef  DEBUG
50 #define dprintf(fmt, args...) printf(fmt, ##args)
51 #else
52 #define dprintf(fmt, args...)
53 #endif
54
55 #define VICIRQSTATUS    0x000
56 #define VICFIQSTATUS    0x004
57 #define VICRAWINTR      0x008
58 #define VICINTSELECT    0x00C
59 #define VICINTENABLE    0x010
60 #define VICINTENCLEAR   0x014
61 #define VICSOFTINT      0x018
62 #define VICSOFTINTCLEAR 0x01C
63 #define VICPROTECTION   0x020
64 #define VICPERIPHID     0xFE0
65 #define VICPRIMECELLID  0xFF0
66
67 #define VIC_NIRQS       32
68
69 struct pl190_intc_irqsrc {
70         struct intr_irqsrc              isrc;
71         u_int                           irq;
72 };
73
74 struct pl190_intc_softc {
75         device_t                dev;
76         struct mtx              mtx;
77         struct resource *       intc_res;
78         struct pl190_intc_irqsrc        isrcs[VIC_NIRQS];
79 };
80
81 #define INTC_VIC_READ_4(sc, reg)                \
82     bus_read_4(sc->intc_res, (reg))
83 #define INTC_VIC_WRITE_4(sc, reg, val)          \
84     bus_write_4(sc->intc_res, (reg), (val))
85
86 #define VIC_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx)
87 #define VIC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx)
88
89 static inline void
90 pl190_intc_irq_dispatch(struct pl190_intc_softc *sc, u_int irq,
91     struct trapframe *tf)
92 {
93         struct pl190_intc_irqsrc *src;
94
95         src = &sc->isrcs[irq];
96         if (intr_isrc_dispatch(&src->isrc, tf) != 0)
97                 device_printf(sc->dev, "Stray irq %u detected\n", irq);
98 }
99
100 static int
101 pl190_intc_intr(void *arg)
102 {
103         struct pl190_intc_softc *sc;
104         u_int cpu;
105         uint32_t num, pending;
106         struct trapframe *tf;
107
108         sc = arg;
109         cpu = PCPU_GET(cpuid);
110         tf = curthread->td_intr_frame;
111
112         VIC_LOCK(sc);
113         pending = INTC_VIC_READ_4(sc, VICIRQSTATUS);
114         VIC_UNLOCK(sc);
115         for (num = 0 ; num < VIC_NIRQS; num++) {
116                 if (pending & (1 << num))
117                         pl190_intc_irq_dispatch(sc, num, tf);
118         }
119
120         return (FILTER_HANDLED);
121 }
122
123 static void
124 pl190_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
125 {
126         struct pl190_intc_softc *sc;
127         struct pl190_intc_irqsrc *src;
128
129         sc = device_get_softc(dev);
130         src = (struct pl190_intc_irqsrc *)isrc;
131
132         VIC_LOCK(sc);
133         INTC_VIC_WRITE_4(sc, VICINTENCLEAR, (1 << src->irq));
134         VIC_UNLOCK(sc);
135 }
136
137 static void
138 pl190_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
139 {
140         struct pl190_intc_softc *sc;
141         struct pl190_intc_irqsrc *src;
142
143         sc = device_get_softc(dev);
144         src = (struct pl190_intc_irqsrc *)isrc;
145
146         VIC_LOCK(sc);
147         INTC_VIC_WRITE_4(sc, VICINTENABLE, (1 << src->irq));
148         VIC_UNLOCK(sc);
149 }
150
151 static int
152 pl190_intc_map_intr(device_t dev, struct intr_map_data *data,
153     struct intr_irqsrc **isrcp)
154 {
155         struct intr_map_data_fdt *daf;
156         struct pl190_intc_softc *sc;
157
158         if (data->type != INTR_MAP_DATA_FDT)
159                 return (ENOTSUP);
160
161         daf = (struct intr_map_data_fdt *)data;
162         if (daf->ncells != 1 || daf->cells[0] >= VIC_NIRQS)
163                 return (EINVAL);
164
165         sc = device_get_softc(dev);
166         *isrcp = &sc->isrcs[daf->cells[0]].isrc;
167         return (0);
168 }
169
170 static void
171 pl190_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
172 {
173         pl190_intc_disable_intr(dev, isrc);
174 }
175
176 static void
177 pl190_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
178 {
179         struct pl190_intc_irqsrc *src;
180
181         src = (struct pl190_intc_irqsrc *)isrc;
182         pl190_intc_enable_intr(dev, isrc);
183         arm_irq_memory_barrier(src->irq);
184 }
185
186 static void
187 pl190_intc_post_filter(device_t dev, struct intr_irqsrc *isrc)
188 {
189         struct pl190_intc_irqsrc *src;
190
191         src = (struct pl190_intc_irqsrc *)isrc;
192         arm_irq_memory_barrier(src->irq);
193 }
194
195 static int
196 pl190_intc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
197     struct resource *res, struct intr_map_data *data)
198 {
199
200         return (0);
201 }
202
203 static int
204 pl190_intc_probe(device_t dev)
205 {
206
207         if (!ofw_bus_status_okay(dev))
208                 return (ENXIO);
209
210         if (!ofw_bus_is_compatible(dev, "arm,versatile-vic"))
211                 return (ENXIO);
212         device_set_desc(dev, "ARM PL190 VIC");
213         return (BUS_PROBE_DEFAULT);
214 }
215
216 static int
217 pl190_intc_attach(device_t dev)
218 {
219         struct          pl190_intc_softc *sc;
220         uint32_t        id;
221         int             i, rid;
222         struct          pl190_intc_irqsrc *isrcs;
223         struct intr_pic *pic;
224         int             error;
225         uint32_t        irq;
226         const char      *name;
227         phandle_t       xref;
228
229         sc = device_get_softc(dev);
230         sc->dev = dev;
231         mtx_init(&sc->mtx, device_get_nameunit(dev), "pl190",
232             MTX_SPIN);
233
234         /* Request memory resources */
235         rid = 0;
236         sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
237             RF_ACTIVE);
238         if (sc->intc_res == NULL) {
239                 device_printf(dev, "Error: could not allocate memory resources\n");
240                 return (ENXIO);
241         }
242
243         /*
244          * All interrupts should use IRQ line
245          */
246         INTC_VIC_WRITE_4(sc, VICINTSELECT, 0x00000000);
247         /* Disable all interrupts */
248         INTC_VIC_WRITE_4(sc, VICINTENCLEAR, 0xffffffff);
249
250         id = 0;
251         for (i = 3; i >= 0; i--) {
252                 id = (id << 8) |
253                      (INTC_VIC_READ_4(sc, VICPERIPHID + i*4) & 0xff);
254         }
255
256         device_printf(dev, "Peripheral ID: %08x\n", id);
257
258         id = 0;
259         for (i = 3; i >= 0; i--) {
260                 id = (id << 8) |
261                      (INTC_VIC_READ_4(sc, VICPRIMECELLID + i*4) & 0xff);
262         }
263
264         device_printf(dev, "PrimeCell ID: %08x\n", id);
265
266         /* PIC attachment */
267         isrcs = sc->isrcs;
268         name = device_get_nameunit(sc->dev);
269         for (irq = 0; irq < VIC_NIRQS; irq++) {
270                 isrcs[irq].irq = irq;
271                 error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
272                     0, "%s,%u", name, irq);
273                 if (error != 0)
274                         return (error);
275         }
276
277         xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
278         pic = intr_pic_register(sc->dev, xref);
279         if (pic == NULL)
280                 return (ENXIO);
281
282         return (intr_pic_claim_root(sc->dev, xref, pl190_intc_intr, sc, 0));
283 }
284
285 static device_method_t pl190_intc_methods[] = {
286         DEVMETHOD(device_probe,         pl190_intc_probe),
287         DEVMETHOD(device_attach,        pl190_intc_attach),
288
289         DEVMETHOD(pic_disable_intr,     pl190_intc_disable_intr),
290         DEVMETHOD(pic_enable_intr,      pl190_intc_enable_intr),
291         DEVMETHOD(pic_map_intr,         pl190_intc_map_intr),
292         DEVMETHOD(pic_post_filter,      pl190_intc_post_filter),
293         DEVMETHOD(pic_post_ithread,     pl190_intc_post_ithread),
294         DEVMETHOD(pic_pre_ithread,      pl190_intc_pre_ithread),
295         DEVMETHOD(pic_setup_intr,       pl190_intc_setup_intr),
296
297         DEVMETHOD_END
298 };
299
300 static driver_t pl190_intc_driver = {
301         "intc",
302         pl190_intc_methods,
303         sizeof(struct pl190_intc_softc),
304 };
305
306 static devclass_t pl190_intc_devclass;
307
308 EARLY_DRIVER_MODULE(intc, simplebus, pl190_intc_driver, pl190_intc_devclass,
309     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);