xref: /kvm-unit-tests/lib/arm/gic.c (revision 56145eb8f7f6aa66b26e1d1232a5f3dfa7cdd058)
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 
13 struct gic_common_ops {
14 	void (*enable_defaults)(void);
15 	u32 (*read_iar)(void);
16 	u32 (*iar_irqnr)(u32 iar);
17 	void (*write_eoir)(u32 irqstat);
18 	void (*ipi_send_single)(int irq, int cpu);
19 	void (*ipi_send_mask)(int irq, const cpumask_t *dest);
20 };
21 
22 static const struct gic_common_ops *gic_common_ops;
23 
24 static const struct gic_common_ops gicv2_common_ops = {
25 	.enable_defaults = gicv2_enable_defaults,
26 	.read_iar = gicv2_read_iar,
27 	.iar_irqnr = gicv2_iar_irqnr,
28 	.write_eoir = gicv2_write_eoir,
29 	.ipi_send_single = gicv2_ipi_send_single,
30 	.ipi_send_mask = gicv2_ipi_send_mask,
31 };
32 
33 static const struct gic_common_ops gicv3_common_ops = {
34 	.enable_defaults = gicv3_enable_defaults,
35 	.read_iar = gicv3_read_iar,
36 	.iar_irqnr = gicv3_iar_irqnr,
37 	.write_eoir = gicv3_write_eoir,
38 	.ipi_send_single = gicv3_ipi_send_single,
39 	.ipi_send_mask = gicv3_ipi_send_mask,
40 };
41 
42 /*
43  * Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt
44  * Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
45  */
46 static bool
47 gic_get_dt_bases(const char *compatible, void **base1, void **base2)
48 {
49 	struct dt_pbus_reg reg;
50 	struct dt_device gic;
51 	struct dt_bus bus;
52 	int node, ret, i;
53 
54 	dt_bus_init_defaults(&bus);
55 	dt_device_init(&gic, &bus, NULL);
56 
57 	node = dt_device_find_compatible(&gic, compatible);
58 	assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
59 
60 	if (node == -FDT_ERR_NOTFOUND)
61 		return false;
62 
63 	dt_device_bind_node(&gic, node);
64 
65 	ret = dt_pbus_translate(&gic, 0, &reg);
66 	assert(ret == 0);
67 	*base1 = ioremap(reg.addr, reg.size);
68 
69 	for (i = 0; i < GICV3_NR_REDISTS; ++i) {
70 		ret = dt_pbus_translate(&gic, i + 1, &reg);
71 		if (ret == -FDT_ERR_NOTFOUND)
72 			break;
73 		assert(ret == 0);
74 		base2[i] = ioremap(reg.addr, reg.size);
75 	}
76 
77 	return true;
78 }
79 
80 int gicv2_init(void)
81 {
82 	return gic_get_dt_bases("arm,cortex-a15-gic",
83 			&gicv2_data.dist_base, &gicv2_data.cpu_base);
84 }
85 
86 int gicv3_init(void)
87 {
88 	return gic_get_dt_bases("arm,gic-v3", &gicv3_data.dist_base,
89 			&gicv3_data.redist_bases[0]);
90 }
91 
92 int gic_version(void)
93 {
94 	if (gic_common_ops == &gicv2_common_ops)
95 		return 2;
96 	else if (gic_common_ops == &gicv3_common_ops)
97 		return 3;
98 	return 0;
99 }
100 
101 int gic_init(void)
102 {
103 	if (gicv2_init())
104 		gic_common_ops = &gicv2_common_ops;
105 	else if (gicv3_init())
106 		gic_common_ops = &gicv3_common_ops;
107 	return gic_version();
108 }
109 
110 void gic_enable_defaults(void)
111 {
112 	if (!gic_common_ops) {
113 		int ret = gic_init();
114 		assert(ret != 0);
115 	} else
116 		assert(gic_common_ops->enable_defaults);
117 	gic_common_ops->enable_defaults();
118 }
119 
120 u32 gic_read_iar(void)
121 {
122 	assert(gic_common_ops && gic_common_ops->read_iar);
123 	return gic_common_ops->read_iar();
124 }
125 
126 u32 gic_iar_irqnr(u32 iar)
127 {
128 	assert(gic_common_ops && gic_common_ops->iar_irqnr);
129 	return gic_common_ops->iar_irqnr(iar);
130 }
131 
132 void gic_write_eoir(u32 irqstat)
133 {
134 	assert(gic_common_ops && gic_common_ops->write_eoir);
135 	gic_common_ops->write_eoir(irqstat);
136 }
137 
138 void gic_ipi_send_single(int irq, int cpu)
139 {
140 	assert(gic_common_ops && gic_common_ops->ipi_send_single);
141 	gic_common_ops->ipi_send_single(irq, cpu);
142 }
143 
144 void gic_ipi_send_mask(int irq, const cpumask_t *dest)
145 {
146 	assert(gic_common_ops && gic_common_ops->ipi_send_mask);
147 	gic_common_ops->ipi_send_mask(irq, dest);
148 }
149 
150 enum gic_irq_state gic_irq_state(int irq)
151 {
152 	enum gic_irq_state state;
153 	void *ispendr, *isactiver;
154 	bool pending, active;
155 	int offset, mask;
156 
157 	assert(gic_common_ops);
158 	assert(irq < 1020);
159 
160 	switch (gic_version()) {
161 	case 2:
162 		ispendr = gicv2_dist_base() + GICD_ISPENDR;
163 		isactiver = gicv2_dist_base() + GICD_ISACTIVER;
164 		break;
165 	case 3:
166 		if (irq < GIC_NR_PRIVATE_IRQS) {
167 			ispendr = gicv3_sgi_base() + GICR_ISPENDR0;
168 			isactiver = gicv3_sgi_base() + GICR_ISACTIVER0;
169 		} else {
170 			ispendr = gicv3_dist_base() + GICD_ISPENDR;
171 			isactiver = gicv3_dist_base() + GICD_ISACTIVER;
172 		}
173 		break;
174 	default:
175 		assert(0);
176 	}
177 
178 	offset = irq / 32 * 4;
179 	mask = 1 << (irq % 32);
180 	pending = readl(ispendr + offset) & mask;
181 	active = readl(isactiver + offset) & mask;
182 
183 	if (!active && !pending)
184 		state = GIC_IRQ_STATE_INACTIVE;
185 	if (pending)
186 		state = GIC_IRQ_STATE_PENDING;
187 	if (active)
188 		state = GIC_IRQ_STATE_ACTIVE;
189 	if (active && pending)
190 		state = GIC_IRQ_STATE_ACTIVE_PENDING;
191 
192 	return state;
193 }
194