190c58941SSteffen Görtz /* 290c58941SSteffen Görtz * nRF51 Random Number Generator 390c58941SSteffen Görtz * 490c58941SSteffen Görtz * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf 590c58941SSteffen Görtz * 690c58941SSteffen Görtz * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de> 790c58941SSteffen Görtz * 890c58941SSteffen Görtz * This code is licensed under the GPL version 2 or later. See 990c58941SSteffen Görtz * the COPYING file in the top-level directory. 1090c58941SSteffen Görtz */ 1190c58941SSteffen Görtz 1290c58941SSteffen Görtz #include "qemu/osdep.h" 1390c58941SSteffen Görtz #include "qemu/log.h" 140b8fa32fSMarkus Armbruster #include "qemu/module.h" 1590c58941SSteffen Görtz #include "qapi/error.h" 1690c58941SSteffen Görtz #include "hw/arm/nrf51.h" 1764552b6bSMarkus Armbruster #include "hw/irq.h" 1890c58941SSteffen Görtz #include "hw/misc/nrf51_rng.h" 19a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 20d6454270SMarkus Armbruster #include "migration/vmstate.h" 2119173fd3SRichard Henderson #include "qemu/guest-random.h" 2290c58941SSteffen Görtz 2390c58941SSteffen Görtz static void update_irq(NRF51RNGState *s) 2490c58941SSteffen Görtz { 2590c58941SSteffen Görtz bool irq = s->interrupt_enabled && s->event_valrdy; 2690c58941SSteffen Görtz qemu_set_irq(s->irq, irq); 2790c58941SSteffen Görtz } 2890c58941SSteffen Görtz 2990c58941SSteffen Görtz static uint64_t rng_read(void *opaque, hwaddr offset, unsigned int size) 3090c58941SSteffen Görtz { 3190c58941SSteffen Görtz NRF51RNGState *s = NRF51_RNG(opaque); 3290c58941SSteffen Görtz uint64_t r = 0; 3390c58941SSteffen Görtz 3490c58941SSteffen Görtz switch (offset) { 3590c58941SSteffen Görtz case NRF51_RNG_EVENT_VALRDY: 3690c58941SSteffen Görtz r = s->event_valrdy; 3790c58941SSteffen Görtz break; 3890c58941SSteffen Görtz case NRF51_RNG_REG_SHORTS: 3990c58941SSteffen Görtz r = s->shortcut_stop_on_valrdy; 4090c58941SSteffen Görtz break; 4190c58941SSteffen Görtz case NRF51_RNG_REG_INTEN: 4290c58941SSteffen Görtz case NRF51_RNG_REG_INTENSET: 4390c58941SSteffen Görtz case NRF51_RNG_REG_INTENCLR: 4490c58941SSteffen Görtz r = s->interrupt_enabled; 4590c58941SSteffen Görtz break; 4690c58941SSteffen Görtz case NRF51_RNG_REG_CONFIG: 4790c58941SSteffen Görtz r = s->filter_enabled; 4890c58941SSteffen Görtz break; 4990c58941SSteffen Görtz case NRF51_RNG_REG_VALUE: 5090c58941SSteffen Görtz r = s->value; 5190c58941SSteffen Görtz break; 5290c58941SSteffen Görtz 5390c58941SSteffen Görtz default: 5490c58941SSteffen Görtz qemu_log_mask(LOG_GUEST_ERROR, 5590c58941SSteffen Görtz "%s: bad read offset 0x%" HWADDR_PRIx "\n", 5690c58941SSteffen Görtz __func__, offset); 5790c58941SSteffen Görtz } 5890c58941SSteffen Görtz 5990c58941SSteffen Görtz return r; 6090c58941SSteffen Görtz } 6190c58941SSteffen Görtz 6290c58941SSteffen Görtz static int64_t calc_next_timeout(NRF51RNGState *s) 6390c58941SSteffen Görtz { 6490c58941SSteffen Görtz int64_t timeout = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL); 6590c58941SSteffen Görtz if (s->filter_enabled) { 6690c58941SSteffen Görtz timeout += s->period_filtered_us; 6790c58941SSteffen Görtz } else { 6890c58941SSteffen Görtz timeout += s->period_unfiltered_us; 6990c58941SSteffen Görtz } 7090c58941SSteffen Görtz 7190c58941SSteffen Görtz return timeout; 7290c58941SSteffen Görtz } 7390c58941SSteffen Görtz 7490c58941SSteffen Görtz 7590c58941SSteffen Görtz static void rng_update_timer(NRF51RNGState *s) 7690c58941SSteffen Görtz { 7790c58941SSteffen Görtz if (s->active) { 7890c58941SSteffen Görtz timer_mod(&s->timer, calc_next_timeout(s)); 7990c58941SSteffen Görtz } else { 8090c58941SSteffen Görtz timer_del(&s->timer); 8190c58941SSteffen Görtz } 8290c58941SSteffen Görtz } 8390c58941SSteffen Görtz 8490c58941SSteffen Görtz 8590c58941SSteffen Görtz static void rng_write(void *opaque, hwaddr offset, 8690c58941SSteffen Görtz uint64_t value, unsigned int size) 8790c58941SSteffen Görtz { 8890c58941SSteffen Görtz NRF51RNGState *s = NRF51_RNG(opaque); 8990c58941SSteffen Görtz 9090c58941SSteffen Görtz switch (offset) { 9190c58941SSteffen Görtz case NRF51_RNG_TASK_START: 9290c58941SSteffen Görtz if (value == NRF51_TRIGGER_TASK) { 9390c58941SSteffen Görtz s->active = 1; 9490c58941SSteffen Görtz rng_update_timer(s); 9590c58941SSteffen Görtz } 9690c58941SSteffen Görtz break; 9790c58941SSteffen Görtz case NRF51_RNG_TASK_STOP: 9890c58941SSteffen Görtz if (value == NRF51_TRIGGER_TASK) { 9990c58941SSteffen Görtz s->active = 0; 10090c58941SSteffen Görtz rng_update_timer(s); 10190c58941SSteffen Görtz } 10290c58941SSteffen Görtz break; 10390c58941SSteffen Görtz case NRF51_RNG_EVENT_VALRDY: 10490c58941SSteffen Görtz if (value == NRF51_EVENT_CLEAR) { 10590c58941SSteffen Görtz s->event_valrdy = 0; 10690c58941SSteffen Görtz } 10790c58941SSteffen Görtz break; 10890c58941SSteffen Görtz case NRF51_RNG_REG_SHORTS: 10990c58941SSteffen Görtz s->shortcut_stop_on_valrdy = 11090c58941SSteffen Görtz (value & BIT_MASK(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0; 11190c58941SSteffen Görtz break; 11290c58941SSteffen Görtz case NRF51_RNG_REG_INTEN: 11390c58941SSteffen Görtz s->interrupt_enabled = 11490c58941SSteffen Görtz (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0; 11590c58941SSteffen Görtz break; 11690c58941SSteffen Görtz case NRF51_RNG_REG_INTENSET: 11790c58941SSteffen Görtz if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { 11890c58941SSteffen Görtz s->interrupt_enabled = 1; 11990c58941SSteffen Görtz } 12090c58941SSteffen Görtz break; 12190c58941SSteffen Görtz case NRF51_RNG_REG_INTENCLR: 12290c58941SSteffen Görtz if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { 12390c58941SSteffen Görtz s->interrupt_enabled = 0; 12490c58941SSteffen Görtz } 12590c58941SSteffen Görtz break; 12690c58941SSteffen Görtz case NRF51_RNG_REG_CONFIG: 12790c58941SSteffen Görtz s->filter_enabled = 12890c58941SSteffen Görtz (value & BIT_MASK(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0; 12990c58941SSteffen Görtz break; 13090c58941SSteffen Görtz 13190c58941SSteffen Görtz default: 13290c58941SSteffen Görtz qemu_log_mask(LOG_GUEST_ERROR, 13390c58941SSteffen Görtz "%s: bad write offset 0x%" HWADDR_PRIx "\n", 13490c58941SSteffen Görtz __func__, offset); 13590c58941SSteffen Görtz } 13690c58941SSteffen Görtz 13790c58941SSteffen Görtz update_irq(s); 13890c58941SSteffen Görtz } 13990c58941SSteffen Görtz 14090c58941SSteffen Görtz static const MemoryRegionOps rng_ops = { 14190c58941SSteffen Görtz .read = rng_read, 14290c58941SSteffen Görtz .write = rng_write, 14390c58941SSteffen Görtz .endianness = DEVICE_LITTLE_ENDIAN, 14490c58941SSteffen Görtz .impl.min_access_size = 4, 14590c58941SSteffen Görtz .impl.max_access_size = 4 14690c58941SSteffen Görtz }; 14790c58941SSteffen Görtz 14890c58941SSteffen Görtz static void nrf51_rng_timer_expire(void *opaque) 14990c58941SSteffen Görtz { 15090c58941SSteffen Görtz NRF51RNGState *s = NRF51_RNG(opaque); 15190c58941SSteffen Görtz 15219173fd3SRichard Henderson qemu_guest_getrandom_nofail(&s->value, 1); 15390c58941SSteffen Görtz 15490c58941SSteffen Görtz s->event_valrdy = 1; 15590c58941SSteffen Görtz qemu_set_irq(s->eep_valrdy, 1); 15690c58941SSteffen Görtz 15790c58941SSteffen Görtz if (s->shortcut_stop_on_valrdy) { 15890c58941SSteffen Görtz s->active = 0; 15990c58941SSteffen Görtz } 16090c58941SSteffen Görtz 16190c58941SSteffen Görtz rng_update_timer(s); 16290c58941SSteffen Görtz update_irq(s); 16390c58941SSteffen Görtz } 16490c58941SSteffen Görtz 16590c58941SSteffen Görtz static void nrf51_rng_tep_start(void *opaque, int n, int level) 16690c58941SSteffen Görtz { 16790c58941SSteffen Görtz NRF51RNGState *s = NRF51_RNG(opaque); 16890c58941SSteffen Görtz 16990c58941SSteffen Görtz if (level) { 17090c58941SSteffen Görtz s->active = 1; 17190c58941SSteffen Görtz rng_update_timer(s); 17290c58941SSteffen Görtz } 17390c58941SSteffen Görtz } 17490c58941SSteffen Görtz 17590c58941SSteffen Görtz static void nrf51_rng_tep_stop(void *opaque, int n, int level) 17690c58941SSteffen Görtz { 17790c58941SSteffen Görtz NRF51RNGState *s = NRF51_RNG(opaque); 17890c58941SSteffen Görtz 17990c58941SSteffen Görtz if (level) { 18090c58941SSteffen Görtz s->active = 0; 18190c58941SSteffen Görtz rng_update_timer(s); 18290c58941SSteffen Görtz } 18390c58941SSteffen Görtz } 18490c58941SSteffen Görtz 18590c58941SSteffen Görtz 18690c58941SSteffen Görtz static void nrf51_rng_init(Object *obj) 18790c58941SSteffen Görtz { 18890c58941SSteffen Görtz NRF51RNGState *s = NRF51_RNG(obj); 18990c58941SSteffen Görtz SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 19090c58941SSteffen Görtz 19190c58941SSteffen Görtz memory_region_init_io(&s->mmio, obj, &rng_ops, s, 19290c58941SSteffen Görtz TYPE_NRF51_RNG, NRF51_RNG_SIZE); 19390c58941SSteffen Görtz sysbus_init_mmio(sbd, &s->mmio); 19490c58941SSteffen Görtz 19590c58941SSteffen Görtz timer_init_us(&s->timer, QEMU_CLOCK_VIRTUAL, nrf51_rng_timer_expire, s); 19690c58941SSteffen Görtz 19790c58941SSteffen Görtz sysbus_init_irq(sbd, &s->irq); 19890c58941SSteffen Görtz 19990c58941SSteffen Görtz /* Tasks */ 20090c58941SSteffen Görtz qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_start, "tep_start", 1); 20190c58941SSteffen Görtz qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_stop, "tep_stop", 1); 20290c58941SSteffen Görtz 20390c58941SSteffen Görtz /* Events */ 20490c58941SSteffen Görtz qdev_init_gpio_out_named(DEVICE(s), &s->eep_valrdy, "eep_valrdy", 1); 20590c58941SSteffen Görtz } 20690c58941SSteffen Görtz 20790c58941SSteffen Görtz static void nrf51_rng_reset(DeviceState *dev) 20890c58941SSteffen Görtz { 20990c58941SSteffen Görtz NRF51RNGState *s = NRF51_RNG(dev); 21090c58941SSteffen Görtz 21190c58941SSteffen Görtz s->value = 0; 21290c58941SSteffen Görtz s->active = 0; 21390c58941SSteffen Görtz s->event_valrdy = 0; 21490c58941SSteffen Görtz s->shortcut_stop_on_valrdy = 0; 21590c58941SSteffen Görtz s->interrupt_enabled = 0; 21690c58941SSteffen Görtz s->filter_enabled = 0; 21790c58941SSteffen Görtz 21890c58941SSteffen Görtz rng_update_timer(s); 21990c58941SSteffen Görtz } 22090c58941SSteffen Görtz 22190c58941SSteffen Görtz 22290c58941SSteffen Görtz static Property nrf51_rng_properties[] = { 22390c58941SSteffen Görtz DEFINE_PROP_UINT16("period_unfiltered_us", NRF51RNGState, 22490c58941SSteffen Görtz period_unfiltered_us, 167), 22590c58941SSteffen Görtz DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState, 22690c58941SSteffen Görtz period_filtered_us, 660), 22790c58941SSteffen Görtz DEFINE_PROP_END_OF_LIST(), 22890c58941SSteffen Görtz }; 22990c58941SSteffen Görtz 23090c58941SSteffen Görtz static const VMStateDescription vmstate_rng = { 23190c58941SSteffen Görtz .name = "nrf51_soc.rng", 23290c58941SSteffen Görtz .version_id = 1, 23390c58941SSteffen Görtz .minimum_version_id = 1, 234*e4ea952fSRichard Henderson .fields = (const VMStateField[]) { 23590c58941SSteffen Görtz VMSTATE_UINT32(active, NRF51RNGState), 23690c58941SSteffen Görtz VMSTATE_UINT32(event_valrdy, NRF51RNGState), 23790c58941SSteffen Görtz VMSTATE_UINT32(shortcut_stop_on_valrdy, NRF51RNGState), 23890c58941SSteffen Görtz VMSTATE_UINT32(interrupt_enabled, NRF51RNGState), 23990c58941SSteffen Görtz VMSTATE_UINT32(filter_enabled, NRF51RNGState), 24090c58941SSteffen Görtz VMSTATE_END_OF_LIST() 24190c58941SSteffen Görtz } 24290c58941SSteffen Görtz }; 24390c58941SSteffen Görtz 24490c58941SSteffen Görtz static void nrf51_rng_class_init(ObjectClass *klass, void *data) 24590c58941SSteffen Görtz { 24690c58941SSteffen Görtz DeviceClass *dc = DEVICE_CLASS(klass); 24790c58941SSteffen Görtz 2484f67d30bSMarc-André Lureau device_class_set_props(dc, nrf51_rng_properties); 24990c58941SSteffen Görtz dc->vmsd = &vmstate_rng; 25090c58941SSteffen Görtz dc->reset = nrf51_rng_reset; 25190c58941SSteffen Görtz } 25290c58941SSteffen Görtz 25390c58941SSteffen Görtz static const TypeInfo nrf51_rng_info = { 25490c58941SSteffen Görtz .name = TYPE_NRF51_RNG, 25590c58941SSteffen Görtz .parent = TYPE_SYS_BUS_DEVICE, 25690c58941SSteffen Görtz .instance_size = sizeof(NRF51RNGState), 25790c58941SSteffen Görtz .instance_init = nrf51_rng_init, 25890c58941SSteffen Görtz .class_init = nrf51_rng_class_init 25990c58941SSteffen Görtz }; 26090c58941SSteffen Görtz 26190c58941SSteffen Görtz static void nrf51_rng_register_types(void) 26290c58941SSteffen Görtz { 26390c58941SSteffen Görtz type_register_static(&nrf51_rng_info); 26490c58941SSteffen Görtz } 26590c58941SSteffen Görtz 26690c58941SSteffen Görtz type_init(nrf51_rng_register_types) 267