1 /* 2 * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com> 3 * 4 * This work is licensed under the terms of the GNU LGPL, version 2. 5 */ 6 #include <devicetree.h> 7 #include <asm/gic.h> 8 #include <asm/io.h> 9 10 struct gicv2_data gicv2_data; 11 struct gicv3_data gicv3_data; 12 struct its_data its_data; 13 14 struct gic_common_ops { 15 void (*enable_defaults)(void); 16 u32 (*read_iar)(void); 17 u32 (*iar_irqnr)(u32 iar); 18 void (*write_eoir)(u32 irqstat); 19 void (*ipi_send_single)(int irq, int cpu); 20 void (*ipi_send_mask)(int irq, const cpumask_t *dest); 21 }; 22 23 static const struct gic_common_ops *gic_common_ops; 24 25 static const struct gic_common_ops gicv2_common_ops = { 26 .enable_defaults = gicv2_enable_defaults, 27 .read_iar = gicv2_read_iar, 28 .iar_irqnr = gicv2_iar_irqnr, 29 .write_eoir = gicv2_write_eoir, 30 .ipi_send_single = gicv2_ipi_send_single, 31 .ipi_send_mask = gicv2_ipi_send_mask, 32 }; 33 34 static const struct gic_common_ops gicv3_common_ops = { 35 .enable_defaults = gicv3_enable_defaults, 36 .read_iar = gicv3_read_iar, 37 .iar_irqnr = gicv3_iar_irqnr, 38 .write_eoir = gicv3_write_eoir, 39 .ipi_send_single = gicv3_ipi_send_single, 40 .ipi_send_mask = gicv3_ipi_send_mask, 41 }; 42 43 /* 44 * Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt 45 * Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt 46 */ 47 static bool 48 gic_get_dt_bases(const char *compatible, void **base1, void **base2, void **base3) 49 { 50 struct dt_pbus_reg reg; 51 struct dt_device gic, its; 52 struct dt_bus bus; 53 int node, subnode, ret, i, len; 54 const void *fdt = dt_fdt(); 55 56 dt_bus_init_defaults(&bus); 57 dt_device_init(&gic, &bus, NULL); 58 59 node = dt_device_find_compatible(&gic, compatible); 60 assert(node >= 0 || node == -FDT_ERR_NOTFOUND); 61 62 if (node == -FDT_ERR_NOTFOUND) 63 return false; 64 65 dt_device_bind_node(&gic, node); 66 67 ret = dt_pbus_translate(&gic, 0, ®); 68 assert(ret == 0); 69 *base1 = ioremap(reg.addr, reg.size); 70 71 for (i = 0; i < GICV3_NR_REDISTS; ++i) { 72 ret = dt_pbus_translate(&gic, i + 1, ®); 73 if (ret == -FDT_ERR_NOTFOUND) 74 break; 75 assert(ret == 0); 76 base2[i] = ioremap(reg.addr, reg.size); 77 } 78 79 if (!base3) { 80 assert(!strcmp(compatible, "arm,cortex-a15-gic")); 81 return true; 82 } 83 84 assert(!strcmp(compatible, "arm,gic-v3")); 85 86 dt_for_each_subnode(node, subnode) { 87 const struct fdt_property *prop; 88 89 prop = fdt_get_property(fdt, subnode, "compatible", &len); 90 if (!strcmp((char *)prop->data, "arm,gic-v3-its")) { 91 dt_device_bind_node(&its, subnode); 92 ret = dt_pbus_translate(&its, 0, ®); 93 assert(ret == 0); 94 *base3 = ioremap(reg.addr, reg.size); 95 break; 96 } 97 } 98 99 return true; 100 } 101 102 int gicv2_init(void) 103 { 104 return gic_get_dt_bases("arm,cortex-a15-gic", 105 &gicv2_data.dist_base, &gicv2_data.cpu_base, NULL); 106 } 107 108 int gicv3_init(void) 109 { 110 return gic_get_dt_bases("arm,gic-v3", &gicv3_data.dist_base, 111 &gicv3_data.redist_bases[0], &its_data.base); 112 } 113 114 int gic_version(void) 115 { 116 if (gic_common_ops == &gicv2_common_ops) 117 return 2; 118 else if (gic_common_ops == &gicv3_common_ops) 119 return 3; 120 return 0; 121 } 122 123 int gic_init(void) 124 { 125 if (gicv2_init()) { 126 gic_common_ops = &gicv2_common_ops; 127 } else if (gicv3_init()) { 128 gic_common_ops = &gicv3_common_ops; 129 #ifdef __aarch64__ 130 its_init(); 131 #endif 132 } 133 return gic_version(); 134 } 135 136 void gic_enable_defaults(void) 137 { 138 if (!gic_common_ops) { 139 int ret = gic_init(); 140 assert(ret != 0); 141 } else 142 assert(gic_common_ops->enable_defaults); 143 gic_common_ops->enable_defaults(); 144 } 145 146 u32 gic_read_iar(void) 147 { 148 assert(gic_common_ops && gic_common_ops->read_iar); 149 return gic_common_ops->read_iar(); 150 } 151 152 u32 gic_iar_irqnr(u32 iar) 153 { 154 assert(gic_common_ops && gic_common_ops->iar_irqnr); 155 return gic_common_ops->iar_irqnr(iar); 156 } 157 158 void gic_write_eoir(u32 irqstat) 159 { 160 assert(gic_common_ops && gic_common_ops->write_eoir); 161 gic_common_ops->write_eoir(irqstat); 162 } 163 164 void gic_ipi_send_single(int irq, int cpu) 165 { 166 assert(gic_common_ops && gic_common_ops->ipi_send_single); 167 gic_common_ops->ipi_send_single(irq, cpu); 168 } 169 170 void gic_ipi_send_mask(int irq, const cpumask_t *dest) 171 { 172 assert(gic_common_ops && gic_common_ops->ipi_send_mask); 173 gic_common_ops->ipi_send_mask(irq, dest); 174 } 175 176 void gic_irq_set_clr_enable(int irq, bool enable) 177 { 178 u32 offset, split = 32, shift = (irq % 32); 179 void *base; 180 181 assert(irq < 1020); 182 183 switch (gic_version()) { 184 case 2: 185 offset = enable ? GICD_ISENABLER : GICD_ICENABLER; 186 base = gicv2_dist_base(); 187 break; 188 case 3: 189 if (irq < 32) { 190 offset = enable ? GICR_ISENABLER0 : GICR_ICENABLER0; 191 base = gicv3_sgi_base(); 192 } else { 193 offset = enable ? GICD_ISENABLER : GICD_ICENABLER; 194 base = gicv3_dist_base(); 195 } 196 break; 197 default: 198 assert(0); 199 } 200 base += offset + (irq / split) * 4; 201 writel(BIT(shift), base); 202 } 203 204 enum gic_irq_state gic_irq_state(int irq) 205 { 206 enum gic_irq_state state; 207 void *ispendr, *isactiver; 208 bool pending, active; 209 int offset, mask; 210 211 assert(gic_common_ops); 212 assert(irq < 1020); 213 214 switch (gic_version()) { 215 case 2: 216 ispendr = gicv2_dist_base() + GICD_ISPENDR; 217 isactiver = gicv2_dist_base() + GICD_ISACTIVER; 218 break; 219 case 3: 220 if (irq < GIC_NR_PRIVATE_IRQS) { 221 ispendr = gicv3_sgi_base() + GICR_ISPENDR0; 222 isactiver = gicv3_sgi_base() + GICR_ISACTIVER0; 223 } else { 224 ispendr = gicv3_dist_base() + GICD_ISPENDR; 225 isactiver = gicv3_dist_base() + GICD_ISACTIVER; 226 } 227 break; 228 default: 229 assert(0); 230 } 231 232 offset = irq / 32 * 4; 233 mask = 1 << (irq % 32); 234 pending = readl(ispendr + offset) & mask; 235 active = readl(isactiver + offset) & mask; 236 237 if (!active && !pending) 238 state = GIC_IRQ_STATE_INACTIVE; 239 if (pending) 240 state = GIC_IRQ_STATE_PENDING; 241 if (active) 242 state = GIC_IRQ_STATE_ACTIVE; 243 if (active && pending) 244 state = GIC_IRQ_STATE_ACTIVE_PENDING; 245 246 return state; 247 } 248 249