summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/clk/clk_stm32mp1.c1
-rw-r--r--drivers/rng/Kconfig22
-rw-r--r--drivers/rng/Makefile8
-rw-r--r--drivers/rng/rng-uclass.c23
-rw-r--r--drivers/rng/sandbox_rng.c56
-rw-r--r--drivers/rng/stm32mp1_rng.c160
-rw-r--r--drivers/virtio/Kconfig6
-rw-r--r--drivers/virtio/Makefile1
-rw-r--r--drivers/virtio/virtio-uclass.c1
-rw-r--r--drivers/virtio/virtio_rng.c88
12 files changed, 369 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 9d99ce0226..e34a22708c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -90,6 +90,8 @@ source "drivers/remoteproc/Kconfig"
source "drivers/reset/Kconfig"
+source "drivers/rng/Kconfig"
+
source "drivers/rtc/Kconfig"
source "drivers/scsi/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 44a08da602..e7b5d22b1d 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -117,4 +117,5 @@ obj-$(CONFIG_W1_EEPROM) += w1-eeprom/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock/
+obj-$(CONFIG_DM_RNG) += rng/
endif
diff --git a/drivers/clk/clk_stm32mp1.c b/drivers/clk/clk_stm32mp1.c
index 3718970dc7..da66bde41b 100644
--- a/drivers/clk/clk_stm32mp1.c
+++ b/drivers/clk/clk_stm32mp1.c
@@ -563,6 +563,7 @@ static const struct stm32mp1_clk_gate stm32mp1_clk_gate[] = {
STM32MP1_CLK_SET_CLR(RCC_MP_AHB4ENSETR, 10, GPIOK, _UNKNOWN_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_AHB5ENSETR, 0, GPIOZ, _UNKNOWN_SEL),
+ STM32MP1_CLK_SET_CLR(RCC_MP_AHB5ENSETR, 6, RNG1_K, _UNKNOWN_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 7, ETHCK_K, _ETH_SEL),
STM32MP1_CLK_SET_CLR(RCC_MP_AHB6ENSETR, 8, ETHTX, _UNKNOWN_SEL),
diff --git a/drivers/rng/Kconfig b/drivers/rng/Kconfig
new file mode 100644
index 0000000000..35a3bd192a
--- /dev/null
+++ b/drivers/rng/Kconfig
@@ -0,0 +1,22 @@
+config DM_RNG
+ bool "Driver support for Random Number Generator devices"
+ depends on DM
+ help
+ Enable driver model for random number generator(rng) devices.
+ This interface is used to initialise the rng device and to
+ read the random seed from the device.
+
+config RNG_SANDBOX
+ bool "Sandbox random number generator"
+ depends on SANDBOX && DM_RNG
+ select CONFIG_LIB_RAND
+ help
+ Enable random number generator for sandbox. This is an
+ emulation of a rng device.
+
+config RNG_STM32MP1
+ bool "Enable random number generator for STM32MP1"
+ depends on ARCH_STM32MP && DM_RNG
+ default n
+ help
+ Enable STM32MP1 rng driver.
diff --git a/drivers/rng/Makefile b/drivers/rng/Makefile
new file mode 100644
index 0000000000..3517005541
--- /dev/null
+++ b/drivers/rng/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2019, Linaro Limited
+#
+
+obj-$(CONFIG_DM_RNG) += rng-uclass.o
+obj-$(CONFIG_RNG_SANDBOX) += sandbox_rng.o
+obj-$(CONFIG_RNG_STM32MP1) += stm32mp1_rng.o
diff --git a/drivers/rng/rng-uclass.c b/drivers/rng/rng-uclass.c
new file mode 100644
index 0000000000..b6af3b8606
--- /dev/null
+++ b/drivers/rng/rng-uclass.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <rng.h>
+
+int dm_rng_read(struct udevice *dev, void *buffer, size_t size)
+{
+ const struct dm_rng_ops *ops = device_get_ops(dev);
+
+ if (!ops->read)
+ return -ENOSYS;
+
+ return ops->read(dev, buffer, size);
+}
+
+UCLASS_DRIVER(rng) = {
+ .name = "rng",
+ .id = UCLASS_RNG,
+};
diff --git a/drivers/rng/sandbox_rng.c b/drivers/rng/sandbox_rng.c
new file mode 100644
index 0000000000..cd0b0ac77b
--- /dev/null
+++ b/drivers/rng/sandbox_rng.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <rng.h>
+
+#include <linux/string.h>
+
+static int sandbox_rng_read(struct udevice *dev, void *data, size_t len)
+{
+ unsigned int i, seed, random;
+ unsigned char *buf = data;
+ size_t nrem, nloops;
+
+ if (!len)
+ return 0;
+
+ nloops = len / sizeof(random);
+ seed = get_timer(0) ^ rand();
+ srand(seed);
+
+ for (i = 0, nrem = len; i < nloops; i++) {
+ random = rand();
+ memcpy(buf, &random, sizeof(random));
+ buf += sizeof(random);
+ nrem -= sizeof(random);
+ }
+
+ if (nrem) {
+ random = rand();
+ memcpy(buf, &random, nrem);
+ }
+
+ return 0;
+}
+
+static const struct dm_rng_ops sandbox_rng_ops = {
+ .read = sandbox_rng_read,
+};
+
+static const struct udevice_id sandbox_rng_match[] = {
+ {
+ .compatible = "sandbox,sandbox-rng",
+ },
+ {},
+};
+
+U_BOOT_DRIVER(sandbox_rng) = {
+ .name = "sandbox-rng",
+ .id = UCLASS_RNG,
+ .of_match = sandbox_rng_match,
+ .ops = &sandbox_rng_ops,
+};
diff --git a/drivers/rng/stm32mp1_rng.c b/drivers/rng/stm32mp1_rng.c
new file mode 100644
index 0000000000..dab3b995eb
--- /dev/null
+++ b/drivers/rng/stm32mp1_rng.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <reset.h>
+#include <rng.h>
+
+#include <asm/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+
+#define RNG_CR 0x00
+#define RNG_CR_RNGEN BIT(2)
+#define RNG_CR_CED BIT(5)
+
+#define RNG_SR 0x04
+#define RNG_SR_SEIS BIT(6)
+#define RNG_SR_CEIS BIT(5)
+#define RNG_SR_SECS BIT(2)
+#define RNG_SR_DRDY BIT(0)
+
+#define RNG_DR 0x08
+
+struct stm32_rng_platdata {
+ fdt_addr_t base;
+ struct clk clk;
+ struct reset_ctl rst;
+};
+
+static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
+{
+ int retval = 0, i;
+ u32 sr, count, reg;
+ size_t increment;
+ struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
+
+ while (len > 0) {
+ retval = readl_poll_timeout(pdata->base + RNG_SR, sr,
+ sr & RNG_SR_DRDY, 10000);
+ if (retval)
+ return retval;
+
+ if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) {
+ /* As per SoC TRM */
+ clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS);
+ for (i = 0; i < 12; i++)
+ readl(pdata->base + RNG_DR);
+ if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) {
+ printf("RNG Noise");
+ return -EIO;
+ }
+ /* start again */
+ continue;
+ }
+
+ /*
+ * Once the DRDY bit is set, the RNG_DR register can
+ * be read four consecutive times.
+ */
+ count = 4;
+ while (len && count) {
+ reg = readl(pdata->base + RNG_DR);
+ memcpy(data, &reg, min(len, sizeof(u32)));
+ increment = min(len, sizeof(u32));
+ data += increment;
+ len -= increment;
+ count--;
+ }
+ }
+
+ return 0;
+}
+
+static int stm32_rng_init(struct stm32_rng_platdata *pdata)
+{
+ int err;
+
+ err = clk_enable(&pdata->clk);
+ if (err)
+ return err;
+
+ /* Disable CED */
+ writel(RNG_CR_RNGEN | RNG_CR_CED, pdata->base + RNG_CR);
+
+ /* clear error indicators */
+ writel(0, pdata->base + RNG_SR);
+
+ return 0;
+}
+
+static int stm32_rng_cleanup(struct stm32_rng_platdata *pdata)
+{
+ writel(0, pdata->base + RNG_CR);
+
+ return clk_disable(&pdata->clk);
+}
+
+static int stm32_rng_probe(struct udevice *dev)
+{
+ struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
+
+ reset_assert(&pdata->rst);
+ udelay(20);
+ reset_deassert(&pdata->rst);
+
+ return stm32_rng_init(pdata);
+}
+
+static int stm32_rng_remove(struct udevice *dev)
+{
+ struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
+
+ return stm32_rng_cleanup(pdata);
+}
+
+static int stm32_rng_ofdata_to_platdata(struct udevice *dev)
+{
+ struct stm32_rng_platdata *pdata = dev_get_platdata(dev);
+ int err;
+
+ pdata->base = dev_read_addr(dev);
+ if (!pdata->base)
+ return -ENOMEM;
+
+ err = clk_get_by_index(dev, 0, &pdata->clk);
+ if (err)
+ return err;
+
+ err = reset_get_by_index(dev, 0, &pdata->rst);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static const struct dm_rng_ops stm32_rng_ops = {
+ .read = stm32_rng_read,
+};
+
+static const struct udevice_id stm32_rng_match[] = {
+ {
+ .compatible = "st,stm32-rng",
+ },
+ {},
+};
+
+U_BOOT_DRIVER(stm32_rng) = {
+ .name = "stm32-rng",
+ .id = UCLASS_RNG,
+ .of_match = stm32_rng_match,
+ .ops = &stm32_rng_ops,
+ .probe = stm32_rng_probe,
+ .remove = stm32_rng_remove,
+ .platdata_auto_alloc_size = sizeof(struct stm32_rng_platdata),
+ .ofdata_to_platdata = stm32_rng_ofdata_to_platdata,
+};
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index a9d5fd07b7..2e3dd3bad0 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -59,4 +59,10 @@ config VIRTIO_BLK
This is the virtual block driver for virtio. It can be used with
QEMU based targets.
+config VIRTIO_RNG
+ bool "virtio rng driver"
+ depends on VIRTIO
+ help
+ This is the virtual random number generator driver. It can be used
+ with Qemu based targets.
endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 4579044ae3..dc8880937a 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci_legacy.o virtio_pci_modern.o
obj-$(CONFIG_VIRTIO_SANDBOX) += virtio_sandbox.o
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
+obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
index 34397d7dbb..436faa46ee 100644
--- a/drivers/virtio/virtio-uclass.c
+++ b/drivers/virtio/virtio-uclass.c
@@ -24,6 +24,7 @@
static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
[VIRTIO_ID_NET] = VIRTIO_NET_DRV_NAME,
[VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME,
+ [VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME,
};
int virtio_get_config(struct udevice *vdev, unsigned int offset,
diff --git a/drivers/virtio/virtio_rng.c b/drivers/virtio/virtio_rng.c
new file mode 100644
index 0000000000..4edffa6f31
--- /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,
+};