diff --git a/hw/misc/bcm2838_rng200.c b/hw/misc/bcm2838_rng200.c
index a17e8f2cda..bfc40658e2 100644
--- a/hw/misc/bcm2838_rng200.c
+++ b/hw/misc/bcm2838_rng200.c
@@ -8,23 +8,194 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/log.h"
 #include "qapi/error.h"
 #include "hw/qdev-properties.h"
 #include "hw/misc/bcm2838_rng200.h"
 #include "trace.h"
 
+#define RNG_CTRL_OFFSET                      0x00
+#define RNG_SOFT_RESET                       0x01
+#define RNG_SOFT_RESET_OFFSET                0x04
+#define RBG_SOFT_RESET_OFFSET                0x08
+#define RNG_TOTAL_BIT_COUNT_OFFSET           0x0C
+#define RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET 0x10
+#define RNG_INT_STATUS_OFFSET                0x18
+#define RNG_INT_ENABLE_OFFSET                0x1C
+#define RNG_FIFO_DATA_OFFSET                 0x20
+#define RNG_FIFO_COUNT_OFFSET                0x24
+
+#define RNG_WARM_UP_PERIOD_ELAPSED           17
+
+#define BCM2838_RNG200_PTIMER_POLICY         (PTIMER_POLICY_CONTINUOUS_TRIGGER)
+
+static void bcm2838_rng200_update_irq(BCM2838Rng200State *state)
+{
+    qemu_set_irq(state->irq, !!(state->rng_int_enable.value
+                              & state->rng_int_status.value));
+}
+
+static void bcm2838_rng200_update_fifo(void *opaque, const void *buf,
+                                       size_t size)
+{
+    BCM2838Rng200State *state = (BCM2838Rng200State *)opaque;
+    Fifo8 *fifo = &state->fifo;
+    size_t num = MIN(size, fifo8_num_free(fifo));
+    uint32_t num_bits = num * 8;
+    uint32_t bit_threshold_left = 0;
+
+    state->rng_total_bit_count += num_bits;
+    if (state->rng_bit_count_threshold > state->rng_total_bit_count) {
+        bit_threshold_left =
+            state->rng_bit_count_threshold - state->rng_total_bit_count;
+    } else {
+        bit_threshold_left = 0;
+    }
+
+    if (bit_threshold_left < num_bits) {
+        num_bits -= bit_threshold_left;
+    } else {
+        num_bits = 0;
+    }
+
+    num = num_bits / 8;
+    if ((num == 0) && (num_bits > 0)) {
+        num = 1;
+    }
+    if (num > 0) {
+        fifo8_push_all(fifo, buf, num);
+
+        if (fifo8_num_used(fifo) > state->rng_fifo_count.thld) {
+            state->rng_int_status.total_bits_count_irq = 1;
+        }
+    }
+
+    state->rng_fifo_count.count = fifo8_num_used(fifo) >> 2;
+    bcm2838_rng200_update_irq(state);
+    trace_bcm2838_rng200_update_fifo(num, fifo8_num_used(fifo));
+}
+
+static void bcm2838_rng200_fill_fifo(BCM2838Rng200State *state)
+{
+    rng_backend_request_entropy(state->rng,
+                                fifo8_num_free(&state->fifo),
+                                bcm2838_rng200_update_fifo, state);
+}
+
+/* state is temporary unused */
+static void bcm2838_rng200_disable_rbg(BCM2838Rng200State *state
+                                       __attribute__((unused)))
+{
+    trace_bcm2838_rng200_disable_rbg();
+}
+
+static void bcm2838_rng200_enable_rbg(BCM2838Rng200State *state)
+{
+    state->rng_total_bit_count = RNG_WARM_UP_PERIOD_ELAPSED;
+
+    bcm2838_rng200_fill_fifo(state);
+
+    trace_bcm2838_rng200_enable_rbg();
+}
+
 static void bcm2838_rng200_rng_reset(BCM2838Rng200State *state)
 {
     state->rng_ctrl.value = 0;
+    state->rng_total_bit_count = 0;
+    state->rng_bit_count_threshold = 0;
+    state->rng_fifo_count.value = 0;
+    state->rng_int_status.value = 0;
+    state->rng_int_status.startup_transition_met_irq = 1;
+    state->rng_int_enable.value = 0;
+    fifo8_reset(&state->fifo);
 
     trace_bcm2838_rng200_rng_soft_reset();
 }
 
+static void bcm2838_rng200_rbg_reset(BCM2838Rng200State *state)
+{
+    trace_bcm2838_rng200_rbg_soft_reset();
+}
+
+static uint32_t bcm2838_rng200_read_fifo_data(BCM2838Rng200State *state)
+{
+    Fifo8 *fifo = &state->fifo;
+    const uint8_t *buf;
+    uint32_t ret = 0;
+    uint32_t num = 0;
+    uint32_t max = MIN(fifo8_num_used(fifo), sizeof(ret));
+
+    if (max > 0) {
+        buf = fifo8_pop_buf(fifo, max, &num);
+        if ((buf != NULL) && (num > 0)) {
+            memcpy(&ret, buf, num);
+        }
+    } else {
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "bcm2838_rng200_read_fifo_data: FIFO is empty\n"
+        );
+    }
+
+    state->rng_fifo_count.count = fifo8_num_used(fifo) >> 2;
+    bcm2838_rng200_fill_fifo(state);
+
+    return ret;
+}
+
+static void bcm2838_rng200_ctrl_write(BCM2838Rng200State *s, uint64_t value)
+{
+    bool rng_enable = s->rng_ctrl.rbg_enable;
+
+    s->rng_ctrl.value = value;
+    if (!s->rng_ctrl.rbg_enable && rng_enable) {
+        bcm2838_rng200_disable_rbg(s);
+    } else if (s->rng_ctrl.rbg_enable && !rng_enable) {
+        bcm2838_rng200_enable_rbg(s);
+    }
+}
+
 static uint64_t bcm2838_rng200_read(void *opaque, hwaddr offset,
                                     unsigned size)
 {
+    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
     uint32_t res = 0;
 
+    switch (offset) {
+    case RNG_CTRL_OFFSET:
+        res = s->rng_ctrl.value;
+        break;
+    case RNG_SOFT_RESET_OFFSET:
+    case RBG_SOFT_RESET_OFFSET:
+        break;
+    case RNG_INT_STATUS_OFFSET:
+        res = s->rng_int_status.value;
+        break;
+    case RNG_INT_ENABLE_OFFSET:
+        res = s->rng_int_enable.value;
+        break;
+    case RNG_FIFO_DATA_OFFSET:
+        res = bcm2838_rng200_read_fifo_data(s);
+        break;
+    case RNG_FIFO_COUNT_OFFSET:
+        res = s->rng_fifo_count.value;
+        break;
+    case RNG_TOTAL_BIT_COUNT_OFFSET:
+        res = s->rng_total_bit_count;
+        break;
+    case RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET:
+        res = s->rng_bit_count_threshold;
+        break;
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "bcm2838_rng200_read: Bad offset 0x%" HWADDR_PRIx "\n",
+            offset
+        );
+        res = 0;
+        break;
+    }
+
     trace_bcm2838_rng200_read((void *)offset, size, res);
     return res;
 }
@@ -32,8 +203,50 @@ static uint64_t bcm2838_rng200_read(void *opaque, hwaddr offset,
 static void bcm2838_rng200_write(void *opaque, hwaddr offset,
                                  uint64_t value, unsigned size)
 {
+    BCM2838Rng200State *s = (BCM2838Rng200State *)opaque;
 
     trace_bcm2838_rng200_write((void *)offset, value, size);
+
+    switch (offset) {
+    case RNG_CTRL_OFFSET:
+        bcm2838_rng200_ctrl_write(s, value);
+        break;
+    case RNG_SOFT_RESET_OFFSET:
+        if (value & RNG_SOFT_RESET) {
+            bcm2838_rng200_rng_reset(s);
+        }
+        break;
+    case RBG_SOFT_RESET_OFFSET:
+        if (value & RNG_SOFT_RESET) {
+            bcm2838_rng200_rbg_reset(s);
+        }
+        break;
+    case RNG_INT_STATUS_OFFSET:
+        s->rng_int_status.value &= ~value;
+        bcm2838_rng200_update_irq(s);
+        break;
+    case RNG_INT_ENABLE_OFFSET:
+        s->rng_int_enable.value = value;
+        bcm2838_rng200_update_irq(s);
+        break;
+    case RNG_FIFO_COUNT_OFFSET:
+        {
+            BCM2838Rng200FifoCount tmp = {.value = value};
+            s->rng_fifo_count.thld = tmp.thld;
+        }
+        break;
+    case RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET:
+        s->rng_bit_count_threshold = value;
+        s->rng_total_bit_count = value + 1;
+        break;
+    default:
+        qemu_log_mask(
+            LOG_GUEST_ERROR,
+            "bcm2838_rng200_write: Bad offset 0x%" HWADDR_PRIx "\n",
+            offset
+        );
+        break;
+    }
 }
 
 static const MemoryRegionOps bcm2838_rng200_ops = {
@@ -57,6 +270,7 @@ static void bcm2838_rng200_realize(DeviceState *dev, Error **errp)
                                  errp);
     }
 
+    fifo8_create(&s->fifo, s->rng_fifo_cap);
     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
 }
 
@@ -81,6 +295,8 @@ static void bcm2838_rng200_init(Object *obj)
 static void bcm2838_rng200_reset(DeviceState *dev)
 {
     BCM2838Rng200State *s = BCM2838_RNG200(dev);
+
+    bcm2838_rng200_rbg_reset(s);
     bcm2838_rng200_rng_reset(s);
 }
 
@@ -89,7 +305,7 @@ static Property bcm2838_rng200_properties[] = {
     DEFINE_PROP_UINT32("rng-fifo-cap", BCM2838Rng200State, rng_fifo_cap, 128),
     DEFINE_PROP_LINK("rng", BCM2838Rng200State, rng,
                      TYPE_RNG_BACKEND, RngBackend *),
-    DEFINE_PROP_BOOL("use-timer", BCM2838Rng200State, use_timer, true),
+    DEFINE_PROP_BOOL("use-timer", BCM2838Rng200State, use_timer, false),
     DEFINE_PROP_END_OF_LIST(),
 };
 
