master
c 292 lines 7.19 KB
Raw
1 /*
2 * Cadence GPIO emulation.
3 *
4 * Author: Kuan-Jui Chiu <kchiu@axiado.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10 #include "hw/gpio/cadence_gpio.h"
11 #include "hw/core/irq.h"
12 #include "migration/vmstate.h"
13 #include "qemu/log.h"
14 #include "trace.h"
15
16 static void cdns_gpio_update_irq(CadenceGPIOState *s)
17 {
18 qemu_set_irq(s->irq, s->isr ? 1 : 0);
19 }
20
21 static void cdns_gpio_update_isr_with_inpvr(CadenceGPIOState *s, uint32_t new)
22 {
23 uint32_t new_isr = 0;
24 uint32_t any_edges, rising_edges, falling_edges, deassert_mask;
25
26 /*
27 * If ITR is set, this is level triggered:
28 * set corresponding ISR bits when IVR matches new inpvr value.
29 */
30 new_isr |= s->itr & ~(s->ivr ^ new);
31
32 /*
33 * If ITR is not set, this is edge-triggered:
34 * If IOAR bit is set, trigger on any edge;
35 * otherwise trigger on rising edge if IVR is set,
36 * trigger on falling edge if IVR bit is 0.
37 */
38 any_edges = s->ioar & (s->inpvr ^ new);
39 rising_edges = s->ivr & ~s->inpvr & new;
40 falling_edges = ~s->ivr & s->inpvr & ~new;
41 new_isr |= ~s->itr & (any_edges | rising_edges | falling_edges);
42
43 /*
44 * In bypass mode or if this isn't an input pin, the corresponding ISR
45 * bit is forced to zero.
46 */
47 deassert_mask = s->bmr | ~s->dmr | s->imr;
48
49 new_isr &= ~deassert_mask;
50 s->isr = new_isr;
51
52 cdns_gpio_update_irq(s);
53 }
54
55 static void cdns_gpio_update_isr(CadenceGPIOState *s)
56 {
57 cdns_gpio_update_isr_with_inpvr(s, s->inpvr);
58 }
59
60 static void cdns_gpio_set(void *opaque, int line, int level)
61 {
62 CadenceGPIOState *s = CADENCE_GPIO(opaque);
63 uint32_t new_inpvr = deposit32(s->inpvr, line, 1, level ? 1 : 0);
64
65 trace_cdns_gpio_set(DEVICE(s)->canonical_path, line, level);
66
67 cdns_gpio_update_isr_with_inpvr(s, new_inpvr);
68
69 /* Sync INPVR with new value */
70 s->inpvr = new_inpvr;
71 }
72
73 static inline void cdns_gpio_update_output_irq(CadenceGPIOState *s)
74 {
75 uint32_t is_output = ~s->bmr & ~s->dmr & s->oer;
76
77 for (int i = 0; i < CDNS_GPIO_NUM; i++) {
78 if (extract32(is_output, i, 1)) {
79 /* Forward the output value to corresponding irq */
80 qemu_set_irq(s->output[i], extract32(s->ovr, i, 1));
81 }
82 }
83 }
84
85 static uint64_t cdns_gpio_read(void *opaque, hwaddr offset, unsigned size)
86 {
87 CadenceGPIOState *s = CADENCE_GPIO(opaque);
88 uint32_t reg_value = 0x0;
89
90 switch (offset) {
91 case CDNS_GPIO_BYPASS_MODE:
92 reg_value = s->bmr;
93 break;
94
95 case CDNS_GPIO_DIRECTION_MODE:
96 reg_value = s->dmr;
97 break;
98
99 case CDNS_GPIO_OUTPUT_EN:
100 reg_value = s->oer;
101 break;
102
103 case CDNS_GPIO_OUTPUT_VALUE:
104 reg_value = s->ovr;
105 break;
106
107 case CDNS_GPIO_INPUT_VALUE:
108 reg_value = s->inpvr;
109 break;
110
111 case CDNS_GPIO_IRQ_MASK:
112 reg_value = s->imr;
113 break;
114
115 case CDNS_GPIO_IRQ_STATUS:
116 reg_value = s->isr;
117 break;
118
119 case CDNS_GPIO_IRQ_TYPE:
120 reg_value = s->itr;
121 break;
122
123 case CDNS_GPIO_IRQ_VALUE:
124 reg_value = s->ivr;
125 break;
126
127 case CDNS_GPIO_IRQ_ANY_EDGE:
128 reg_value = s->ioar;
129 break;
130
131 default:
132 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
133 HWADDR_PRIx "\n", TYPE_CADENCE_GPIO, __func__, offset);
134 break;
135 }
136
137 trace_cdns_gpio_read(DEVICE(s)->canonical_path, offset, reg_value);
138
139 return reg_value;
140 }
141
142 static void cdns_gpio_write(void *opaque, hwaddr offset, uint64_t value,
143 unsigned size)
144 {
145 CadenceGPIOState *s = CADENCE_GPIO(opaque);
146
147 trace_cdns_gpio_write(DEVICE(s)->canonical_path, offset, value);
148
149 switch (offset) {
150 case CDNS_GPIO_BYPASS_MODE:
151 s->bmr = value;
152 cdns_gpio_update_output_irq(s);
153 cdns_gpio_update_isr(s);
154 break;
155
156 case CDNS_GPIO_DIRECTION_MODE:
157 s->dmr = value;
158 cdns_gpio_update_output_irq(s);
159 cdns_gpio_update_isr(s);
160 break;
161
162 case CDNS_GPIO_OUTPUT_EN:
163 s->oer = value;
164 cdns_gpio_update_output_irq(s);
165 break;
166
167 case CDNS_GPIO_OUTPUT_VALUE:
168 s->ovr = value;
169 cdns_gpio_update_output_irq(s);
170 break;
171
172 case CDNS_GPIO_IRQ_EN:
173 s->imr &= ~value;
174 cdns_gpio_update_isr(s);
175 break;
176
177 case CDNS_GPIO_IRQ_DIS:
178 s->imr |= value;
179 cdns_gpio_update_isr(s);
180 break;
181
182 case CDNS_GPIO_IRQ_TYPE:
183 s->itr = value;
184 break;
185
186 case CDNS_GPIO_IRQ_VALUE:
187 s->ivr = value;
188 break;
189
190 case CDNS_GPIO_IRQ_ANY_EDGE:
191 s->ioar = value;
192 break;
193
194 case CDNS_GPIO_INPUT_VALUE:
195 case CDNS_GPIO_IRQ_MASK:
196 case CDNS_GPIO_IRQ_STATUS:
197 /* Read-Only */
198 break;
199
200 default:
201 qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
202 HWADDR_PRIx "\n", TYPE_CADENCE_GPIO, __func__, offset);
203 break;
204 }
205 }
206
207 static const MemoryRegionOps cdns_gpio_ops = {
208 .read = cdns_gpio_read,
209 .write = cdns_gpio_write,
210 .endianness = DEVICE_LITTLE_ENDIAN,
211 .impl = {
212 .min_access_size = 4,
213 .max_access_size = 4,
214 },
215 .valid = {
216 .min_access_size = 4,
217 .max_access_size = 4,
218 }
219 };
220
221 static const VMStateDescription vmstate_cdns_gpio = {
222 .name = TYPE_CADENCE_GPIO,
223 .version_id = 1,
224 .minimum_version_id = 1,
225 .fields = (const VMStateField[]) {
226 VMSTATE_UINT32(bmr, CadenceGPIOState),
227 VMSTATE_UINT32(dmr, CadenceGPIOState),
228 VMSTATE_UINT32(oer, CadenceGPIOState),
229 VMSTATE_UINT32(ovr, CadenceGPIOState),
230 VMSTATE_UINT32(inpvr, CadenceGPIOState),
231 VMSTATE_UINT32(imr, CadenceGPIOState),
232 VMSTATE_UINT32(isr, CadenceGPIOState),
233 VMSTATE_UINT32(itr, CadenceGPIOState),
234 VMSTATE_UINT32(ivr, CadenceGPIOState),
235 VMSTATE_UINT32(ioar, CadenceGPIOState),
236 VMSTATE_END_OF_LIST()
237 }
238 };
239
240 static void cdns_gpio_reset(DeviceState *dev)
241 {
242 CadenceGPIOState *s = CADENCE_GPIO(dev);
243
244 s->bmr = 0;
245 s->dmr = 0;
246 s->oer = 0;
247 s->ovr = 0;
248 s->inpvr = 0;
249 s->imr = 0xffffffff;
250 s->isr = 0;
251 s->itr = 0;
252 s->ivr = 0;
253 s->ioar = 0;
254 }
255
256 static void cdns_gpio_init(Object *obj)
257 {
258 CadenceGPIOState *s = CADENCE_GPIO(obj);
259
260 memory_region_init_io(&s->iomem, obj, &cdns_gpio_ops, s,
261 TYPE_CADENCE_GPIO, CDNS_GPIO_REG_SIZE);
262
263 qdev_init_gpio_in(DEVICE(s), cdns_gpio_set, CDNS_GPIO_NUM);
264 qdev_init_gpio_out(DEVICE(s), s->output, CDNS_GPIO_NUM);
265
266 sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
267 sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
268 }
269
270 static void cdns_gpio_class_init(ObjectClass *klass, const void *data)
271 {
272 DeviceClass *dc = DEVICE_CLASS(klass);
273
274 device_class_set_legacy_reset(dc, cdns_gpio_reset);
275 dc->vmsd = &vmstate_cdns_gpio;
276 dc->desc = "Cadence GPIO controller";
277 }
278
279 static const TypeInfo cdns_gpio_info = {
280 .name = TYPE_CADENCE_GPIO,
281 .parent = TYPE_SYS_BUS_DEVICE,
282 .instance_size = sizeof(CadenceGPIOState),
283 .instance_init = cdns_gpio_init,
284 .class_init = cdns_gpio_class_init,
285 };
286
287 static void cdns_gpio_register_types(void)
288 {
289 type_register_static(&cdns_gpio_info);
290 }
291
292 type_init(cdns_gpio_register_types)