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; 12ba74b106SEric Auger struct its_data its_data; 1300cc96f0SAndrew Jones 142e2d471dSAndrew Jones struct gic_common_ops { 152e2d471dSAndrew Jones void (*enable_defaults)(void); 162e2d471dSAndrew Jones u32 (*read_iar)(void); 172e2d471dSAndrew Jones u32 (*iar_irqnr)(u32 iar); 182e2d471dSAndrew Jones void (*write_eoir)(u32 irqstat); 192e2d471dSAndrew Jones void (*ipi_send_single)(int irq, int cpu); 202e2d471dSAndrew Jones void (*ipi_send_mask)(int irq, const cpumask_t *dest); 212e2d471dSAndrew Jones }; 222e2d471dSAndrew Jones 232e2d471dSAndrew Jones static const struct gic_common_ops *gic_common_ops; 242e2d471dSAndrew Jones 252e2d471dSAndrew Jones static const struct gic_common_ops gicv2_common_ops = { 262e2d471dSAndrew Jones .enable_defaults = gicv2_enable_defaults, 272e2d471dSAndrew Jones .read_iar = gicv2_read_iar, 282e2d471dSAndrew Jones .iar_irqnr = gicv2_iar_irqnr, 292e2d471dSAndrew Jones .write_eoir = gicv2_write_eoir, 302e2d471dSAndrew Jones .ipi_send_single = gicv2_ipi_send_single, 312e2d471dSAndrew Jones .ipi_send_mask = gicv2_ipi_send_mask, 322e2d471dSAndrew Jones }; 332e2d471dSAndrew Jones 342e2d471dSAndrew Jones static const struct gic_common_ops gicv3_common_ops = { 352e2d471dSAndrew Jones .enable_defaults = gicv3_enable_defaults, 362e2d471dSAndrew Jones .read_iar = gicv3_read_iar, 372e2d471dSAndrew Jones .iar_irqnr = gicv3_iar_irqnr, 382e2d471dSAndrew Jones .write_eoir = gicv3_write_eoir, 392e2d471dSAndrew Jones .ipi_send_single = gicv3_ipi_send_single, 402e2d471dSAndrew Jones .ipi_send_mask = gicv3_ipi_send_mask, 412e2d471dSAndrew Jones }; 422e2d471dSAndrew Jones 4300cc96f0SAndrew Jones /* 4400cc96f0SAndrew Jones * Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt 4591a6c3ceSAndrew Jones * Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt 4600cc96f0SAndrew Jones */ 4700cc96f0SAndrew Jones static bool 48ba74b106SEric Auger gic_get_dt_bases(const char *compatible, void **base1, void **base2, void **base3) 4900cc96f0SAndrew Jones { 5000cc96f0SAndrew Jones struct dt_pbus_reg reg; 51ba74b106SEric Auger struct dt_device gic, its; 5200cc96f0SAndrew Jones struct dt_bus bus; 53ba74b106SEric Auger int node, subnode, ret, i, len; 54ba74b106SEric Auger const void *fdt = dt_fdt(); 5500cc96f0SAndrew Jones 5600cc96f0SAndrew Jones dt_bus_init_defaults(&bus); 5700cc96f0SAndrew Jones dt_device_init(&gic, &bus, NULL); 5800cc96f0SAndrew Jones 5900cc96f0SAndrew Jones node = dt_device_find_compatible(&gic, compatible); 6000cc96f0SAndrew Jones assert(node >= 0 || node == -FDT_ERR_NOTFOUND); 6100cc96f0SAndrew Jones 6200cc96f0SAndrew Jones if (node == -FDT_ERR_NOTFOUND) 6300cc96f0SAndrew Jones return false; 6400cc96f0SAndrew Jones 6500cc96f0SAndrew Jones dt_device_bind_node(&gic, node); 6600cc96f0SAndrew Jones 6700cc96f0SAndrew Jones ret = dt_pbus_translate(&gic, 0, ®); 6800cc96f0SAndrew Jones assert(ret == 0); 6900cc96f0SAndrew Jones *base1 = ioremap(reg.addr, reg.size); 7000cc96f0SAndrew Jones 71a5a2d35cSAndrew Jones for (i = 0; i < GICV3_NR_REDISTS; ++i) { 72a5a2d35cSAndrew Jones ret = dt_pbus_translate(&gic, i + 1, ®); 73a5a2d35cSAndrew Jones if (ret == -FDT_ERR_NOTFOUND) 74a5a2d35cSAndrew Jones break; 7500cc96f0SAndrew Jones assert(ret == 0); 76a5a2d35cSAndrew Jones base2[i] = ioremap(reg.addr, reg.size); 77a5a2d35cSAndrew Jones } 7800cc96f0SAndrew Jones 79ba74b106SEric Auger if (!base3) { 80ba74b106SEric Auger assert(!strcmp(compatible, "arm,cortex-a15-gic")); 81ba74b106SEric Auger return true; 82ba74b106SEric Auger } 83ba74b106SEric Auger 84ba74b106SEric Auger assert(!strcmp(compatible, "arm,gic-v3")); 85ba74b106SEric Auger 86ba74b106SEric Auger dt_for_each_subnode(node, subnode) { 87ba74b106SEric Auger const struct fdt_property *prop; 88ba74b106SEric Auger 89ba74b106SEric Auger prop = fdt_get_property(fdt, subnode, "compatible", &len); 90ba74b106SEric Auger if (!strcmp((char *)prop->data, "arm,gic-v3-its")) { 91ba74b106SEric Auger dt_device_bind_node(&its, subnode); 92ba74b106SEric Auger ret = dt_pbus_translate(&its, 0, ®); 93ba74b106SEric Auger assert(ret == 0); 94ba74b106SEric Auger *base3 = ioremap(reg.addr, reg.size); 95ba74b106SEric Auger break; 96ba74b106SEric Auger } 97ba74b106SEric Auger } 98ba74b106SEric Auger 9900cc96f0SAndrew Jones return true; 10000cc96f0SAndrew Jones } 10100cc96f0SAndrew Jones 10200cc96f0SAndrew Jones int gicv2_init(void) 10300cc96f0SAndrew Jones { 10400cc96f0SAndrew Jones return gic_get_dt_bases("arm,cortex-a15-gic", 105ba74b106SEric Auger &gicv2_data.dist_base, &gicv2_data.cpu_base, NULL); 10600cc96f0SAndrew Jones } 10700cc96f0SAndrew Jones 10891a6c3ceSAndrew Jones int gicv3_init(void) 10991a6c3ceSAndrew Jones { 11091a6c3ceSAndrew Jones return gic_get_dt_bases("arm,gic-v3", &gicv3_data.dist_base, 111ba74b106SEric Auger &gicv3_data.redist_bases[0], &its_data.base); 11291a6c3ceSAndrew Jones } 11391a6c3ceSAndrew Jones 114db198544SAndrew Jones int gic_version(void) 115db198544SAndrew Jones { 116db198544SAndrew Jones if (gic_common_ops == &gicv2_common_ops) 117db198544SAndrew Jones return 2; 118db198544SAndrew Jones else if (gic_common_ops == &gicv3_common_ops) 119db198544SAndrew Jones return 3; 120db198544SAndrew Jones return 0; 121db198544SAndrew Jones } 122db198544SAndrew Jones 12300cc96f0SAndrew Jones int gic_init(void) 12400cc96f0SAndrew Jones { 125ba74b106SEric Auger if (gicv2_init()) { 1262e2d471dSAndrew Jones gic_common_ops = &gicv2_common_ops; 127ba74b106SEric Auger } else if (gicv3_init()) { 1282e2d471dSAndrew Jones gic_common_ops = &gicv3_common_ops; 129ba74b106SEric Auger #ifdef __aarch64__ 130ba74b106SEric Auger its_init(); 131ba74b106SEric Auger #endif 132ba74b106SEric Auger } 133db198544SAndrew Jones return gic_version(); 13400cc96f0SAndrew Jones } 1352e2d471dSAndrew Jones 1362e2d471dSAndrew Jones void gic_enable_defaults(void) 1372e2d471dSAndrew Jones { 1382e2d471dSAndrew Jones if (!gic_common_ops) { 1392e2d471dSAndrew Jones int ret = gic_init(); 1402e2d471dSAndrew Jones assert(ret != 0); 1412e2d471dSAndrew Jones } else 1422e2d471dSAndrew Jones assert(gic_common_ops->enable_defaults); 1432e2d471dSAndrew Jones gic_common_ops->enable_defaults(); 1442e2d471dSAndrew Jones } 1452e2d471dSAndrew Jones 1462e2d471dSAndrew Jones u32 gic_read_iar(void) 1472e2d471dSAndrew Jones { 1482e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->read_iar); 1492e2d471dSAndrew Jones return gic_common_ops->read_iar(); 1502e2d471dSAndrew Jones } 1512e2d471dSAndrew Jones 1522e2d471dSAndrew Jones u32 gic_iar_irqnr(u32 iar) 1532e2d471dSAndrew Jones { 1542e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->iar_irqnr); 1552e2d471dSAndrew Jones return gic_common_ops->iar_irqnr(iar); 1562e2d471dSAndrew Jones } 1572e2d471dSAndrew Jones 1582e2d471dSAndrew Jones void gic_write_eoir(u32 irqstat) 1592e2d471dSAndrew Jones { 1602e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->write_eoir); 1612e2d471dSAndrew Jones gic_common_ops->write_eoir(irqstat); 1622e2d471dSAndrew Jones } 1632e2d471dSAndrew Jones 1642e2d471dSAndrew Jones void gic_ipi_send_single(int irq, int cpu) 1652e2d471dSAndrew Jones { 1662e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->ipi_send_single); 1672e2d471dSAndrew Jones gic_common_ops->ipi_send_single(irq, cpu); 1682e2d471dSAndrew Jones } 1692e2d471dSAndrew Jones 1702e2d471dSAndrew Jones void gic_ipi_send_mask(int irq, const cpumask_t *dest) 1712e2d471dSAndrew Jones { 1722e2d471dSAndrew Jones assert(gic_common_ops && gic_common_ops->ipi_send_mask); 1732e2d471dSAndrew Jones gic_common_ops->ipi_send_mask(irq, dest); 1742e2d471dSAndrew Jones } 17556145eb8SAndrew Jones 176cb573c2fSEric Auger void gic_irq_set_clr_enable(int irq, bool enable) 177cb573c2fSEric Auger { 178cb573c2fSEric Auger u32 offset, split = 32, shift = (irq % 32); 179cb573c2fSEric Auger void *base; 180cb573c2fSEric Auger 181cb573c2fSEric Auger assert(irq < 1020); 182cb573c2fSEric Auger 183cb573c2fSEric Auger switch (gic_version()) { 184cb573c2fSEric Auger case 2: 185cb573c2fSEric Auger offset = enable ? GICD_ISENABLER : GICD_ICENABLER; 186cb573c2fSEric Auger base = gicv2_dist_base(); 187cb573c2fSEric Auger break; 188cb573c2fSEric Auger case 3: 189cb573c2fSEric Auger if (irq < 32) { 190cb573c2fSEric Auger offset = enable ? GICR_ISENABLER0 : GICR_ICENABLER0; 191cb573c2fSEric Auger base = gicv3_sgi_base(); 192cb573c2fSEric Auger } else { 193cb573c2fSEric Auger offset = enable ? GICD_ISENABLER : GICD_ICENABLER; 194cb573c2fSEric Auger base = gicv3_dist_base(); 195cb573c2fSEric Auger } 196cb573c2fSEric Auger break; 197cb573c2fSEric Auger default: 198cb573c2fSEric Auger assert(0); 199cb573c2fSEric Auger } 200cb573c2fSEric Auger base += offset + (irq / split) * 4; 201*1db00b26SShaoqin Huang writel(BIT(shift), base); 202cb573c2fSEric Auger } 203cb573c2fSEric Auger 20456145eb8SAndrew Jones enum gic_irq_state gic_irq_state(int irq) 20556145eb8SAndrew Jones { 20656145eb8SAndrew Jones enum gic_irq_state state; 20756145eb8SAndrew Jones void *ispendr, *isactiver; 20856145eb8SAndrew Jones bool pending, active; 20956145eb8SAndrew Jones int offset, mask; 21056145eb8SAndrew Jones 21156145eb8SAndrew Jones assert(gic_common_ops); 21256145eb8SAndrew Jones assert(irq < 1020); 21356145eb8SAndrew Jones 21456145eb8SAndrew Jones switch (gic_version()) { 21556145eb8SAndrew Jones case 2: 21656145eb8SAndrew Jones ispendr = gicv2_dist_base() + GICD_ISPENDR; 21756145eb8SAndrew Jones isactiver = gicv2_dist_base() + GICD_ISACTIVER; 21856145eb8SAndrew Jones break; 21956145eb8SAndrew Jones case 3: 22056145eb8SAndrew Jones if (irq < GIC_NR_PRIVATE_IRQS) { 22156145eb8SAndrew Jones ispendr = gicv3_sgi_base() + GICR_ISPENDR0; 22256145eb8SAndrew Jones isactiver = gicv3_sgi_base() + GICR_ISACTIVER0; 22356145eb8SAndrew Jones } else { 22456145eb8SAndrew Jones ispendr = gicv3_dist_base() + GICD_ISPENDR; 22556145eb8SAndrew Jones isactiver = gicv3_dist_base() + GICD_ISACTIVER; 22656145eb8SAndrew Jones } 22756145eb8SAndrew Jones break; 22856145eb8SAndrew Jones default: 22956145eb8SAndrew Jones assert(0); 23056145eb8SAndrew Jones } 23156145eb8SAndrew Jones 23256145eb8SAndrew Jones offset = irq / 32 * 4; 23356145eb8SAndrew Jones mask = 1 << (irq % 32); 23456145eb8SAndrew Jones pending = readl(ispendr + offset) & mask; 23556145eb8SAndrew Jones active = readl(isactiver + offset) & mask; 23656145eb8SAndrew Jones 23756145eb8SAndrew Jones if (!active && !pending) 23856145eb8SAndrew Jones state = GIC_IRQ_STATE_INACTIVE; 23956145eb8SAndrew Jones if (pending) 24056145eb8SAndrew Jones state = GIC_IRQ_STATE_PENDING; 24156145eb8SAndrew Jones if (active) 24256145eb8SAndrew Jones state = GIC_IRQ_STATE_ACTIVE; 24356145eb8SAndrew Jones if (active && pending) 24456145eb8SAndrew Jones state = GIC_IRQ_STATE_ACTIVE_PENDING; 24556145eb8SAndrew Jones 24656145eb8SAndrew Jones return state; 24756145eb8SAndrew Jones } 248cb573c2fSEric Auger 249