1/*- 2 * Copyright (c) 2015 Alexander Kabaev 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification, immediately at the beginning of the file. 11 * 2. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include "opt_platform.h" 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD$"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/bus.h> 36#include <sys/kernel.h> 37#include <sys/ktr.h> 38#include <sys/module.h> 39#include <sys/malloc.h> 40#include <sys/rman.h> 41#include <sys/pcpu.h> 42#include <sys/proc.h> 43#include <sys/cpuset.h> 44#include <sys/lock.h> 45#include <sys/mutex.h> 46#include <sys/smp.h> 47#include <sys/sched.h> 48#include <machine/bus.h> 49#include <machine/intr.h> 50#include <machine/smp.h> 51 52#include <dev/fdt/fdt_common.h> 53#include <dev/ofw/openfirm.h> 54#include <dev/ofw/ofw_bus.h> 55#include <dev/ofw/ofw_bus_subr.h> 56 57#include <mips/ingenic/jz4780_regs.h> 58 59#include "pic_if.h" 60 61#define JZ4780_NIRQS 64 62 63static int jz4780_pic_intr(void *); 64 65struct jz4780_pic_isrc { 66 struct intr_irqsrc isrc; 67 u_int irq; 68}; 69 70struct jz4780_pic_softc { 71 device_t pic_dev; 72 void * pic_intrhand; 73 struct resource * pic_res[2]; 74 struct jz4780_pic_isrc pic_irqs[JZ4780_NIRQS]; 75 uint32_t nirqs; 76}; 77 78#define PIC_INTR_ISRC(sc, irq) (&(sc)->pic_irqs[(irq)].isrc) 79 80static struct resource_spec jz4780_pic_spec[] = { 81 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Registers */ 82 { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Parent interrupt */ 83 { -1, 0 } 84}; 85 86static struct ofw_compat_data compat_data[] = { 87 {"ingenic,jz4780-intc", true}, 88 {NULL, false} 89}; 90 91#define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg) 92#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val) 93 94static int 95jz4780_pic_probe(device_t dev) 96{ 97 98 if (!ofw_bus_status_okay(dev)) 99 return (ENXIO); 100 101 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 102 return (ENXIO); 103 device_set_desc(dev, "JZ4780 Interrupt Controller"); 104 return (BUS_PROBE_DEFAULT); 105} 106 107static inline void 108pic_irq_unmask(struct jz4780_pic_softc *sc, u_int irq) 109{ 110 if (irq < 32) 111 WRITE4(sc, JZ_ICMCR0, (1u << irq)); 112 else 113 WRITE4(sc, JZ_ICMCR1, (1u << (irq - 32))); 114} 115 116static inline void 117pic_irq_mask(struct jz4780_pic_softc *sc, u_int irq) 118{ 119 if (irq < 32) 120 WRITE4(sc, JZ_ICMSR0, (1u << irq)); 121 else 122 WRITE4(sc, JZ_ICMSR1, (1u << (irq - 32))); 123} 124 125static inline intptr_t 126pic_xref(device_t dev) 127{ 128 return (OF_xref_from_node(ofw_bus_get_node(dev))); 129} 130 131static int 132jz4780_pic_register_isrcs(struct jz4780_pic_softc *sc) 133{ 134 int error; 135 uint32_t irq, i; 136 struct intr_irqsrc *isrc; 137 const char *name; 138 139 name = device_get_nameunit(sc->pic_dev); 140 for (irq = 0; irq < sc->nirqs; irq++) { 141 sc->pic_irqs[irq].irq = irq; 142 isrc = PIC_INTR_ISRC(sc, irq); 143 error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s,%d", 144 name, irq); 145 if (error != 0) { 146 for (i = 0; i < irq; i++) 147 intr_isrc_deregister(PIC_INTR_ISRC(sc, irq)); 148 device_printf(sc->pic_dev, "%s failed", __func__); 149 return (error); 150 } 151 } 152 153 return (0); 154} 155 156static int 157jz4780_pic_attach(device_t dev) 158{ 159 struct jz4780_pic_softc *sc; 160 intptr_t xref; 161 162 xref = pic_xref(dev); 163 164 sc = device_get_softc(dev); 165 166 if (bus_alloc_resources(dev, jz4780_pic_spec, sc->pic_res)) { 167 device_printf(dev, "could not allocate resources\n"); 168 return (ENXIO); 169 } 170 171 sc->pic_dev = dev; 172 173 /* Set the number of interrupts */ 174 sc->nirqs = nitems(sc->pic_irqs); 175 176 /* Mask all interrupts */ 177 WRITE4(sc, JZ_ICMR0, 0xFFFFFFFF); 178 WRITE4(sc, JZ_ICMR1, 0xFFFFFFFF); 179 180 /* Register the interrupts */ 181 if (jz4780_pic_register_isrcs(sc) != 0) { 182 device_printf(dev, "could not register PIC ISRCs\n"); 183 goto cleanup; 184 } 185 186 /* 187 * Now, when everything is initialized, it's right time to 188 * register interrupt controller to interrupt framefork. 189 */ 190 if (intr_pic_register(dev, xref) == NULL) { 191 device_printf(dev, "could not register PIC\n"); 192 goto cleanup; 193 } 194 195 if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK, 196 jz4780_pic_intr, NULL, sc, &sc->pic_intrhand)) { 197 device_printf(dev, "could not setup irq handler\n"); 198 intr_pic_deregister(dev, xref); 199 goto cleanup; 200 } 201 202 return (0); 203 204cleanup: 205 bus_release_resources(dev, jz4780_pic_spec, sc->pic_res); 206 207 return(ENXIO); 208} 209 210static int 211jz4780_pic_intr(void *arg) 212{ 213 struct jz4780_pic_softc *sc = arg; 214 struct intr_irqsrc *isrc; 215 struct thread *td; 216 uint32_t i, intr; 217 218 td = curthread; 219 /* Workaround: do not inflate intr nesting level */ 220 td->td_intr_nesting_level--; 221 222 intr = READ4(sc, JZ_ICPR0); 223 while ((i = fls(intr)) != 0) { 224 i--; 225 intr &= ~(1u << i); 226 227 isrc = PIC_INTR_ISRC(sc, i); 228 if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { 229 device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); 230 pic_irq_mask(sc, i); 231 continue; 232 } 233 } 234 235 KASSERT(i == 0, ("all interrupts handled")); 236 237 intr = READ4(sc, JZ_ICPR1); 238 while ((i = fls(intr)) != 0) { 239 i--; 240 intr &= ~(1u << i); 241 i += 32; 242 243 isrc = PIC_INTR_ISRC(sc, i); 244 if (intr_isrc_dispatch(isrc, curthread->td_intr_frame) != 0) { 245 device_printf(sc->pic_dev, "Stray interrupt %u detected\n", i); 246 pic_irq_mask(sc, i); 247 continue; 248 } 249 } 250 251 KASSERT(i == 0, ("all interrupts handled")); 252 td->td_intr_nesting_level++; 253 254 return (FILTER_HANDLED); 255} 256 257static int 258jz4780_pic_map_intr(device_t dev, struct intr_map_data *data, 259 struct intr_irqsrc **isrcp) 260{ 261#ifdef FDT 262 struct jz4780_pic_softc *sc; 263 struct intr_map_data_fdt *daf; 264 265 sc = device_get_softc(dev); 266 daf = (struct intr_map_data_fdt *)data; 267 268 if (data == NULL || data->type != INTR_MAP_DATA_FDT || 269 daf->ncells != 1 || daf->cells[0] >= sc->nirqs) 270 return (EINVAL); 271 272 *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); 273 return (0); 274#else 275 return (EINVAL); 276#endif 277} 278 279static void 280jz4780_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 281{ 282 struct jz4780_pic_isrc *pic_isrc; 283 284 pic_isrc = (struct jz4780_pic_isrc *)isrc; 285 pic_irq_unmask(device_get_softc(dev), pic_isrc->irq); 286} 287 288static void 289jz4780_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 290{ 291 struct jz4780_pic_isrc *pic_isrc; 292 293 pic_isrc = (struct jz4780_pic_isrc *)isrc; 294 pic_irq_mask(device_get_softc(dev), pic_isrc->irq); 295} 296 297static void 298jz4780_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 299{ 300 301 jz4780_pic_disable_intr(dev, isrc); 302} 303 304static void 305jz4780_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 306{ 307 308 jz4780_pic_enable_intr(dev, isrc); 309} 310 311static device_method_t jz4780_pic_methods[] = { 312 /* Device interface */ 313 DEVMETHOD(device_probe, jz4780_pic_probe), 314 DEVMETHOD(device_attach, jz4780_pic_attach), 315 /* Interrupt controller interface */ 316 DEVMETHOD(pic_enable_intr, jz4780_pic_enable_intr), 317 DEVMETHOD(pic_disable_intr, jz4780_pic_disable_intr), 318 DEVMETHOD(pic_map_intr, jz4780_pic_map_intr), 319 DEVMETHOD(pic_post_ithread, jz4780_pic_post_ithread), 320 DEVMETHOD(pic_pre_ithread, jz4780_pic_pre_ithread), 321 { 0, 0 } 322}; 323 324static driver_t jz4780_pic_driver = { 325 "intc", 326 jz4780_pic_methods, 327 sizeof(struct jz4780_pic_softc), 328}; 329 330static devclass_t jz4780_pic_devclass; 331 332EARLY_DRIVER_MODULE(intc, ofwbus, jz4780_pic_driver, jz4780_pic_devclass, 0, 0, 333 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 334