]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/arm/gic_fdt.c
Import CK as of commit 0f017230ccc86929f56bf44ef2dca93d7df8076b.
[FreeBSD/FreeBSD.git] / sys / arm / arm / gic_fdt.c
1 /*-
2  * Copyright (c) 2011,2016 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Andrew Turner under
6  * sponsorship from the FreeBSD Foundation.
7  *
8  * Developed by Damjan Marion <damjan.marion@gmail.com>
9  *
10  * Based on OMAP4 GIC code by Ben Gray
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. The name of the company nor the name of the author may be used to
21  *    endorse or promote products derived from this software without specific
22  *    prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #include "opt_platform.h"
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/bus.h>
45 #include <sys/kernel.h>
46 #include <sys/module.h>
47
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 <arm/arm/gic.h>
55 #include <arm/arm/gic_common.h>
56
57 #ifdef INTRNG
58 struct arm_gic_devinfo {
59         struct ofw_bus_devinfo  obdinfo;
60         struct resource_list    rl;
61 };
62 #endif
63
64 struct arm_gic_fdt_softc {
65         struct arm_gic_softc    base;
66         pcell_t                 addr_cells;
67         pcell_t                 size_cells;
68 };
69
70 static device_probe_t gic_fdt_probe;
71 static device_attach_t gic_fdt_attach;
72 static ofw_bus_get_devinfo_t gic_ofw_get_devinfo;
73 #ifdef INTRNG
74 static bus_get_resource_list_t gic_fdt_get_resource_list;
75 static bool arm_gic_add_children(device_t);
76 #endif
77
78 static struct ofw_compat_data compat_data[] = {
79         {"arm,gic",             true},  /* Non-standard, used in FreeBSD dts. */
80         {"arm,gic-400",         true},
81         {"arm,cortex-a15-gic",  true},
82         {"arm,cortex-a9-gic",   true},
83         {"arm,cortex-a7-gic",   true},
84         {"arm,arm11mp-gic",     true},
85         {"brcm,brahma-b15-gic", true},
86         {"qcom,msm-qgic2",      true},
87         {NULL,                  false}
88 };
89
90 static device_method_t gic_fdt_methods[] = {
91         /* Device interface */
92         DEVMETHOD(device_probe,         gic_fdt_probe),
93         DEVMETHOD(device_attach,        gic_fdt_attach),
94
95 #ifdef INTRNG
96         /* Bus interface */
97         DEVMETHOD(bus_get_resource_list,gic_fdt_get_resource_list),
98
99         /* ofw_bus interface */
100         DEVMETHOD(ofw_bus_get_devinfo,  gic_ofw_get_devinfo),
101         DEVMETHOD(ofw_bus_get_compat,   ofw_bus_gen_get_compat),
102         DEVMETHOD(ofw_bus_get_model,    ofw_bus_gen_get_model),
103         DEVMETHOD(ofw_bus_get_name,     ofw_bus_gen_get_name),
104         DEVMETHOD(ofw_bus_get_node,     ofw_bus_gen_get_node),
105         DEVMETHOD(ofw_bus_get_type,     ofw_bus_gen_get_type),
106 #endif
107
108         DEVMETHOD_END,
109 };
110
111 DEFINE_CLASS_1(gic, gic_fdt_driver, gic_fdt_methods,
112     sizeof(struct arm_gic_fdt_softc), arm_gic_driver);
113
114 static devclass_t gic_fdt_devclass;
115
116 EARLY_DRIVER_MODULE(gic, simplebus, gic_fdt_driver, gic_fdt_devclass, 0, 0,
117     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
118 EARLY_DRIVER_MODULE(gic, ofwbus, gic_fdt_driver, gic_fdt_devclass, 0, 0,
119     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
120
121 static int
122 gic_fdt_probe(device_t dev)
123 {
124
125         if (!ofw_bus_status_okay(dev))
126                 return (ENXIO);
127
128         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
129                 return (ENXIO);
130         device_set_desc(dev, "ARM Generic Interrupt Controller");
131         return (BUS_PROBE_DEFAULT);
132 }
133
134 static int
135 gic_fdt_attach(device_t dev)
136 {
137 #ifdef INTRNG
138         struct arm_gic_fdt_softc *sc = device_get_softc(dev);
139         phandle_t pxref;
140         intptr_t xref;
141 #endif
142         int err;
143
144 #ifdef INTRNG
145         sc->base.gic_bus = GIC_BUS_FDT;
146 #endif
147
148         err = arm_gic_attach(dev);
149         if (err != 0)
150                 return (err);
151
152 #ifdef INTRNG
153         xref = OF_xref_from_node(ofw_bus_get_node(dev));
154
155         /*
156          * Now, when everything is initialized, it's right time to
157          * register interrupt controller to interrupt framefork.
158          */
159         if (intr_pic_register(dev, xref) == NULL) {
160                 device_printf(dev, "could not register PIC\n");
161                 goto cleanup;
162         }
163
164         /*
165          * Controller is root if:
166          * - doesn't have interrupt parent
167          * - his interrupt parent is this controller
168          */
169         pxref = ofw_bus_find_iparent(ofw_bus_get_node(dev));
170         if (pxref == 0 || xref == pxref) {
171                 if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc,
172                     GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
173                         device_printf(dev, "could not set PIC as a root\n");
174                         intr_pic_deregister(dev, xref);
175                         goto cleanup;
176                 }
177         } else {
178                 if (sc->base.gic_res[2] == NULL) {
179                         device_printf(dev,
180                             "not root PIC must have defined interrupt\n");
181                         intr_pic_deregister(dev, xref);
182                         goto cleanup;
183                 }
184                 if (bus_setup_intr(dev, sc->base.gic_res[2], INTR_TYPE_CLK,
185                     arm_gic_intr, NULL, sc, &sc->base.gic_intrhand)) {
186                         device_printf(dev, "could not setup irq handler\n");
187                         intr_pic_deregister(dev, xref);
188                         goto cleanup;
189                 }
190         }
191
192         OF_device_register_xref(xref, dev);
193
194         /* If we have children probe and attach them */
195         if (arm_gic_add_children(dev)) {
196                 bus_generic_probe(dev);
197                 return (bus_generic_attach(dev));
198         }
199 #endif
200
201         return (0);
202
203 #ifdef INTRNG
204 cleanup:
205         arm_gic_detach(dev);
206         return(ENXIO);
207 #endif
208 }
209
210 #ifdef INTRNG
211 static struct resource_list *
212 gic_fdt_get_resource_list(device_t bus, device_t child)
213 {
214         struct arm_gic_devinfo *di;
215
216         di = device_get_ivars(child);
217         KASSERT(di != NULL, ("gic_fdt_get_resource_list: No devinfo"));
218
219         return (&di->rl);
220 }
221
222 static int
223 arm_gic_fill_ranges(phandle_t node, struct arm_gic_fdt_softc *sc)
224 {
225         pcell_t host_cells;
226         cell_t *base_ranges;
227         ssize_t nbase_ranges;
228         int i, j, k;
229
230         host_cells = 1;
231         OF_getencprop(OF_parent(node), "#address-cells", &host_cells,
232             sizeof(host_cells));
233         sc->addr_cells = 2;
234         OF_getencprop(node, "#address-cells", &sc->addr_cells,
235             sizeof(sc->addr_cells));
236         sc->size_cells = 2;
237         OF_getencprop(node, "#size-cells", &sc->size_cells,
238             sizeof(sc->size_cells));
239
240         nbase_ranges = OF_getproplen(node, "ranges");
241         if (nbase_ranges < 0)
242                 return (-1);
243         sc->base.nranges = nbase_ranges / sizeof(cell_t) /
244             (sc->addr_cells + host_cells + sc->size_cells);
245         if (sc->base.nranges == 0)
246                 return (0);
247
248         sc->base.ranges = malloc(sc->base.nranges * sizeof(sc->base.ranges[0]),
249             M_DEVBUF, M_WAITOK);
250         base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
251         OF_getencprop(node, "ranges", base_ranges, nbase_ranges);
252
253         for (i = 0, j = 0; i < sc->base.nranges; i++) {
254                 sc->base.ranges[i].bus = 0;
255                 for (k = 0; k < sc->addr_cells; k++) {
256                         sc->base.ranges[i].bus <<= 32;
257                         sc->base.ranges[i].bus |= base_ranges[j++];
258                 }
259                 sc->base.ranges[i].host = 0;
260                 for (k = 0; k < host_cells; k++) {
261                         sc->base.ranges[i].host <<= 32;
262                         sc->base.ranges[i].host |= base_ranges[j++];
263                 }
264                 sc->base.ranges[i].size = 0;
265                 for (k = 0; k < sc->size_cells; k++) {
266                         sc->base.ranges[i].size <<= 32;
267                         sc->base.ranges[i].size |= base_ranges[j++];
268                 }
269         }
270
271         free(base_ranges, M_DEVBUF);
272         return (sc->base.nranges);
273 }
274
275 static bool
276 arm_gic_add_children(device_t dev)
277 {
278         struct arm_gic_fdt_softc *sc;
279         struct arm_gic_devinfo *dinfo;
280         phandle_t child, node;
281         device_t cdev;
282
283         sc = device_get_softc(dev);
284         node = ofw_bus_get_node(dev);
285
286         /* If we have no children don't probe for them */
287         child = OF_child(node);
288         if (child == 0)
289                 return (false);
290
291         if (arm_gic_fill_ranges(node, sc) < 0) {
292                 device_printf(dev, "Have a child, but no ranges\n");
293                 return (false);
294         }
295
296         for (; child != 0; child = OF_peer(child)) {
297                 dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
298
299                 if (ofw_bus_gen_setup_devinfo(&dinfo->obdinfo, child) != 0) {
300                         free(dinfo, M_DEVBUF);
301                         continue;
302                 }
303
304                 resource_list_init(&dinfo->rl);
305                 ofw_bus_reg_to_rl(dev, child, sc->addr_cells,
306                     sc->size_cells, &dinfo->rl);
307
308                 cdev = device_add_child(dev, NULL, -1);
309                 if (cdev == NULL) {
310                         device_printf(dev, "<%s>: device_add_child failed\n",
311                             dinfo->obdinfo.obd_name);
312                         resource_list_free(&dinfo->rl);
313                         ofw_bus_gen_destroy_devinfo(&dinfo->obdinfo);
314                         free(dinfo, M_DEVBUF);
315                         continue;
316                 }
317                 device_set_ivars(cdev, dinfo);
318         }
319
320         return (true);
321 }
322
323 static const struct ofw_bus_devinfo *
324 gic_ofw_get_devinfo(device_t bus __unused, device_t child)
325 {
326         struct arm_gic_devinfo *di;
327
328         di = device_get_ivars(child);
329
330         return (&di->obdinfo);
331 }
332
333 static struct ofw_compat_data gicv2m_compat_data[] = {
334         {"arm,gic-v2m-frame",   true},
335         {NULL,                  false}
336 };
337
338 static int
339 arm_gicv2m_fdt_probe(device_t dev)
340 {
341
342         if (!ofw_bus_status_okay(dev))
343                 return (ENXIO);
344
345         if (!ofw_bus_search_compatible(dev, gicv2m_compat_data)->ocd_data)
346                 return (ENXIO);
347
348         device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
349         return (BUS_PROBE_DEFAULT);
350 }
351
352 static int
353 arm_gicv2m_fdt_attach(device_t dev)
354 {
355         struct arm_gicv2m_softc *sc;
356
357         sc = device_get_softc(dev);
358         sc->sc_xref = OF_xref_from_node(ofw_bus_get_node(dev));
359
360         return (arm_gicv2m_attach(dev));
361 }
362
363 static device_method_t arm_gicv2m_fdt_methods[] = {
364         /* Device interface */
365         DEVMETHOD(device_probe,         arm_gicv2m_fdt_probe),
366         DEVMETHOD(device_attach,        arm_gicv2m_fdt_attach),
367
368         /* End */
369         DEVMETHOD_END
370 };
371
372 DEFINE_CLASS_1(gicv2m, arm_gicv2m_fdt_driver, arm_gicv2m_fdt_methods,
373     sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver);
374
375 static devclass_t arm_gicv2m_fdt_devclass;
376
377 EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_fdt_driver,
378     arm_gicv2m_fdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
379 #endif