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