100cc96f0SAndrew Jones /* 200cc96f0SAndrew Jones * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com> 300cc96f0SAndrew Jones * 400cc96f0SAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 500cc96f0SAndrew Jones */ 600cc96f0SAndrew Jones #include <devicetree.h> 700cc96f0SAndrew Jones #include <asm/gic.h> 800cc96f0SAndrew Jones #include <asm/io.h> 900cc96f0SAndrew Jones 1000cc96f0SAndrew Jones struct gicv2_data gicv2_data; 1191a6c3ceSAndrew Jones struct gicv3_data gicv3_data; 1200cc96f0SAndrew Jones 132e2d471dSAndrew Jones struct gic_common_ops { 142e2d471dSAndrew Jones void (*enable_defaults)(void); 152e2d471dSAndrew Jones u32 (*read_iar)(void); 162e2d471dSAndrew Jones u32 (*iar_irqnr)(u32 iar); 172e2d471dSAndrew Jones void (*write_eoir)(u32 irqstat); 182e2d471dSAndrew Jones void (*ipi_send_single)(int irq, int cpu); 192e2d471dSAndrew Jones void (*ipi_send_mask)(int irq, const cpumask_t *dest); 202e2d471dSAndrew Jones }; 212e2d471dSAndrew Jones 222e2d471dSAndrew Jones static const struct gic_common_ops *gic_common_ops; 232e2d471dSAndrew Jones 242e2d471dSAndrew Jones static const struct gic_common_ops gicv2_common_ops = { 252e2d471dSAndrew Jones .enable_defaults = gicv2_enable_defaults, 262e2d471dSAndrew Jones .read_iar = gicv2_read_iar, 272e2d471dSAndrew Jones .iar_irqnr = gicv2_iar_irqnr, 282e2d471dSAndrew Jones .write_eoir = gicv2_write_eoir, 292e2d471dSAndrew Jones .ipi_send_single = gicv2_ipi_send_single, 302e2d471dSAndrew Jones .ipi_send_mask = gicv2_ipi_send_mask, 312e2d471dSAndrew Jones }; 322e2d471dSAndrew Jones 332e2d471dSAndrew Jones static const struct gic_common_ops gicv3_common_ops = { 342e2d471dSAndrew Jones .enable_defaults = gicv3_enable_defaults, 352e2d471dSAndrew Jones .read_iar = gicv3_read_iar, 362e2d471dSAndrew Jones .iar_irqnr = gicv3_iar_irqnr, 372e2d471dSAndrew Jones .write_eoir = gicv3_write_eoir, 382e2d471dSAndrew Jones .ipi_send_single = gicv3_ipi_send_single, 392e2d471dSAndrew Jones .ipi_send_mask = gicv3_ipi_send_mask, 402e2d471dSAndrew Jones }; 412e2d471dSAndrew Jones 4200cc96f0SAndrew Jones /* 4300cc96f0SAndrew Jones * Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt 4491a6c3ceSAndrew Jones * Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt 4500cc96f0SAndrew Jones */ 4600cc96f0SAndrew Jones static bool 4700cc96f0SAndrew Jones gic_get_dt_bases(const char *compatible, void **base1, void **base2) 4800cc96f0SAndrew Jones { 4900cc96f0SAndrew Jones struct dt_pbus_reg reg; 5000cc96f0SAndrew Jones struct dt_device gic; 5100cc96f0SAndrew Jones struct dt_bus bus; 52a5a2d35cSAndrew Jones int node, ret, i; 5300cc96f0SAndrew Jones 5400cc96f0SAndrew Jones dt_bus_init_defaults(&bus); 5500cc96f0SAndrew Jones dt_device_init(&gic, &bus, NULL); 5600cc96f0SAndrew Jones 5700cc96f0SAndrew Jones node = dt_device_find_compatible(&gic, compatible); 5800cc96f0SAndrew Jones assert(node >= 0 || node == -FDT_ERR_NOTFOUND); 5900cc96f0SAndrew Jones 6000cc96f0SAndrew Jones if (node == -FDT_ERR_NOTFOUND) 6100cc96f0SAndrew Jones return false; 6200cc96f0SAndrew Jones 6300cc96f0SAndrew Jones dt_device_bind_node(&gic, node); 6400cc96f0SAndrew Jones 6500cc96f0SAndrew Jones ret = dt_pbus_translate(&gic, 0, ®); 6600cc96f0SAndrew Jones assert(ret == 0); 6700cc96f0SAndrew Jones *base1 = ioremap(reg.addr, reg.size); 6800cc96f0SAndrew Jones 69a5a2d35cSAndrew Jones for (i = 0; i < GICV3_NR_REDISTS; ++i) { 70a5a2d35cSAndrew Jones ret = dt_pbus_translate(&gic, i + 1, ®); 71a5a2d35cSAndrew Jones if (ret == -FDT_ERR_NOTFOUND) 72a5a2d35cSAndrew Jones break; 7300cc96f0SAndrew Jones assert(ret == 0); 74a5a2d35cSAndrew Jones base2[i] = ioremap(reg.addr, reg.size); 75a5a2d35cSAndrew Jones } 7600cc96f0SAndrew Jones 7700cc96f0SAndrew Jones return true; 7800cc96f0SAndrew Jones } 7900cc96f0SAndrew Jones 8000cc96f0SAndrew Jones int gicv2_init(void) 8100cc96f0SAndrew Jones { 8200cc96f0SAndrew Jones return gic_get_dt_bases("arm,cortex-a15-gic", 8300cc96f0SAndrew Jones &gicv2_data.dist_base, &gicv2_data.cpu_base); 8400cc96f0SAndrew Jones } 8500cc96f0SAndrew Jones 8691a6c3ceSAndrew Jones int gicv3_init(void) 8791a6c3ceSAndrew Jones { 8891a6c3ceSAndrew Jones return gic_get_dt_bases("arm,gic-v3", &gicv3_data.dist_base, 89a5a2d35cSAndrew Jones &gicv3_data.redist_bases[0]); 9091a6c3ceSAndrew Jones } 9191a6c3ceSAndrew Jones 92db198544SAndrew Jones int gic_version(void) 93db198544SAndrew Jones { 94db198544SAndrew Jones if (gic_common_ops == &gicv2_common_ops) 95db198544SAndrew Jones return 2; 96db198544SAndrew Jones else if (gic_common_ops == &gicv3_common_ops) 97db198544SAndrew Jones return 3; 98db198544SAndrew Jones return 0; 99db198544SAndrew Jones } 100db198544SAndrew Jones 10100cc96f0SAndrew Jones int gic_init(void) 10200cc96f0SAndrew Jones { 103db198544SAndrew Jones if (gicv2_init()) 1042e2d471dSAndrew Jones gic_common_ops = &gicv2_common_ops; 105db198544SAndrew Jones else if (gicv3_init()) 1062e2d471dSAndrew Jones gic_common_ops = &gicv3_common_ops; 107db198544SAndrew Jones return gic_version(); 10800cc96f0SAndrew Jones } 1092e2d471dSAndrew Jones 1102e2d471dSAndrew Jones void gic_enable_defaults(void) 1112e2d471dSAndrew Jones { 1122e2d471dSAndrew Jones if (!gic_common_ops) { 1132e2d471dSAndrew Jones int ret = gic_init(); 1142e2d471dSAndrew Jones assert(ret != 0); 1152e2d471dSAndrew Jones } else 1162e2d471dSAndrew Jones assert(gic_common_ops->enable_defaults); 1172e2d471dSAndrew Jones gic_common_ops->enable_defaults(); 1182e2d471dSAndrew Jones } 1192e2d471dSAndrew Jones 1202e2d471dSAndrew Jones u32 gic_read_iar(void) 1212e2d471dSAndrew Jones { 1222e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->read_iar); 1232e2d471dSAndrew Jones return gic_common_ops->read_iar(); 1242e2d471dSAndrew Jones } 1252e2d471dSAndrew Jones 1262e2d471dSAndrew Jones u32 gic_iar_irqnr(u32 iar) 1272e2d471dSAndrew Jones { 1282e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->iar_irqnr); 1292e2d471dSAndrew Jones return gic_common_ops->iar_irqnr(iar); 1302e2d471dSAndrew Jones } 1312e2d471dSAndrew Jones 1322e2d471dSAndrew Jones void gic_write_eoir(u32 irqstat) 1332e2d471dSAndrew Jones { 1342e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->write_eoir); 1352e2d471dSAndrew Jones gic_common_ops->write_eoir(irqstat); 1362e2d471dSAndrew Jones } 1372e2d471dSAndrew Jones 1382e2d471dSAndrew Jones void gic_ipi_send_single(int irq, int cpu) 1392e2d471dSAndrew Jones { 1402e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->ipi_send_single); 1412e2d471dSAndrew Jones gic_common_ops->ipi_send_single(irq, cpu); 1422e2d471dSAndrew Jones } 1432e2d471dSAndrew Jones 1442e2d471dSAndrew Jones void gic_ipi_send_mask(int irq, const cpumask_t *dest) 1452e2d471dSAndrew Jones { 1462e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->ipi_send_mask); 1472e2d471dSAndrew Jones gic_common_ops->ipi_send_mask(irq, dest); 1482e2d471dSAndrew Jones } 14956145eb8SAndrew Jones 150*cb573c2fSEric Auger void gic_irq_set_clr_enable(int irq, bool enable) 151*cb573c2fSEric Auger { 152*cb573c2fSEric Auger u32 offset, split = 32, shift = (irq % 32); 153*cb573c2fSEric Auger u32 reg, mask = BIT(shift); 154*cb573c2fSEric Auger void *base; 155*cb573c2fSEric Auger 156*cb573c2fSEric Auger assert(irq < 1020); 157*cb573c2fSEric Auger 158*cb573c2fSEric Auger switch (gic_version()) { 159*cb573c2fSEric Auger case 2: 160*cb573c2fSEric Auger offset = enable ? GICD_ISENABLER : GICD_ICENABLER; 161*cb573c2fSEric Auger base = gicv2_dist_base(); 162*cb573c2fSEric Auger break; 163*cb573c2fSEric Auger case 3: 164*cb573c2fSEric Auger if (irq < 32) { 165*cb573c2fSEric Auger offset = enable ? GICR_ISENABLER0 : GICR_ICENABLER0; 166*cb573c2fSEric Auger base = gicv3_sgi_base(); 167*cb573c2fSEric Auger } else { 168*cb573c2fSEric Auger offset = enable ? GICD_ISENABLER : GICD_ICENABLER; 169*cb573c2fSEric Auger base = gicv3_dist_base(); 170*cb573c2fSEric Auger } 171*cb573c2fSEric Auger break; 172*cb573c2fSEric Auger default: 173*cb573c2fSEric Auger assert(0); 174*cb573c2fSEric Auger } 175*cb573c2fSEric Auger base += offset + (irq / split) * 4; 176*cb573c2fSEric Auger reg = readl(base); 177*cb573c2fSEric Auger writel(reg | mask, base); 178*cb573c2fSEric Auger } 179*cb573c2fSEric Auger 18056145eb8SAndrew Jones enum gic_irq_state gic_irq_state(int irq) 18156145eb8SAndrew Jones { 18256145eb8SAndrew Jones enum gic_irq_state state; 18356145eb8SAndrew Jones void *ispendr, *isactiver; 18456145eb8SAndrew Jones bool pending, active; 18556145eb8SAndrew Jones int offset, mask; 18656145eb8SAndrew Jones 18756145eb8SAndrew Jones assert(gic_common_ops); 18856145eb8SAndrew Jones assert(irq < 1020); 18956145eb8SAndrew Jones 19056145eb8SAndrew Jones switch (gic_version()) { 19156145eb8SAndrew Jones case 2: 19256145eb8SAndrew Jones ispendr = gicv2_dist_base() + GICD_ISPENDR; 19356145eb8SAndrew Jones isactiver = gicv2_dist_base() + GICD_ISACTIVER; 19456145eb8SAndrew Jones break; 19556145eb8SAndrew Jones case 3: 19656145eb8SAndrew Jones if (irq < GIC_NR_PRIVATE_IRQS) { 19756145eb8SAndrew Jones ispendr = gicv3_sgi_base() + GICR_ISPENDR0; 19856145eb8SAndrew Jones isactiver = gicv3_sgi_base() + GICR_ISACTIVER0; 19956145eb8SAndrew Jones } else { 20056145eb8SAndrew Jones ispendr = gicv3_dist_base() + GICD_ISPENDR; 20156145eb8SAndrew Jones isactiver = gicv3_dist_base() + GICD_ISACTIVER; 20256145eb8SAndrew Jones } 20356145eb8SAndrew Jones break; 20456145eb8SAndrew Jones default: 20556145eb8SAndrew Jones assert(0); 20656145eb8SAndrew Jones } 20756145eb8SAndrew Jones 20856145eb8SAndrew Jones offset = irq / 32 * 4; 20956145eb8SAndrew Jones mask = 1 << (irq % 32); 21056145eb8SAndrew Jones pending = readl(ispendr + offset) & mask; 21156145eb8SAndrew Jones active = readl(isactiver + offset) & mask; 21256145eb8SAndrew Jones 21356145eb8SAndrew Jones if (!active && !pending) 21456145eb8SAndrew Jones state = GIC_IRQ_STATE_INACTIVE; 21556145eb8SAndrew Jones if (pending) 21656145eb8SAndrew Jones state = GIC_IRQ_STATE_PENDING; 21756145eb8SAndrew Jones if (active) 21856145eb8SAndrew Jones state = GIC_IRQ_STATE_ACTIVE; 21956145eb8SAndrew Jones if (active && pending) 22056145eb8SAndrew Jones state = GIC_IRQ_STATE_ACTIVE_PENDING; 22156145eb8SAndrew Jones 22256145eb8SAndrew Jones return state; 22356145eb8SAndrew Jones } 224*cb573c2fSEric Auger 225