xref: /kvm-unit-tests/lib/arm/gic.c (revision 56145eb8f7f6aa66b26e1d1232a5f3dfa7cdd058)
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, &reg);
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, &reg);
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 }
149*56145eb8SAndrew Jones 
150*56145eb8SAndrew Jones enum gic_irq_state gic_irq_state(int irq)
151*56145eb8SAndrew Jones {
152*56145eb8SAndrew Jones 	enum gic_irq_state state;
153*56145eb8SAndrew Jones 	void *ispendr, *isactiver;
154*56145eb8SAndrew Jones 	bool pending, active;
155*56145eb8SAndrew Jones 	int offset, mask;
156*56145eb8SAndrew Jones 
157*56145eb8SAndrew Jones 	assert(gic_common_ops);
158*56145eb8SAndrew Jones 	assert(irq < 1020);
159*56145eb8SAndrew Jones 
160*56145eb8SAndrew Jones 	switch (gic_version()) {
161*56145eb8SAndrew Jones 	case 2:
162*56145eb8SAndrew Jones 		ispendr = gicv2_dist_base() + GICD_ISPENDR;
163*56145eb8SAndrew Jones 		isactiver = gicv2_dist_base() + GICD_ISACTIVER;
164*56145eb8SAndrew Jones 		break;
165*56145eb8SAndrew Jones 	case 3:
166*56145eb8SAndrew Jones 		if (irq < GIC_NR_PRIVATE_IRQS) {
167*56145eb8SAndrew Jones 			ispendr = gicv3_sgi_base() + GICR_ISPENDR0;
168*56145eb8SAndrew Jones 			isactiver = gicv3_sgi_base() + GICR_ISACTIVER0;
169*56145eb8SAndrew Jones 		} else {
170*56145eb8SAndrew Jones 			ispendr = gicv3_dist_base() + GICD_ISPENDR;
171*56145eb8SAndrew Jones 			isactiver = gicv3_dist_base() + GICD_ISACTIVER;
172*56145eb8SAndrew Jones 		}
173*56145eb8SAndrew Jones 		break;
174*56145eb8SAndrew Jones 	default:
175*56145eb8SAndrew Jones 		assert(0);
176*56145eb8SAndrew Jones 	}
177*56145eb8SAndrew Jones 
178*56145eb8SAndrew Jones 	offset = irq / 32 * 4;
179*56145eb8SAndrew Jones 	mask = 1 << (irq % 32);
180*56145eb8SAndrew Jones 	pending = readl(ispendr + offset) & mask;
181*56145eb8SAndrew Jones 	active = readl(isactiver + offset) & mask;
182*56145eb8SAndrew Jones 
183*56145eb8SAndrew Jones 	if (!active && !pending)
184*56145eb8SAndrew Jones 		state = GIC_IRQ_STATE_INACTIVE;
185*56145eb8SAndrew Jones 	if (pending)
186*56145eb8SAndrew Jones 		state = GIC_IRQ_STATE_PENDING;
187*56145eb8SAndrew Jones 	if (active)
188*56145eb8SAndrew Jones 		state = GIC_IRQ_STATE_ACTIVE;
189*56145eb8SAndrew Jones 	if (active && pending)
190*56145eb8SAndrew Jones 		state = GIC_IRQ_STATE_ACTIVE_PENDING;
191*56145eb8SAndrew Jones 
192*56145eb8SAndrew Jones 	return state;
193*56145eb8SAndrew Jones }
194