qemu/hw/gpio/nrf51_gpio.c
<<
>>
Prefs
   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/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 */
  26static 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
  49static 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
  60static 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
  76static void update_state(NRF51GPIOState *s)
  77{
  78    int pull;
  79    size_t i;
  80    bool connected_out, dir, connected_in, out, in, input;
  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                /*
 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                }
 115            }
 116        }
 117        update_output_irq(s, i, connected_out, out);
 118    }
 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 */
 126static 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
 137static 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
 172static 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
 227static 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
 235static 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
 251static 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
 268static 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
 284static 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
 296static 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
 305static 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
 313static void nrf51_gpio_register_types(void)
 314{
 315    type_register_static(&nrf51_gpio_info);
 316}
 317
 318type_init(nrf51_gpio_register_types)
 319