xref: /kvm-unit-tests/lib/arm/gic.c (revision 1db00b26ade099a89147bd1d169f378b2f271cf5)
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, &reg);
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, &reg);
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, &reg);
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