diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/clk_stm32mp1.c | 1 | ||||
-rw-r--r-- | drivers/rng/Kconfig | 22 | ||||
-rw-r--r-- | drivers/rng/Makefile | 8 | ||||
-rw-r--r-- | drivers/rng/rng-uclass.c | 23 | ||||
-rw-r--r-- | drivers/rng/sandbox_rng.c | 56 | ||||
-rw-r--r-- | drivers/rng/stm32mp1_rng.c | 160 | ||||
-rw-r--r-- | drivers/virtio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/virtio/Makefile | 1 | ||||
-rw-r--r-- | drivers/virtio/virtio-uclass.c | 1 | ||||
-rw-r--r-- | drivers/virtio/virtio_rng.c | 88 |
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, ®, 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, +}; |