]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/arm/pl190.c
dts: Update our device tree sources file fomr Linux 4.13
[FreeBSD/FreeBSD.git] / sys / arm / arm / pl190.c
1 /*-
2  * Copyright (c) 2012-2017 Oleksandr Tymoshenko <gonzo@bluezbox.com>
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 <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/ktr.h>
35 #include <sys/module.h>
36 #include <sys/proc.h>
37 #include <sys/rman.h>
38 #include <machine/bus.h>
39 #include <machine/intr.h>
40
41 #include <dev/ofw/openfirm.h>
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44
45 #include "pic_if.h"
46
47 #ifdef  DEBUG
48 #define dprintf(fmt, args...) printf(fmt, ##args)
49 #else
50 #define dprintf(fmt, args...)
51 #endif
52
53 #define VICIRQSTATUS    0x000
54 #define VICFIQSTATUS    0x004
55 #define VICRAWINTR      0x008
56 #define VICINTSELECT    0x00C
57 #define VICINTENABLE    0x010
58 #define VICINTENCLEAR   0x014
59 #define VICSOFTINT      0x018
60 #define VICSOFTINTCLEAR 0x01C
61 #define VICPROTECTION   0x020
62 #define VICPERIPHID     0xFE0
63 #define VICPRIMECELLID  0xFF0
64
65 #define VIC_NIRQS       32
66
67 struct pl190_intc_irqsrc {
68         struct intr_irqsrc              isrc;
69         u_int                           irq;
70 };
71
72 struct pl190_intc_softc {
73         device_t                dev;
74         struct mtx              mtx;
75         struct resource *       intc_res;
76         struct pl190_intc_irqsrc        isrcs[VIC_NIRQS];
77 };
78
79 #define INTC_VIC_READ_4(sc, reg)                \
80     bus_read_4(sc->intc_res, (reg))
81 #define INTC_VIC_WRITE_4(sc, reg, val)          \
82     bus_write_4(sc->intc_res, (reg), (val))
83
84 #define VIC_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx)
85 #define VIC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx)
86
87 static inline void
88 pl190_intc_irq_dispatch(struct pl190_intc_softc *sc, u_int irq,
89     struct trapframe *tf)
90 {
91         struct pl190_intc_irqsrc *src;
92
93         src = &sc->isrcs[irq];
94         if (intr_isrc_dispatch(&src->isrc, tf) != 0)
95                 device_printf(sc->dev, "Stray irq %u detected\n", irq);
96 }
97
98 static int
99 pl190_intc_intr(void *arg)
100 {
101         struct pl190_intc_softc *sc;
102         u_int cpu;
103         uint32_t num, pending;
104         struct trapframe *tf;
105
106         sc = arg;
107         cpu = PCPU_GET(cpuid);
108         tf = curthread->td_intr_frame;
109
110         VIC_LOCK(sc);
111         pending = INTC_VIC_READ_4(sc, VICIRQSTATUS);
112         VIC_UNLOCK(sc);
113         for (num = 0 ; num < VIC_NIRQS; num++) {
114                 if (pending & (1 << num))
115                         pl190_intc_irq_dispatch(sc, num, tf);
116         }
117
118         return (FILTER_HANDLED);
119 }
120
121 static void
122 pl190_intc_disable_intr(device_t dev, struct intr_irqsrc *isrc)
123 {
124         struct pl190_intc_softc *sc;
125         struct pl190_intc_irqsrc *src;
126
127         sc = device_get_softc(dev);
128         src = (struct pl190_intc_irqsrc *)isrc;
129
130         VIC_LOCK(sc);
131         INTC_VIC_WRITE_4(sc, VICINTENCLEAR, (1 << src->irq));
132         VIC_UNLOCK(sc);
133 }
134
135 static void
136 pl190_intc_enable_intr(device_t dev, struct intr_irqsrc *isrc)
137 {
138         struct pl190_intc_softc *sc;
139         struct pl190_intc_irqsrc *src;
140
141         sc = device_get_softc(dev);
142         src = (struct pl190_intc_irqsrc *)isrc;
143
144         VIC_LOCK(sc);
145         INTC_VIC_WRITE_4(sc, VICINTENABLE, (1 << src->irq));
146         VIC_UNLOCK(sc);
147 }
148
149 static int
150 pl190_intc_map_intr(device_t dev, struct intr_map_data *data,
151     struct intr_irqsrc **isrcp)
152 {
153         struct intr_map_data_fdt *daf;
154         struct pl190_intc_softc *sc;
155
156         if (data->type != INTR_MAP_DATA_FDT)
157                 return (ENOTSUP);
158
159         daf = (struct intr_map_data_fdt *)data;
160         if (daf->ncells != 1 || daf->cells[0] >= VIC_NIRQS)
161                 return (EINVAL);
162
163         sc = device_get_softc(dev);
164         *isrcp = &sc->isrcs[daf->cells[0]].isrc;
165         return (0);
166 }
167
168 static void
169 pl190_intc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
170 {
171         pl190_intc_disable_intr(dev, isrc);
172 }
173
174 static void
175 pl190_intc_post_ithread(device_t dev, struct intr_irqsrc *isrc)
176 {
177         struct pl190_intc_irqsrc *src;
178
179         src = (struct pl190_intc_irqsrc *)isrc;
180         pl190_intc_enable_intr(dev, isrc);
181         arm_irq_memory_barrier(src->irq);
182 }
183
184 static void
185 pl190_intc_post_filter(device_t dev, struct intr_irqsrc *isrc)
186 {
187         struct pl190_intc_irqsrc *src;
188
189         src = (struct pl190_intc_irqsrc *)isrc;
190         arm_irq_memory_barrier(src->irq);
191 }
192
193 static int
194 pl190_intc_setup_intr(device_t dev, struct intr_irqsrc *isrc,
195     struct resource *res, struct intr_map_data *data)
196 {
197
198         return (0);
199 }
200
201 static int
202 pl190_intc_probe(device_t dev)
203 {
204
205         if (!ofw_bus_status_okay(dev))
206                 return (ENXIO);
207
208         if (!ofw_bus_is_compatible(dev, "arm,versatile-vic"))
209                 return (ENXIO);
210         device_set_desc(dev, "ARM PL190 VIC");
211         return (BUS_PROBE_DEFAULT);
212 }
213
214 static int
215 pl190_intc_attach(device_t dev)
216 {
217         struct          pl190_intc_softc *sc;
218         uint32_t        id;
219         int             i, rid;
220         struct          pl190_intc_irqsrc *isrcs;
221         struct intr_pic *pic;
222         int             error;
223         uint32_t        irq;
224         const char      *name;
225         phandle_t       xref;
226
227         sc = device_get_softc(dev);
228         sc->dev = dev;
229         mtx_init(&sc->mtx, device_get_nameunit(dev), "pl190",
230             MTX_SPIN);
231
232         /* Request memory resources */
233         rid = 0;
234         sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
235             RF_ACTIVE);
236         if (sc->intc_res == NULL) {
237                 device_printf(dev, "Error: could not allocate memory resources\n");
238                 return (ENXIO);
239         }
240
241         /*
242          * All interrupts should use IRQ line
243          */
244         INTC_VIC_WRITE_4(sc, VICINTSELECT, 0x00000000);
245         /* Disable all interrupts */
246         INTC_VIC_WRITE_4(sc, VICINTENCLEAR, 0xffffffff);
247
248         id = 0;
249         for (i = 3; i >= 0; i--) {
250                 id = (id << 8) |
251                      (INTC_VIC_READ_4(sc, VICPERIPHID + i*4) & 0xff);
252         }
253
254         device_printf(dev, "Peripheral ID: %08x\n", id);
255
256         id = 0;
257         for (i = 3; i >= 0; i--) {
258                 id = (id << 8) |
259                      (INTC_VIC_READ_4(sc, VICPRIMECELLID + i*4) & 0xff);
260         }
261
262         device_printf(dev, "PrimeCell ID: %08x\n", id);
263
264         /* PIC attachment */
265         isrcs = sc->isrcs;
266         name = device_get_nameunit(sc->dev);
267         for (irq = 0; irq < VIC_NIRQS; irq++) {
268                 isrcs[irq].irq = irq;
269                 error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
270                     0, "%s,%u", name, irq);
271                 if (error != 0)
272                         return (error);
273         }
274
275         xref = OF_xref_from_node(ofw_bus_get_node(sc->dev));
276         pic = intr_pic_register(sc->dev, xref);
277         if (pic == NULL)
278                 return (ENXIO);
279
280         return (intr_pic_claim_root(sc->dev, xref, pl190_intc_intr, sc, 0));
281 }
282
283 static device_method_t pl190_intc_methods[] = {
284         DEVMETHOD(device_probe,         pl190_intc_probe),
285         DEVMETHOD(device_attach,        pl190_intc_attach),
286
287         DEVMETHOD(pic_disable_intr,     pl190_intc_disable_intr),
288         DEVMETHOD(pic_enable_intr,      pl190_intc_enable_intr),
289         DEVMETHOD(pic_map_intr,         pl190_intc_map_intr),
290         DEVMETHOD(pic_post_filter,      pl190_intc_post_filter),
291         DEVMETHOD(pic_post_ithread,     pl190_intc_post_ithread),
292         DEVMETHOD(pic_pre_ithread,      pl190_intc_pre_ithread),
293         DEVMETHOD(pic_setup_intr,       pl190_intc_setup_intr),
294
295         DEVMETHOD_END
296 };
297
298 static driver_t pl190_intc_driver = {
299         "intc",
300         pl190_intc_methods,
301         sizeof(struct pl190_intc_softc),
302 };
303
304 static devclass_t pl190_intc_devclass;
305
306 EARLY_DRIVER_MODULE(intc, simplebus, pl190_intc_driver, pl190_intc_devclass,
307     0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);