]>
Commit | Line | Data |
---|---|---|
805f61bb SG |
1 | /* |
2 | * nRF51 System-on-Chip general purpose input/output register definition | |
3 | * | |
4 | * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf | |
5 | * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf | |
6 | * | |
7 | * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de> | |
8 | * | |
9 | * This code is licensed under the GPL version 2 or later. See | |
10 | * the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "qemu/log.h" | |
0b8fa32f | 15 | #include "qemu/module.h" |
805f61bb | 16 | #include "hw/gpio/nrf51_gpio.h" |
64552b6b | 17 | #include "hw/irq.h" |
d6454270 | 18 | #include "migration/vmstate.h" |
805f61bb SG |
19 | #include "trace.h" |
20 | ||
21 | /* | |
22 | * Check if the output driver is connected to the direction switch | |
23 | * given the current configuration and logic level. | |
24 | * It is not differentiated between standard and "high"(-power) drive modes. | |
25 | */ | |
26 | static bool is_connected(uint32_t config, uint32_t level) | |
27 | { | |
28 | bool state; | |
29 | uint32_t drive_config = extract32(config, 8, 3); | |
30 | ||
31 | switch (drive_config) { | |
32 | case 0 ... 3: | |
33 | state = true; | |
34 | break; | |
35 | case 4 ... 5: | |
36 | state = level != 0; | |
37 | break; | |
38 | case 6 ... 7: | |
39 | state = level == 0; | |
40 | break; | |
41 | default: | |
42 | g_assert_not_reached(); | |
43 | break; | |
44 | } | |
45 | ||
46 | return state; | |
47 | } | |
48 | ||
4261b2f9 PB |
49 | static int pull_value(uint32_t config) |
50 | { | |
51 | int pull = extract32(config, 2, 2); | |
52 | if (pull == NRF51_GPIO_PULLDOWN) { | |
53 | return 0; | |
54 | } else if (pull == NRF51_GPIO_PULLUP) { | |
55 | return 1; | |
56 | } | |
57 | return -1; | |
58 | } | |
59 | ||
805f61bb SG |
60 | static void update_output_irq(NRF51GPIOState *s, size_t i, |
61 | bool connected, bool level) | |
62 | { | |
63 | int64_t irq_level = connected ? level : -1; | |
64 | bool old_connected = extract32(s->old_out_connected, i, 1); | |
65 | bool old_level = extract32(s->old_out, i, 1); | |
66 | ||
67 | if ((old_connected != connected) || (old_level != level)) { | |
68 | qemu_set_irq(s->output[i], irq_level); | |
69 | trace_nrf51_gpio_update_output_irq(i, irq_level); | |
70 | } | |
71 | ||
72 | s->old_out = deposit32(s->old_out, i, 1, level); | |
73 | s->old_out_connected = deposit32(s->old_out_connected, i, 1, connected); | |
74 | } | |
75 | ||
76 | static void update_state(NRF51GPIOState *s) | |
77 | { | |
4261b2f9 | 78 | int pull; |
805f61bb | 79 | size_t i; |
4261b2f9 | 80 | bool connected_out, dir, connected_in, out, in, input; |
805f61bb SG |
81 | |
82 | for (i = 0; i < NRF51_GPIO_PINS; i++) { | |
4261b2f9 | 83 | pull = pull_value(s->cnf[i]); |
805f61bb SG |
84 | dir = extract32(s->cnf[i], 0, 1); |
85 | connected_in = extract32(s->in_mask, i, 1); | |
86 | out = extract32(s->out, i, 1); | |
4261b2f9 | 87 | in = extract32(s->in, i, 1); |
805f61bb SG |
88 | input = !extract32(s->cnf[i], 1, 1); |
89 | connected_out = is_connected(s->cnf[i], out) && dir; | |
90 | ||
4261b2f9 PB |
91 | if (!input) { |
92 | if (pull >= 0) { | |
93 | /* Input buffer disconnected from external drives */ | |
94 | s->in = deposit32(s->in, i, 1, pull); | |
95 | } | |
96 | } else { | |
97 | if (connected_out && connected_in && out != in) { | |
98 | /* Pin both driven externally and internally */ | |
99 | qemu_log_mask(LOG_GUEST_ERROR, | |
100 | "GPIO pin %zu short circuited\n", i); | |
101 | } | |
102 | if (!connected_in) { | |
103 | /* | |
104 | * Floating input: the output stimulates IN if connected, | |
105 | * otherwise pull-up/pull-down resistors put a value on both | |
106 | * IN and OUT. | |
107 | */ | |
108 | if (pull >= 0 && !connected_out) { | |
109 | connected_out = true; | |
110 | out = pull; | |
111 | } | |
112 | if (connected_out) { | |
113 | s->in = deposit32(s->in, i, 1, out); | |
114 | } | |
805f61bb SG |
115 | } |
116 | } | |
4261b2f9 | 117 | update_output_irq(s, i, connected_out, out); |
805f61bb | 118 | } |
805f61bb SG |
119 | } |
120 | ||
121 | /* | |
122 | * Direction is exposed in both the DIR register and the DIR bit | |
123 | * of each PINs CNF configuration register. Reflect bits for pins in DIR | |
124 | * to individual pin configuration registers. | |
125 | */ | |
126 | static void reflect_dir_bit_in_cnf(NRF51GPIOState *s) | |
127 | { | |
128 | size_t i; | |
129 | ||
130 | uint32_t value = s->dir; | |
131 | ||
132 | for (i = 0; i < NRF51_GPIO_PINS; i++) { | |
133 | s->cnf[i] = (s->cnf[i] & ~(1UL)) | ((value >> i) & 0x01); | |
134 | } | |
135 | } | |
136 | ||
137 | static uint64_t nrf51_gpio_read(void *opaque, hwaddr offset, unsigned int size) | |
138 | { | |
139 | NRF51GPIOState *s = NRF51_GPIO(opaque); | |
140 | uint64_t r = 0; | |
141 | size_t idx; | |
142 | ||
143 | switch (offset) { | |
144 | case NRF51_GPIO_REG_OUT ... NRF51_GPIO_REG_OUTCLR: | |
145 | r = s->out; | |
146 | break; | |
147 | ||
148 | case NRF51_GPIO_REG_IN: | |
149 | r = s->in; | |
150 | break; | |
151 | ||
152 | case NRF51_GPIO_REG_DIR ... NRF51_GPIO_REG_DIRCLR: | |
153 | r = s->dir; | |
154 | break; | |
155 | ||
156 | case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: | |
157 | idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; | |
158 | r = s->cnf[idx]; | |
159 | break; | |
160 | ||
161 | default: | |
162 | qemu_log_mask(LOG_GUEST_ERROR, | |
163 | "%s: bad read offset 0x%" HWADDR_PRIx "\n", | |
164 | __func__, offset); | |
165 | } | |
166 | ||
167 | trace_nrf51_gpio_read(offset, r); | |
168 | ||
169 | return r; | |
170 | } | |
171 | ||
172 | static void nrf51_gpio_write(void *opaque, hwaddr offset, | |
173 | uint64_t value, unsigned int size) | |
174 | { | |
175 | NRF51GPIOState *s = NRF51_GPIO(opaque); | |
176 | size_t idx; | |
177 | ||
178 | trace_nrf51_gpio_write(offset, value); | |
179 | ||
180 | switch (offset) { | |
181 | case NRF51_GPIO_REG_OUT: | |
182 | s->out = value; | |
183 | break; | |
184 | ||
185 | case NRF51_GPIO_REG_OUTSET: | |
186 | s->out |= value; | |
187 | break; | |
188 | ||
189 | case NRF51_GPIO_REG_OUTCLR: | |
190 | s->out &= ~value; | |
191 | break; | |
192 | ||
193 | case NRF51_GPIO_REG_DIR: | |
194 | s->dir = value; | |
195 | reflect_dir_bit_in_cnf(s); | |
196 | break; | |
197 | ||
198 | case NRF51_GPIO_REG_DIRSET: | |
199 | s->dir |= value; | |
200 | reflect_dir_bit_in_cnf(s); | |
201 | break; | |
202 | ||
203 | case NRF51_GPIO_REG_DIRCLR: | |
204 | s->dir &= ~value; | |
205 | reflect_dir_bit_in_cnf(s); | |
206 | break; | |
207 | ||
208 | case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: | |
209 | idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; | |
210 | s->cnf[idx] = value; | |
211 | /* | |
212 | * direction is exposed in both the DIR register and the DIR bit | |
213 | * of each PINs CNF configuration register. | |
214 | */ | |
215 | s->dir = (s->dir & ~(1UL << idx)) | ((value & 0x01) << idx); | |
216 | break; | |
217 | ||
218 | default: | |
219 | qemu_log_mask(LOG_GUEST_ERROR, | |
220 | "%s: bad write offset 0x%" HWADDR_PRIx "\n", | |
221 | __func__, offset); | |
222 | } | |
223 | ||
224 | update_state(s); | |
225 | } | |
226 | ||
227 | static const MemoryRegionOps gpio_ops = { | |
228 | .read = nrf51_gpio_read, | |
229 | .write = nrf51_gpio_write, | |
230 | .endianness = DEVICE_LITTLE_ENDIAN, | |
231 | .impl.min_access_size = 4, | |
232 | .impl.max_access_size = 4, | |
233 | }; | |
234 | ||
235 | static void nrf51_gpio_set(void *opaque, int line, int value) | |
236 | { | |
237 | NRF51GPIOState *s = NRF51_GPIO(opaque); | |
238 | ||
239 | trace_nrf51_gpio_set(line, value); | |
240 | ||
241 | assert(line >= 0 && line < NRF51_GPIO_PINS); | |
242 | ||
243 | s->in_mask = deposit32(s->in_mask, line, 1, value >= 0); | |
244 | if (value >= 0) { | |
245 | s->in = deposit32(s->in, line, 1, value != 0); | |
246 | } | |
247 | ||
248 | update_state(s); | |
249 | } | |
250 | ||
251 | static void nrf51_gpio_reset(DeviceState *dev) | |
252 | { | |
253 | NRF51GPIOState *s = NRF51_GPIO(dev); | |
254 | size_t i; | |
255 | ||
256 | s->out = 0; | |
257 | s->old_out = 0; | |
258 | s->old_out_connected = 0; | |
259 | s->in = 0; | |
260 | s->in_mask = 0; | |
261 | s->dir = 0; | |
262 | ||
263 | for (i = 0; i < NRF51_GPIO_PINS; i++) { | |
264 | s->cnf[i] = 0x00000002; | |
265 | } | |
266 | } | |
267 | ||
268 | static const VMStateDescription vmstate_nrf51_gpio = { | |
269 | .name = TYPE_NRF51_GPIO, | |
270 | .version_id = 1, | |
271 | .minimum_version_id = 1, | |
272 | .fields = (VMStateField[]) { | |
273 | VMSTATE_UINT32(out, NRF51GPIOState), | |
274 | VMSTATE_UINT32(in, NRF51GPIOState), | |
275 | VMSTATE_UINT32(in_mask, NRF51GPIOState), | |
276 | VMSTATE_UINT32(dir, NRF51GPIOState), | |
277 | VMSTATE_UINT32_ARRAY(cnf, NRF51GPIOState, NRF51_GPIO_PINS), | |
278 | VMSTATE_UINT32(old_out, NRF51GPIOState), | |
279 | VMSTATE_UINT32(old_out_connected, NRF51GPIOState), | |
280 | VMSTATE_END_OF_LIST() | |
281 | } | |
282 | }; | |
283 | ||
284 | static void nrf51_gpio_init(Object *obj) | |
285 | { | |
286 | NRF51GPIOState *s = NRF51_GPIO(obj); | |
287 | ||
288 | memory_region_init_io(&s->mmio, obj, &gpio_ops, s, | |
289 | TYPE_NRF51_GPIO, NRF51_GPIO_SIZE); | |
290 | sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); | |
291 | ||
292 | qdev_init_gpio_in(DEVICE(s), nrf51_gpio_set, NRF51_GPIO_PINS); | |
293 | qdev_init_gpio_out(DEVICE(s), s->output, NRF51_GPIO_PINS); | |
294 | } | |
295 | ||
296 | static void nrf51_gpio_class_init(ObjectClass *klass, void *data) | |
297 | { | |
298 | DeviceClass *dc = DEVICE_CLASS(klass); | |
299 | ||
300 | dc->vmsd = &vmstate_nrf51_gpio; | |
301 | dc->reset = nrf51_gpio_reset; | |
302 | dc->desc = "nRF51 GPIO"; | |
303 | } | |
304 | ||
305 | static const TypeInfo nrf51_gpio_info = { | |
306 | .name = TYPE_NRF51_GPIO, | |
307 | .parent = TYPE_SYS_BUS_DEVICE, | |
308 | .instance_size = sizeof(NRF51GPIOState), | |
309 | .instance_init = nrf51_gpio_init, | |
310 | .class_init = nrf51_gpio_class_init | |
311 | }; | |
312 | ||
313 | static void nrf51_gpio_register_types(void) | |
314 | { | |
315 | type_register_static(&nrf51_gpio_info); | |
316 | } | |
317 | ||
318 | type_init(nrf51_gpio_register_types) |