]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hwrng: bcm74110 - Add Broadcom BCM74110 RNG driver
authorMarkus Mayer <mmayer@broadcom.com>
Fri, 1 Nov 2024 21:13:15 +0000 (14:13 -0700)
committerHerbert Xu <herbert@gondor.apana.org.au>
Sun, 10 Nov 2024 03:51:26 +0000 (11:51 +0800)
Add a driver for the random number generator present on the Broadcom
BCM74110 SoC.

Signed-off-by: Markus Mayer <mmayer@broadcom.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
drivers/char/hw_random/Kconfig
drivers/char/hw_random/Makefile
drivers/char/hw_random/bcm74110-rng.c [new file with mode: 0644]

index bda283f290bcb7944e2c39a9e99cfda62d4023f0..bf9b2354766aefd8a34c40d30c7c6fc8db9b276e 100644 (file)
@@ -112,6 +112,19 @@ config HW_RANDOM_BCM2835
 
          If unsure, say Y.
 
+config HW_RANDOM_BCM74110
+       tristate "Broadcom BCM74110 Random Number Generator support"
+       depends on ARCH_BRCMSTB || COMPILE_TEST
+       default HW_RANDOM
+       help
+         This driver provides kernel-side support for the Random Number
+         Generator hardware found on the Broadcom BCM74110 SoCs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bcm74110-rng
+
+         If unsure, say Y.
+
 config HW_RANDOM_IPROC_RNG200
        tristate "Broadcom iProc/STB RNG200 support"
        depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BCMBCA || ARCH_BRCMSTB || COMPILE_TEST
index dfb717b12f0b570f905b90c4e695275c56a75510..b9132b3f5d2103d40f43e91814f021b348d2b041 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o
 obj-$(CONFIG_HW_RANDOM_HISI)   += hisi-rng.o
 obj-$(CONFIG_HW_RANDOM_HISTB) += histb-rng.o
 obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o
+obj-$(CONFIG_HW_RANDOM_BCM74110) += bcm74110-rng.o
 obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o
 obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o
 obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o
diff --git a/drivers/char/hw_random/bcm74110-rng.c b/drivers/char/hw_random/bcm74110-rng.c
new file mode 100644 (file)
index 0000000..5c64148
--- /dev/null
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Broadcom
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/hw_random.h>
+
+#define HOST_REV_ID            0x00
+#define HOST_FIFO_DEPTH                0x04
+#define HOST_FIFO_COUNT                0x08
+#define HOST_FIFO_THRESHOLD    0x0c
+#define HOST_FIFO_DATA         0x10
+
+#define HOST_FIFO_COUNT_MASK           0xffff
+
+/* Delay range in microseconds */
+#define FIFO_DELAY_MIN_US              3
+#define FIFO_DELAY_MAX_US              7
+#define FIFO_DELAY_MAX_COUNT           10
+
+struct bcm74110_priv {
+       void __iomem *base;
+};
+
+static inline int bcm74110_rng_fifo_count(void __iomem *mem)
+{
+       return readl_relaxed(mem) & HOST_FIFO_COUNT_MASK;
+}
+
+static int bcm74110_rng_read(struct hwrng *rng, void *buf, size_t max,
+                       bool wait)
+{
+       struct bcm74110_priv *priv = (struct bcm74110_priv *)rng->priv;
+       void __iomem *fc_addr = priv->base + HOST_FIFO_COUNT;
+       void __iomem *fd_addr = priv->base + HOST_FIFO_DATA;
+       unsigned underrun_count = 0;
+       u32 max_words = max / sizeof(u32);
+       u32 num_words;
+       unsigned i;
+
+       /*
+        * We need to check how many words are available in the RNG FIFO. If
+        * there aren't any, we need to wait for some to become available.
+        */
+       while ((num_words = bcm74110_rng_fifo_count(fc_addr)) == 0) {
+               if (!wait)
+                       return 0;
+               /*
+                * As a precaution, limit how long we wait. If the FIFO doesn't
+                * refill within the allotted time, return 0 (=no data) to the
+                * caller.
+                */
+               if (likely(underrun_count < FIFO_DELAY_MAX_COUNT))
+                       usleep_range(FIFO_DELAY_MIN_US, FIFO_DELAY_MAX_US);
+               else
+                       return 0;
+               underrun_count++;
+       }
+       if (num_words > max_words)
+               num_words = max_words;
+
+       /* Bail early if we run out of random numbers unexpectedly */
+       for (i = 0; i < num_words && bcm74110_rng_fifo_count(fc_addr) > 0; i++)
+               ((u32 *)buf)[i] = readl_relaxed(fd_addr);
+
+       return i * sizeof(u32);
+}
+
+static struct hwrng bcm74110_hwrng = {
+       .read = bcm74110_rng_read,
+};
+
+static int bcm74110_rng_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct bcm74110_priv *priv;
+       int rc;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       bcm74110_hwrng.name = pdev->name;
+       bcm74110_hwrng.priv = (unsigned long)priv;
+
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       rc = devm_hwrng_register(dev, &bcm74110_hwrng);
+       if (rc)
+               dev_err(dev, "hwrng registration failed (%d)\n", rc);
+       else
+               dev_info(dev, "hwrng registered\n");
+
+       return rc;
+}
+
+static const struct of_device_id bcm74110_rng_match[] = {
+       { .compatible   = "brcm,bcm74110-rng", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bcm74110_rng_match);
+
+static struct platform_driver bcm74110_rng_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = bcm74110_rng_match,
+       },
+       .probe  = bcm74110_rng_probe,
+};
+module_platform_driver(bcm74110_rng_driver);
+
+MODULE_AUTHOR("Markus Mayer <mmayer@broadcom.com>");
+MODULE_DESCRIPTION("BCM 74110 Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL v2");