Signed-off-by: Sergey Kambalin <sergey.kambalin@auriga.com>
---
hw/misc/bcm2838_rng200.c | 152 +++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
hw/misc/trace-events | 9 ++
include/hw/misc/bcm2838_rng200.h | 51 +++++++++++
4 files changed, 213 insertions(+)
create mode 100644 hw/misc/bcm2838_rng200.c
create mode 100644 include/hw/misc/bcm2838_rng200.h
diff --git a/hw/misc/bcm2838_rng200.c b/hw/misc/bcm2838_rng200.c
new file mode 100644
index 0000000000..8f64e6a20f
--- /dev/null
+++ b/hw/misc/bcm2838_rng200.c
@@ -0,0 +1,152 @@
+/*
+ * BCM2838 Random Number Generator emulation
+ *
+ * Copyright (C) 2022 Sergey Pushkarev <sergey.pushkarev@auriga.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/bcm2838_rng200.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+static const VMStateDescription vmstate_bcm2838_rng200_regs = {
+ .name = "bcm2838_rng200_regs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ctrl, BCM2838_rng_regs_t),
+ VMSTATE_UINT32(int_status, BCM2838_rng_regs_t),
+ VMSTATE_UINT32(fifo_count, BCM2838_rng_regs_t),
+ VMSTATE_UINT32(fifo_count_threshold, BCM2838_rng_regs_t),
+ VMSTATE_UINT32(total_bit_count_threshold, BCM2838_rng_regs_t),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_bcm2838_rng200 = {
+ .name = "bcm2838_rng200",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(rbg_period, BCM2838Rng200State),
+ VMSTATE_UINT32(rng_fifo_cap, BCM2838Rng200State),
+ VMSTATE_BOOL(use_timer, BCM2838Rng200State),
+
+ VMSTATE_STRUCT(regs, BCM2838Rng200State, 0, vmstate_bcm2838_rng200_regs,
+ BCM2838_rng_regs_t),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void bcm2838_rng200_rng_reset(BCM2838Rng200State *state)
+{
+ state->regs.ctrl = 0;
+
+ trace_bcm2838_rng200_rng_soft_reset();
+}
+
+static uint64_t bcm2838_rng200_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ uint32_t res = 0;
+
+ /* will be implemented in upcoming commits */
+ return res;
+}
+
+static void bcm2838_rng200_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ /* will be implemented in upcoming commits */
+}
+
+static const MemoryRegionOps bcm2838_rng200_ops = {
+ .read = bcm2838_rng200_read,
+ .write = bcm2838_rng200_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void bcm2838_rng200_realize(DeviceState *dev, Error **errp)
+{
+ BCM2838Rng200State *s = BCM2838_RNG200(dev);
+
+ if (s->rng == NULL) {
+ Object *default_backend = object_new(TYPE_RNG_BUILTIN);
+
+ object_property_add_child(OBJECT(dev), "default-backend",
+ default_backend);
+ object_unref(default_backend);
+
+ object_property_set_link(OBJECT(dev), "rng", default_backend,
+ errp);
+ }
+
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+}
+
+static void bcm2838_rng200_init(Object *obj)
+{
+ BCM2838Rng200State *s = BCM2838_RNG200(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ s->rbg_period = 250;
+ s->use_timer = true;
+
+ s->rng_fifo_cap = 128;
+
+ s->clock = qdev_init_clock_in(DEVICE(s), "rbg-clock",
+ NULL, s,
+ ClockPreUpdate);
+ if (s->clock == NULL) {
+ error_setg(&error_fatal, "Failed to init RBG clock");
+ return;
+ }
+
+ memory_region_init_io(&s->iomem, obj, &bcm2838_rng200_ops, s,
+ TYPE_BCM2838_RNG200, 0x28);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void bcm2838_rng200_reset(DeviceState *dev)
+{
+ BCM2838Rng200State *s = BCM2838_RNG200(dev);
+ bcm2838_rng200_rng_reset(s);
+}
+
+static Property bcm2838_rng200_properties[] = {
+ DEFINE_PROP_LINK("rng", BCM2838Rng200State, rng,
+ TYPE_RNG_BACKEND, RngBackend *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void bcm2838_rng200_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = bcm2838_rng200_realize;
+ dc->reset = bcm2838_rng200_reset;
+ dc->vmsd = &vmstate_bcm2838_rng200;
+
+ device_class_set_props(dc, bcm2838_rng200_properties);
+}
+
+static const TypeInfo bcm2838_rng200_info = {
+ .name = TYPE_BCM2838_RNG200,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(BCM2838Rng200State),
+ .class_init = bcm2838_rng200_class_init,
+ .instance_init = bcm2838_rng200_init,
+};
+
+static void bcm2838_rng200_register_types(void)
+{
+ type_register_static(&bcm2838_rng200_info);
+}
+
+type_init(bcm2838_rng200_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 36c20d5637..b899e6b596 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -91,6 +91,7 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files(
'bcm2835_thermal.c',
'bcm2835_cprman.c',
'bcm2835_powermgt.c',
+ 'bcm2838_rng200.c'
))
system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 05ff692441..5e5d3fe61a 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -330,3 +330,12 @@ djmemc_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRI
# iosb.c
iosb_read(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u"
iosb_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u"
+# bcm2838_rng200.c
+bcm2838_rng200_rng_soft_reset(void) "RNumG soft reset"
+bcm2838_rng200_rbg_soft_reset(void) "RBitG soft reset"
+bcm2838_rng200_enable_rbg(void) "RBitG enabled"
+bcm2838_rng200_disable_rbg(void) "RBitG disabled"
+bcm2838_rng200_update_fifo(uint32_t len, uint32_t fifo_len) "len %u, fifo_len %u"
+bcm2838_rng200_fifo_full(void) "RNumG FIFO full"
+bcm2838_rng200_write(uint64_t addr, uint64_t value, unsigned size) "addr: 0x%"PRIx64" value: 0x%016" PRIx64 " size: %u"
+bcm2838_rng200_read(uint64_t addr, unsigned size, uint64_t value) "addr: 0x%"PRIx64" size: %u value: 0x%016" PRIx64
diff --git a/include/hw/misc/bcm2838_rng200.h b/include/hw/misc/bcm2838_rng200.h
new file mode 100644
index 0000000000..c9c52f84be
--- /dev/null
+++ b/include/hw/misc/bcm2838_rng200.h
@@ -0,0 +1,51 @@
+/*
+ * BCM2838 Random Number Generator emulation
+ *
+ * Copyright (C) 2022 Sergey Pushkarev <sergey.pushkarev@auriga.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef BCM2838_RNG200_H
+#define BCM2838_RNG200_H
+
+#include <stdbool.h>
+#include "qom/object.h"
+#include "qemu/fifo8.h"
+#include "sysemu/rng.h"
+#include "hw/sysbus.h"
+#include "hw/ptimer.h"
+#include "hw/qdev-clock.h"
+#include "hw/irq.h"
+
+#define TYPE_BCM2838_RNG200 "bcm2838-rng200"
+OBJECT_DECLARE_SIMPLE_TYPE(BCM2838Rng200State, BCM2838_RNG200)
+
+typedef struct {
+ uint32_t ctrl;
+ uint32_t int_status;
+ uint32_t fifo_count;
+ uint32_t fifo_count_threshold;
+ uint32_t total_bit_count_threshold;
+} BCM2838_rng_regs_t;
+
+struct BCM2838Rng200State {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ ptimer_state *ptimer;
+ RngBackend *rng;
+ Clock *clock;
+
+ uint32_t rbg_period;
+ uint32_t rng_fifo_cap;
+ bool use_timer;
+
+ Fifo8 fifo;
+ qemu_irq irq;
+
+ BCM2838_rng_regs_t regs;
+};
+
+#endif /* BCM2838_RNG200_H */
--
2.34.1