virtio: rng: Add a random number generator(rng) driver

Add a driver for the virtio-rng device on the qemu platform. The
device uses pci as a transport medium. The driver can be enabled with
the following configs

CONFIG_VIRTIO
CONFIG_DM_RNG
CONFIG_VIRTIO_PCI
CONFIG_VIRTIO_RNG

Signed-off-by: Sughosh Ganu <sughosh.ganu@linaro.org>
diff --git a/drivers/virtio/virtio_rng.c b/drivers/virtio/virtio_rng.c
new file mode 100644
index 0000000..4edffa6
--- /dev/null
+++ b/drivers/virtio/virtio_rng.c
@@ -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,
+};