gic.c revision 276028
1/*- 2 * Copyright (c) 2011 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * Developed by Damjan Marion <damjan.marion@gmail.com> 6 * 7 * Based on OMAP4 GIC code by Ben Gray 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of the company nor the name of the author may be used to 18 * endorse or promote products derived from this software without specific 19 * prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: head/sys/arm/arm/gic.c 276028 2014-12-21 17:25:21Z andrew $"); 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/bus.h> 40#include <sys/kernel.h> 41#include <sys/ktr.h> 42#include <sys/module.h> 43#include <sys/rman.h> 44#include <sys/pcpu.h> 45#include <sys/proc.h> 46#include <sys/cpuset.h> 47#include <sys/lock.h> 48#include <sys/mutex.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/* We are using GICv2 register naming */ 59 60/* Distributor Registers */ 61#define GICD_CTLR 0x000 /* v1 ICDDCR */ 62#define GICD_TYPER 0x004 /* v1 ICDICTR */ 63#define GICD_IIDR 0x008 /* v1 ICDIIDR */ 64#define GICD_IGROUPR(n) (0x0080 + ((n) * 4)) /* v1 ICDISER */ 65#define GICD_ISENABLER(n) (0x0100 + ((n) * 4)) /* v1 ICDISER */ 66#define GICD_ICENABLER(n) (0x0180 + ((n) * 4)) /* v1 ICDICER */ 67#define GICD_ISPENDR(n) (0x0200 + ((n) * 4)) /* v1 ICDISPR */ 68#define GICD_ICPENDR(n) (0x0280 + ((n) * 4)) /* v1 ICDICPR */ 69#define GICD_ICACTIVER(n) (0x0380 + ((n) * 4)) /* v1 ICDABR */ 70#define GICD_IPRIORITYR(n) (0x0400 + ((n) * 4)) /* v1 ICDIPR */ 71#define GICD_ITARGETSR(n) (0x0800 + ((n) * 4)) /* v1 ICDIPTR */ 72#define GICD_ICFGR(n) (0x0C00 + ((n) * 4)) /* v1 ICDICFR */ 73#define GICD_SGIR(n) (0x0F00 + ((n) * 4)) /* v1 ICDSGIR */ 74 75/* CPU Registers */ 76#define GICC_CTLR 0x0000 /* v1 ICCICR */ 77#define GICC_PMR 0x0004 /* v1 ICCPMR */ 78#define GICC_BPR 0x0008 /* v1 ICCBPR */ 79#define GICC_IAR 0x000C /* v1 ICCIAR */ 80#define GICC_EOIR 0x0010 /* v1 ICCEOIR */ 81#define GICC_RPR 0x0014 /* v1 ICCRPR */ 82#define GICC_HPPIR 0x0018 /* v1 ICCHPIR */ 83#define GICC_ABPR 0x001C /* v1 ICCABPR */ 84#define GICC_IIDR 0x00FC /* v1 ICCIIDR*/ 85 86#define GIC_FIRST_IPI 0 /* Irqs 0-15 are SGIs/IPIs. */ 87#define GIC_LAST_IPI 15 88#define GIC_FIRST_PPI 16 /* Irqs 16-31 are private (per */ 89#define GIC_LAST_PPI 31 /* core) peripheral interrupts. */ 90#define GIC_FIRST_SPI 32 /* Irqs 32+ are shared peripherals. */ 91 92/* First bit is a polarity bit (0 - low, 1 - high) */ 93#define GICD_ICFGR_POL_LOW (0 << 0) 94#define GICD_ICFGR_POL_HIGH (1 << 0) 95#define GICD_ICFGR_POL_MASK 0x1 96/* Second bit is a trigger bit (0 - level, 1 - edge) */ 97#define GICD_ICFGR_TRIG_LVL (0 << 1) 98#define GICD_ICFGR_TRIG_EDGE (1 << 1) 99#define GICD_ICFGR_TRIG_MASK 0x2 100 101struct arm_gic_softc { 102 device_t gic_dev; 103 struct resource * gic_res[3]; 104 bus_space_tag_t gic_c_bst; 105 bus_space_tag_t gic_d_bst; 106 bus_space_handle_t gic_c_bsh; 107 bus_space_handle_t gic_d_bsh; 108 uint8_t ver; 109 struct mtx mutex; 110 uint32_t nirqs; 111}; 112 113static struct resource_spec arm_gic_spec[] = { 114 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ 115 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ 116 { -1, 0 } 117}; 118 119static struct arm_gic_softc *arm_gic_sc = NULL; 120 121#define gic_c_read_4(_sc, _reg) \ 122 bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg)) 123#define gic_c_write_4(_sc, _reg, _val) \ 124 bus_space_write_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg), (_val)) 125#define gic_d_read_4(_sc, _reg) \ 126 bus_space_read_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg)) 127#define gic_d_write_4(_sc, _reg, _val) \ 128 bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) 129 130static int gic_config_irq(int irq, enum intr_trigger trig, 131 enum intr_polarity pol); 132static void gic_post_filter(void *); 133 134static struct ofw_compat_data compat_data[] = { 135 {"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */ 136 {"arm,gic-400", true}, 137 {"arm,cortex-a15-gic", true}, 138 {"arm,cortex-a9-gic", true}, 139 {"arm,cortex-a7-gic", true}, 140 {"arm,arm11mp-gic", true}, 141 {"brcm,brahma-b15-gic", true}, 142 {NULL, false} 143}; 144 145static int 146arm_gic_probe(device_t dev) 147{ 148 149 if (!ofw_bus_status_okay(dev)) 150 return (ENXIO); 151 152 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 153 return (ENXIO); 154 device_set_desc(dev, "ARM Generic Interrupt Controller"); 155 return (BUS_PROBE_DEFAULT); 156} 157 158void 159gic_init_secondary(void) 160{ 161 struct arm_gic_softc *sc = arm_gic_sc; 162 int i; 163 164 for (i = 0; i < sc->nirqs; i += 4) 165 gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); 166 167 /* Set all the interrupts to be in Group 0 (secure) */ 168 for (i = 0; i < sc->nirqs; i += 32) { 169 gic_d_write_4(sc, GICD_IGROUPR(i >> 5), 0); 170 } 171 172 /* Enable CPU interface */ 173 gic_c_write_4(sc, GICC_CTLR, 1); 174 175 /* Set priority mask register. */ 176 gic_c_write_4(sc, GICC_PMR, 0xff); 177 178 /* Enable interrupt distribution */ 179 gic_d_write_4(sc, GICD_CTLR, 0x01); 180 181 /* 182 * Activate the timer interrupts: virtual, secure, and non-secure. 183 */ 184 gic_d_write_4(sc, GICD_ISENABLER(27 >> 5), (1UL << (27 & 0x1F))); 185 gic_d_write_4(sc, GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F))); 186 gic_d_write_4(sc, GICD_ISENABLER(30 >> 5), (1UL << (30 & 0x1F))); 187} 188 189int 190gic_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt, 191 int *trig, int *pol) 192{ 193 static u_int num_intr_cells; 194 195 if (num_intr_cells == 0) { 196 if (OF_searchencprop(OF_node_from_xref(iparent), 197 "#interrupt-cells", &num_intr_cells, 198 sizeof(num_intr_cells)) == -1) { 199 num_intr_cells = 1; 200 } 201 } 202 203 if (num_intr_cells == 1) { 204 *interrupt = fdt32_to_cpu(intr[0]); 205 *trig = INTR_TRIGGER_CONFORM; 206 *pol = INTR_POLARITY_CONFORM; 207 } else { 208 if (intr[0] == 0) 209 *interrupt = fdt32_to_cpu(intr[1]) + GIC_FIRST_SPI; 210 else 211 *interrupt = fdt32_to_cpu(intr[1]) + GIC_FIRST_PPI; 212 /* 213 * In intr[2], bits[3:0] are trigger type and level flags. 214 * 1 = low-to-high edge triggered 215 * 2 = high-to-low edge triggered 216 * 4 = active high level-sensitive 217 * 8 = active low level-sensitive 218 * The hardware only supports active-high-level or rising-edge. 219 */ 220 if (intr[2] & 0x0a) { 221 printf("unsupported trigger/polarity configuration " 222 "0x%2x\n", intr[2] & 0x0f); 223 return (ENOTSUP); 224 } 225 *pol = INTR_POLARITY_CONFORM; 226 if (intr[2] & 0x01) 227 *trig = INTR_TRIGGER_EDGE; 228 else 229 *trig = INTR_TRIGGER_LEVEL; 230 } 231 return (0); 232} 233 234static int 235arm_gic_attach(device_t dev) 236{ 237 struct arm_gic_softc *sc; 238 int i; 239 uint32_t icciidr; 240 241 if (arm_gic_sc) 242 return (ENXIO); 243 244 sc = device_get_softc(dev); 245 246 if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) { 247 device_printf(dev, "could not allocate resources\n"); 248 return (ENXIO); 249 } 250 251 sc->gic_dev = dev; 252 arm_gic_sc = sc; 253 254 /* Initialize mutex */ 255 mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); 256 257 /* Distributor Interface */ 258 sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]); 259 sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]); 260 261 /* CPU Interface */ 262 sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); 263 sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); 264 265 /* Disable interrupt forwarding to the CPU interface */ 266 gic_d_write_4(sc, GICD_CTLR, 0x00); 267 268 /* Get the number of interrupts */ 269 sc->nirqs = gic_d_read_4(sc, GICD_TYPER); 270 sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); 271 272 /* Set up function pointers */ 273 arm_post_filter = gic_post_filter; 274 arm_config_irq = gic_config_irq; 275 276 icciidr = gic_c_read_4(sc, GICC_IIDR); 277 device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n", 278 icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf, 279 (icciidr & 0xfff), sc->nirqs); 280 281 /* Set all global interrupts to be level triggered, active low. */ 282 for (i = 32; i < sc->nirqs; i += 16) { 283 gic_d_write_4(sc, GICD_ICFGR(i >> 4), 0x00000000); 284 } 285 286 /* Disable all interrupts. */ 287 for (i = 32; i < sc->nirqs; i += 32) { 288 gic_d_write_4(sc, GICD_ICENABLER(i >> 5), 0xFFFFFFFF); 289 } 290 291 for (i = 0; i < sc->nirqs; i += 4) { 292 gic_d_write_4(sc, GICD_IPRIORITYR(i >> 2), 0); 293 gic_d_write_4(sc, GICD_ITARGETSR(i >> 2), 294 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24); 295 } 296 297 /* Set all the interrupts to be in Group 0 (secure) */ 298 for (i = 0; i < sc->nirqs; i += 32) { 299 gic_d_write_4(sc, GICD_IGROUPR(i >> 5), 0); 300 } 301 302 /* Enable CPU interface */ 303 gic_c_write_4(sc, GICC_CTLR, 1); 304 305 /* Set priority mask register. */ 306 gic_c_write_4(sc, GICC_PMR, 0xff); 307 308 /* Enable interrupt distribution */ 309 gic_d_write_4(sc, GICD_CTLR, 0x01); 310 311 return (0); 312} 313 314static void 315gic_post_filter(void *arg) 316{ 317 struct arm_gic_softc *sc = arm_gic_sc; 318 uintptr_t irq = (uintptr_t) arg; 319 320 if (irq > GIC_LAST_IPI) 321 arm_irq_memory_barrier(irq); 322 gic_c_write_4(sc, GICC_EOIR, irq); 323} 324 325int 326arm_get_next_irq(int last_irq) 327{ 328 struct arm_gic_softc *sc = arm_gic_sc; 329 uint32_t active_irq; 330 331 active_irq = gic_c_read_4(sc, GICC_IAR); 332 333 /* 334 * Immediatly EOIR the SGIs, because doing so requires the other 335 * bits (ie CPU number), not just the IRQ number, and we do not 336 * have this information later. 337 */ 338 if ((active_irq & 0x3ff) <= GIC_LAST_IPI) 339 gic_c_write_4(sc, GICC_EOIR, active_irq); 340 active_irq &= 0x3FF; 341 342 if (active_irq == 0x3FF) { 343 if (last_irq == -1) 344 printf("Spurious interrupt detected\n"); 345 return -1; 346 } 347 348 return active_irq; 349} 350 351void 352arm_mask_irq(uintptr_t nb) 353{ 354 struct arm_gic_softc *sc = arm_gic_sc; 355 356 gic_d_write_4(sc, GICD_ICENABLER(nb >> 5), (1UL << (nb & 0x1F))); 357 gic_c_write_4(sc, GICC_EOIR, nb); 358} 359 360void 361arm_unmask_irq(uintptr_t nb) 362{ 363 struct arm_gic_softc *sc = arm_gic_sc; 364 365 if (nb > GIC_LAST_IPI) 366 arm_irq_memory_barrier(nb); 367 gic_d_write_4(sc, GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F))); 368} 369 370static int 371gic_config_irq(int irq, enum intr_trigger trig, 372 enum intr_polarity pol) 373{ 374 struct arm_gic_softc *sc = arm_gic_sc; 375 device_t dev = sc->gic_dev; 376 uint32_t reg; 377 uint32_t mask; 378 379 /* Function is public-accessible, so validate input arguments */ 380 if ((irq < 0) || (irq >= sc->nirqs)) 381 goto invalid_args; 382 if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) && 383 (trig != INTR_TRIGGER_CONFORM)) 384 goto invalid_args; 385 if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) && 386 (pol != INTR_POLARITY_CONFORM)) 387 goto invalid_args; 388 389 mtx_lock_spin(&sc->mutex); 390 391 reg = gic_d_read_4(sc, GICD_ICFGR(irq >> 4)); 392 mask = (reg >> 2*(irq % 16)) & 0x3; 393 394 if (pol == INTR_POLARITY_LOW) { 395 mask &= ~GICD_ICFGR_POL_MASK; 396 mask |= GICD_ICFGR_POL_LOW; 397 } else if (pol == INTR_POLARITY_HIGH) { 398 mask &= ~GICD_ICFGR_POL_MASK; 399 mask |= GICD_ICFGR_POL_HIGH; 400 } 401 402 if (trig == INTR_TRIGGER_LEVEL) { 403 mask &= ~GICD_ICFGR_TRIG_MASK; 404 mask |= GICD_ICFGR_TRIG_LVL; 405 } else if (trig == INTR_TRIGGER_EDGE) { 406 mask &= ~GICD_ICFGR_TRIG_MASK; 407 mask |= GICD_ICFGR_TRIG_EDGE; 408 } 409 410 /* Set mask */ 411 reg = reg & ~(0x3 << 2*(irq % 16)); 412 reg = reg | (mask << 2*(irq % 16)); 413 gic_d_write_4(sc, GICD_ICFGR(irq >> 4), reg); 414 415 mtx_unlock_spin(&sc->mutex); 416 417 return (0); 418 419invalid_args: 420 device_printf(dev, "gic_config_irg, invalid parameters\n"); 421 return (EINVAL); 422} 423 424#ifdef SMP 425void 426pic_ipi_send(cpuset_t cpus, u_int ipi) 427{ 428 struct arm_gic_softc *sc = arm_gic_sc; 429 uint32_t val = 0, i; 430 431 for (i = 0; i < MAXCPU; i++) 432 if (CPU_ISSET(i, &cpus)) 433 val |= 1 << (16 + i); 434 435 gic_d_write_4(sc, GICD_SGIR(0), val | ipi); 436} 437 438int 439pic_ipi_get(int i) 440{ 441 442 if (i != -1) { 443 /* 444 * The intr code will automagically give the frame pointer 445 * if the interrupt argument is 0. 446 */ 447 if ((unsigned int)i > 16) 448 return (0); 449 return (i); 450 } 451 452 return (0x3ff); 453} 454 455void 456pic_ipi_clear(int ipi) 457{ 458} 459#endif 460 461static device_method_t arm_gic_methods[] = { 462 /* Device interface */ 463 DEVMETHOD(device_probe, arm_gic_probe), 464 DEVMETHOD(device_attach, arm_gic_attach), 465 { 0, 0 } 466}; 467 468static driver_t arm_gic_driver = { 469 "gic", 470 arm_gic_methods, 471 sizeof(struct arm_gic_softc), 472}; 473 474static devclass_t arm_gic_devclass; 475 476EARLY_DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0, 477 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 478EARLY_DRIVER_MODULE(gic, ofwbus, arm_gic_driver, arm_gic_devclass, 0, 0, 479 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 480