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