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