summaryrefslogtreecommitdiffstats
path: root/drivers/hw_random
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hw_random')
-rw-r--r--drivers/hw_random/Kconfig63
-rw-r--r--drivers/hw_random/Makefile9
-rw-r--r--drivers/hw_random/atmel-rng.c165
-rw-r--r--drivers/hw_random/bcm2835-rng.c199
-rw-r--r--drivers/hw_random/core.c19
-rw-r--r--drivers/hw_random/dev-random.c6
-rw-r--r--drivers/hw_random/efi-rng.c53
-rw-r--r--drivers/hw_random/iproc-rng200.c220
-rw-r--r--drivers/hw_random/mxc-rngc.c23
-rw-r--r--drivers/hw_random/omap-rng.c436
-rw-r--r--drivers/hw_random/optee-rng.c302
-rw-r--r--drivers/hw_random/rockchip-rng.c259
-rw-r--r--drivers/hw_random/starfive-vic-rng.c9
-rw-r--r--drivers/hw_random/stm32-rng.c15
-rw-r--r--drivers/hw_random/timeriomem-rng.c145
15 files changed, 1877 insertions, 46 deletions
diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig
index 764911f4d3..763929f7d6 100644
--- a/drivers/hw_random/Kconfig
+++ b/drivers/hw_random/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
menuconfig HWRNG
bool "HWRNG Support"
help
@@ -7,16 +8,23 @@ menuconfig HWRNG
if HWRNG
+config HW_RANDOM_TIMERIOMEM
+ tristate "Timer IOMEM HW Random Number Generator support"
+ help
+ This driver provides barebox support for a generic Random
+ Number Generator used by reading a 'dumb' iomem address that
+ is to be read no faster than, for example, once a second.
+
config HWRNG_MXC_RNGC
tristate "Freescale i.MX RNGC Random Number Generator"
depends on ARCH_IMX25 || ARCH_IMX35 || ARCH_IMX53 || COMPILE_TEST
help
- This driver provides kernel-side support for the Random Number
+ This driver provides barebox support for the Random Number
Generator hardware found on some Freescale i.MX processors.
config HWRNG_STM32
tristate "STM32 Random Number Generator"
- depends on ARCH_STM32MP || COMPILE_TEST
+ depends on ARCH_STM32 || COMPILE_TEST
help
This driver provides barebox support for the Random Number
Generator hardware found on the STM32 family of MPUs and MCUs.
@@ -43,4 +51,55 @@ config HW_RANDOM_STARFIVE
This driver provides barebox support for the Random Number
Generator hardware found on the StarFive family of SoCs.
+config HW_RANDOM_EFI
+ tristate "EFI Random Number Generator"
+ depends on EFI
+ help
+ This driver provides barebox support for the Random Number
+ Generator Protocol offered by EFI firmware
+
+config HW_RANDOM_OPTEE
+ tristate "OP-TEE based Random Number Generator support"
+ depends on OPTEE
+ help
+ This driver provides support for OP-TEE based Random Number
+ Generator on ARM SoCs where hardware entropy sources are not
+ accessible to normal world (barebox and e.g. Linux after it).
+
+config HW_RANDOM_ATMEL
+ tristate "Atmel Random Number Generator support"
+ depends on ARCH_AT91 || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on Atmel AT91 devices.
+
+config HW_RANDOM_BCM2835
+ tristate "Broadcom BCM2835/BCM63xx Random Number Generator support"
+ depends on ARCH_BCM283X || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on the Broadcom BCM2835 SoCs.
+
+config HW_RANDOM_IPROC_RNG200
+ tristate "Broadcom iProc/STB RNG200 support"
+ depends on ARCH_BCM283X || COMPILE_TEST
+ help
+ This driver provides barebox support for the RNG200
+ hardware found on the BCM2711.
+
+config HW_RANDOM_ROCKCHIP
+ tristate "Rockchip Random Number Generator support"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on Rockchip cpus.
+
+config HW_RANDOM_OMAP
+ tristate "OMAP Random Number Generator support"
+ depends on ARCH_OMAP || ARCH_K3 || COMPILE_TEST
+ help
+ This driver provides barebox support for the Random Number
+ Generator hardware found on OMAP2/3/4/5, AM33xx/AM43xx
+ multimedia processors, and Marvell Armada 7k/8k SoCs.
+
endif
diff --git a/drivers/hw_random/Makefile b/drivers/hw_random/Makefile
index 4cf33d2d93..7f65a6c41e 100644
--- a/drivers/hw_random/Makefile
+++ b/drivers/hw_random/Makefile
@@ -1,6 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_HWRNG) += core.o
obj-$(CONFIG_HWRNG_MXC_RNGC) += mxc-rngc.o
obj-$(CONFIG_HWRNG_STM32) += stm32-rng.o
obj-$(CONFIG_HWRNG_DEV_RANDOM) += dev-random.o
obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o
obj-$(CONFIG_HW_RANDOM_STARFIVE) += starfive-vic-rng.o
+obj-$(CONFIG_HW_RANDOM_EFI) += efi-rng.o
+obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o
+obj-$(CONFIG_HW_RANDOM_ATMEL) += atmel-rng.o
+obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
+obj-$(CONFIG_HW_RANDOM_ROCKCHIP) += rockchip-rng.o
+obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
+obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
diff --git a/drivers/hw_random/atmel-rng.c b/drivers/hw_random/atmel-rng.c
new file mode 100644
index 0000000000..bdd2139b08
--- /dev/null
+++ b/drivers/hw_random/atmel-rng.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2011 Peter Korsgaard <jacmet@sunsite.dk>
+
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/hw_random.h>
+#include <of.h>
+#include <linux/device.h>
+
+#define TRNG_CR 0x00
+#define TRNG_MR 0x04
+#define TRNG_ISR 0x1c
+#define TRNG_ISR_DATRDY BIT(0)
+#define TRNG_ODATA 0x50
+
+#define TRNG_KEY 0x524e4700 /* RNG */
+
+#define TRNG_HALFR BIT(0) /* generate RN every 168 cycles */
+
+struct atmel_trng_data {
+ bool has_half_rate;
+};
+
+struct atmel_trng {
+ struct clk *clk;
+ void __iomem *base;
+ struct hwrng rng;
+ bool has_half_rate;
+};
+
+static bool atmel_trng_wait_ready(struct atmel_trng *trng, bool wait)
+{
+ int ready;
+
+ ready = readl(trng->base + TRNG_ISR) & TRNG_ISR_DATRDY;
+ if (!ready && wait)
+ readl_poll_timeout(trng->base + TRNG_ISR, ready,
+ ready & TRNG_ISR_DATRDY, 20000);
+
+ return !!ready;
+}
+
+static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
+ u32 *data = buf;
+ int ret;
+
+ ret = atmel_trng_wait_ready(trng, wait);
+ if (!ret)
+ return 0;
+
+ *data = readl(trng->base + TRNG_ODATA);
+ /*
+ * ensure data ready is only set again AFTER the next data word is ready
+ * in case it got set between checking ISR and reading ODATA, so we
+ * don't risk re-reading the same word
+ */
+ readl(trng->base + TRNG_ISR);
+ ret = 4;
+
+ return ret;
+}
+
+static int atmel_trng_init(struct hwrng *rng)
+{
+ struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
+ unsigned long rate;
+ int ret;
+
+ ret = clk_prepare_enable(trng->clk);
+ if (ret)
+ return ret;
+
+ if (trng->has_half_rate) {
+ rate = clk_get_rate(trng->clk);
+
+ /* if peripheral clk is above 100MHz, set HALFR */
+ if (rate > 100000000)
+ writel(TRNG_HALFR, trng->base + TRNG_MR);
+ }
+
+ writel(TRNG_KEY | 1, trng->base + TRNG_CR);
+
+ return 0;
+}
+
+static void atmel_trng_cleanup(struct atmel_trng *trng)
+{
+ writel(TRNG_KEY, trng->base + TRNG_CR);
+ clk_disable_unprepare(trng->clk);
+}
+
+static int atmel_trng_probe(struct device *dev)
+{
+ struct atmel_trng *trng;
+ const struct atmel_trng_data *data;
+
+ trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);
+ if (!trng)
+ return -ENOMEM;
+
+ trng->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(trng->base))
+ return PTR_ERR(trng->base);
+
+ trng->clk = clk_get(dev, NULL);
+ if (IS_ERR(trng->clk))
+ return PTR_ERR(trng->clk);
+ data = device_get_match_data(dev);
+ if (!data)
+ return -ENODEV;
+
+ trng->has_half_rate = data->has_half_rate;
+ trng->rng.name = dev_name(dev);
+ trng->rng.read = atmel_trng_read;
+ trng->rng.init = atmel_trng_init;
+ dev->priv = trng;
+
+ return hwrng_register(dev, &trng->rng);
+}
+
+static void atmel_trng_remove(struct device *dev)
+{
+ atmel_trng_cleanup(dev->priv);
+}
+
+static const struct atmel_trng_data at91sam9g45_config = {
+ .has_half_rate = false,
+};
+
+static const struct atmel_trng_data sam9x60_config = {
+ .has_half_rate = true,
+};
+
+static const struct of_device_id atmel_trng_dt_ids[] = {
+ {
+ .compatible = "atmel,at91sam9g45-trng",
+ .data = &at91sam9g45_config,
+ }, {
+ .compatible = "microchip,sam9x60-trng",
+ .data = &sam9x60_config,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
+
+static struct driver atmel_trng_driver = {
+ .name = "atmel-trng",
+ .probe = atmel_trng_probe,
+ .remove = atmel_trng_remove,
+ .of_match_table = atmel_trng_dt_ids,
+};
+device_platform_driver(atmel_trng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Atmel true random number generator driver");
diff --git a/drivers/hw_random/bcm2835-rng.c b/drivers/hw_random/bcm2835-rng.c
new file mode 100644
index 0000000000..d82331c950
--- /dev/null
+++ b/drivers/hw_random/bcm2835-rng.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2010-2012 Broadcom. All rights reserved.
+ * Copyright (c) 2013 Lubomir Rintel
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/reset.h>
+#include <of.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+
+#define RNG_CTRL 0x0
+#define RNG_STATUS 0x4
+#define RNG_DATA 0x8
+#define RNG_INT_MASK 0x10
+
+/* enable rng */
+#define RNG_RBGEN 0x1
+
+/* the initial numbers generated are "less random" so will be discarded */
+#define RNG_WARMUP_COUNT 0x40000
+
+#define RNG_INT_OFF 0x1
+
+struct bcm2835_rng_priv {
+ struct hwrng rng;
+ void __iomem *base;
+ bool mask_interrupts;
+ struct clk *clk;
+ struct reset_control *reset;
+};
+
+static inline struct bcm2835_rng_priv *to_rng_priv(struct hwrng *rng)
+{
+ return container_of(rng, struct bcm2835_rng_priv, rng);
+}
+
+static inline u32 rng_readl(struct bcm2835_rng_priv *priv, u32 offset)
+{
+ /* MIPS chips strapped for BE will automagically configure the
+ * peripheral registers for CPU-native byte order.
+ */
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ return __raw_readl(priv->base + offset);
+ else
+ return readl(priv->base + offset);
+}
+
+static inline void rng_writel(struct bcm2835_rng_priv *priv, u32 val,
+ u32 offset)
+{
+ if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ __raw_writel(val, priv->base + offset);
+ else
+ writel(val, priv->base + offset);
+}
+
+static int bcm2835_rng_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct bcm2835_rng_priv *priv = to_rng_priv(rng);
+ u32 max_words = max / sizeof(u32);
+ u32 num_words, count;
+
+ while ((rng_readl(priv, RNG_STATUS) >> 24) == 0) {
+ if (!wait)
+ return 0;
+ hwrng_yield(rng);
+ }
+
+ num_words = rng_readl(priv, RNG_STATUS) >> 24;
+ if (num_words > max_words)
+ num_words = max_words;
+
+ for (count = 0; count < num_words; count++)
+ ((u32 *)buf)[count] = rng_readl(priv, RNG_DATA);
+
+ return num_words * sizeof(u32);
+}
+
+static int bcm2835_rng_init(struct hwrng *rng)
+{
+ struct bcm2835_rng_priv *priv = to_rng_priv(rng);
+ int ret = 0;
+ u32 val;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_control_reset(priv->reset);
+ if (ret)
+ return ret;
+
+ if (priv->mask_interrupts) {
+ /* mask the interrupt */
+ val = rng_readl(priv, RNG_INT_MASK);
+ val |= RNG_INT_OFF;
+ rng_writel(priv, val, RNG_INT_MASK);
+ }
+
+ /* set warm-up count & enable */
+ rng_writel(priv, RNG_WARMUP_COUNT, RNG_STATUS);
+ rng_writel(priv, RNG_RBGEN, RNG_CTRL);
+
+ return ret;
+}
+
+static void bcm2835_rng_cleanup(struct bcm2835_rng_priv *priv)
+{
+ /* disable rng hardware */
+ rng_writel(priv, 0, RNG_CTRL);
+
+ clk_disable_unprepare(priv->clk);
+}
+
+struct bcm2835_rng_of_data {
+ bool mask_interrupts;
+};
+
+static const struct bcm2835_rng_of_data nsp_rng_of_data = {
+ .mask_interrupts = true,
+};
+
+static const struct of_device_id bcm2835_rng_of_match[] = {
+ { .compatible = "brcm,bcm2835-rng"},
+ { .compatible = "brcm,bcm-nsp-rng", .data = &nsp_rng_of_data },
+ { .compatible = "brcm,bcm5301x-rng", .data = &nsp_rng_of_data },
+ { .compatible = "brcm,bcm6368-rng"},
+ {},
+};
+
+static int bcm2835_rng_probe(struct device *dev)
+{
+ const struct bcm2835_rng_of_data *of_data;
+ struct bcm2835_rng_priv *priv;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* map peripheral */
+ priv->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ /* Clock is optional on most platforms */
+ priv->clk = clk_get_optional(dev, NULL);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->reset = reset_control_get_optional(dev, NULL);
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+
+ priv->rng.name = dev_name(dev);
+ priv->rng.init = bcm2835_rng_init;
+ priv->rng.read = bcm2835_rng_read;
+
+ of_data = device_get_match_data(dev);
+ if (of_data)
+ priv->mask_interrupts = of_data->mask_interrupts;
+
+ /* register driver */
+ err = hwrng_register(dev, &priv->rng);
+ if (err)
+ dev_err(dev, "hwrng registration failed\n");
+ else
+ dev_info(dev, "hwrng registered\n");
+
+ dev->priv = priv;
+
+ return err;
+}
+
+static void bcm2835_rng_remove(struct device *dev)
+{
+ bcm2835_rng_cleanup(dev->priv);
+}
+
+MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match);
+
+static struct driver bcm2835_rng_driver = {
+ .name = "bcm2835-rng",
+ .of_match_table = bcm2835_rng_of_match,
+ .probe = bcm2835_rng_probe,
+ .remove = bcm2835_rng_remove,
+};
+device_platform_driver(bcm2835_rng_driver);
+
+MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
+MODULE_DESCRIPTION("BCM2835 Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hw_random/core.c b/drivers/hw_random/core.c
index 86214dc8ba..7bc3c33319 100644
--- a/drivers/hw_random/core.c
+++ b/drivers/hw_random/core.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
* derived from Linux kernel drivers/char/hw_random/core.c
*/
@@ -47,10 +44,10 @@ static ssize_t rng_dev_read(struct cdev *cdev, void *buf, size_t size,
while (count) {
int max = min(count, (size_t)RNG_BUFFER_SIZE);
len = hwrng_get_data(rng, rng->buf, max, true);
- if (len < 0) {
- cur = len;
- break;
- }
+ if (len < 0)
+ return len;
+ if (!len && ctrlc())
+ return cur;
memcpy(buf + cur, rng->buf, len);
@@ -67,12 +64,12 @@ static struct cdev_operations rng_chrdev_ops = {
static int hwrng_register_cdev(struct hwrng *rng)
{
- struct device_d *dev = rng->dev;
+ struct device *dev = rng->dev;
const char *alias;
char *devname;
int err;
- alias = of_alias_get(dev->device_node);
+ alias = of_alias_get(dev->of_node);
if (alias) {
devname = xstrdup(alias);
} else {
@@ -106,7 +103,7 @@ struct hwrng *hwrng_get_first(void)
return list_first_entry(&hwrngs, struct hwrng, list);
}
-int hwrng_register(struct device_d *dev, struct hwrng *rng)
+int hwrng_register(struct device *dev, struct hwrng *rng)
{
int err;
diff --git a/drivers/hw_random/dev-random.c b/drivers/hw_random/dev-random.c
index 2170db7437..52f4847e4a 100644
--- a/drivers/hw_random/dev-random.c
+++ b/drivers/hw_random/dev-random.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2019 Ahmad Fatoum, Pengutronix
*/
@@ -33,7 +33,7 @@ static int devrandom_rnd_init(struct hwrng *hwrng)
return 0;
}
-static int devrandom_rnd_probe(struct device_d *dev)
+static int devrandom_rnd_probe(struct device *dev)
{
struct devrandom_rnd *rnd;
int ret;
@@ -56,7 +56,7 @@ static int devrandom_rnd_probe(struct device_d *dev)
return 0;
}
-static struct driver_d devrandom_rnd_driver = {
+static struct driver devrandom_rnd_driver = {
.name = "devrandom",
.probe = devrandom_rnd_probe,
};
diff --git a/drivers/hw_random/efi-rng.c b/drivers/hw_random/efi-rng.c
new file mode 100644
index 0000000000..61cb01caf6
--- /dev/null
+++ b/drivers/hw_random/efi-rng.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <linux/hw_random.h>
+#include <efi.h>
+#include <efi/efi-device.h>
+
+struct efi_rng_priv {
+ struct efi_rng_protocol *protocol;
+ struct hwrng hwrng;
+};
+
+static inline struct efi_rng_priv *to_efi_rng(struct hwrng *hwrng)
+{
+ return container_of(hwrng, struct efi_rng_priv, hwrng);
+}
+
+static int efi_rng_read(struct hwrng *hwrng, void *data, size_t len, bool wait)
+{
+ struct efi_rng_protocol *protocol = to_efi_rng(hwrng)->protocol;
+ efi_status_t efiret;
+
+ efiret = protocol->get_rng(protocol, NULL, len, data);
+
+ return -efi_errno(efiret) ?: len;
+}
+
+static int efi_rng_probe(struct efi_device *efidev)
+{
+ struct efi_rng_priv *priv;
+
+ priv = xzalloc(sizeof(*priv));
+
+ BS->handle_protocol(efidev->handle, &efi_rng_protocol_guid,
+ (void **)&priv->protocol);
+ if (!priv->protocol)
+ return -ENODEV;
+
+ priv->hwrng.name = dev_name(&efidev->dev);
+ priv->hwrng.read = efi_rng_read;
+
+ return hwrng_register(&efidev->dev, &priv->hwrng);
+}
+
+static struct efi_driver efi_rng_driver = {
+ .driver = {
+ .name = "efi-rng",
+ },
+ .probe = efi_rng_probe,
+ .guid = EFI_RNG_PROTOCOL_GUID,
+};
+device_efi_driver(efi_rng_driver);
diff --git a/drivers/hw_random/iproc-rng200.c b/drivers/hw_random/iproc-rng200.c
new file mode 100644
index 0000000000..4cb3573a7d
--- /dev/null
+++ b/drivers/hw_random/iproc-rng200.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+*/
+/*
+ * DESCRIPTION: The Broadcom iProc RNG200 Driver
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/device.h>
+#include <clock.h>
+
+/* Registers */
+#define RNG_CTRL_OFFSET 0x00
+#define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF
+#define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001
+
+#define RNG_SOFT_RESET_OFFSET 0x04
+#define RNG_SOFT_RESET 0x00000001
+
+#define RBG_SOFT_RESET_OFFSET 0x08
+#define RBG_SOFT_RESET 0x00000001
+
+#define RNG_INT_STATUS_OFFSET 0x18
+#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000
+#define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000
+#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020
+#define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001
+
+#define RNG_FIFO_DATA_OFFSET 0x20
+
+#define RNG_FIFO_COUNT_OFFSET 0x24
+#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF
+
+struct iproc_rng200_dev {
+ struct hwrng rng;
+ void __iomem *base;
+};
+
+#define to_rng_priv(rng) container_of(rng, struct iproc_rng200_dev, rng)
+
+static void iproc_rng200_enable_set(void __iomem *rng_base, bool enable)
+{
+ u32 val;
+
+ val = ioread32(rng_base + RNG_CTRL_OFFSET);
+ val &= ~RNG_CTRL_RNG_RBGEN_MASK;
+
+ if (enable)
+ val |= RNG_CTRL_RNG_RBGEN_ENABLE;
+
+ iowrite32(val, rng_base + RNG_CTRL_OFFSET);
+}
+
+static void iproc_rng200_restart(void __iomem *rng_base)
+{
+ uint32_t val;
+
+ iproc_rng200_enable_set(rng_base, false);
+
+ /* Clear all interrupt status */
+ iowrite32(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET);
+
+ /* Reset RNG and RBG */
+ val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);
+ val |= RBG_SOFT_RESET;
+ iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);
+
+ val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);
+ val |= RNG_SOFT_RESET;
+ iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);
+
+ val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET);
+ val &= ~RNG_SOFT_RESET;
+ iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET);
+
+ val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET);
+ val &= ~RBG_SOFT_RESET;
+ iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET);
+
+ iproc_rng200_enable_set(rng_base, true);
+}
+
+static int iproc_rng200_read(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct iproc_rng200_dev *priv = to_rng_priv(rng);
+ uint32_t num_remaining = max;
+ uint32_t status;
+ u64 start;
+
+ #define MAX_RESETS_PER_READ 1
+ uint32_t num_resets = 0;
+
+ #define MAX_IDLE_TIME_NS (NSEC_PER_SEC)
+
+ start = get_time_ns();
+
+ while ((num_remaining > 0) && !is_timeout(start, MAX_IDLE_TIME_NS)) {
+
+ /* Is RNG sane? If not, reset it. */
+ status = ioread32(priv->base + RNG_INT_STATUS_OFFSET);
+ if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK |
+ RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) {
+
+ if (num_resets >= MAX_RESETS_PER_READ)
+ return max - num_remaining;
+
+ iproc_rng200_restart(priv->base);
+ num_resets++;
+ }
+
+ /* Are there any random numbers available? */
+ if ((ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
+ RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) {
+
+ if (num_remaining >= sizeof(uint32_t)) {
+ /* Buffer has room to store entire word */
+ *(uint32_t *)buf = ioread32(priv->base +
+ RNG_FIFO_DATA_OFFSET);
+ buf += sizeof(uint32_t);
+ num_remaining -= sizeof(uint32_t);
+ } else {
+ /* Buffer can only store partial word */
+ uint32_t rnd_number = ioread32(priv->base +
+ RNG_FIFO_DATA_OFFSET);
+ memcpy(buf, &rnd_number, num_remaining);
+ buf += num_remaining;
+ num_remaining = 0;
+ }
+
+ /* Reset the IDLE timeout */
+ start = get_time_ns();
+ } else {
+ if (!wait)
+ /* Cannot wait, return immediately */
+ return max - num_remaining;
+ }
+ }
+
+ return max - num_remaining;
+}
+
+static int iproc_rng200_init(struct hwrng *rng)
+{
+ struct iproc_rng200_dev *priv = to_rng_priv(rng);
+
+ iproc_rng200_enable_set(priv->base, true);
+
+ return 0;
+}
+
+static void iproc_rng200_cleanup(struct iproc_rng200_dev *priv)
+{
+ iproc_rng200_enable_set(priv->base, false);
+}
+
+static int iproc_rng200_probe(struct device *dev)
+{
+ struct iproc_rng200_dev *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Map peripheral */
+ priv->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(priv->base)) {
+ dev_err(dev, "failed to remap rng regs\n");
+ return PTR_ERR(priv->base);
+ }
+
+ dev->priv = priv;
+
+ priv->rng.name = "iproc-rng200";
+ priv->rng.read = iproc_rng200_read;
+ priv->rng.init = iproc_rng200_init;
+
+ /* Register driver */
+ ret = hwrng_register(dev, &priv->rng);
+ if (ret) {
+ dev_err(dev, "hwrng registration failed\n");
+ return ret;
+ }
+
+ dev_info(dev, "hwrng registered\n");
+
+ return 0;
+}
+
+static void iproc_rng200_remove(struct device *dev)
+{
+ iproc_rng200_cleanup(dev->priv);
+}
+
+static const struct of_device_id iproc_rng200_of_match[] = {
+ { .compatible = "brcm,bcm2711-rng200", },
+ { .compatible = "brcm,bcm7211-rng200", },
+ { .compatible = "brcm,bcm7278-rng200", },
+ { .compatible = "brcm,iproc-rng200", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, iproc_rng200_of_match);
+
+static struct driver iproc_rng200_driver = {
+ .name = "iproc-rng200",
+ .of_match_table = iproc_rng200_of_match,
+ .probe = iproc_rng200_probe,
+ .remove = iproc_rng200_remove,
+};
+device_platform_driver(iproc_rng200_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hw_random/mxc-rngc.c b/drivers/hw_random/mxc-rngc.c
index 075c20e437..39608b97af 100644
--- a/drivers/hw_random/mxc-rngc.c
+++ b/drivers/hw_random/mxc-rngc.c
@@ -1,19 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* RNG driver for Freescale RNGC
*
* Copyright (C) 2008-2012 Freescale Semiconductor, Inc.
- */
-
-/*
- * The code contained herein is licensed under the GNU General Public
- * License. You may obtain a copy of the GNU General Public License
- * Version 2 or later at the following locations:
*
- * http://www.opensource.org/licenses/gpl-license.html
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-/*
* Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
* (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
*
@@ -27,10 +17,6 @@
* Hardware driver for Intel i810 Random Number Generator (RNG)
* Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
* Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
*/
#include <common.h>
@@ -115,7 +101,7 @@
#define RNG_ADDR_RANGE 0x34
struct mxc_rngc {
- struct device_d *dev;
+ struct device *dev;
struct clk *clk;
void __iomem *base;
struct hwrng rng;
@@ -254,7 +240,7 @@ static int mxc_rngc_init(struct hwrng *rng)
return 0;
}
-static int mxc_rngc_probe(struct device_d *dev)
+static int mxc_rngc_probe(struct device *dev)
{
struct mxc_rngc *rngc;
int ret;
@@ -296,8 +282,9 @@ static const struct of_device_id mxc_rngc_dt_ids[] = {
{ .compatible = "fsl,imx25-rngb" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, mxc_rngc_dt_ids);
-static struct driver_d mxc_rngc_driver = {
+static struct driver mxc_rngc_driver = {
.name = "mxc_rngc",
.probe = mxc_rngc_probe,
.of_compatible = mxc_rngc_dt_ids,
diff --git a/drivers/hw_random/omap-rng.c b/drivers/hw_random/omap-rng.c
new file mode 100644
index 0000000000..9fa50bc8e7
--- /dev/null
+++ b/drivers/hw_random/omap-rng.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * omap-rng.c - RNG driver for TI OMAP CPU family
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * Mostly based on original driver:
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ */
+
+#include <init.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/hw_random.h>
+#include <clock.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <of.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#define RNG_REG_STATUS_RDY (1 << 0)
+
+#define RNG_REG_INTACK_RDY_MASK (1 << 0)
+#define RNG_REG_INTACK_SHUTDOWN_OFLO_MASK (1 << 1)
+#define RNG_SHUTDOWN_OFLO_MASK (1 << 1)
+
+#define RNG_CONTROL_STARTUP_CYCLES_SHIFT 16
+#define RNG_CONTROL_STARTUP_CYCLES_MASK (0xffff << 16)
+#define RNG_CONTROL_ENABLE_TRNG_SHIFT 10
+#define RNG_CONTROL_ENABLE_TRNG_MASK (1 << 10)
+
+#define RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT 16
+#define RNG_CONFIG_MAX_REFIL_CYCLES_MASK (0xffff << 16)
+#define RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT 0
+#define RNG_CONFIG_MIN_REFIL_CYCLES_MASK (0xff << 0)
+
+#define RNG_CONTROL_STARTUP_CYCLES 0xff
+#define RNG_CONFIG_MIN_REFIL_CYCLES 0x21
+#define RNG_CONFIG_MAX_REFIL_CYCLES 0x22
+
+#define RNG_ALARMCNT_ALARM_TH_SHIFT 0x0
+#define RNG_ALARMCNT_ALARM_TH_MASK (0xff << 0)
+#define RNG_ALARMCNT_SHUTDOWN_TH_SHIFT 16
+#define RNG_ALARMCNT_SHUTDOWN_TH_MASK (0x1f << 16)
+#define RNG_ALARM_THRESHOLD 0xff
+#define RNG_SHUTDOWN_THRESHOLD 0x4
+
+#define RNG_REG_FROENABLE_MASK 0xffffff
+#define RNG_REG_FRODETUNE_MASK 0xffffff
+
+#define OMAP2_RNG_OUTPUT_SIZE 0x4
+#define OMAP4_RNG_OUTPUT_SIZE 0x8
+#define EIP76_RNG_OUTPUT_SIZE 0x10
+
+/*
+ * EIP76 RNG takes approx. 700us to produce 16 bytes of output data
+ * as per testing results. And to account for the lack of udelay()'s
+ * reliability, we keep the timeout as 1000us.
+ */
+#define RNG_DATA_FILL_TIMEOUT 100
+
+enum {
+ RNG_OUTPUT_0_REG = 0,
+ RNG_OUTPUT_1_REG,
+ RNG_OUTPUT_2_REG,
+ RNG_OUTPUT_3_REG,
+ RNG_STATUS_REG,
+ RNG_INTMASK_REG,
+ RNG_INTACK_REG,
+ RNG_CONTROL_REG,
+ RNG_CONFIG_REG,
+ RNG_ALARMCNT_REG,
+ RNG_FROENABLE_REG,
+ RNG_FRODETUNE_REG,
+ RNG_ALARMMASK_REG,
+ RNG_ALARMSTOP_REG,
+ RNG_REV_REG,
+ RNG_SYSCONFIG_REG,
+};
+
+static const u16 reg_map_omap2[] = {
+ [RNG_OUTPUT_0_REG] = 0x0,
+ [RNG_STATUS_REG] = 0x4,
+ [RNG_CONFIG_REG] = 0x28,
+ [RNG_REV_REG] = 0x3c,
+ [RNG_SYSCONFIG_REG] = 0x40,
+};
+
+static const u16 reg_map_omap4[] = {
+ [RNG_OUTPUT_0_REG] = 0x0,
+ [RNG_OUTPUT_1_REG] = 0x4,
+ [RNG_STATUS_REG] = 0x8,
+ [RNG_INTMASK_REG] = 0xc,
+ [RNG_INTACK_REG] = 0x10,
+ [RNG_CONTROL_REG] = 0x14,
+ [RNG_CONFIG_REG] = 0x18,
+ [RNG_ALARMCNT_REG] = 0x1c,
+ [RNG_FROENABLE_REG] = 0x20,
+ [RNG_FRODETUNE_REG] = 0x24,
+ [RNG_ALARMMASK_REG] = 0x28,
+ [RNG_ALARMSTOP_REG] = 0x2c,
+ [RNG_REV_REG] = 0x1FE0,
+ [RNG_SYSCONFIG_REG] = 0x1FE4,
+};
+
+static const u16 reg_map_eip76[] = {
+ [RNG_OUTPUT_0_REG] = 0x0,
+ [RNG_OUTPUT_1_REG] = 0x4,
+ [RNG_OUTPUT_2_REG] = 0x8,
+ [RNG_OUTPUT_3_REG] = 0xc,
+ [RNG_STATUS_REG] = 0x10,
+ [RNG_INTACK_REG] = 0x10,
+ [RNG_CONTROL_REG] = 0x14,
+ [RNG_CONFIG_REG] = 0x18,
+ [RNG_ALARMCNT_REG] = 0x1c,
+ [RNG_FROENABLE_REG] = 0x20,
+ [RNG_FRODETUNE_REG] = 0x24,
+ [RNG_ALARMMASK_REG] = 0x28,
+ [RNG_ALARMSTOP_REG] = 0x2c,
+ [RNG_REV_REG] = 0x7c,
+};
+
+struct omap_rng_dev;
+/**
+ * struct omap_rng_pdata - RNG IP block-specific data
+ * @regs: Pointer to the register offsets structure.
+ * @data_size: No. of bytes in RNG output.
+ * @data_present: Callback to determine if data is available.
+ * @init: Callback for IP specific initialization sequence.
+ * @cleanup: Callback for IP specific cleanup sequence.
+ */
+struct omap_rng_pdata {
+ u16 *regs;
+ u32 data_size;
+ u32 (*data_present)(struct omap_rng_dev *priv);
+ int (*init)(struct omap_rng_dev *priv);
+ void (*cleanup)(struct omap_rng_dev *priv);
+};
+
+struct omap_rng_dev {
+ void __iomem *base;
+ struct device *dev;
+ const struct omap_rng_pdata *pdata;
+ struct hwrng rng;
+ struct clk *clk;
+ struct clk *clk_reg;
+};
+
+static inline u32 omap_rng_read(struct omap_rng_dev *priv, u16 reg)
+{
+ return __raw_readl(priv->base + priv->pdata->regs[reg]);
+}
+
+static inline void omap_rng_write(struct omap_rng_dev *priv, u16 reg,
+ u32 val)
+{
+ __raw_writel(val, priv->base + priv->pdata->regs[reg]);
+}
+
+
+static int omap_rng_do_read(struct hwrng *rng, void *data, size_t max,
+ bool wait)
+{
+ struct omap_rng_dev *priv;
+ int i, present;
+
+ priv = (struct omap_rng_dev *)rng->priv;
+
+ /* In Linux, max is always at least 32 bytes, which is greater than
+ * the 4 bytes required by the IP not to raise a data abort.
+ * In barebox, reading 4 bytes from a HWRNG is something we want
+ * support, so we check against 4 here and restrict memcpy_fromio
+ * size below.
+ */
+ if (max < sizeof(u32))
+ return -EFAULT;
+
+ for (i = 0; i < RNG_DATA_FILL_TIMEOUT; i++) {
+ present = priv->pdata->data_present(priv);
+ if (present || !wait)
+ break;
+
+ udelay(10);
+ }
+ if (!present)
+ return 0;
+
+ max = min(max, priv->pdata->data_size);
+
+ memcpy_fromio(data, priv->base + priv->pdata->regs[RNG_OUTPUT_0_REG], max);
+
+ if (priv->pdata->regs[RNG_INTACK_REG])
+ omap_rng_write(priv, RNG_INTACK_REG, RNG_REG_INTACK_RDY_MASK);
+
+ return max;
+}
+
+static int omap_rng_init(struct hwrng *rng)
+{
+ struct omap_rng_dev *priv;
+
+ priv = (struct omap_rng_dev *)rng->priv;
+ return priv->pdata->init(priv);
+}
+
+static inline u32 omap2_rng_data_present(struct omap_rng_dev *priv)
+{
+ return omap_rng_read(priv, RNG_STATUS_REG) ? 0 : 1;
+}
+
+static int omap2_rng_init(struct omap_rng_dev *priv)
+{
+ omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x1);
+ return 0;
+}
+
+static void omap2_rng_cleanup(struct omap_rng_dev *priv)
+{
+ omap_rng_write(priv, RNG_SYSCONFIG_REG, 0x0);
+}
+
+static struct omap_rng_pdata omap2_rng_pdata = {
+ .regs = (u16 *)reg_map_omap2,
+ .data_size = OMAP2_RNG_OUTPUT_SIZE,
+ .data_present = omap2_rng_data_present,
+ .init = omap2_rng_init,
+ .cleanup = omap2_rng_cleanup,
+};
+
+static inline u32 omap4_rng_data_present(struct omap_rng_dev *priv)
+{
+ return omap_rng_read(priv, RNG_STATUS_REG) & RNG_REG_STATUS_RDY;
+}
+
+static int eip76_rng_init(struct omap_rng_dev *priv)
+{
+ u32 val;
+
+ /* Return if RNG is already running. */
+ if (omap_rng_read(priv, RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK)
+ return 0;
+
+ /* Number of 512 bit blocks of raw Noise Source output data that must
+ * be processed by either the Conditioning Function or the
+ * SP 800-90 DRBG ‘BC_DF’ functionality to yield a ‘full entropy’
+ * output value.
+ */
+ val = 0x5 << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
+
+ /* Number of FRO samples that are XOR-ed together into one bit to be
+ * shifted into the main shift register
+ */
+ val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
+ omap_rng_write(priv, RNG_CONFIG_REG, val);
+
+ /* Enable TRNG */
+ val = RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONTROL_REG, val);
+
+ return 0;
+}
+
+static int omap4_rng_init(struct omap_rng_dev *priv)
+{
+ u32 val;
+
+ /* Return if RNG is already running. */
+ if (omap_rng_read(priv, RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK)
+ return 0;
+
+ val = RNG_CONFIG_MIN_REFIL_CYCLES << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT;
+ val |= RNG_CONFIG_MAX_REFIL_CYCLES << RNG_CONFIG_MAX_REFIL_CYCLES_SHIFT;
+ omap_rng_write(priv, RNG_CONFIG_REG, val);
+
+ val = RNG_CONTROL_STARTUP_CYCLES << RNG_CONTROL_STARTUP_CYCLES_SHIFT;
+ val |= RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONTROL_REG, val);
+
+ return 0;
+}
+
+static void omap4_rng_cleanup(struct omap_rng_dev *priv)
+{
+ int val;
+
+ val = omap_rng_read(priv, RNG_CONTROL_REG);
+ val &= ~RNG_CONTROL_ENABLE_TRNG_MASK;
+ omap_rng_write(priv, RNG_CONTROL_REG, val);
+}
+
+static struct omap_rng_pdata omap4_rng_pdata = {
+ .regs = (u16 *)reg_map_omap4,
+ .data_size = OMAP4_RNG_OUTPUT_SIZE,
+ .data_present = omap4_rng_data_present,
+ .init = omap4_rng_init,
+ .cleanup = omap4_rng_cleanup,
+};
+
+static struct omap_rng_pdata eip76_rng_pdata = {
+ .regs = (u16 *)reg_map_eip76,
+ .data_size = EIP76_RNG_OUTPUT_SIZE,
+ .data_present = omap4_rng_data_present,
+ .init = eip76_rng_init,
+ .cleanup = omap4_rng_cleanup,
+};
+
+static const struct of_device_id omap_rng_of_match[] __maybe_unused = {
+ {
+ .compatible = "ti,omap2-rng",
+ .data = &omap2_rng_pdata,
+ },
+ {
+ .compatible = "ti,omap4-rng",
+ .data = &omap4_rng_pdata,
+ },
+ {
+ .compatible = "inside-secure,safexcel-eip76",
+ .data = &eip76_rng_pdata,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap_rng_of_match);
+
+static int of_get_omap_rng_device_details(struct omap_rng_dev *priv,
+ struct device *dev)
+{
+ priv->pdata = device_get_match_data(dev);
+ if (!priv->pdata)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct clk *ti_sysc_clk_get_enabled(struct device *dev, const char *clk_id)
+{
+ struct clk *clk;
+
+ clk = clk_get_optional_enabled(dev, clk_id);
+ if (!clk)
+ clk = clk_get_optional_enabled(dev->parent, clk_id);
+
+ if (IS_ERR(clk))
+ dev_errp_probe(dev, clk, "Unable to enable the clk\n");
+ return clk;
+}
+
+static int omap_rng_probe(struct device *dev)
+{
+ struct omap_rng_dev *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct omap_rng_dev), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->rng.read = omap_rng_do_read;
+ priv->rng.init = omap_rng_init;
+
+ priv->rng.priv = (unsigned long)priv;
+ dev->priv = priv;
+ priv->dev = dev;
+
+ priv->base = dev_platform_ioremap_resource(dev, 0);
+ if (IS_ERR(priv->base)) {
+ ret = PTR_ERR(priv->base);
+ goto err_ioremap;
+ }
+
+ priv->rng.name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+ if (!priv->rng.name) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ priv->clk = ti_sysc_clk_get_enabled(dev, NULL);
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ goto err_ioremap;
+ }
+
+ priv->clk_reg = ti_sysc_clk_get_enabled(dev, "reg");
+ if (IS_ERR(priv->clk_reg)) {
+ ret = PTR_ERR(priv->clk_reg);
+ goto err_ioremap;
+ }
+
+ ret = of_get_omap_rng_device_details(priv, dev);
+ if (ret)
+ goto err_register;
+
+ ret = hwrng_register(dev, &priv->rng);
+ if (ret)
+ goto err_register;
+
+ dev_info(dev, "Random Number Generator ver. %02x\n",
+ omap_rng_read(priv, RNG_REV_REG));
+
+ return 0;
+
+err_register:
+ priv->base = NULL;
+
+ clk_disable_unprepare(priv->clk_reg);
+ clk_disable_unprepare(priv->clk);
+err_ioremap:
+ dev_err(dev, "initialization failed.\n");
+ return ret;
+}
+
+static void omap_rng_remove(struct device *dev)
+{
+ struct omap_rng_dev *priv = dev->priv;
+
+
+ priv->pdata->cleanup(priv);
+
+ clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_reg);
+}
+
+static struct driver omap_rng_driver = {
+ .name = "omap_rng",
+ .of_match_table = of_match_ptr(omap_rng_of_match),
+ .probe = omap_rng_probe,
+ .remove = omap_rng_remove,
+};
+
+device_platform_driver(omap_rng_driver);
+MODULE_ALIAS("platform:omap_rng");
+MODULE_AUTHOR("Deepak Saxena (and others)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hw_random/optee-rng.c b/drivers/hw_random/optee-rng.c
new file mode 100644
index 0000000000..d1d2904821
--- /dev/null
+++ b/drivers/hw_random/optee-rng.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018-2019 Linaro Ltd.
+ */
+
+#include <of.h>
+#include <linux/hw_random.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/uuid.h>
+
+#define DRIVER_NAME "optee-rng"
+
+#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001
+
+/*
+ * TA_CMD_GET_ENTROPY - Get Entropy from RNG
+ *
+ * param[0] (inout memref) - Entropy buffer memory reference
+ * param[1] unused
+ * param[2] unused
+ * param[3] unused
+ *
+ * Result:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool
+ * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed
+ */
+#define TA_CMD_GET_ENTROPY 0x0
+
+/*
+ * TA_CMD_GET_RNG_INFO - Get RNG information
+ *
+ * param[0] (out value) - value.a: RNG data-rate in bytes per second
+ * value.b: Quality/Entropy per 1024 bit of data
+ * param[1] unused
+ * param[2] unused
+ * param[3] unused
+ *
+ * Result:
+ * TEE_SUCCESS - Invoke command success
+ * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
+ */
+#define TA_CMD_GET_RNG_INFO 0x1
+
+#define MAX_ENTROPY_REQ_SZ (4 * 1024)
+
+/**
+ * struct optee_rng_private - OP-TEE Random Number Generator private data
+ * @dev: OP-TEE based RNG device.
+ * @ctx: OP-TEE context handler.
+ * @session_id: RNG TA session identifier.
+ * @data_rate: RNG data rate.
+ * @entropy_shm_pool: Memory pool shared with RNG device.
+ * @optee_rng: OP-TEE RNG driver structure.
+ */
+struct optee_rng_private {
+ struct device *dev;
+ struct tee_context *ctx;
+ u32 session_id;
+ u32 data_rate;
+ struct tee_shm *entropy_shm_pool;
+ struct hwrng optee_rng;
+ u16 quality;
+ void (*bus_devinfo)(struct device *);
+};
+
+#define to_optee_rng_private(r) \
+ container_of(r, struct optee_rng_private, optee_rng)
+
+static size_t get_optee_rng_data(struct optee_rng_private *pvt_data,
+ void *buf, size_t req_size)
+{
+ int ret = 0;
+ u8 *rng_data = NULL;
+ size_t rng_size = 0;
+ struct tee_ioctl_invoke_arg inv_arg;
+ struct tee_param param[4];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(&param, 0, sizeof(param));
+
+ /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */
+ inv_arg.func = TA_CMD_GET_ENTROPY;
+ inv_arg.session = pvt_data->session_id;
+ inv_arg.num_params = 4;
+
+ /* Fill invoke cmd params */
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ param[0].u.memref.shm = pvt_data->entropy_shm_pool;
+ param[0].u.memref.size = req_size;
+ param[0].u.memref.shm_offs = 0;
+
+ ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param);
+ if ((ret < 0) || (inv_arg.ret != 0)) {
+ dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n",
+ inv_arg.ret);
+ return 0;
+ }
+
+ rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0);
+ if (IS_ERR(rng_data)) {
+ dev_err(pvt_data->dev, "tee_shm_get_va failed\n");
+ return 0;
+ }
+
+ rng_size = param[0].u.memref.size;
+ memcpy(buf, rng_data, rng_size);
+
+ return rng_size;
+}
+
+static int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
+ size_t read = 0, rng_size;
+ int timeout = 1;
+ u8 *data = buf;
+
+ if (max > MAX_ENTROPY_REQ_SZ)
+ max = MAX_ENTROPY_REQ_SZ;
+
+ while (read < max) {
+ rng_size = get_optee_rng_data(pvt_data, data, (max - read));
+
+ data += rng_size;
+ read += rng_size;
+
+ if (wait && pvt_data->data_rate) {
+ if ((timeout-- == 0) || (read == max))
+ return read;
+ } else {
+ return read;
+ }
+ }
+
+ return read;
+}
+
+static int optee_rng_init(struct hwrng *rng)
+{
+ struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
+ struct tee_shm *entropy_shm_pool = NULL;
+
+ entropy_shm_pool = tee_shm_alloc_kernel_buf(pvt_data->ctx,
+ MAX_ENTROPY_REQ_SZ);
+ if (IS_ERR(entropy_shm_pool)) {
+ dev_err(pvt_data->dev, "tee_shm_alloc_kernel_buf failed\n");
+ return PTR_ERR(entropy_shm_pool);
+ }
+
+ pvt_data->entropy_shm_pool = entropy_shm_pool;
+
+ return 0;
+}
+
+static struct optee_rng_private pvt_data = {
+ .optee_rng = {
+ .name = DRIVER_NAME,
+ .init = optee_rng_init,
+ .read = optee_rng_read,
+ }
+};
+
+static int get_optee_rng_info(struct device *dev)
+{
+ int ret = 0;
+ struct tee_ioctl_invoke_arg inv_arg;
+ struct tee_param param[4];
+
+ memset(&inv_arg, 0, sizeof(inv_arg));
+ memset(&param, 0, sizeof(param));
+
+ /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */
+ inv_arg.func = TA_CMD_GET_RNG_INFO;
+ inv_arg.session = pvt_data.session_id;
+ inv_arg.num_params = 4;
+
+ /* Fill invoke cmd params */
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+
+ ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
+ if ((ret < 0) || (inv_arg.ret != 0)) {
+ dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n",
+ inv_arg.ret);
+ return -EINVAL;
+ }
+
+ pvt_data.data_rate = param[0].u.value.a;
+ pvt_data.quality = param[0].u.value.b;
+
+ return 0;
+}
+
+static void optee_rng_devinfo(struct device *dev)
+{
+ printf("Data rate: %u\n", pvt_data.data_rate);
+ printf("Quality: %u\n", pvt_data.quality);
+
+ if (pvt_data.bus_devinfo)
+ pvt_data.bus_devinfo(dev);
+}
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+ return 1;
+ else
+ return 0;
+}
+
+static int optee_rng_probe(struct device *dev)
+{
+ struct tee_client_device *rng_device = to_tee_client_device(dev);
+ int ret = 0, err = -ENODEV;
+ struct tee_ioctl_open_session_arg sess_arg;
+
+ memset(&sess_arg, 0, sizeof(sess_arg));
+
+ /* Open context with TEE driver */
+ pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
+ NULL);
+ if (IS_ERR(pvt_data.ctx))
+ return -ENODEV;
+
+ /* Open session with hwrng Trusted App */
+ export_uuid(sess_arg.uuid, &rng_device->id.uuid);
+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
+ sess_arg.num_params = 0;
+
+ ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
+ if ((ret < 0) || (sess_arg.ret != 0)) {
+ dev_err(dev, "tee_client_open_session failed, err: %x\n",
+ sess_arg.ret);
+ err = -EINVAL;
+ goto out_ctx;
+ }
+ pvt_data.session_id = sess_arg.session;
+
+ err = get_optee_rng_info(dev);
+ if (err)
+ goto out_sess;
+
+ err = hwrng_register(dev, &pvt_data.optee_rng);
+ if (err) {
+ dev_err(dev, "hwrng registration failed (%d)\n", err);
+ goto out_sess;
+ }
+
+ pvt_data.dev = dev;
+ pvt_data.bus_devinfo = dev->info;
+ dev->info = optee_rng_devinfo;
+
+ return 0;
+
+out_sess:
+ tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
+out_ctx:
+ tee_client_close_context(pvt_data.ctx);
+
+ return err;
+}
+
+static void optee_rng_remove(struct device *dev)
+{
+ hwrng_unregister(&pvt_data.optee_rng);
+
+ tee_shm_free(pvt_data.entropy_shm_pool);
+
+ tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
+ tee_client_close_context(pvt_data.ctx);
+}
+
+static const struct tee_client_device_id optee_rng_id_table[] = {
+ {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f,
+ 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(tee, optee_rng_id_table);
+
+static struct tee_client_driver optee_rng_driver = {
+ .id_table = optee_rng_id_table,
+ .driver = {
+ .name = DRIVER_NAME,
+ .bus = &tee_bus_type,
+ .probe = optee_rng_probe,
+ .remove = optee_rng_remove,
+ },
+};
+
+static int __init optee_rng_mod_init(void)
+{
+ return driver_register(&optee_rng_driver.driver);
+}
+device_initcall(optee_rng_mod_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>");
+MODULE_DESCRIPTION("OP-TEE based random number generator driver");
diff --git a/drivers/hw_random/rockchip-rng.c b/drivers/hw_random/rockchip-rng.c
new file mode 100644
index 0000000000..990e5fc111
--- /dev/null
+++ b/drivers/hw_random/rockchip-rng.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rockchip-rng.c Random Number Generator driver for the Rockchip
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd.
+ * Author: Lin Jinhan <troy.lin@rock-chips.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/hw_random.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+#include <linux/bitfield.h>
+#include <linux/mod_devicetable.h>
+#include <of.h>
+#include <of_address.h>
+#include <linux/device.h>
+
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+#define ROCKCHIP_AUTOSUSPEND_DELAY 100
+#define ROCKCHIP_POLL_PERIOD_US 100
+#define ROCKCHIP_POLL_TIMEOUT_US 10000
+#define RK_MAX_RNG_BYTE (32)
+
+#define CRYPTO_V1_CTRL 0x0008
+#define CRYPTO_V1_RNG_START BIT(8)
+#define CRYPTO_V1_RNG_FLUSH BIT(9)
+#define CRYPTO_V1_TRNG_CTRL 0x0200
+#define CRYPTO_V1_OSC_ENABLE BIT(16)
+#define CRYPTO_V1_TRNG_SAMPLE_PERIOD(x) (x)
+#define CRYPTO_V1_TRNG_DOUT_0 0x0204
+
+#define CRYPTO_V2_RNG_CTL 0x0400
+#define CRYPTO_V2_RNG_BIT_LEN GENMASK(5, 4)
+#define CRYPTO_V2_RNG_64_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 0)
+#define CRYPTO_V2_RNG_128_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 1)
+#define CRYPTO_V2_RNG_192_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 2)
+#define CRYPTO_V2_RNG_256_BIT_LEN FIELD_PREP(CRYPTO_V2_RNG_BIT_LEN, 3)
+#define CRYPTO_V2_RNG_SOC_RING GENMASK(3, 2)
+#define CRYPTO_V2_RNG_FASTEST_SOC_RING FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 0)
+#define CRYPTO_V2_RNG_SLOWER_SOC_RING_0 FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 1)
+#define CRYPTO_V2_RNG_SLOWER_SOC_RING_1 FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 2)
+#define CRYPTO_V2_RNG_SLOWEST_SOC_RING FIELD_PREP(CRYPTO_V2_RNG_SOC_RING, 3)
+#define CRYPTO_V2_RNG_ENABLE BIT(1)
+#define CRYPTO_V2_RNG_START BIT(0)
+#define CRYPTO_V2_RNG_SAMPLE_CNT 0x0404
+#define CRYPTO_V2_RNG_DOUT_0 0x0410
+
+struct rk_rng_soc_data {
+ const char * const *clks;
+ int clks_num;
+ int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait);
+};
+
+struct rk_rng {
+ struct device *dev;
+ struct hwrng rng;
+ void __iomem *mem;
+ struct rk_rng_soc_data *soc_data;
+ u32 clk_num;
+ struct clk_bulk_data *clk_bulks;
+};
+
+static void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset)
+{
+ __raw_writel(val, rng->mem + offset);
+}
+
+static u32 rk_rng_readl(struct rk_rng *rng, u32 offset)
+{
+ return __raw_readl(rng->mem + offset);
+}
+
+static int rk_rng_init(struct hwrng *rng)
+{
+ int ret;
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks);
+ if (ret < 0) {
+ dev_err(rk_rng->dev, "failed to enable clks %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rk_rng_cleanup(struct rk_rng *rk_rng)
+{
+ clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks);
+}
+
+static void rk_rng_read_regs(struct rk_rng *rng, u32 offset, void *buf,
+ size_t size)
+{
+ u32 i, sample;
+
+ for (i = 0; i < size; i += 4) {
+ sample = rk_rng_readl(rng, offset + i);
+ memcpy(buf + i, &sample, sizeof(sample));
+ }
+}
+
+static int rk_rng_v1_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ int ret = 0;
+ u32 reg_ctrl = 0;
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ /* enable osc_ring to get entropy, sample period is set as 100 */
+ reg_ctrl = CRYPTO_V1_OSC_ENABLE | CRYPTO_V1_TRNG_SAMPLE_PERIOD(100);
+ rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_TRNG_CTRL);
+
+ reg_ctrl = HIWORD_UPDATE(CRYPTO_V1_RNG_START, CRYPTO_V1_RNG_START, 0);
+
+ rk_rng_writel(rk_rng, reg_ctrl, CRYPTO_V1_CTRL);
+
+ ret = readl_poll_timeout(rk_rng->mem + CRYPTO_V1_CTRL, reg_ctrl,
+ !(reg_ctrl & CRYPTO_V1_RNG_START),
+ ROCKCHIP_POLL_TIMEOUT_US);
+ if (ret < 0)
+ goto out;
+
+ ret = min_t(size_t, max, RK_MAX_RNG_BYTE);
+
+ rk_rng_read_regs(rk_rng, CRYPTO_V1_TRNG_DOUT_0, buf, ret);
+
+out:
+ /* close TRNG */
+ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, CRYPTO_V1_RNG_START, 0),
+ CRYPTO_V1_CTRL);
+
+ return ret;
+}
+
+static int rk_rng_v2_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ int ret = 0;
+ u32 reg_ctrl = 0;
+ struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+
+ /* enable osc_ring to get entropy, sample period is set as 100 */
+ rk_rng_writel(rk_rng, 100, CRYPTO_V2_RNG_SAMPLE_CNT);
+
+ reg_ctrl |= CRYPTO_V2_RNG_256_BIT_LEN;
+ reg_ctrl |= CRYPTO_V2_RNG_SLOWER_SOC_RING_0;
+ reg_ctrl |= CRYPTO_V2_RNG_ENABLE;
+ reg_ctrl |= CRYPTO_V2_RNG_START;
+
+ rk_rng_writel(rk_rng, HIWORD_UPDATE(reg_ctrl, 0xffff, 0),
+ CRYPTO_V2_RNG_CTL);
+
+ ret = readl_poll_timeout(rk_rng->mem + CRYPTO_V2_RNG_CTL, reg_ctrl,
+ !(reg_ctrl & CRYPTO_V2_RNG_START),
+ ROCKCHIP_POLL_TIMEOUT_US);
+ if (ret < 0)
+ goto out;
+
+ ret = min_t(size_t, max, RK_MAX_RNG_BYTE);
+
+ rk_rng_read_regs(rk_rng, CRYPTO_V2_RNG_DOUT_0, buf, ret);
+
+out:
+ /* close TRNG */
+ rk_rng_writel(rk_rng, HIWORD_UPDATE(0, 0xffff, 0), CRYPTO_V2_RNG_CTL);
+
+ return ret;
+}
+
+static const struct rk_rng_soc_data rk_rng_rk3399_soc_data = {
+ .clks_num = 3,
+ .rk_rng_read = rk_rng_v1_read,
+};
+
+static const struct rk_rng_soc_data rk_rng_v1_soc_data = {
+ .clks_num = 2,
+ .rk_rng_read = rk_rng_v1_read,
+};
+
+static const struct rk_rng_soc_data rk_rng_v2_soc_data = {
+ .clks_num = 2,
+ .rk_rng_read = rk_rng_v2_read,
+};
+
+static const struct of_device_id rk_rng_dt_match[] = {
+ {
+ .compatible = "rockchip,rk3399-crypto",
+ .data = (void *)&rk_rng_rk3399_soc_data,
+ },
+ {
+ .compatible = "rockchip,cryptov1-rng",
+ .data = (void *)&rk_rng_v1_soc_data,
+ },
+ {
+ .compatible = "rockchip,cryptov2-rng",
+ .data = (void *)&rk_rng_v2_soc_data,
+ },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, rk_rng_dt_match);
+
+static int rk_rng_probe(struct device *dev)
+{
+ int ret;
+ struct rk_rng *rk_rng;
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *match;
+
+ rk_rng = devm_kzalloc(dev, sizeof(struct rk_rng), GFP_KERNEL);
+ if (!rk_rng)
+ return -ENOMEM;
+
+ match = of_match_node(rk_rng_dt_match, np);
+ rk_rng->soc_data = (struct rk_rng_soc_data *)match->data;
+
+ rk_rng->dev = dev;
+ rk_rng->rng.name = "rockchip";
+ rk_rng->rng.init = rk_rng_init;
+ rk_rng->rng.read = rk_rng->soc_data->rk_rng_read;
+
+ rk_rng->clk_num = clk_bulk_get_all(dev, &rk_rng->clk_bulks);
+ if (rk_rng->clk_num < rk_rng->soc_data->clks_num)
+ return dev_err_probe(dev, -EINVAL,
+ "Missing clocks, got %d instead of %d\n",
+ rk_rng->clk_num, rk_rng->soc_data->clks_num);
+
+ ret = device_reset_us(dev, 2);
+ if (ret)
+ return ret;
+
+ rk_rng->mem = of_iomap(dev->device_node, 0);
+ if (IS_ERR(rk_rng->mem))
+ return PTR_ERR(rk_rng->mem);
+
+ dev->priv = rk_rng;
+
+ return hwrng_register(dev, &rk_rng->rng);
+}
+
+static void rk_rng_remove(struct device *dev)
+{
+ rk_rng_cleanup(dev->priv);
+}
+
+static struct driver rk_rng_driver = {
+ .name = "rockchip-rng",
+ .of_match_table = rk_rng_dt_match,
+ .probe = rk_rng_probe,
+ .remove = rk_rng_remove,
+};
+
+device_platform_driver(rk_rng_driver);
+
+MODULE_DESCRIPTION("ROCKCHIP H/W Random Number Generator driver");
+MODULE_AUTHOR("Lin Jinhan <troy.lin@rock-chips.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hw_random/starfive-vic-rng.c b/drivers/hw_random/starfive-vic-rng.c
index f7b7585884..329e845197 100644
--- a/drivers/hw_random/starfive-vic-rng.c
+++ b/drivers/hw_random/starfive-vic-rng.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* COPYRIGHT 2020 Shanghai StarFive Technology Co., Ltd.
*/
@@ -72,7 +72,7 @@
#define to_vic_rng(p) container_of(p, struct vic_rng, rng)
struct vic_rng {
- struct device_d *dev;
+ struct device *dev;
void __iomem *base;
struct hwrng rng;
};
@@ -170,7 +170,7 @@ static int vic_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
return max;
}
-static int vic_rng_probe(struct device_d *dev)
+static int vic_rng_probe(struct device *dev)
{
struct vic_rng *hrng;
struct resource *res;
@@ -195,8 +195,9 @@ static const struct of_device_id vic_rng_dt_ids[] = {
{ .compatible = "starfive,vic-rng" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, vic_rng_dt_ids);
-static struct driver_d vic_rng_driver = {
+static struct driver vic_rng_driver = {
.name = "vic-rng",
.probe = vic_rng_probe,
.of_compatible = vic_rng_dt_ids,
diff --git a/drivers/hw_random/stm32-rng.c b/drivers/hw_random/stm32-rng.c
index 440b53684f..03bc4a5cbf 100644
--- a/drivers/hw_random/stm32-rng.c
+++ b/drivers/hw_random/stm32-rng.c
@@ -51,7 +51,7 @@ static int stm32_rng_read(struct hwrng *hwrng, void *data, size_t len, bool wait
ret = readl_poll_timeout(rng->base + RNG_SR, sr,
sr & RNG_SR_DRDY, 10 * USEC_PER_MSEC);
if (ret)
- goto out;
+ return ret;
if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) {
int i;
@@ -61,8 +61,7 @@ static int stm32_rng_read(struct hwrng *hwrng, void *data, size_t len, bool wait
readl(rng->base + RNG_DR);
if (readl(rng->base + RNG_SR) & RNG_SR_SEIS) {
pr_warn("RNG Noise");
- ret = -EIO;
- goto out;
+ return -EIO;
}
/* start again */
@@ -84,8 +83,7 @@ static int stm32_rng_read(struct hwrng *hwrng, void *data, size_t len, bool wait
}
}
-out:
- return len ?: ret;
+ return len;
}
static int stm32_rng_init(struct hwrng *hwrng)
@@ -106,7 +104,7 @@ static int stm32_rng_init(struct hwrng *hwrng)
return 0;
}
-static void stm32_rng_remove(struct device_d *dev)
+static void stm32_rng_remove(struct device *dev)
{
struct stm32_rng *rng = dev->priv;
@@ -114,7 +112,7 @@ static void stm32_rng_remove(struct device_d *dev)
clk_disable(rng->clk);
}
-static int stm32_rng_probe(struct device_d *dev)
+static int stm32_rng_probe(struct device *dev)
{
struct stm32_rng *rng;
struct resource *res;
@@ -154,8 +152,9 @@ static const struct of_device_id stm32_rng_dt_ids[] = {
{ .compatible = "st,stm32-rng" },
{ /* sentinel */},
};
+MODULE_DEVICE_TABLE(of, stm32_rng_dt_ids);
-static struct driver_d stm32_rng_driver = {
+static struct driver stm32_rng_driver = {
.name = "stm32-rng",
.probe = stm32_rng_probe,
.remove = stm32_rng_remove,
diff --git a/drivers/hw_random/timeriomem-rng.c b/drivers/hw_random/timeriomem-rng.c
new file mode 100644
index 0000000000..8d47058306
--- /dev/null
+++ b/drivers/hw_random/timeriomem-rng.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * drivers/char/hw_random/timeriomem-rng.c
+ *
+ * Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk>
+ *
+ * Derived from drivers/char/hw_random/omap-rng.c
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Overview:
+ * This driver is useful for platforms that have an IO range that provides
+ * periodic random data from a single IO memory address. All the platform
+ * has to do is provide the address and 'wait time' that new data becomes
+ * available.
+ *
+ * TODO: add support for reading sizes other than 32bits and masking
+ */
+
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <of.h>
+#include <linux/device.h>
+#include <linux/time.h>
+
+struct timeriomem_rng_private {
+ void __iomem *io_base;
+
+ ktime_t period;
+ ktime_t next_read;
+
+ struct hwrng rng_ops;
+};
+
+static int timeriomem_rng_read(struct hwrng *hwrng, void *data,
+ size_t max, bool wait)
+{
+ struct timeriomem_rng_private *priv =
+ container_of(hwrng, struct timeriomem_rng_private, rng_ops);
+ int retval = 0;
+ int period_us = ktime_to_us(priv->period);
+ ktime_t now = ktime_get();
+
+ /*
+ * There may not have been enough time for new data to be generated
+ * since the last request. If the caller doesn't want to wait, let them
+ * bail out. Otherwise, wait for the completion. If the new data has
+ * already been generated, the completion should already be available.
+ */
+ if (ktime_before(now, priv->next_read)) {
+ if (!wait)
+ return 0;
+
+ udelay(ktime_to_us(ktime_sub(priv->next_read, now)));
+ }
+
+ do {
+ /*
+ * After the first read, all additional reads will need to wait
+ * for the RNG to generate new data. Since the period can have
+ * a wide range of values (1us to 1s have been observed), allow
+ * for 1% tolerance in the sleep time rather than a fixed value.
+ */
+ if (retval > 0)
+ udelay(period_us);
+
+ *(u32 *)data = readl(priv->io_base);
+ retval += sizeof(u32);
+ data += sizeof(u32);
+ max -= sizeof(u32);
+ } while (wait && max > sizeof(u32));
+
+ /*
+ * Block any new callers until the RNG has had time to generate new
+ * data.
+ */
+ priv->next_read = ktime_add(ktime_get(), priv->period);
+
+ return retval;
+}
+
+static int timeriomem_rng_probe(struct device *dev)
+{
+ struct timeriomem_rng_private *priv;
+ struct resource *res;
+ int err = 0;
+ int period;
+
+ /* Allocate memory for the device structure (and zero it) */
+ priv = devm_kzalloc(dev,
+ sizeof(struct timeriomem_rng_private), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->io_base = dev_platform_get_and_ioremap_resource(dev, 0, &res);
+ if (IS_ERR(priv->io_base))
+ return PTR_ERR(priv->io_base);
+
+ if (res->start % 4 != 0 || resource_size(res) < 4) {
+ dev_err(dev,
+ "address must be at least four bytes wide and 32-bit aligned\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(dev->of_node, "period", &period))
+ return dev_err_probe(dev, -EINVAL, "missing period\n");
+
+ priv->period = ns_to_ktime(period * NSEC_PER_USEC);
+
+ priv->rng_ops.name = dev_name(dev);
+ priv->rng_ops.read = timeriomem_rng_read;
+
+ /* Assume random data is already available. */
+ priv->next_read = ktime_get();
+
+ err = hwrng_register(dev, &priv->rng_ops);
+ if (err) {
+ dev_err(dev, "problem registering\n");
+ return err;
+ }
+
+ dev_info(dev, "32bits from 0x%p @ %dus\n",
+ priv->io_base, period);
+
+ return 0;
+}
+
+static const struct of_device_id timeriomem_rng_match[] = {
+ { .compatible = "timeriomem_rng" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, timeriomem_rng_match);
+
+static struct driver timeriomem_rng_driver = {
+ .name = "timeriomem_rng",
+ .of_match_table = timeriomem_rng_match,
+ .probe = timeriomem_rng_probe,
+};
+
+device_platform_driver(timeriomem_rng_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>");
+MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");