|
@@ -0,0 +1,88 @@
|
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
|
+/*
|
|
|
+ * Copyright (c) 2019, Linaro Limited
|
|
|
+ */
|
|
|
+
|
|
|
+#include <common.h>
|
|
|
+#include <dm.h>
|
|
|
+#include <rng.h>
|
|
|
+#include <virtio_types.h>
|
|
|
+#include <virtio.h>
|
|
|
+#include <virtio_ring.h>
|
|
|
+
|
|
|
+#define BUFFER_SIZE 16UL
|
|
|
+
|
|
|
+struct virtio_rng_priv {
|
|
|
+ struct virtqueue *rng_vq;
|
|
|
+};
|
|
|
+
|
|
|
+static int virtio_rng_read(struct udevice *dev, void *data, size_t len)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ unsigned int rsize;
|
|
|
+ unsigned char buf[BUFFER_SIZE] __aligned(4);
|
|
|
+ unsigned char *ptr = data;
|
|
|
+ struct virtio_sg sg;
|
|
|
+ struct virtio_sg *sgs[1];
|
|
|
+ struct virtio_rng_priv *priv = dev_get_priv(dev);
|
|
|
+
|
|
|
+ while (len) {
|
|
|
+ sg.addr = buf;
|
|
|
+ sg.length = min(len, sizeof(buf));
|
|
|
+ sgs[0] = &sg;
|
|
|
+
|
|
|
+ ret = virtqueue_add(priv->rng_vq, sgs, 0, 1);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ virtqueue_kick(priv->rng_vq);
|
|
|
+
|
|
|
+ while (!virtqueue_get_buf(priv->rng_vq, &rsize))
|
|
|
+ ;
|
|
|
+
|
|
|
+ memcpy(ptr, buf, rsize);
|
|
|
+ len -= rsize;
|
|
|
+ ptr += rsize;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int virtio_rng_bind(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
|
|
|
+
|
|
|
+ /* Indicate what driver features we support */
|
|
|
+ virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int virtio_rng_probe(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct virtio_rng_priv *priv = dev_get_priv(dev);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = virtio_find_vqs(dev, 1, &priv->rng_vq);
|
|
|
+ if (ret < 0) {
|
|
|
+ debug("%s: virtio_find_vqs failed\n", __func__);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct dm_rng_ops virtio_rng_ops = {
|
|
|
+ .read = virtio_rng_read,
|
|
|
+};
|
|
|
+
|
|
|
+U_BOOT_DRIVER(virtio_rng) = {
|
|
|
+ .name = VIRTIO_RNG_DRV_NAME,
|
|
|
+ .id = UCLASS_RNG,
|
|
|
+ .bind = virtio_rng_bind,
|
|
|
+ .probe = virtio_rng_probe,
|
|
|
+ .remove = virtio_reset,
|
|
|
+ .ops = &virtio_rng_ops,
|
|
|
+ .priv_auto_alloc_size = sizeof(struct virtio_rng_priv),
|
|
|
+ .flags = DM_FLAG_ACTIVE_DMA,
|
|
|
+};
|