gic.c revision 266152
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: stable/10/sys/arm/arm/gic.c 266152 2014-05-15 16:11:06Z ian $"); 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/* First bit is a polarity bit (0 - low, 1 - high) */ 87#define GICD_ICFGR_POL_LOW (0 << 0) 88#define GICD_ICFGR_POL_HIGH (1 << 0) 89#define GICD_ICFGR_POL_MASK 0x1 90/* Second bit is a trigger bit (0 - level, 1 - edge) */ 91#define GICD_ICFGR_TRIG_LVL (0 << 1) 92#define GICD_ICFGR_TRIG_EDGE (1 << 1) 93#define GICD_ICFGR_TRIG_MASK 0x2 94 95struct arm_gic_softc { 96 struct resource * gic_res[3]; 97 bus_space_tag_t gic_c_bst; 98 bus_space_tag_t gic_d_bst; 99 bus_space_handle_t gic_c_bsh; 100 bus_space_handle_t gic_d_bsh; 101 uint8_t ver; 102 device_t dev; 103 struct mtx mutex; 104 uint32_t nirqs; 105}; 106 107static struct resource_spec arm_gic_spec[] = { 108 { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ 109 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ 110 { -1, 0 } 111}; 112 113static struct arm_gic_softc *arm_gic_sc = NULL; 114 115#define gic_c_read_4(reg) \ 116 bus_space_read_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg) 117#define gic_c_write_4(reg, val) \ 118 bus_space_write_4(arm_gic_sc->gic_c_bst, arm_gic_sc->gic_c_bsh, reg, val) 119#define gic_d_read_4(reg) \ 120 bus_space_read_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg) 121#define gic_d_write_4(reg, val) \ 122 bus_space_write_4(arm_gic_sc->gic_d_bst, arm_gic_sc->gic_d_bsh, reg, val) 123 124static int gic_config_irq(int irq, enum intr_trigger trig, 125 enum intr_polarity pol); 126static void gic_post_filter(void *); 127 128static int 129arm_gic_probe(device_t dev) 130{ 131 132 if (!ofw_bus_status_okay(dev)) 133 return (ENXIO); 134 135 if (!ofw_bus_is_compatible(dev, "arm,gic")) 136 return (ENXIO); 137 device_set_desc(dev, "ARM Generic Interrupt Controller"); 138 return (BUS_PROBE_DEFAULT); 139} 140 141void 142gic_init_secondary(void) 143{ 144 int i, nirqs; 145 146 /* Get the number of interrupts */ 147 nirqs = gic_d_read_4(GICD_TYPER); 148 nirqs = 32 * ((nirqs & 0x1f) + 1); 149 150 for (i = 0; i < nirqs; i += 4) 151 gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0); 152 153 /* Set all the interrupts to be in Group 0 (secure) */ 154 for (i = 0; i < nirqs; i += 32) { 155 gic_d_write_4(GICD_IGROUPR(i >> 5), 0); 156 } 157 158 /* Enable CPU interface */ 159 gic_c_write_4(GICC_CTLR, 1); 160 161 /* Set priority mask register. */ 162 gic_c_write_4(GICC_PMR, 0xff); 163 164 /* Enable interrupt distribution */ 165 gic_d_write_4(GICD_CTLR, 0x01); 166 167 /* Activate IRQ 29, ie private timer IRQ*/ 168 gic_d_write_4(GICD_ISENABLER(29 >> 5), (1UL << (29 & 0x1F))); 169} 170 171static int 172arm_gic_attach(device_t dev) 173{ 174 struct arm_gic_softc *sc; 175 int i; 176 uint32_t icciidr; 177 178 if (arm_gic_sc) 179 return (ENXIO); 180 181 sc = device_get_softc(dev); 182 sc->dev = dev; 183 184 if (bus_alloc_resources(dev, arm_gic_spec, sc->gic_res)) { 185 device_printf(dev, "could not allocate resources\n"); 186 return (ENXIO); 187 } 188 189 /* Initialize mutex */ 190 mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); 191 192 /* Distributor Interface */ 193 sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]); 194 sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]); 195 196 /* CPU Interface */ 197 sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); 198 sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); 199 200 arm_gic_sc = sc; 201 202 /* Disable interrupt forwarding to the CPU interface */ 203 gic_d_write_4(GICD_CTLR, 0x00); 204 205 /* Get the number of interrupts */ 206 sc->nirqs = gic_d_read_4(GICD_TYPER); 207 sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); 208 209 /* Set up function pointers */ 210 arm_post_filter = gic_post_filter; 211 arm_config_irq = gic_config_irq; 212 213 icciidr = gic_c_read_4(GICC_IIDR); 214 device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x sc->nirqs %u\n", 215 icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf, 216 (icciidr & 0xfff), sc->nirqs); 217 218 /* Set all global interrupts to be level triggered, active low. */ 219 for (i = 32; i < sc->nirqs; i += 16) { 220 gic_d_write_4(GICD_ICFGR(i >> 4), 0x00000000); 221 } 222 223 /* Disable all interrupts. */ 224 for (i = 32; i < sc->nirqs; i += 32) { 225 gic_d_write_4(GICD_ICENABLER(i >> 5), 0xFFFFFFFF); 226 } 227 228 for (i = 0; i < sc->nirqs; i += 4) { 229 gic_d_write_4(GICD_IPRIORITYR(i >> 2), 0); 230 gic_d_write_4(GICD_ITARGETSR(i >> 2), 1 << 0 | 1 << 8 | 1 << 16 | 1 << 24); 231 } 232 233 /* Set all the interrupts to be in Group 0 (secure) */ 234 for (i = 0; i < sc->nirqs; i += 32) { 235 gic_d_write_4(GICD_IGROUPR(i >> 5), 0); 236 } 237 238 /* Enable CPU interface */ 239 gic_c_write_4(GICC_CTLR, 1); 240 241 /* Set priority mask register. */ 242 gic_c_write_4(GICC_PMR, 0xff); 243 244 /* Enable interrupt distribution */ 245 gic_d_write_4(GICD_CTLR, 0x01); 246 247 return (0); 248} 249 250static device_method_t arm_gic_methods[] = { 251 DEVMETHOD(device_probe, arm_gic_probe), 252 DEVMETHOD(device_attach, arm_gic_attach), 253 { 0, 0 } 254}; 255 256static driver_t arm_gic_driver = { 257 "gic", 258 arm_gic_methods, 259 sizeof(struct arm_gic_softc), 260}; 261 262static devclass_t arm_gic_devclass; 263 264DRIVER_MODULE(gic, simplebus, arm_gic_driver, arm_gic_devclass, 0, 0); 265 266static void 267gic_post_filter(void *arg) 268{ 269 uintptr_t irq = (uintptr_t) arg; 270 271 gic_c_write_4(GICC_EOIR, irq); 272} 273 274int 275arm_get_next_irq(int last_irq) 276{ 277 uint32_t active_irq; 278 279 active_irq = gic_c_read_4(GICC_IAR); 280 281 /* 282 * Immediatly EOIR the SGIs, because doing so requires the other 283 * bits (ie CPU number), not just the IRQ number, and we do not 284 * have this information later. 285 */ 286 287 if ((active_irq & 0x3ff) < 16) 288 gic_c_write_4(GICC_EOIR, active_irq); 289 active_irq &= 0x3FF; 290 291 if (active_irq == 0x3FF) { 292 if (last_irq == -1) 293 printf("Spurious interrupt detected [0x%08x]\n", active_irq); 294 return -1; 295 } 296 297 return active_irq; 298} 299 300void 301arm_mask_irq(uintptr_t nb) 302{ 303 304 gic_d_write_4(GICD_ICENABLER(nb >> 5), (1UL << (nb & 0x1F))); 305 gic_c_write_4(GICC_EOIR, nb); 306} 307 308void 309arm_unmask_irq(uintptr_t nb) 310{ 311 312 gic_d_write_4(GICD_ISENABLER(nb >> 5), (1UL << (nb & 0x1F))); 313} 314 315static int 316gic_config_irq(int irq, enum intr_trigger trig, 317 enum intr_polarity pol) 318{ 319 uint32_t reg; 320 uint32_t mask; 321 322 /* Function is public-accessible, so validate input arguments */ 323 if ((irq < 0) || (irq >= arm_gic_sc->nirqs)) 324 goto invalid_args; 325 if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL) && 326 (trig != INTR_TRIGGER_CONFORM)) 327 goto invalid_args; 328 if ((pol != INTR_POLARITY_HIGH) && (pol != INTR_POLARITY_LOW) && 329 (pol != INTR_POLARITY_CONFORM)) 330 goto invalid_args; 331 332 mtx_lock_spin(&arm_gic_sc->mutex); 333 334 reg = gic_d_read_4(GICD_ICFGR(irq >> 4)); 335 mask = (reg >> 2*(irq % 16)) & 0x3; 336 337 if (pol == INTR_POLARITY_LOW) { 338 mask &= ~GICD_ICFGR_POL_MASK; 339 mask |= GICD_ICFGR_POL_LOW; 340 } else if (pol == INTR_POLARITY_HIGH) { 341 mask &= ~GICD_ICFGR_POL_MASK; 342 mask |= GICD_ICFGR_POL_HIGH; 343 } 344 345 if (trig == INTR_TRIGGER_LEVEL) { 346 mask &= ~GICD_ICFGR_TRIG_MASK; 347 mask |= GICD_ICFGR_TRIG_LVL; 348 } else if (trig == INTR_TRIGGER_EDGE) { 349 mask &= ~GICD_ICFGR_TRIG_MASK; 350 mask |= GICD_ICFGR_TRIG_EDGE; 351 } 352 353 /* Set mask */ 354 reg = reg & ~(0x3 << 2*(irq % 16)); 355 reg = reg | (mask << 2*(irq % 16)); 356 gic_d_write_4(GICD_ICFGR(irq >> 4), reg); 357 358 mtx_unlock_spin(&arm_gic_sc->mutex); 359 360 return (0); 361 362invalid_args: 363 device_printf(arm_gic_sc->dev, "gic_config_irg, invalid parameters\n"); 364 return (EINVAL); 365} 366 367#ifdef SMP 368void 369pic_ipi_send(cpuset_t cpus, u_int ipi) 370{ 371 uint32_t val = 0, i; 372 373 for (i = 0; i < MAXCPU; i++) 374 if (CPU_ISSET(i, &cpus)) 375 val |= 1 << (16 + i); 376 gic_d_write_4(GICD_SGIR(0), val | ipi); 377 378} 379 380int 381pic_ipi_get(int i) 382{ 383 384 if (i != -1) { 385 /* 386 * The intr code will automagically give the frame pointer 387 * if the interrupt argument is 0. 388 */ 389 if ((unsigned int)i > 16) 390 return (0); 391 return (i); 392 } 393 return (0x3ff); 394} 395 396void 397pic_ipi_clear(int ipi) 398{ 399} 400#endif 401 402