qemu/hw/misc/nrf51_rng.c
<<
>>
Prefs
   1/*
   2 * nRF51 Random Number Generator
   3 *
   4 * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf
   5 *
   6 * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
   7 *
   8 * This code is licensed under the GPL version 2 or later.  See
   9 * the COPYING file in the top-level directory.
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "qemu/log.h"
  14#include "qemu/module.h"
  15#include "qapi/error.h"
  16#include "hw/arm/nrf51.h"
  17#include "hw/irq.h"
  18#include "hw/misc/nrf51_rng.h"
  19#include "hw/qdev-properties.h"
  20#include "migration/vmstate.h"
  21#include "qemu/guest-random.h"
  22
  23static void update_irq(NRF51RNGState *s)
  24{
  25    bool irq = s->interrupt_enabled && s->event_valrdy;
  26    qemu_set_irq(s->irq, irq);
  27}
  28
  29static uint64_t rng_read(void *opaque, hwaddr offset, unsigned int size)
  30{
  31    NRF51RNGState *s = NRF51_RNG(opaque);
  32    uint64_t r = 0;
  33
  34    switch (offset) {
  35    case NRF51_RNG_EVENT_VALRDY:
  36        r = s->event_valrdy;
  37        break;
  38    case NRF51_RNG_REG_SHORTS:
  39        r = s->shortcut_stop_on_valrdy;
  40        break;
  41    case NRF51_RNG_REG_INTEN:
  42    case NRF51_RNG_REG_INTENSET:
  43    case NRF51_RNG_REG_INTENCLR:
  44        r = s->interrupt_enabled;
  45        break;
  46    case NRF51_RNG_REG_CONFIG:
  47        r = s->filter_enabled;
  48        break;
  49    case NRF51_RNG_REG_VALUE:
  50        r = s->value;
  51        break;
  52
  53    default:
  54        qemu_log_mask(LOG_GUEST_ERROR,
  55                      "%s: bad read offset 0x%" HWADDR_PRIx "\n",
  56                      __func__, offset);
  57    }
  58
  59    return r;
  60}
  61
  62static int64_t calc_next_timeout(NRF51RNGState *s)
  63{
  64    int64_t timeout = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
  65    if (s->filter_enabled) {
  66        timeout += s->period_filtered_us;
  67    } else {
  68        timeout += s->period_unfiltered_us;
  69    }
  70
  71    return timeout;
  72}
  73
  74
  75static void rng_update_timer(NRF51RNGState *s)
  76{
  77    if (s->active) {
  78        timer_mod(&s->timer, calc_next_timeout(s));
  79    } else {
  80        timer_del(&s->timer);
  81    }
  82}
  83
  84
  85static void rng_write(void *opaque, hwaddr offset,
  86                       uint64_t value, unsigned int size)
  87{
  88    NRF51RNGState *s = NRF51_RNG(opaque);
  89
  90    switch (offset) {
  91    case NRF51_RNG_TASK_START:
  92        if (value == NRF51_TRIGGER_TASK) {
  93            s->active = 1;
  94            rng_update_timer(s);
  95        }
  96        break;
  97    case NRF51_RNG_TASK_STOP:
  98        if (value == NRF51_TRIGGER_TASK) {
  99            s->active = 0;
 100            rng_update_timer(s);
 101        }
 102        break;
 103    case NRF51_RNG_EVENT_VALRDY:
 104        if (value == NRF51_EVENT_CLEAR) {
 105            s->event_valrdy = 0;
 106        }
 107        break;
 108    case NRF51_RNG_REG_SHORTS:
 109        s->shortcut_stop_on_valrdy =
 110                (value & BIT_MASK(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0;
 111        break;
 112    case NRF51_RNG_REG_INTEN:
 113        s->interrupt_enabled =
 114                (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0;
 115        break;
 116    case NRF51_RNG_REG_INTENSET:
 117        if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) {
 118            s->interrupt_enabled = 1;
 119        }
 120        break;
 121    case NRF51_RNG_REG_INTENCLR:
 122        if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) {
 123            s->interrupt_enabled = 0;
 124        }
 125        break;
 126    case NRF51_RNG_REG_CONFIG:
 127        s->filter_enabled =
 128                      (value & BIT_MASK(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0;
 129        break;
 130
 131    default:
 132        qemu_log_mask(LOG_GUEST_ERROR,
 133                      "%s: bad write offset 0x%" HWADDR_PRIx "\n",
 134                      __func__, offset);
 135    }
 136
 137    update_irq(s);
 138}
 139
 140static const MemoryRegionOps rng_ops = {
 141    .read =  rng_read,
 142    .write = rng_write,
 143    .endianness = DEVICE_LITTLE_ENDIAN,
 144    .impl.min_access_size = 4,
 145    .impl.max_access_size = 4
 146};
 147
 148static void nrf51_rng_timer_expire(void *opaque)
 149{
 150    NRF51RNGState *s = NRF51_RNG(opaque);
 151
 152    qemu_guest_getrandom_nofail(&s->value, 1);
 153
 154    s->event_valrdy = 1;
 155    qemu_set_irq(s->eep_valrdy, 1);
 156
 157    if (s->shortcut_stop_on_valrdy) {
 158        s->active = 0;
 159    }
 160
 161    rng_update_timer(s);
 162    update_irq(s);
 163}
 164
 165static void nrf51_rng_tep_start(void *opaque, int n, int level)
 166{
 167    NRF51RNGState *s = NRF51_RNG(opaque);
 168
 169    if (level) {
 170        s->active = 1;
 171        rng_update_timer(s);
 172    }
 173}
 174
 175static void nrf51_rng_tep_stop(void *opaque, int n, int level)
 176{
 177    NRF51RNGState *s = NRF51_RNG(opaque);
 178
 179    if (level) {
 180        s->active = 0;
 181        rng_update_timer(s);
 182    }
 183}
 184
 185
 186static void nrf51_rng_init(Object *obj)
 187{
 188    NRF51RNGState *s = NRF51_RNG(obj);
 189    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 190
 191    memory_region_init_io(&s->mmio, obj, &rng_ops, s,
 192            TYPE_NRF51_RNG, NRF51_RNG_SIZE);
 193    sysbus_init_mmio(sbd, &s->mmio);
 194
 195    timer_init_us(&s->timer, QEMU_CLOCK_VIRTUAL, nrf51_rng_timer_expire, s);
 196
 197    sysbus_init_irq(sbd, &s->irq);
 198
 199    /* Tasks */
 200    qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_start, "tep_start", 1);
 201    qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_stop, "tep_stop", 1);
 202
 203    /* Events */
 204    qdev_init_gpio_out_named(DEVICE(s), &s->eep_valrdy, "eep_valrdy", 1);
 205}
 206
 207static void nrf51_rng_reset(DeviceState *dev)
 208{
 209    NRF51RNGState *s = NRF51_RNG(dev);
 210
 211    s->value = 0;
 212    s->active = 0;
 213    s->event_valrdy = 0;
 214    s->shortcut_stop_on_valrdy = 0;
 215    s->interrupt_enabled = 0;
 216    s->filter_enabled = 0;
 217
 218    rng_update_timer(s);
 219}
 220
 221
 222static Property nrf51_rng_properties[] = {
 223    DEFINE_PROP_UINT16("period_unfiltered_us", NRF51RNGState,
 224            period_unfiltered_us, 167),
 225    DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState,
 226            period_filtered_us, 660),
 227    DEFINE_PROP_END_OF_LIST(),
 228};
 229
 230static const VMStateDescription vmstate_rng = {
 231    .name = "nrf51_soc.rng",
 232    .version_id = 1,
 233    .minimum_version_id = 1,
 234    .fields = (VMStateField[]) {
 235        VMSTATE_UINT32(active, NRF51RNGState),
 236        VMSTATE_UINT32(event_valrdy, NRF51RNGState),
 237        VMSTATE_UINT32(shortcut_stop_on_valrdy, NRF51RNGState),
 238        VMSTATE_UINT32(interrupt_enabled, NRF51RNGState),
 239        VMSTATE_UINT32(filter_enabled, NRF51RNGState),
 240        VMSTATE_END_OF_LIST()
 241    }
 242};
 243
 244static void nrf51_rng_class_init(ObjectClass *klass, void *data)
 245{
 246    DeviceClass *dc = DEVICE_CLASS(klass);
 247
 248    device_class_set_props(dc, nrf51_rng_properties);
 249    dc->vmsd = &vmstate_rng;
 250    dc->reset = nrf51_rng_reset;
 251}
 252
 253static const TypeInfo nrf51_rng_info = {
 254    .name = TYPE_NRF51_RNG,
 255    .parent = TYPE_SYS_BUS_DEVICE,
 256    .instance_size = sizeof(NRF51RNGState),
 257    .instance_init = nrf51_rng_init,
 258    .class_init = nrf51_rng_class_init
 259};
 260
 261static void nrf51_rng_register_types(void)
 262{
 263    type_register_static(&nrf51_rng_info);
 264}
 265
 266type_init(nrf51_rng_register_types)
 267