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 0x009c 63#define MTK_IRQ1STAT 0x00a0 64#define MTK_INTTYPE 0x0000 65#define MTK_INTRAW 0x00a4 66#define MTK_INTENA 0x0080 67#define MTK_INTDIS 0x0078 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,mt7628an-intc", 1 }, 96 { NULL, 0 } 97}; 98 99#define READ4(_sc, _reg) bus_read_4((_sc)->pic_res[0], _reg) 100#define WRITE4(_sc, _reg, _val) bus_write_4((_sc)->pic_res[0], _reg, _val) 101 102static int 103mtk_pic_probe(device_t dev) 104{ 105 106 if (!ofw_bus_status_okay(dev)) 107 return (ENXIO); 108 109 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 110 return (ENXIO); 111 112 device_set_desc(dev, "MTK Interrupt Controller (v2)"); 113 return (BUS_PROBE_DEFAULT); 114} 115 116static inline void 117pic_irq_unmask(struct mtk_pic_softc *sc, u_int irq) 118{ 119 120 WRITE4(sc, MTK_INTENA, (1u << (irq))); 121} 122 123static inline void 124pic_irq_mask(struct mtk_pic_softc *sc, u_int irq) 125{ 126 127 WRITE4(sc, MTK_INTDIS, (1u << (irq))); 128} 129 130static inline intptr_t 131pic_xref(device_t dev) 132{ 133 return (OF_xref_from_node(ofw_bus_get_node(dev))); 134} 135 136static int 137mtk_pic_register_isrcs(struct mtk_pic_softc *sc) 138{ 139 int error; 140 uint32_t irq; 141 struct intr_irqsrc *isrc; 142 const char *name; 143 144 name = device_get_nameunit(sc->pic_dev); 145 for (irq = 0; irq < sc->nirqs; irq++) { 146 sc->pic_irqs[irq].irq = irq; 147 isrc = PIC_INTR_ISRC(sc, irq); 148 error = intr_isrc_register(isrc, sc->pic_dev, 0, "%s", name); 149 if (error != 0) { 150 /* XXX call intr_isrc_deregister */ 151 device_printf(sc->pic_dev, "%s failed", __func__); 152 return (error); 153 } 154 } 155 156 return (0); 157} 158 159static int 160mtk_pic_attach(device_t dev) 161{ 162 struct mtk_pic_softc *sc; 163 intptr_t xref = pic_xref(dev); 164 165 sc = device_get_softc(dev); 166 167 if (bus_alloc_resources(dev, mtk_pic_spec, sc->pic_res)) { 168 device_printf(dev, "could not allocate resources\n"); 169 return (ENXIO); 170 } 171 172 sc->pic_dev = dev; 173 174 /* Initialize mutex */ 175 mtx_init(&sc->mutex, "PIC lock", "", MTX_SPIN); 176 177 /* Set the number of interrupts */ 178 sc->nirqs = nitems(sc->pic_irqs); 179 180 /* Mask all interrupts */ 181 WRITE4(sc, MTK_INTDIS, 0xFFFFFFFF); 182 183 /* But enable interrupt generation/masking */ 184 WRITE4(sc, MTK_INTENA, 0x00000000); 185 186 /* Set all interrupts to type 0 */ 187 WRITE4(sc, MTK_INTTYPE, 0xFFFFFFFF); 188 189 /* Register the interrupts */ 190 if (mtk_pic_register_isrcs(sc) != 0) { 191 device_printf(dev, "could not register PIC ISRCs\n"); 192 goto cleanup; 193 } 194 195 /* 196 * Now, when everything is initialized, it's right time to 197 * register interrupt controller to interrupt framefork. 198 */ 199 if (intr_pic_register(dev, xref) == NULL) { 200 device_printf(dev, "could not register PIC\n"); 201 goto cleanup; 202 } 203 204 if (bus_setup_intr(dev, sc->pic_res[1], INTR_TYPE_CLK, 205 mtk_pic_intr, NULL, sc, &sc->pic_intrhand)) { 206 device_printf(dev, "could not setup irq handler\n"); 207 intr_pic_deregister(dev, xref); 208 goto cleanup; 209 } 210 return (0); 211 212cleanup: 213 bus_release_resources(dev, mtk_pic_spec, sc->pic_res); 214 return(ENXIO); 215} 216 217static int 218mtk_pic_intr(void *arg) 219{ 220 struct mtk_pic_softc *sc = arg; 221 struct thread *td; 222 uint32_t i, intr; 223 224 td = curthread; 225 /* Workaround: do not inflate intr nesting level */ 226 td->td_intr_nesting_level--; 227 228#ifdef _notyet_ 229 intr = READ4(sc, MTK_IRQ1STAT); 230 while ((i = fls(intr)) != 0) { 231 i--; 232 intr &= ~(1u << i); 233 234 if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), 235 curthread->td_intr_frame) != 0) { 236 device_printf(sc->pic_dev, 237 "Stray interrupt %u detected\n", i); 238 pic_irq_mask(sc, i); 239 continue; 240 } 241 } 242 243 KASSERT(i == 0, ("all interrupts handled")); 244#endif 245 246 intr = READ4(sc, MTK_IRQ0STAT); 247 248 while ((i = fls(intr)) != 0) { 249 i--; 250 intr &= ~(1u << i); 251 252 if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, i), 253 curthread->td_intr_frame) != 0) { 254 device_printf(sc->pic_dev, 255 "Stray interrupt %u detected\n", i); 256 pic_irq_mask(sc, i); 257 continue; 258 } 259 } 260 261 KASSERT(i == 0, ("all interrupts handled")); 262 263 td->td_intr_nesting_level++; 264 265 return (FILTER_HANDLED); 266} 267 268static int 269mtk_pic_map_intr(device_t dev, struct intr_map_data *data, 270 struct intr_irqsrc **isrcp) 271{ 272#ifdef FDT 273 struct intr_map_data_fdt *daf; 274 struct mtk_pic_softc *sc; 275 276 if (data->type != INTR_MAP_DATA_FDT) 277 return (ENOTSUP); 278 279 sc = device_get_softc(dev); 280 daf = (struct intr_map_data_fdt *)data; 281 282 if (daf->ncells != 1 || daf->cells[0] >= sc->nirqs) 283 return (EINVAL); 284 285 *isrcp = PIC_INTR_ISRC(sc, daf->cells[0]); 286 return (0); 287#else 288 return (ENOTSUP); 289#endif 290} 291 292static void 293mtk_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 294{ 295 u_int irq; 296 297 irq = ((struct mtk_pic_irqsrc *)isrc)->irq; 298 pic_irq_unmask(device_get_softc(dev), irq); 299} 300 301static void 302mtk_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 303{ 304 u_int irq; 305 306 irq = ((struct mtk_pic_irqsrc *)isrc)->irq; 307 pic_irq_mask(device_get_softc(dev), irq); 308} 309 310static void 311mtk_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 312{ 313 314 mtk_pic_disable_intr(dev, isrc); 315} 316 317static void 318mtk_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 319{ 320 321 mtk_pic_enable_intr(dev, isrc); 322} 323 324static void 325mtk_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) 326{ 327} 328 329static device_method_t mtk_pic_methods[] = { 330 /* Device interface */ 331 DEVMETHOD(device_probe, mtk_pic_probe), 332 DEVMETHOD(device_attach, mtk_pic_attach), 333 /* Interrupt controller interface */ 334 DEVMETHOD(pic_disable_intr, mtk_pic_disable_intr), 335 DEVMETHOD(pic_enable_intr, mtk_pic_enable_intr), 336 DEVMETHOD(pic_map_intr, mtk_pic_map_intr), 337 DEVMETHOD(pic_post_filter, mtk_pic_post_filter), 338 DEVMETHOD(pic_post_ithread, mtk_pic_post_ithread), 339 DEVMETHOD(pic_pre_ithread, mtk_pic_pre_ithread), 340 { 0, 0 } 341}; 342 343static driver_t mtk_pic_driver = { 344 "intc", 345 mtk_pic_methods, 346 sizeof(struct mtk_pic_softc), 347}; 348 349static devclass_t mtk_pic_devclass; 350 351EARLY_DRIVER_MODULE(intc_v2, simplebus, mtk_pic_driver, mtk_pic_devclass, 0, 0, 352 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 353