gic.c revision 271595
1226031Sstas/*- 2226031Sstas * Copyright (c) 2011 The FreeBSD Foundation 3226031Sstas * All rights reserved. 4226031Sstas * 5226031Sstas * Developed by Damjan Marion <damjan.marion@gmail.com> 6226031Sstas * 7226031Sstas * Based on OMAP4 GIC code by Ben Gray 8226031Sstas * 9226031Sstas * Redistribution and use in source and binary forms, with or without 10226031Sstas * modification, are permitted provided that the following conditions 11226031Sstas * are met: 12226031Sstas * 1. Redistributions of source code must retain the above copyright 13226031Sstas * notice, this list of conditions and the following disclaimer. 14226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 15226031Sstas * notice, this list of conditions and the following disclaimer in the 16226031Sstas * documentation and/or other materials provided with the distribution. 17226031Sstas * 3. The name of the company nor the name of the author may be used to 18226031Sstas * endorse or promote products derived from this software without specific 19226031Sstas * prior written permission. 20226031Sstas * 21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31226031Sstas * SUCH DAMAGE. 32226031Sstas */ 33226031Sstas 34226031Sstas#include <sys/cdefs.h> 35226031Sstas__FBSDID("$FreeBSD: head/sys/arm/arm/gic.c 271595 2014-09-14 17:47:04Z ian $"); 36226031Sstas 37226031Sstas#include <sys/param.h> 38226031Sstas#include <sys/systm.h> 39226031Sstas#include <sys/bus.h> 40226031Sstas#include <sys/kernel.h> 41226031Sstas#include <sys/ktr.h> 42226031Sstas#include <sys/module.h> 43226031Sstas#include <sys/rman.h> 44226031Sstas#include <sys/pcpu.h> 45226031Sstas#include <sys/proc.h> 46226031Sstas#include <sys/cpuset.h> 47226031Sstas#include <sys/lock.h> 48226031Sstas#include <sys/mutex.h> 49226031Sstas#include <machine/bus.h> 50226031Sstas#include <machine/intr.h> 51226031Sstas#include <machine/smp.h> 52226031Sstas 53226031Sstas#include <dev/fdt/fdt_common.h> 54226031Sstas#include <dev/ofw/openfirm.h> 55226031Sstas#include <dev/ofw/ofw_bus.h> 56226031Sstas#include <dev/ofw/ofw_bus_subr.h> 57226031Sstas 58226031Sstas/* We are using GICv2 register naming */ 59226031Sstas 60226031Sstas/* Distributor Registers */ 61226031Sstas#define GICD_CTLR 0x000 /* v1 ICDDCR */ 62226031Sstas#define GICD_TYPER 0x004 /* v1 ICDICTR */ 63226031Sstas#define GICD_IIDR 0x008 /* v1 ICDIIDR */ 64226031Sstas#define GICD_IGROUPR(n) (0x0080 + ((n) * 4)) /* v1 ICDISER */ 65226031Sstas#define GICD_ISENABLER(n) (0x0100 + ((n) * 4)) /* v1 ICDISER */ 66226031Sstas#define GICD_ICENABLER(n) (0x0180 + ((n) * 4)) /* v1 ICDICER */ 67226031Sstas#define GICD_ISPENDR(n) (0x0200 + ((n) * 4)) /* v1 ICDISPR */ 68226031Sstas#define GICD_ICPENDR(n) (0x0280 + ((n) * 4)) /* v1 ICDICPR */ 69226031Sstas#define GICD_ICACTIVER(n) (0x0380 + ((n) * 4)) /* v1 ICDABR */ 70226031Sstas#define GICD_IPRIORITYR(n) (0x0400 + ((n) * 4)) /* v1 ICDIPR */ 71226031Sstas#define GICD_ITARGETSR(n) (0x0800 + ((n) * 4)) /* v1 ICDIPTR */ 72226031Sstas#define GICD_ICFGR(n) (0x0C00 + ((n) * 4)) /* v1 ICDICFR */ 73226031Sstas#define GICD_SGIR(n) (0x0F00 + ((n) * 4)) /* v1 ICDSGIR */ 74226031Sstas 75226031Sstas/* CPU Registers */ 76226031Sstas#define GICC_CTLR 0x0000 /* v1 ICCICR */ 77226031Sstas#define GICC_PMR 0x0004 /* v1 ICCPMR */ 78226031Sstas#define GICC_BPR 0x0008 /* v1 ICCBPR */ 79226031Sstas#define GICC_IAR 0x000C /* v1 ICCIAR */ 80226031Sstas#define GICC_EOIR 0x0010 /* v1 ICCEOIR */ 81226031Sstas#define GICC_RPR 0x0014 /* v1 ICCRPR */ 82226031Sstas#define GICC_HPPIR 0x0018 /* v1 ICCHPIR */ 83226031Sstas#define GICC_ABPR 0x001C /* v1 ICCABPR */ 84226031Sstas#define GICC_IIDR 0x00FC /* v1 ICCIIDR*/ 85226031Sstas 86226031Sstas#define GIC_LAST_IPI 15 /* Irqs 0-15 are IPIs. */ 87226031Sstas 88226031Sstas/* First bit is a polarity bit (0 - low, 1 - high) */ 89226031Sstas#define GICD_ICFGR_POL_LOW (0 << 0) 90226031Sstas#define GICD_ICFGR_POL_HIGH (1 << 0) 91226031Sstas#define GICD_ICFGR_POL_MASK 0x1 92226031Sstas/* Second bit is a trigger bit (0 - level, 1 - edge) */ 93226031Sstas#define GICD_ICFGR_TRIG_LVL (0 << 1) 94226031Sstas#define GICD_ICFGR_TRIG_EDGE (1 << 1) 95226031Sstas#define GICD_ICFGR_TRIG_MASK 0x2 96226031Sstas 97226031Sstasstruct arm_gic_softc { 98226031Sstas struct resource * gic_res[3]; 99226031Sstas bus_space_tag_t gic_c_bst; 100226031Sstas bus_space_tag_t gic_d_bst; 101226031Sstas bus_space_handle_t gic_c_bsh; 102226031Sstas bus_space_handle_t gic_d_bsh; 103226031Sstas uint8_t ver; 104226031Sstas device_t dev; 105226031Sstas struct mtx mutex; 106226031Sstas uint32_t nirqs; 107226031Sstas}; 108226031Sstas 109226031Sstasstatic struct resource_spec arm_gic_spec[] = { 110226031Sstas { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ 111226031Sstas { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ 112226031Sstas { -1, 0 } 113226031Sstas}; 114226031Sstas 115226031Sstasstatic struct arm_gic_softc *arm_gic_sc = NULL; 116226031Sstas 117226031Sstas#define gic_c_read_4(reg) \ 118226031Sstas bus_space_read_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg) 119226031Sstas#define gic_c_write_4(reg, val) \ 120226031Sstas bus_space_write_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg, val) 121226031Sstas#define gic_d_read_4(reg) \ 122226031Sstas bus_space_read_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg) 123226031Sstas#define gic_d_write_4(reg, val) \ 124226031Sstas bus_space_write_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg, val) 125226031Sstas 126226031Sstasstatic int gic_config_irq(int irq, enum intr_trigger trig, 127226031Sstas enum intr_polarity pol); 128226031Sstasstatic void gic_post_filter(void *); 129226031Sstas 130226031Sstasstatic struct ofw_compat_data compat_data[] = { 131226031Sstas {"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */ 132226031Sstas {"arm,gic-400", true}, 133226031Sstas {"arm,cortex-a15-gic", true}, 134226031Sstas {"arm,cortex-a9-gic", true}, 135226031Sstas {"arm,cortex-a7-gic", true}, 136226031Sstas {"arm,arm11mp-gic", true}, 137226031Sstas {"brcm,brahma-b15-gic", true}, 138226031Sstas {NULL, false} 139226031Sstas}; 140226031Sstas 141226031Sstasstatic int 142226031Sstasarm_gic_probe(device_t dev) 143226031Sstas{ 144226031Sstas 145226031Sstas if (!ofw_bus_status_okay(dev)) 146226031Sstas return (ENXIO); 147226031Sstas 148226031Sstas if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 149226031Sstas return (ENXIO); 150226031Sstas device_set_desc(dev, "ARM Generic Interrupt Controller"); 151226031Sstas return (BUS_PROBE_DEFAULT); 152226031Sstas} 153226031Sstas 154226031Sstasvoid 155226031Sstasgic_init_secondary(void) 156226031Sstas{ 157226031Sstas int i, nirqs; 158 159 /* Get the number of interrupts */ 160 nirqs = gic_d_read_4(GICD_TYPER); 161 nirqs = 32 * ((nirqs & 0x1f) + 1); 162 163 for (i = 0; i < nirqs; i += 4) 164 gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0); 165 166 /* Set all the interrupts to be in Group 0 (secure) */ 167 for (i = 0; i < nirqs; i += 32) { 168 gic_d_write_4(GICD_IGROUPR(i >> 5), 0); 169 } 170 171 /* Enable CPU interface */ 172 gic_c_write_4(GICC_CTLR, 1); 173 174 /* Set priority mask register. */ 175 gic_c_write_4(GICC_PMR, 0xff); 176 177 /* Enable interrupt distribution */ 178 gic_d_write_4(GICD_CTLR, 0x01); 179 180 /* 181 * Activate the timer interrupts: virtual, secure, and non-secure. 182 */ 183 gic_d_write_4(GICD_ISENABLER(27 >> 5), (1UL << (27 & 0x1F))); 184 gic_d_write_4(GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F))); 185 gic_d_write_4(GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F))); 186} 187 188static int 189arm_gic_attach(device_t dev) 190{ 191 struct arm_gic_softc *sc; 192 int i; 193 uint32_t icciidr; 194 195 if (arm_gic_sc) 196 return (ENXIO); 197 198 sc = device_get_softc(dev); 199 sc->dev = dev; 200 201 if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) { 202 device_printf(dev, "could not allocate resources\n"); 203 return (ENXIO); 204 } 205 206 /* Initialize mutex */ 207 mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); 208 209 /* Distributor Interface */ 210 sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]); 211 sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]); 212 213 /* CPU Interface */ 214 sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); 215 sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); 216 217 arm_gic_sc = sc; 218 219 /* Disable interrupt forwarding to the CPU interface */ 220 gic_d_write_4(GICD_CTLR, 0x00); 221 222 /* Get the number of interrupts */ 223 sc->nirqs = gic_d_read_4(GICD_TYPER); 224 sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); 225 226 /* Set up function pointers */ 227 arm_post_filter = gic_post_filter; 228 arm_config_irq = gic_config_irq; 229 230 icciidr = gic_c_read_4(GICC_IIDR); 231 device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x sc->nirqs %u\n", 232 icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf, 233 (icciidr & 0xfff), sc->nirqs); 234 235 /* Set all global interrupts to be level triggered, active low. */ 236 for (i = 32; i < sc->nirqs; i += 16) { 237 gic_d_write_4(GICD_ICFGR(i >> 4), 0x00000000); 238 } 239 240 /* Disable all interrupts. */ 241 for (i = 32; i < sc->nirqs; i += 32) { 242 gic_d_write_4(GICD_ICENABLER(i >> 5), 0xFFFFFFFF); 243 } 244 245 for (i = 0; i < sc->nirqs; i += 4) { 246 gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0); 247 gic_d_write_4(GICD_ITARGETSR(i >> 2), 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24); 248 } 249 250 /* Set all the interrupts to be in Group 0 (secure) */ 251 for (i = 0; i < sc->nirqs; i += 32) { 252 gic_d_write_4(GICD_IGROUPR(i >> 5), 0); 253 } 254 255 /* Enable CPU interface */ 256 gic_c_write_4(GICC_CTLR, 1); 257 258 /* Set priority mask register. */ 259 gic_c_write_4(GICC_PMR, 0xff); 260 261 /* Enable interrupt distribution */ 262 gic_d_write_4(GICD_CTLR, 0x01); 263 264 return (0); 265} 266 267static device_method_t arm_gic_methods[] = { 268 DEVMETHOD(device_probe, arm_gic_probe), 269 DEVMETHOD(device_attach, arm_gic_attach), 270 { 0, 0 } 271}; 272 273static driver_t arm_gic_driver = { 274 "gic", 275 arm_gic_methods, 276 sizeof(struct arm_gic_softc), 277}; 278 279static devclass_t arm_gic_devclass; 280 281EARLY_DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0, 282 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 283EARLY_DRIVER_MODULE(gic, ofwbus, arm_gic_driver, arm_gic_devclass, 0, 0, 284 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 285 286static void 287gic_post_filter(void *arg) 288{ 289 uintptr_t irq = (uintptr_t) arg; 290 291 if (irq > GIC_LAST_IPI) 292 arm_irq_memory_barrier(irq); 293 gic_c_write_4(GICC_EOIR, irq); 294} 295 296int 297arm_get_next_irq(int last_irq) 298{ 299 uint32_t active_irq; 300 301 active_irq = gic_c_read_4(GICC_IAR); 302 303 /* 304 * Immediatly EOIR the SGIs, because doing so requires the other 305 * bits (ie CPU number), not just the IRQ number, and we do not 306 * have this information later. 307 */ 308 309 if ((active_irq & 0x3ff) <= GIC_LAST_IPI) 310 gic_c_write_4(GICC_EOIR, active_irq); 311 active_irq &= 0x3FF; 312 313 if (active_irq == 0x3FF) { 314 if (last_irq == -1) 315 printf("Spurious interrupt detected\n"); 316 return -1; 317 } 318 319 return active_irq; 320} 321 322void 323arm_mask_irq(uintptr_t nb) 324{ 325 326 gic_d_write_4(GICD_ICENABLER(nb >> 5), (1UL << (nb & 0x1F))); 327 gic_c_write_4(GICC_EOIR, nb); 328} 329 330void 331arm_unmask_irq(uintptr_t nb) 332{ 333 334 if (nb > GIC_LAST_IPI) 335 arm_irq_memory_barrier(nb); 336 gic_d_write_4(GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F))); 337} 338 339static int 340gic_config_irq(int irq, enum intr_trigger trig, 341 enum intr_polarity pol) 342{ 343 uint32_t reg; 344 uint32_t mask; 345 346 /* Function is public-accessible, so validate input arguments */ 347 if ((irq < 0) || (irq >= arm_gic_sc->nirqs)) 348 goto invalid_args; 349 if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) && 350 (trig != INTR_TRIGGER_CONFORM)) 351 goto invalid_args; 352 if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) && 353 (pol != INTR_POLARITY_CONFORM)) 354 goto invalid_args; 355 356 mtx_lock_spin(&arm_gic_sc->mutex); 357 358 reg = gic_d_read_4(GICD_ICFGR(irq >> 4)); 359 mask = (reg >> 2*(irq % 16)) & 0x3; 360 361 if (pol == INTR_POLARITY_LOW) { 362 mask &= ~GICD_ICFGR_POL_MASK; 363 mask |= GICD_ICFGR_POL_LOW; 364 } else if (pol == INTR_POLARITY_HIGH) { 365 mask &= ~GICD_ICFGR_POL_MASK; 366 mask |= GICD_ICFGR_POL_HIGH; 367 } 368 369 if (trig == INTR_TRIGGER_LEVEL) { 370 mask &= ~GICD_ICFGR_TRIG_MASK; 371 mask |= GICD_ICFGR_TRIG_LVL; 372 } else if (trig == INTR_TRIGGER_EDGE) { 373 mask &= ~GICD_ICFGR_TRIG_MASK; 374 mask |= GICD_ICFGR_TRIG_EDGE; 375 } 376 377 /* Set mask */ 378 reg = reg & ~(0x3 << 2*(irq % 16)); 379 reg = reg | (mask << 2*(irq % 16)); 380 gic_d_write_4(GICD_ICFGR(irq >> 4), reg); 381 382 mtx_unlock_spin(&arm_gic_sc->mutex); 383 384 return (0); 385 386invalid_args: 387 device_printf(arm_gic_sc->dev, "gic_config_irg, invalid parameters\n"); 388 return (EINVAL); 389} 390 391#ifdef SMP 392void 393pic_ipi_send(cpuset_t cpus, u_int ipi) 394{ 395 uint32_t val = 0, i; 396 397 for (i = 0; i < MAXCPU; i++) 398 if (CPU_ISSET(i, &cpus)) 399 val |= 1 << (16 + i); 400 gic_d_write_4(GICD_SGIR(0), val | ipi); 401 402} 403 404int 405pic_ipi_get(int i) 406{ 407 408 if (i != -1) { 409 /* 410 * The intr code will automagically give the frame pointer 411 * if the interrupt argument is 0. 412 */ 413 if ((unsigned int)i > 16) 414 return (0); 415 return (i); 416 } 417 return (0x3ff); 418} 419 420void 421pic_ipi_clear(int ipi) 422{ 423} 424#endif 425 426