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