master
c 341 lines 9.98 KB
Raw
1 /*
2 * ASPEED Serial GPIO Controller
3 *
4 * Copyright 2025 Google LLC.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10 #include "qemu/host-utils.h"
11 #include "qemu/log.h"
12 #include "qemu/error-report.h"
13 #include "qapi/error.h"
14 #include "qapi/visitor.h"
15 #include "hw/core/irq.h"
16 #include "hw/core/qdev-properties.h"
17 #include "hw/gpio/aspeed_sgpio.h"
18
19 /*
20 * For each set of gpios there are three sensitivity registers that control
21 * the interrupt trigger mode.
22 *
23 * | 2 | 1 | 0 | trigger mode
24 * -----------------------------
25 * | 0 | 0 | 0 | falling-edge
26 * | 0 | 0 | 1 | rising-edge
27 * | 0 | 1 | 0 | level-low
28 * | 0 | 1 | 1 | level-high
29 * | 1 | X | X | dual-edge
30 */
31
32 /* GPIO Interrupt Triggers */
33 #define ASPEED_FALLING_EDGE 0
34 #define ASPEED_RISING_EDGE 1
35 #define ASPEED_LEVEL_LOW 2
36 #define ASPEED_LEVEL_HIGH 3
37 #define ASPEED_DUAL_EDGE 4
38
39 static void aspeed_clear_irq(AspeedSGPIOState *s, int idx)
40 {
41 uint32_t reg_index = idx / 32;
42 uint32_t bit_index = idx % 32;
43 uint32_t pending = extract32(s->int_regs[reg_index], bit_index, 1);
44
45 assert(s->pending >= pending);
46
47 /* No change to s->pending if pending is 0 */
48 s->pending -= pending;
49
50 /*
51 * The write acknowledged the interrupt regardless of whether it
52 * was pending or not. The post-condition is that it mustn't be
53 * pending. Unconditionally clear the status bit.
54 */
55 s->int_regs[reg_index] = deposit32(s->int_regs[reg_index], bit_index, 1, 0);
56 }
57
58 static void aspeed_evaluate_irq(AspeedSGPIOState *s, int sgpio_prev_high,
59 int sgpio_curr_high, int idx)
60 {
61 uint32_t ctrl = s->ctrl_regs[idx];
62 uint32_t falling_edge = 0, rising_edge = 0;
63 uint32_t int_trigger = SHARED_FIELD_EX32(ctrl, SGPIO_INT_TYPE);
64 uint32_t int_enabled = SHARED_FIELD_EX32(ctrl, SGPIO_INT_EN);
65 uint32_t reg_index = idx / 32;
66 uint32_t bit_index = idx % 32;
67
68 if (!int_enabled) {
69 return;
70 }
71
72 /* Detect edges */
73 if (sgpio_curr_high && !sgpio_prev_high) {
74 rising_edge = 1;
75 } else if (!sgpio_curr_high && sgpio_prev_high) {
76 falling_edge = 1;
77 }
78
79 if (((int_trigger == ASPEED_FALLING_EDGE) && falling_edge) ||
80 ((int_trigger == ASPEED_RISING_EDGE) && rising_edge) ||
81 ((int_trigger == ASPEED_LEVEL_LOW) && !sgpio_curr_high) ||
82 ((int_trigger == ASPEED_LEVEL_HIGH) && sgpio_curr_high) ||
83 ((int_trigger >= ASPEED_DUAL_EDGE) && (rising_edge || falling_edge)))
84 {
85 s->int_regs[reg_index] = deposit32(s->int_regs[reg_index],
86 bit_index, 1, 1);
87 /* Trigger the VIC IRQ */
88 s->pending++;
89 }
90 }
91
92 static void aspeed_sgpio_update(AspeedSGPIOState *s, uint32_t idx,
93 uint32_t value)
94 {
95 uint32_t old = s->ctrl_regs[idx];
96 uint32_t new = value;
97 uint32_t diff = (old ^ new);
98 if (diff) {
99 /* If the interrupt clear bit is set */
100 if (SHARED_FIELD_EX32(new, SGPIO_INT_STATUS)) {
101 aspeed_clear_irq(s, idx);
102 /* Clear the interrupt clear bit */
103 new &= ~SGPIO_INT_STATUS_MASK;
104 }
105
106 /* Update the control register. */
107 s->ctrl_regs[idx] = new;
108
109 /* If the output value is changed */
110 if (SHARED_FIELD_EX32(diff, SGPIO_SERIAL_OUT_VAL)) {
111 /* ...trigger the line-state IRQ */
112 qemu_set_irq(s->sgpios[idx], 1);
113 }
114
115 /* If the input value is changed */
116 if (SHARED_FIELD_EX32(diff, SGPIO_SERIAL_IN_VAL)) {
117 aspeed_evaluate_irq(s,
118 SHARED_FIELD_EX32(old, SGPIO_SERIAL_IN_VAL),
119 SHARED_FIELD_EX32(new, SGPIO_SERIAL_IN_VAL),
120 idx);
121 }
122 }
123 qemu_set_irq(s->irq, !!(s->pending));
124 }
125
126 static uint64_t aspeed_sgpio_2700_read_int_status_reg(AspeedSGPIOState *s,
127 uint32_t reg)
128 {
129 uint32_t idx = reg - R_SGPIO_INT_STATUS_0;
130 if (idx >= ASPEED_SGPIO_MAX_INT) {
131 qemu_log_mask(LOG_GUEST_ERROR,
132 "%s: interrupt status index: %d, out of bounds\n",
133 __func__, idx);
134 return 0;
135 }
136 return s->int_regs[idx];
137 }
138
139 static uint64_t aspeed_sgpio_2700_read_control_reg(AspeedSGPIOState *s,
140 uint32_t reg)
141 {
142 AspeedSGPIOClass *agc = ASPEED_SGPIO_GET_CLASS(s);
143 uint32_t idx = reg - R_SGPIO_0_CONTROL;
144 if (idx >= agc->nr_sgpio_pin_pairs) {
145 qemu_log_mask(LOG_GUEST_ERROR, "%s: pin index: %d, out of bounds\n",
146 __func__, idx);
147 return 0;
148 }
149 return s->ctrl_regs[idx];
150 }
151
152 static void aspeed_sgpio_2700_write_control_reg(AspeedSGPIOState *s,
153 uint32_t reg, uint64_t data)
154 {
155 AspeedSGPIOClass *agc = ASPEED_SGPIO_GET_CLASS(s);
156 uint32_t idx = reg - R_SGPIO_0_CONTROL;
157 if (idx >= agc->nr_sgpio_pin_pairs) {
158 qemu_log_mask(LOG_GUEST_ERROR, "%s: pin index: %d, out of bounds\n",
159 __func__, idx);
160 return;
161 }
162 aspeed_sgpio_update(s, idx, data);
163 }
164
165 static uint64_t aspeed_sgpio_2700_read(void *opaque, hwaddr offset,
166 uint32_t size)
167 {
168 AspeedSGPIOState *s = ASPEED_SGPIO(opaque);
169 uint64_t value = 0;
170 uint64_t reg;
171
172 reg = offset >> 2;
173
174 switch (reg) {
175 case R_SGPIO_INT_STATUS_0 ... R_SGPIO_INT_STATUS_7:
176 value = aspeed_sgpio_2700_read_int_status_reg(s, reg);
177 break;
178 case R_SGPIO_0_CONTROL ... R_SGPIO_255_CONTROL:
179 value = aspeed_sgpio_2700_read_control_reg(s, reg);
180 break;
181 default:
182 qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
183 HWADDR_PRIx"\n", __func__, offset);
184 return 0;
185 }
186
187 return value;
188 }
189
190 static void aspeed_sgpio_2700_write(void *opaque, hwaddr offset, uint64_t data,
191 uint32_t size)
192 {
193 AspeedSGPIOState *s = ASPEED_SGPIO(opaque);
194 uint64_t reg;
195
196 reg = offset >> 2;
197
198 switch (reg) {
199 case R_SGPIO_0_CONTROL ... R_SGPIO_255_CONTROL:
200 aspeed_sgpio_2700_write_control_reg(s, reg, data);
201 break;
202 default:
203 qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
204 HWADDR_PRIx"\n", __func__, offset);
205 return;
206 }
207 }
208
209 static bool aspeed_sgpio_get_pin_level(AspeedSGPIOState *s, int pin)
210 {
211 uint32_t value = s->ctrl_regs[pin >> 1];
212 bool is_input = !(pin % 2);
213 uint32_t bit_mask = 0;
214
215 if (is_input) {
216 bit_mask = SGPIO_SERIAL_IN_VAL_MASK;
217 } else {
218 bit_mask = SGPIO_SERIAL_OUT_VAL_MASK;
219 }
220
221 return value & bit_mask;
222 }
223
224 static void aspeed_sgpio_set_pin_level(AspeedSGPIOState *s, int pin, bool level)
225 {
226 uint32_t value = s->ctrl_regs[pin >> 1];
227 bool is_input = !(pin % 2);
228 uint32_t bit_mask = 0;
229
230 if (is_input) {
231 bit_mask = SGPIO_SERIAL_IN_VAL_MASK;
232 } else {
233 bit_mask = SGPIO_SERIAL_OUT_VAL_MASK;
234 }
235
236 if (level) {
237 value |= bit_mask;
238 } else {
239 value &= ~bit_mask;
240 }
241 aspeed_sgpio_update(s, pin >> 1, value);
242 }
243
244 static void aspeed_sgpio_get_pin(Object *obj, Visitor *v, const char *name,
245 void *opaque, Error **errp)
246 {
247 bool level = true;
248 int pin = 0xfff;
249 AspeedSGPIOState *s = ASPEED_SGPIO(obj);
250
251 if (sscanf(name, "sgpio%03d", &pin) != 1) {
252 error_setg(errp, "%s: error reading %s", __func__, name);
253 return;
254 }
255 level = aspeed_sgpio_get_pin_level(s, pin);
256 visit_type_bool(v, name, &level, errp);
257 }
258
259 static void aspeed_sgpio_set_pin(Object *obj, Visitor *v, const char *name,
260 void *opaque, Error **errp)
261 {
262 bool level;
263 int pin = 0xfff;
264 AspeedSGPIOState *s = ASPEED_SGPIO(obj);
265
266 if (!visit_type_bool(v, name, &level, errp)) {
267 return;
268 }
269 if (sscanf(name, "sgpio%03d", &pin) != 1) {
270 error_setg(errp, "%s: error reading %s", __func__, name);
271 return;
272 }
273 aspeed_sgpio_set_pin_level(s, pin, level);
274 }
275
276 static const MemoryRegionOps aspeed_sgpio_2700_ops = {
277 .read = aspeed_sgpio_2700_read,
278 .write = aspeed_sgpio_2700_write,
279 .endianness = DEVICE_LITTLE_ENDIAN,
280 .valid.min_access_size = 4,
281 .valid.max_access_size = 4,
282 };
283
284 static void aspeed_sgpio_realize(DeviceState *dev, Error **errp)
285 {
286 AspeedSGPIOState *s = ASPEED_SGPIO(dev);
287 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
288 AspeedSGPIOClass *agc = ASPEED_SGPIO_GET_CLASS(s);
289
290 /* Interrupt parent line */
291 sysbus_init_irq(sbd, &s->irq);
292
293 memory_region_init_io(&s->iomem, OBJECT(s), agc->reg_ops, s,
294 TYPE_ASPEED_SGPIO, agc->mem_size);
295
296 sysbus_init_mmio(sbd, &s->iomem);
297 }
298
299 static void aspeed_sgpio_init(Object *obj)
300 {
301 for (int i = 0; i < ASPEED_SGPIO_MAX_PIN_PAIR * 2; i++) {
302 g_autofree char *name = g_strdup_printf("sgpio%03d", i);
303 object_property_add(obj, name, "bool", aspeed_sgpio_get_pin,
304 aspeed_sgpio_set_pin, NULL, NULL);
305 }
306 }
307
308 static void aspeed_sgpio_class_init(ObjectClass *klass, const void *data)
309 {
310 DeviceClass *dc = DEVICE_CLASS(klass);
311
312 dc->realize = aspeed_sgpio_realize;
313 dc->desc = "Aspeed SGPIO Controller";
314 }
315
316 static void aspeed_sgpio_2700_class_init(ObjectClass *klass, const void *data)
317 {
318 AspeedSGPIOClass *agc = ASPEED_SGPIO_CLASS(klass);
319 agc->nr_sgpio_pin_pairs = ASPEED_SGPIO_MAX_PIN_PAIR;
320 agc->mem_size = 0x1000;
321 agc->reg_ops = &aspeed_sgpio_2700_ops;
322 }
323
324 static const TypeInfo aspeed_sgpio_types[] = {
325 {
326 .name = TYPE_ASPEED_SGPIO,
327 .parent = TYPE_SYS_BUS_DEVICE,
328 .instance_size = sizeof(AspeedSGPIOState),
329 .class_size = sizeof(AspeedSGPIOClass),
330 .class_init = aspeed_sgpio_class_init,
331 .abstract = true,
332 },
333 {
334 .name = TYPE_ASPEED_SGPIO "-ast2700",
335 .parent = TYPE_ASPEED_SGPIO,
336 .class_init = aspeed_sgpio_2700_class_init,
337 .instance_init = aspeed_sgpio_init,
338 }
339 };
340
341 DEFINE_TYPES(aspeed_sgpio_types)