1 /*- 2 * Copyright (c) 2016 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/systm.h> 33 #include <sys/bus.h> 34 #include <sys/kernel.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/fdt/fdt_intr.h> 42 #include <dev/ofw/openfirm.h> 43 #include <dev/ofw/ofw_bus.h> 44 #include <dev/ofw/ofw_bus_subr.h> 45 46 #include "pic_if.h" 47 48 #define NMI_IRQ_CTRL_REG 0x0 49 #define NMI_IRQ_LOW_LEVEL 0x0 50 #define NMI_IRQ_LOW_EDGE 0x1 51 #define NMI_IRQ_HIGH_LEVEL 0x2 52 #define NMI_IRQ_HIGH_EDGE 0x3 53 #define NMI_IRQ_PENDING_REG 0x4 54 #define NMI_IRQ_ACK (1U << 0) 55 #define A20_NMI_IRQ_ENABLE_REG 0x8 56 #define A31_NMI_IRQ_ENABLE_REG 0x34 57 #define NMI_IRQ_ENABLE (1U << 0) 58 59 #define R_NMI_IRQ_CTRL_REG 0x0c 60 #define R_NMI_IRQ_PENDING_REG 0x10 61 #define R_NMI_IRQ_ENABLE_REG 0x40 62 63 #define SC_NMI_READ(_sc, _reg) bus_read_4(_sc->res[0], _reg) 64 #define SC_NMI_WRITE(_sc, _reg, _val) bus_write_4(_sc->res[0], _reg, _val) 65 66 static struct resource_spec aw_nmi_res_spec[] = { 67 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 68 { SYS_RES_IRQ, 0, RF_ACTIVE }, 69 { -1, 0, 0 } 70 }; 71 72 struct aw_nmi_intr { 73 struct intr_irqsrc isrc; 74 u_int irq; 75 enum intr_polarity pol; 76 enum intr_trigger tri; 77 }; 78 79 struct aw_nmi_reg_cfg { 80 uint8_t ctrl_reg; 81 uint8_t pending_reg; 82 uint8_t enable_reg; 83 }; 84 85 struct aw_nmi_softc { 86 device_t dev; 87 struct resource * res[2]; 88 void * intrcookie; 89 struct aw_nmi_intr intr; 90 struct aw_nmi_reg_cfg * cfg; 91 }; 92 93 static struct aw_nmi_reg_cfg a20_nmi_cfg = { 94 .ctrl_reg = NMI_IRQ_CTRL_REG, 95 .pending_reg = NMI_IRQ_PENDING_REG, 96 .enable_reg = A20_NMI_IRQ_ENABLE_REG, 97 }; 98 99 static struct aw_nmi_reg_cfg a31_nmi_cfg = { 100 .ctrl_reg = NMI_IRQ_CTRL_REG, 101 .pending_reg = NMI_IRQ_PENDING_REG, 102 .enable_reg = A31_NMI_IRQ_ENABLE_REG, 103 }; 104 105 static struct aw_nmi_reg_cfg a83t_r_nmi_cfg = { 106 .ctrl_reg = R_NMI_IRQ_CTRL_REG, 107 .pending_reg = R_NMI_IRQ_PENDING_REG, 108 .enable_reg = R_NMI_IRQ_ENABLE_REG, 109 }; 110 111 static struct ofw_compat_data compat_data[] = { 112 {"allwinner,sun7i-a20-sc-nmi", (uintptr_t)&a20_nmi_cfg}, 113 {"allwinner,sun6i-a31-sc-nmi", (uintptr_t)&a31_nmi_cfg}, 114 {"allwinner,sun6i-a31-r-intc", (uintptr_t)&a83t_r_nmi_cfg}, 115 {"allwinner,sun8i-a83t-r-intc", (uintptr_t)&a83t_r_nmi_cfg}, 116 {NULL, 0}, 117 }; 118 119 static int 120 aw_nmi_intr(void *arg) 121 { 122 struct aw_nmi_softc *sc; 123 124 sc = arg; 125 126 if (SC_NMI_READ(sc, sc->cfg->pending_reg) == 0) { 127 device_printf(sc->dev, "Spurious interrupt\n"); 128 return (FILTER_HANDLED); 129 } 130 131 if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) { 132 SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE); 133 device_printf(sc->dev, "Stray interrupt, NMI disabled\n"); 134 } 135 136 return (FILTER_HANDLED); 137 } 138 139 static void 140 aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc) 141 { 142 struct aw_nmi_softc *sc; 143 144 sc = device_get_softc(dev); 145 146 SC_NMI_WRITE(sc, sc->cfg->enable_reg, NMI_IRQ_ENABLE); 147 } 148 149 static void 150 aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc) 151 { 152 struct aw_nmi_softc *sc; 153 154 sc = device_get_softc(dev); 155 156 SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE); 157 } 158 159 static int 160 aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, 161 enum intr_polarity *polp, enum intr_trigger *trigp) 162 { 163 u_int irq, tripol; 164 enum intr_polarity pol; 165 enum intr_trigger trig; 166 167 if (ncells != 2) { 168 device_printf(dev, "Invalid #interrupt-cells\n"); 169 return (EINVAL); 170 } 171 172 irq = cells[0]; 173 if (irq != 0) { 174 device_printf(dev, "Controller only support irq 0\n"); 175 return (EINVAL); 176 } 177 178 tripol = cells[1]; 179 180 switch (tripol) { 181 case FDT_INTR_EDGE_RISING: 182 trig = INTR_TRIGGER_EDGE; 183 pol = INTR_POLARITY_HIGH; 184 break; 185 case FDT_INTR_EDGE_FALLING: 186 trig = INTR_TRIGGER_EDGE; 187 pol = INTR_POLARITY_LOW; 188 break; 189 case FDT_INTR_LEVEL_HIGH: 190 trig = INTR_TRIGGER_LEVEL; 191 pol = INTR_POLARITY_HIGH; 192 break; 193 case FDT_INTR_LEVEL_LOW: 194 trig = INTR_TRIGGER_LEVEL; 195 pol = INTR_POLARITY_LOW; 196 break; 197 default: 198 device_printf(dev, "unsupported trigger/polarity 0x%2x\n", 199 tripol); 200 return (ENOTSUP); 201 } 202 203 *irqp = irq; 204 if (polp != NULL) 205 *polp = pol; 206 if (trigp != NULL) 207 *trigp = trig; 208 return (0); 209 } 210 211 static int 212 aw_nmi_map_intr(device_t dev, struct intr_map_data *data, 213 struct intr_irqsrc **isrcp) 214 { 215 struct intr_map_data_fdt *daf; 216 struct aw_nmi_softc *sc; 217 int error; 218 u_int irq; 219 220 if (data->type != INTR_MAP_DATA_FDT) 221 return (ENOTSUP); 222 223 sc = device_get_softc(dev); 224 daf = (struct intr_map_data_fdt *)data; 225 226 error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL); 227 if (error == 0) 228 *isrcp = &sc->intr.isrc; 229 230 return (error); 231 } 232 233 static int 234 aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc, 235 struct resource *res, struct intr_map_data *data) 236 { 237 struct intr_map_data_fdt *daf; 238 struct aw_nmi_softc *sc; 239 struct aw_nmi_intr *nmi_intr; 240 int error, icfg; 241 u_int irq; 242 enum intr_trigger trig; 243 enum intr_polarity pol; 244 245 /* Get config for interrupt. */ 246 if (data == NULL || data->type != INTR_MAP_DATA_FDT) 247 return (ENOTSUP); 248 249 sc = device_get_softc(dev); 250 nmi_intr = (struct aw_nmi_intr *)isrc; 251 daf = (struct intr_map_data_fdt *)data; 252 253 error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig); 254 if (error != 0) 255 return (error); 256 if (nmi_intr->irq != irq) 257 return (EINVAL); 258 259 /* Compare config if this is not first setup. */ 260 if (isrc->isrc_handlers != 0) { 261 if (pol != nmi_intr->pol || trig != nmi_intr->tri) 262 return (EINVAL); 263 else 264 return (0); 265 } 266 267 nmi_intr->pol = pol; 268 nmi_intr->tri = trig; 269 270 if (trig == INTR_TRIGGER_LEVEL) { 271 if (pol == INTR_POLARITY_LOW) 272 icfg = NMI_IRQ_LOW_LEVEL; 273 else 274 icfg = NMI_IRQ_HIGH_LEVEL; 275 } else { 276 if (pol == INTR_POLARITY_HIGH) 277 icfg = NMI_IRQ_HIGH_EDGE; 278 else 279 icfg = NMI_IRQ_LOW_EDGE; 280 } 281 282 SC_NMI_WRITE(sc, sc->cfg->ctrl_reg, icfg); 283 284 return (0); 285 } 286 287 static int 288 aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 289 struct resource *res, struct intr_map_data *data) 290 { 291 struct aw_nmi_softc *sc; 292 293 sc = device_get_softc(dev); 294 295 if (isrc->isrc_handlers == 0) { 296 sc->intr.pol = INTR_POLARITY_CONFORM; 297 sc->intr.tri = INTR_TRIGGER_CONFORM; 298 299 SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE); 300 } 301 302 return (0); 303 } 304 305 static void 306 aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 307 { 308 struct aw_nmi_softc *sc; 309 310 sc = device_get_softc(dev); 311 aw_nmi_disable_intr(dev, isrc); 312 SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK); 313 } 314 315 static void 316 aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc) 317 { 318 319 arm_irq_memory_barrier(0); 320 aw_nmi_enable_intr(dev, isrc); 321 } 322 323 static void 324 aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc) 325 { 326 struct aw_nmi_softc *sc; 327 328 sc = device_get_softc(dev); 329 330 arm_irq_memory_barrier(0); 331 SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK); 332 } 333 334 static int 335 aw_nmi_probe(device_t dev) 336 { 337 338 if (!ofw_bus_status_okay(dev)) 339 return (ENXIO); 340 341 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 342 return (ENXIO); 343 device_set_desc(dev, "Allwinner NMI Controller"); 344 return (BUS_PROBE_DEFAULT); 345 } 346 347 static int 348 aw_nmi_attach(device_t dev) 349 { 350 struct aw_nmi_softc *sc; 351 phandle_t xref; 352 353 sc = device_get_softc(dev); 354 sc->dev = dev; 355 sc->cfg = (struct aw_nmi_reg_cfg *) 356 ofw_bus_search_compatible(dev, compat_data)->ocd_data; 357 358 if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) { 359 device_printf(dev, "can't allocate device resources\n"); 360 return (ENXIO); 361 } 362 if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC, 363 aw_nmi_intr, NULL, sc, &sc->intrcookie))) { 364 device_printf(dev, "unable to register interrupt handler\n"); 365 bus_release_resources(dev, aw_nmi_res_spec, sc->res); 366 return (ENXIO); 367 } 368 369 /* Disable and clear interrupts */ 370 SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE); 371 SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK); 372 373 xref = OF_xref_from_node(ofw_bus_get_node(dev)); 374 /* Register our isrc */ 375 sc->intr.irq = 0; 376 sc->intr.pol = INTR_POLARITY_CONFORM; 377 sc->intr.tri = INTR_TRIGGER_CONFORM; 378 if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u", 379 device_get_nameunit(sc->dev), sc->intr.irq) != 0) 380 goto error; 381 382 if (intr_pic_register(dev, (intptr_t)xref) == NULL) { 383 device_printf(dev, "could not register pic\n"); 384 goto error; 385 } 386 return (0); 387 388 error: 389 bus_teardown_intr(dev, sc->res[1], sc->intrcookie); 390 bus_release_resources(dev, aw_nmi_res_spec, sc->res); 391 return (ENXIO); 392 } 393 394 static device_method_t aw_nmi_methods[] = { 395 DEVMETHOD(device_probe, aw_nmi_probe), 396 DEVMETHOD(device_attach, aw_nmi_attach), 397 398 /* Interrupt controller interface */ 399 DEVMETHOD(pic_disable_intr, aw_nmi_disable_intr), 400 DEVMETHOD(pic_enable_intr, aw_nmi_enable_intr), 401 DEVMETHOD(pic_map_intr, aw_nmi_map_intr), 402 DEVMETHOD(pic_setup_intr, aw_nmi_setup_intr), 403 DEVMETHOD(pic_teardown_intr, aw_nmi_teardown_intr), 404 DEVMETHOD(pic_post_filter, aw_nmi_post_filter), 405 DEVMETHOD(pic_post_ithread, aw_nmi_post_ithread), 406 DEVMETHOD(pic_pre_ithread, aw_nmi_pre_ithread), 407 408 {0, 0}, 409 }; 410 411 static driver_t aw_nmi_driver = { 412 "aw_nmi", 413 aw_nmi_methods, 414 sizeof(struct aw_nmi_softc), 415 }; 416 417 EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver, 0, 0, 418 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); 419