1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012-2017 Oleksandr Tymoshenko <[email protected]> 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 SIC_STATUS 0x00 56 #define SIC_RAWSTAT 0x04 57 #define SIC_ENABLE 0x08 58 #define SIC_ENSET 0x08 59 #define SIC_ENCLR 0x0C 60 #define SIC_SOFTINTSET 0x10 61 #define SIC_SOFTINTCLR 0x14 62 #define SIC_PICENABLE 0x20 63 #define SIC_PICENSET 0x20 64 #define SIC_PICENCLR 0x24 65 66 #define SIC_NIRQS 32 67 68 struct versatile_sic_irqsrc { 69 struct intr_irqsrc isrc; 70 u_int irq; 71 }; 72 73 struct versatile_sic_softc { 74 device_t dev; 75 struct mtx mtx; 76 struct resource * mem_res; 77 struct resource * irq_res; 78 void *intrh; 79 struct versatile_sic_irqsrc isrcs[SIC_NIRQS]; 80 }; 81 82 #define SIC_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx) 83 #define SIC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx) 84 85 #define SIC_READ_4(sc, reg) \ 86 bus_read_4(sc->mem_res, (reg)) 87 #define SIC_WRITE_4(sc, reg, val) \ 88 bus_write_4(sc->mem_res, (reg), (val)) 89 90 /* 91 * Driver stuff 92 */ 93 static int versatile_sic_probe(device_t); 94 static int versatile_sic_attach(device_t); 95 static int versatile_sic_detach(device_t); 96 97 static void 98 versatile_sic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 99 { 100 struct versatile_sic_softc *sc; 101 struct versatile_sic_irqsrc *src; 102 103 sc = device_get_softc(dev); 104 src = (struct versatile_sic_irqsrc *)isrc; 105 106 SIC_LOCK(sc); 107 SIC_WRITE_4(sc, SIC_ENCLR, (1 << src->irq)); 108 SIC_UNLOCK(sc); 109 } 110 111 static void 112 versatile_sic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 113 { 114 struct versatile_sic_softc *sc; 115 struct versatile_sic_irqsrc *src; 116 117 sc = device_get_softc(dev); 118 src = (struct versatile_sic_irqsrc *)isrc; 119 120 SIC_LOCK(sc); 121 SIC_WRITE_4(sc, SIC_ENSET, (1 << src->irq)); 122 SIC_UNLOCK(sc); 123 } 124 125 static int 126 versatile_sic_map_intr(device_t dev, struct intr_map_data *data, 127 struct intr_irqsrc **isrcp) 128 { 129 struct intr_map_data_fdt *daf; 130 struct versatile_sic_softc *sc; 131 132 if (data->type != INTR_MAP_DATA_FDT) 133 return (ENOTSUP); 134 135 daf = (struct intr_map_data_fdt *)data; 136 if (daf->ncells != 1 || daf->cells[0] >= SIC_NIRQS) 137 return (EINVAL); 138 139 sc = device_get_softc(dev); 140 *isrcp = &sc->isrcs[daf->cells[0]].isrc; 141 return (0); 142 } 143 144 static void 145 versatile_sic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 146 { 147 versatile_sic_disable_intr(dev, isrc); 148 } 149 150 static void 151 versatile_sic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 152 { 153 struct versatile_sic_irqsrc *src; 154 155 src = (struct versatile_sic_irqsrc *)isrc; 156 arm_irq_memory_barrier(src->irq); 157 versatile_sic_enable_intr(dev, isrc); 158 } 159 160 static void 161 versatile_sic_post_filter(device_t dev, struct intr_irqsrc *isrc) 162 { 163 struct versatile_sic_irqsrc *src; 164 165 src = (struct versatile_sic_irqsrc *)isrc; 166 arm_irq_memory_barrier(src->irq); 167 } 168 169 static int 170 versatile_sic_setup_intr(device_t dev, struct intr_irqsrc *isrc, 171 struct resource *res, struct intr_map_data *data) 172 { 173 174 return (0); 175 } 176 177 static int 178 versatile_sic_filter(void *arg) 179 { 180 struct versatile_sic_softc *sc; 181 struct intr_irqsrc *isrc; 182 uint32_t i, interrupts; 183 184 sc = arg; 185 SIC_LOCK(sc); 186 interrupts = SIC_READ_4(sc, SIC_STATUS); 187 SIC_UNLOCK(sc); 188 for (i = 0; interrupts != 0; i++, interrupts >>= 1) { 189 if ((interrupts & 0x1) == 0) 190 continue; 191 isrc = &sc->isrcs[i].isrc; 192 if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { 193 versatile_sic_disable_intr(sc->dev, isrc); 194 versatile_sic_post_filter(sc->dev, isrc); 195 device_printf(sc->dev, "Stray irq %u disabled\n", i); 196 } 197 } 198 199 return (FILTER_HANDLED); 200 } 201 202 static int 203 versatile_sic_probe(device_t dev) 204 { 205 206 if (!ofw_bus_status_okay(dev)) 207 return (ENXIO); 208 209 if (!ofw_bus_is_compatible(dev, "arm,versatile-sic")) 210 return (ENXIO); 211 device_set_desc(dev, "ARM Versatile SIC"); 212 return (BUS_PROBE_DEFAULT); 213 } 214 215 static int 216 versatile_sic_attach(device_t dev) 217 { 218 struct versatile_sic_softc *sc = device_get_softc(dev); 219 int rid, error; 220 uint32_t irq; 221 const char *name; 222 struct versatile_sic_irqsrc *isrcs; 223 224 sc->dev = dev; 225 mtx_init(&sc->mtx, device_get_nameunit(dev), "sic", 226 MTX_SPIN); 227 228 /* Request memory resources */ 229 rid = 0; 230 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 231 RF_ACTIVE); 232 if (sc->mem_res == NULL) { 233 device_printf(dev, "Error: could not allocate memory resources\n"); 234 return (ENXIO); 235 } 236 237 /* Request memory resources */ 238 rid = 0; 239 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 240 RF_ACTIVE); 241 if (sc->irq_res == NULL) { 242 device_printf(dev, "could not allocate IRQ resources\n"); 243 versatile_sic_detach(dev); 244 return (ENXIO); 245 } 246 247 if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, 248 versatile_sic_filter, NULL, sc, &sc->intrh))) { 249 device_printf(dev, 250 "unable to register interrupt handler\n"); 251 versatile_sic_detach(dev); 252 return (ENXIO); 253 } 254 255 /* Disable all interrupts on SIC */ 256 SIC_WRITE_4(sc, SIC_ENCLR, 0xffffffff); 257 258 /* PIC attachment */ 259 isrcs = sc->isrcs; 260 name = device_get_nameunit(sc->dev); 261 for (irq = 0; irq < SIC_NIRQS; irq++) { 262 isrcs[irq].irq = irq; 263 error = intr_isrc_register(&isrcs[irq].isrc, sc->dev, 264 0, "%s,%u", name, irq); 265 if (error != 0) 266 return (error); 267 } 268 269 intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); 270 271 return (0); 272 } 273 274 static int 275 versatile_sic_detach(device_t dev) 276 { 277 struct versatile_sic_softc *sc; 278 279 sc = device_get_softc(dev); 280 281 if (sc->intrh) 282 bus_teardown_intr(dev, sc->irq_res, sc->intrh); 283 284 if (sc->mem_res == NULL) 285 bus_release_resource(dev, SYS_RES_MEMORY, 286 rman_get_rid(sc->mem_res), sc->mem_res); 287 288 if (sc->irq_res == NULL) 289 bus_release_resource(dev, SYS_RES_IRQ, 290 rman_get_rid(sc->irq_res), sc->irq_res); 291 292 mtx_destroy(&sc->mtx); 293 294 return (0); 295 296 } 297 298 static device_method_t versatile_sic_methods[] = { 299 DEVMETHOD(device_probe, versatile_sic_probe), 300 DEVMETHOD(device_attach, versatile_sic_attach), 301 DEVMETHOD(device_detach, versatile_sic_detach), 302 303 DEVMETHOD(pic_disable_intr, versatile_sic_disable_intr), 304 DEVMETHOD(pic_enable_intr, versatile_sic_enable_intr), 305 DEVMETHOD(pic_map_intr, versatile_sic_map_intr), 306 DEVMETHOD(pic_post_filter, versatile_sic_post_filter), 307 DEVMETHOD(pic_post_ithread, versatile_sic_post_ithread), 308 DEVMETHOD(pic_pre_ithread, versatile_sic_pre_ithread), 309 DEVMETHOD(pic_setup_intr, versatile_sic_setup_intr), 310 311 DEVMETHOD_END 312 }; 313 314 static driver_t versatile_sic_driver = { 315 "sic", 316 versatile_sic_methods, 317 sizeof(struct versatile_sic_softc), 318 }; 319 320 static devclass_t versatile_sic_devclass; 321 322 DRIVER_MODULE(sic, simplebus, versatile_sic_driver, versatile_sic_devclass, 0, 0); 323