| 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" |
| 15 | #include "qemu/module.h" |
| 16 | #include "hw/gpio/nrf51_gpio.h" |
| 17 | #include "hw/core/irq.h" |
| 18 | #include "migration/vmstate.h" |
| 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 | } |
| 44 | |
| 45 | return state; |
| 46 | } |
| 47 | |
| 48 | static int pull_value(uint32_t config) |
| 49 | { |
| 50 | int pull = extract32(config, 2, 2); |
| 51 | if (pull == NRF51_GPIO_PULLDOWN) { |
| 52 | return 0; |
| 53 | } else if (pull == NRF51_GPIO_PULLUP) { |
| 54 | return 1; |
| 55 | } |
| 56 | return -1; |
| 57 | } |
| 58 | |
| 59 | static void update_output_irq(NRF51GPIOState *s, size_t i, |
| 60 | bool connected, bool level) |
| 61 | { |
| 62 | int64_t irq_level = connected ? level : -1; |
| 63 | bool old_connected = extract32(s->old_out_connected, i, 1); |
| 64 | bool old_level = extract32(s->old_out, i, 1); |
| 65 | |
| 66 | if ((old_connected != connected) || (old_level != level)) { |
| 67 | qemu_set_irq(s->output[i], irq_level); |
| 68 | trace_nrf51_gpio_update_output_irq(i, irq_level); |
| 69 | } |
| 70 | |
| 71 | s->old_out = deposit32(s->old_out, i, 1, level); |
| 72 | s->old_out_connected = deposit32(s->old_out_connected, i, 1, connected); |
| 73 | } |
| 74 | |
| 75 | static void update_state(NRF51GPIOState *s) |
| 76 | { |
| 77 | int pull; |
| 78 | size_t i; |
| 79 | bool connected_out, dir, connected_in, out, in, input; |
| 80 | bool assert_detect = false; |
| 81 | |
| 82 | for (i = 0; i < NRF51_GPIO_PINS; i++) { |
| 83 | pull = pull_value(s->cnf[i]); |
| 84 | dir = extract32(s->cnf[i], 0, 1); |
| 85 | connected_in = extract32(s->in_mask, i, 1); |
| 86 | out = extract32(s->out, i, 1); |
| 87 | in = extract32(s->in, i, 1); |
| 88 | input = !extract32(s->cnf[i], 1, 1); |
| 89 | connected_out = is_connected(s->cnf[i], out) && dir; |
| 90 | |
| 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 | uint32_t detect_config = extract32(s->cnf[i], 16, 2); |
| 104 | if ((detect_config == 2) && (in == 1)) { |
| 105 | assert_detect = true; |
| 106 | } |
| 107 | if ((detect_config == 3) && (in == 0)) { |
| 108 | assert_detect = true; |
| 109 | } |
| 110 | } else { |
| 111 | /* |
| 112 | * Floating input: the output stimulates IN if connected, |
| 113 | * otherwise pull-up/pull-down resistors put a value on both |
| 114 | * IN and OUT. |
| 115 | */ |
| 116 | if (pull >= 0 && !connected_out) { |
| 117 | connected_out = true; |
| 118 | out = pull; |
| 119 | } |
| 120 | if (connected_out) { |
| 121 | s->in = deposit32(s->in, i, 1, out); |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | update_output_irq(s, i, connected_out, out); |
| 126 | } |
| 127 | |
| 128 | qemu_set_irq(s->detect, assert_detect); |
| 129 | } |
| 130 | |
| 131 | /* |
| 132 | * Direction is exposed in both the DIR register and the DIR bit |
| 133 | * of each PINs CNF configuration register. Reflect bits for pins in DIR |
| 134 | * to individual pin configuration registers. |
| 135 | */ |
| 136 | static void reflect_dir_bit_in_cnf(NRF51GPIOState *s) |
| 137 | { |
| 138 | size_t i; |
| 139 | |
| 140 | uint32_t value = s->dir; |
| 141 | |
| 142 | for (i = 0; i < NRF51_GPIO_PINS; i++) { |
| 143 | s->cnf[i] = (s->cnf[i] & ~(1UL)) | ((value >> i) & 0x01); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | static uint64_t nrf51_gpio_read(void *opaque, hwaddr offset, unsigned int size) |
| 148 | { |
| 149 | NRF51GPIOState *s = NRF51_GPIO(opaque); |
| 150 | uint64_t r = 0; |
| 151 | size_t idx; |
| 152 | |
| 153 | switch (offset) { |
| 154 | case NRF51_GPIO_REG_OUT ... NRF51_GPIO_REG_OUTCLR: |
| 155 | r = s->out; |
| 156 | break; |
| 157 | |
| 158 | case NRF51_GPIO_REG_IN: |
| 159 | r = s->in; |
| 160 | break; |
| 161 | |
| 162 | case NRF51_GPIO_REG_DIR ... NRF51_GPIO_REG_DIRCLR: |
| 163 | r = s->dir; |
| 164 | break; |
| 165 | |
| 166 | case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: |
| 167 | idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; |
| 168 | r = s->cnf[idx]; |
| 169 | break; |
| 170 | |
| 171 | default: |
| 172 | qemu_log_mask(LOG_GUEST_ERROR, |
| 173 | "%s: bad read offset 0x%" HWADDR_PRIx "\n", |
| 174 | __func__, offset); |
| 175 | } |
| 176 | |
| 177 | trace_nrf51_gpio_read(offset, r); |
| 178 | |
| 179 | return r; |
| 180 | } |
| 181 | |
| 182 | static void nrf51_gpio_write(void *opaque, hwaddr offset, |
| 183 | uint64_t value, unsigned int size) |
| 184 | { |
| 185 | NRF51GPIOState *s = NRF51_GPIO(opaque); |
| 186 | size_t idx; |
| 187 | |
| 188 | trace_nrf51_gpio_write(offset, value); |
| 189 | |
| 190 | switch (offset) { |
| 191 | case NRF51_GPIO_REG_OUT: |
| 192 | s->out = value; |
| 193 | break; |
| 194 | |
| 195 | case NRF51_GPIO_REG_OUTSET: |
| 196 | s->out |= value; |
| 197 | break; |
| 198 | |
| 199 | case NRF51_GPIO_REG_OUTCLR: |
| 200 | s->out &= ~value; |
| 201 | break; |
| 202 | |
| 203 | case NRF51_GPIO_REG_DIR: |
| 204 | s->dir = value; |
| 205 | reflect_dir_bit_in_cnf(s); |
| 206 | break; |
| 207 | |
| 208 | case NRF51_GPIO_REG_DIRSET: |
| 209 | s->dir |= value; |
| 210 | reflect_dir_bit_in_cnf(s); |
| 211 | break; |
| 212 | |
| 213 | case NRF51_GPIO_REG_DIRCLR: |
| 214 | s->dir &= ~value; |
| 215 | reflect_dir_bit_in_cnf(s); |
| 216 | break; |
| 217 | |
| 218 | case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: |
| 219 | idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; |
| 220 | s->cnf[idx] = value; |
| 221 | /* |
| 222 | * direction is exposed in both the DIR register and the DIR bit |
| 223 | * of each PINs CNF configuration register. |
| 224 | */ |
| 225 | s->dir = (s->dir & ~(1UL << idx)) | ((value & 0x01) << idx); |
| 226 | break; |
| 227 | |
| 228 | default: |
| 229 | qemu_log_mask(LOG_GUEST_ERROR, |
| 230 | "%s: bad write offset 0x%" HWADDR_PRIx "\n", |
| 231 | __func__, offset); |
| 232 | } |
| 233 | |
| 234 | update_state(s); |
| 235 | } |
| 236 | |
| 237 | static const MemoryRegionOps gpio_ops = { |
| 238 | .read = nrf51_gpio_read, |
| 239 | .write = nrf51_gpio_write, |
| 240 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 241 | .impl.min_access_size = 4, |
| 242 | .impl.max_access_size = 4, |
| 243 | }; |
| 244 | |
| 245 | static void nrf51_gpio_set(void *opaque, int line, int value) |
| 246 | { |
| 247 | NRF51GPIOState *s = NRF51_GPIO(opaque); |
| 248 | |
| 249 | trace_nrf51_gpio_set(line, value); |
| 250 | |
| 251 | assert(line >= 0 && line < NRF51_GPIO_PINS); |
| 252 | |
| 253 | s->in_mask = deposit32(s->in_mask, line, 1, value >= 0); |
| 254 | if (value >= 0) { |
| 255 | s->in = deposit32(s->in, line, 1, value != 0); |
| 256 | } |
| 257 | |
| 258 | update_state(s); |
| 259 | } |
| 260 | |
| 261 | static void nrf51_gpio_reset(DeviceState *dev) |
| 262 | { |
| 263 | NRF51GPIOState *s = NRF51_GPIO(dev); |
| 264 | size_t i; |
| 265 | |
| 266 | s->out = 0; |
| 267 | s->old_out = 0; |
| 268 | s->old_out_connected = 0; |
| 269 | s->in = 0; |
| 270 | s->in_mask = 0; |
| 271 | s->dir = 0; |
| 272 | |
| 273 | for (i = 0; i < NRF51_GPIO_PINS; i++) { |
| 274 | s->cnf[i] = 0x00000002; |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | static const VMStateDescription vmstate_nrf51_gpio = { |
| 279 | .name = TYPE_NRF51_GPIO, |
| 280 | .version_id = 1, |
| 281 | .minimum_version_id = 1, |
| 282 | .fields = (const VMStateField[]) { |
| 283 | VMSTATE_UINT32(out, NRF51GPIOState), |
| 284 | VMSTATE_UINT32(in, NRF51GPIOState), |
| 285 | VMSTATE_UINT32(in_mask, NRF51GPIOState), |
| 286 | VMSTATE_UINT32(dir, NRF51GPIOState), |
| 287 | VMSTATE_UINT32_ARRAY(cnf, NRF51GPIOState, NRF51_GPIO_PINS), |
| 288 | VMSTATE_UINT32(old_out, NRF51GPIOState), |
| 289 | VMSTATE_UINT32(old_out_connected, NRF51GPIOState), |
| 290 | VMSTATE_END_OF_LIST() |
| 291 | } |
| 292 | }; |
| 293 | |
| 294 | static void nrf51_gpio_init(Object *obj) |
| 295 | { |
| 296 | NRF51GPIOState *s = NRF51_GPIO(obj); |
| 297 | |
| 298 | memory_region_init_io(&s->mmio, obj, &gpio_ops, s, |
| 299 | TYPE_NRF51_GPIO, NRF51_GPIO_SIZE); |
| 300 | sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); |
| 301 | |
| 302 | qdev_init_gpio_in(DEVICE(s), nrf51_gpio_set, NRF51_GPIO_PINS); |
| 303 | qdev_init_gpio_out(DEVICE(s), s->output, NRF51_GPIO_PINS); |
| 304 | qdev_init_gpio_out_named(DEVICE(s), &s->detect, "detect", 1); |
| 305 | } |
| 306 | |
| 307 | static void nrf51_gpio_class_init(ObjectClass *klass, const void *data) |
| 308 | { |
| 309 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 310 | |
| 311 | dc->vmsd = &vmstate_nrf51_gpio; |
| 312 | device_class_set_legacy_reset(dc, nrf51_gpio_reset); |
| 313 | dc->desc = "nRF51 GPIO"; |
| 314 | } |
| 315 | |
| 316 | static const TypeInfo nrf51_gpio_info = { |
| 317 | .name = TYPE_NRF51_GPIO, |
| 318 | .parent = TYPE_SYS_BUS_DEVICE, |
| 319 | .instance_size = sizeof(NRF51GPIOState), |
| 320 | .instance_init = nrf51_gpio_init, |
| 321 | .class_init = nrf51_gpio_class_init |
| 322 | }; |
| 323 | |
| 324 | static void nrf51_gpio_register_types(void) |
| 325 | { |
| 326 | type_register_static(&nrf51_gpio_info); |
| 327 | } |
| 328 | |
| 329 | type_init(nrf51_gpio_register_types) |