1 /*- 2 * Copyright (c) 2021 Emmanuel Vadot <manu@freebsd.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include "opt_platform.h" 30 31 #include <sys/param.h> 32 #include <sys/bus.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <machine/intr.h> 36 37 #include <dev/ofw/openfirm.h> 38 #include <dev/ofw/ofw_bus.h> 39 #include <dev/ofw/ofw_bus_subr.h> 40 41 #include "pic_if.h" 42 43 struct aw_r_intc_gicp_softc { 44 device_t dev; 45 device_t parent; 46 struct resource *res; 47 48 struct intr_map_data_fdt *parent_map_data; 49 }; 50 51 static struct ofw_compat_data compat_data[] = { 52 {"allwinner,sun6i-a31-r-intc", 1}, 53 {NULL, 0} 54 }; 55 56 static int 57 aw_r_intc_gicp_probe(device_t dev) 58 { 59 60 if (!ofw_bus_status_okay(dev)) 61 return (ENXIO); 62 63 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 64 return (ENXIO); 65 66 device_set_desc(dev, "Allwinner R INTC"); 67 return (BUS_PROBE_DEFAULT); 68 } 69 70 static int 71 aw_r_intc_gicp_attach(device_t dev) 72 { 73 struct aw_r_intc_gicp_softc *sc; 74 phandle_t node, xref, intr_parent; 75 76 sc = device_get_softc(dev); 77 sc->dev = dev; 78 node = ofw_bus_get_node(dev); 79 80 /* Look for our parent */ 81 if ((intr_parent = ofw_bus_find_iparent(node)) == 0) { 82 device_printf(dev, 83 "Cannot find our parent interrupt controller\n"); 84 return (ENXIO); 85 } 86 if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) { 87 device_printf(dev, 88 "cannot find parent interrupt controller device\n"); 89 return (ENXIO); 90 } 91 92 /* Register ourself as a interrupt controller */ 93 xref = OF_xref_from_node(node); 94 if (intr_pic_register(dev, xref) == NULL) { 95 device_printf(dev, "Cannot register GICP\n"); 96 return (ENXIO); 97 } 98 99 /* Allocate GIC compatible mapping */ 100 sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data( 101 INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) + 102 + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO); 103 104 /* Register ourself to device can find us */ 105 OF_device_register_xref(xref, dev); 106 107 return (0); 108 } 109 110 static int 111 aw_r_intc_gicp_detach(device_t dev) 112 { 113 114 return (EBUSY); 115 } 116 117 static struct intr_map_data * 118 aw_r_intc_gicp_convert_map_data(struct aw_r_intc_gicp_softc *sc, 119 struct intr_map_data *data) 120 { 121 struct intr_map_data_fdt *daf; 122 123 daf = (struct intr_map_data_fdt *)data; 124 125 /* We only support GIC forward for now */ 126 if (daf->ncells != 3) 127 return (NULL); 128 129 /* Check if this is a GIC_SPI type */ 130 if (daf->cells[0] != 0) 131 return (NULL); 132 133 sc->parent_map_data->ncells = 3; 134 sc->parent_map_data->cells[0] = 0; 135 sc->parent_map_data->cells[1] = daf->cells[1]; 136 sc->parent_map_data->cells[2] = daf->cells[2]; 137 138 return ((struct intr_map_data *)sc->parent_map_data); 139 } 140 141 static int 142 aw_r_intc_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc, 143 struct resource *res, struct intr_map_data *data) 144 { 145 struct aw_r_intc_gicp_softc *sc; 146 147 sc = device_get_softc(dev); 148 data = aw_r_intc_gicp_convert_map_data(sc, data); 149 if (data == NULL) 150 return (EINVAL); 151 152 return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); 153 } 154 155 static void 156 aw_r_intc_gicp_enable_intr(device_t dev, struct intr_irqsrc *isrc) 157 { 158 struct aw_r_intc_gicp_softc *sc; 159 160 sc = device_get_softc(dev); 161 162 PIC_ENABLE_INTR(sc->parent, isrc); 163 } 164 165 static void 166 aw_r_intc_gicp_disable_intr(device_t dev, struct intr_irqsrc *isrc) 167 { 168 struct aw_r_intc_gicp_softc *sc; 169 170 sc = device_get_softc(dev); 171 172 PIC_DISABLE_INTR(sc->parent, isrc); 173 } 174 175 static int 176 aw_r_intc_gicp_map_intr(device_t dev, struct intr_map_data *data, 177 struct intr_irqsrc **isrcp) 178 { 179 struct aw_r_intc_gicp_softc *sc; 180 int ret; 181 182 sc = device_get_softc(dev); 183 184 if (data->type != INTR_MAP_DATA_FDT) 185 return (ENOTSUP); 186 187 data = aw_r_intc_gicp_convert_map_data(sc, data); 188 if (data == NULL) 189 return (EINVAL); 190 191 ret = PIC_MAP_INTR(sc->parent, data, isrcp); 192 (*isrcp)->isrc_dev = sc->dev; 193 return(ret); 194 } 195 196 static int 197 aw_r_intc_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, 198 struct resource *res, struct intr_map_data *data) 199 { 200 struct aw_r_intc_gicp_softc *sc; 201 202 sc = device_get_softc(dev); 203 204 data = aw_r_intc_gicp_convert_map_data(sc, data); 205 if (data == NULL) 206 return (EINVAL); 207 208 return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data)); 209 } 210 211 static int 212 aw_r_intc_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc, 213 struct resource *res, struct intr_map_data *data) 214 { 215 struct aw_r_intc_gicp_softc *sc; 216 217 sc = device_get_softc(dev); 218 data = aw_r_intc_gicp_convert_map_data(sc, data); 219 if (data == NULL) 220 return (EINVAL); 221 222 return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); 223 } 224 225 static int 226 aw_r_intc_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 227 struct resource *res, struct intr_map_data *data) 228 { 229 struct aw_r_intc_gicp_softc *sc; 230 231 sc = device_get_softc(dev); 232 data = aw_r_intc_gicp_convert_map_data(sc, data); 233 if (data == NULL) 234 return (EINVAL); 235 236 return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); 237 } 238 239 static void 240 aw_r_intc_gicp_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 241 { 242 struct aw_r_intc_gicp_softc *sc; 243 244 sc = device_get_softc(dev); 245 246 PIC_PRE_ITHREAD(sc->parent, isrc); 247 } 248 249 static void 250 aw_r_intc_gicp_post_ithread(device_t dev, struct intr_irqsrc *isrc) 251 { 252 struct aw_r_intc_gicp_softc *sc; 253 254 sc = device_get_softc(dev); 255 256 PIC_POST_ITHREAD(sc->parent, isrc); 257 } 258 259 static void 260 aw_r_intc_gicp_post_filter(device_t dev, struct intr_irqsrc *isrc) 261 { 262 struct aw_r_intc_gicp_softc *sc; 263 264 sc = device_get_softc(dev); 265 266 PIC_POST_FILTER(sc->parent, isrc); 267 } 268 269 static device_method_t aw_r_intc_gicp_methods[] = { 270 /* Device interface */ 271 DEVMETHOD(device_probe, aw_r_intc_gicp_probe), 272 DEVMETHOD(device_attach, aw_r_intc_gicp_attach), 273 DEVMETHOD(device_detach, aw_r_intc_gicp_detach), 274 275 /* Interrupt controller interface */ 276 DEVMETHOD(pic_activate_intr, aw_r_intc_gicp_activate_intr), 277 DEVMETHOD(pic_disable_intr, aw_r_intc_gicp_disable_intr), 278 DEVMETHOD(pic_enable_intr, aw_r_intc_gicp_enable_intr), 279 DEVMETHOD(pic_map_intr, aw_r_intc_gicp_map_intr), 280 DEVMETHOD(pic_deactivate_intr, aw_r_intc_gicp_deactivate_intr), 281 DEVMETHOD(pic_setup_intr, aw_r_intc_gicp_setup_intr), 282 DEVMETHOD(pic_teardown_intr, aw_r_intc_gicp_teardown_intr), 283 DEVMETHOD(pic_post_filter, aw_r_intc_gicp_post_filter), 284 DEVMETHOD(pic_post_ithread, aw_r_intc_gicp_post_ithread), 285 DEVMETHOD(pic_pre_ithread, aw_r_intc_gicp_pre_ithread), 286 287 DEVMETHOD_END 288 }; 289 290 static devclass_t aw_r_intc_gicp_devclass; 291 292 static driver_t aw_r_intc_gicp_driver = { 293 "aw_r_intc_gicp", 294 aw_r_intc_gicp_methods, 295 sizeof(struct aw_r_intc_gicp_softc), 296 }; 297 298 EARLY_DRIVER_MODULE(aw_r_intc_gicp, simplebus, aw_r_intc_gicp_driver, 299 aw_r_intc_gicp_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 300