1
0
Fork 0
forked from mirror/openwrt

mediatek: replace downstream TRNGv2 driver

Replace the downstream mtk-rng-v2 driver which was acquires random bytes
from TF-A via SMC. A new approach is needed as TF-A for MT7986 has
changed and now requires to use SMC instead of directly accessing the
TRNG via MMIO. However, we can't know whether we are on old or new TF-A,
many devices (like the BananaPi BPi-R3) allow updating TF-A BL3 in the
field, so it may be of the old or new type, and the RNG driver will have
to figure it out somehow.

This currently means that MT7986 with newer TF-A has broken/non-working
HWRNG in Linux:
root@OpenWrt:~# hexdump -C /dev/hwrng
hexdump: /dev/hwrng: I/O error

Fix this by creating a new combined driver which replaces the previous
mtk-rng-v2 driver, and is able to auto-detect which convention to use
on MT7986.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This commit is contained in:
Daniel Golle 2026-03-04 01:18:08 +00:00
parent 57bf713ef7
commit b7a2f80740
6 changed files with 272 additions and 136 deletions

View file

@ -222,7 +222,6 @@ CONFIG_HAS_IOPORT_MAP=y
CONFIG_HWMON=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MTK=y
CONFIG_HW_RANDOM_MTK_V2=y
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y

View file

@ -221,7 +221,6 @@ CONFIG_HAS_IOPORT_MAP=y
CONFIG_HWMON=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MTK=y
# CONFIG_HW_RANDOM_MTK_V2 is not set
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_CHARDEV=y

View file

@ -161,7 +161,6 @@ CONFIG_HOTPLUG_CORE_SYNC_DEAD=y
CONFIG_HOTPLUG_CPU=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MTK=y
# CONFIG_HW_RANDOM_MTK_V2 is not set
CONFIG_HZ_FIXED=0
# CONFIG_IDPF is not set
CONFIG_INITRAMFS_SOURCE=""

View file

@ -0,0 +1,60 @@
From f9c0c36eefaa8c6ee224634bf9c0b8b4ed87b43a Mon Sep 17 00:00:00 2001
From: Sakari Ailus <sakari.ailus@linux.intel.com>
Date: Thu, 10 Apr 2025 18:22:38 +0300
Subject: [PATCH] hwrng: mtk - Add struct device pointer to device context struct
Add a struct device pointer field to the device's context struct. This
makes using the unsigned long priv pointer in struct hwrng unnecessary, so
remove that one as well.
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---
drivers/char/hw_random/mtk-rng.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
--- a/drivers/char/hw_random/mtk-rng.c
+++ b/drivers/char/hw_random/mtk-rng.c
@@ -36,6 +36,7 @@ struct mtk_rng {
void __iomem *base;
struct clk *clk;
struct hwrng rng;
+ struct device *dev;
};
static int mtk_rng_init(struct hwrng *rng)
@@ -85,7 +86,7 @@ static int mtk_rng_read(struct hwrng *rn
struct mtk_rng *priv = to_mtk_rng(rng);
int retval = 0;
- pm_runtime_get_sync((struct device *)priv->rng.priv);
+ pm_runtime_get_sync(priv->dev);
while (max >= sizeof(u32)) {
if (!mtk_rng_wait_ready(rng, wait))
@@ -97,8 +98,8 @@ static int mtk_rng_read(struct hwrng *rn
max -= sizeof(u32);
}
- pm_runtime_mark_last_busy((struct device *)priv->rng.priv);
- pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv);
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_sync_autosuspend(priv->dev);
return retval || !wait ? retval : -EIO;
}
@@ -112,13 +113,13 @@ static int mtk_rng_probe(struct platform
if (!priv)
return -ENOMEM;
+ priv->dev = &pdev->dev;
priv->rng.name = pdev->name;
#ifndef CONFIG_PM
priv->rng.init = mtk_rng_init;
priv->rng.cleanup = mtk_rng_cleanup;
#endif
priv->rng.read = mtk_rng_read;
- priv->rng.priv = (unsigned long)&pdev->dev;
priv->rng.quality = 900;
priv->clk = devm_clk_get(&pdev->dev, "rng");

View file

@ -1,133 +0,0 @@
From 9837930d6738e9fdc323ad887ace7c236a61d70c Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Wed, 25 Jan 2023 00:27:49 +0000
Subject: [PATCH] hwrng: add driver for MediaTek TRNG SMC
Add driver providing kernel-side support for the Random Number
Generator hardware found on Mediatek SoCs which have a driver in ARM
TrustedFirmware-A allowing Linux to read random numbers using a
non-standard vendor-defined Secure Monitor Call.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/char/hw_random/Kconfig | 16 +++++++
drivers/char/hw_random/Makefile | 1 +
drivers/char/hw_random/mtk-rng-v2.c | 74 +++++++++++++++++++++++++++++
3 files changed, 91 insertions(+)
create mode 100644 drivers/char/hw_random/mtk-rng-v2.c
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -452,6 +452,23 @@ config HW_RANDOM_MTK
If unsure, say Y.
+config HW_RANDOM_MTK_V2
+ tristate "Mediatek Random Number Generator support (v2/SMC)"
+ depends on HAVE_ARM_SMCCC
+ depends on HW_RANDOM
+ depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST
+ default y
+ help
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Mediatek SoCs which have a driver
+ in ARM TrustedFirmware-A allowing Linux to read using a non-
+ standard vendor-defined Secure Monitor Call.
+
+ To compile this driver as a module, choose M here. the
+ module will be called mtk-rng-v2.
+
+ If unsure, say Y.
+
config HW_RANDOM_S390
tristate "S390 True Random Number Generator support"
depends on S390
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_HW_RANDOM_PIC32) += pic32-r
obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o
obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o
obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o
+obj-$(CONFIG_HW_RANDOM_MTK_V2) += mtk-rng-v2.o
obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o
obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o
obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o
--- /dev/null
+++ b/drivers/char/hw_random/mtk-rng-v2.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Mediatek Hardware Random Number Generator (v2/SMCC)
+ *
+ * Copyright (C) 2023 Daniel Golle <daniel@makrotopia.org>
+ * based on patch from Mingming Su <Mingming.Su@mediatek.com>
+ */
+#define MTK_RNG_DEV KBUILD_MODNAME
+
+#include <linux/arm-smccc.h>
+#include <linux/err.h>
+#include <linux/hw_random.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+
+#define MTK_SIP_KERNEL_GET_RND MTK_SIP_SMC_CMD(0x550)
+
+static int mtk_rng_v2_read(struct hwrng *rng, void *buf, size_t max, bool wait)
+{
+ struct arm_smccc_res res;
+ int retval = 0;
+
+ while (max >= sizeof(u32)) {
+ arm_smccc_smc(MTK_SIP_KERNEL_GET_RND, 0, 0, 0, 0, 0, 0, 0,
+ &res);
+ if (res.a0)
+ break;
+
+ *(u32 *)buf = res.a1;
+ retval += sizeof(u32);
+ buf += sizeof(u32);
+ max -= sizeof(u32);
+ }
+
+ return retval || !wait ? retval : -EIO;
+}
+
+static int mtk_rng_v2_probe(struct platform_device *pdev)
+{
+ struct hwrng *trng;
+
+ trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
+ if (!trng)
+ return -ENOMEM;
+
+ trng->name = pdev->name;
+ trng->read = mtk_rng_v2_read;
+ trng->quality = 900;
+
+ return devm_hwrng_register(&pdev->dev, trng);
+}
+
+static const struct of_device_id mtk_rng_v2_match[] = {
+ { .compatible = "mediatek,mt7981-rng" },
+ { .compatible = "mediatek,mt7987-rng" },
+ { .compatible = "mediatek,mt7988-rng" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_rng_v2_match);
+
+static struct platform_driver mtk_rng_v2_driver = {
+ .probe = mtk_rng_v2_probe,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = mtk_rng_v2_match,
+ },
+};
+module_platform_driver(mtk_rng_v2_driver);
+
+MODULE_DESCRIPTION("Mediatek Random Number Generator Driver (v2/SMC)");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_LICENSE("GPL");

View file

@ -0,0 +1,212 @@
From d211e2184d820207d14f5e8f84938c639875bf0d Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 3 Mar 2026 23:45:46 +0000
Subject: [PATCH] hwrng: mtk - add support for hw access via SMCC
Newer versions of ARM TrustedFirmware-A on MediaTek's ARMv8 SoCs no longer
allow accessing the TRNG from outside of the trusted firmware.
Instead, a vendor-defined custom Secure Monitor Call can be used to
acquire random bytes.
Add support for newer SoCs (MT7981, MT7987, MT7988). On MT7986 the best bet
is to test if firmware blocks direct access to the hwrng and if so, expect
the SMCC interface to be usable.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/char/hw_random/mtk-rng.c | 128 ++++++++++++++++++++++++++-----
1 file changed, 107 insertions(+), 21 deletions(-)
--- a/drivers/char/hw_random/mtk-rng.c
+++ b/drivers/char/hw_random/mtk-rng.c
@@ -3,6 +3,7 @@
* Driver for Mediatek Hardware Random Number Generator
*
* Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
+ * Copyright (C) 2026 Daniel Golle <daniel@makrotopia.org>
*/
#define MTK_RNG_DEV KBUILD_MODNAME
@@ -17,6 +18,8 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/arm-smccc.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
/* Runtime PM autosuspend timeout: */
#define RNG_AUTOSUSPEND_TIMEOUT 100
@@ -30,6 +33,11 @@
#define RNG_DATA 0x08
+/* Driver feature flags */
+#define MTK_RNG_SMC BIT(0)
+
+#define MTK_SIP_KERNEL_GET_RND MTK_SIP_SMC_CMD(0x550)
+
#define to_mtk_rng(p) container_of(p, struct mtk_rng, rng)
struct mtk_rng {
@@ -37,6 +45,7 @@ struct mtk_rng {
struct clk *clk;
struct hwrng rng;
struct device *dev;
+ unsigned long flags;
};
static int mtk_rng_init(struct hwrng *rng)
@@ -104,6 +113,56 @@ static int mtk_rng_read(struct hwrng *rn
return retval || !wait ? retval : -EIO;
}
+static int mtk_rng_read_smc(struct hwrng *rng, void *buf, size_t max,
+ bool wait)
+{
+ struct arm_smccc_res res;
+ int retval = 0;
+
+ while (max >= sizeof(u32)) {
+ arm_smccc_smc(MTK_SIP_KERNEL_GET_RND, 0, 0, 0, 0, 0, 0, 0,
+ &res);
+ if (res.a0)
+ break;
+
+ *(u32 *)buf = res.a1;
+ retval += sizeof(u32);
+ buf += sizeof(u32);
+ max -= sizeof(u32);
+ }
+
+ return retval || !wait ? retval : -EIO;
+}
+
+static bool mtk_rng_hw_accessible(struct mtk_rng *priv)
+{
+ u32 val;
+ int err;
+
+ err = clk_prepare_enable(priv->clk);
+ if (err)
+ return false;
+
+ val = readl(priv->base + RNG_CTRL);
+ val |= RNG_EN;
+ writel(val, priv->base + RNG_CTRL);
+
+ val = readl(priv->base + RNG_CTRL);
+
+ if (val & RNG_EN) {
+ /* HW is accessible, clean up: disable RNG and clock */
+ writel(val & ~RNG_EN, priv->base + RNG_CTRL);
+ clk_disable_unprepare(priv->clk);
+ return true;
+ }
+
+ /*
+ * If TF-A blocks direct access, the register reads back as 0.
+ * Leave the clock enabled as TF-A's SMC handler needs it.
+ */
+ return false;
+}
+
static int mtk_rng_probe(struct platform_device *pdev)
{
int ret;
@@ -115,23 +174,42 @@ static int mtk_rng_probe(struct platform
priv->dev = &pdev->dev;
priv->rng.name = pdev->name;
-#ifndef CONFIG_PM
- priv->rng.init = mtk_rng_init;
- priv->rng.cleanup = mtk_rng_cleanup;
-#endif
- priv->rng.read = mtk_rng_read;
priv->rng.quality = 900;
+ priv->flags = (unsigned long)device_get_match_data(&pdev->dev);
- priv->clk = devm_clk_get(&pdev->dev, "rng");
- if (IS_ERR(priv->clk)) {
- ret = PTR_ERR(priv->clk);
- dev_err(&pdev->dev, "no clock for device: %d\n", ret);
- return ret;
+ if (!(priv->flags & MTK_RNG_SMC)) {
+ priv->clk = devm_clk_get(&pdev->dev, "rng");
+ if (IS_ERR(priv->clk)) {
+ ret = PTR_ERR(priv->clk);
+ dev_err(&pdev->dev, "no clock for device: %d\n", ret);
+ return ret;
+ }
+
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ if (IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) &&
+ of_device_is_compatible(pdev->dev.of_node,
+ "mediatek,mt7986-rng") &&
+ !mtk_rng_hw_accessible(priv)) {
+ priv->flags |= MTK_RNG_SMC;
+ dev_info(&pdev->dev,
+ "HW RNG not MMIO accessible, using SMC\n");
+ }
}
- priv->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(priv->base))
- return PTR_ERR(priv->base);
+ if (priv->flags & MTK_RNG_SMC) {
+ if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC))
+ return -ENODEV;
+ priv->rng.read = mtk_rng_read_smc;
+ } else {
+#ifndef CONFIG_PM
+ priv->rng.init = mtk_rng_init;
+ priv->rng.cleanup = mtk_rng_cleanup;
+#endif
+ priv->rng.read = mtk_rng_read;
+ }
ret = devm_hwrng_register(&pdev->dev, &priv->rng);
if (ret) {
@@ -140,12 +218,15 @@ static int mtk_rng_probe(struct platform
return ret;
}
- dev_set_drvdata(&pdev->dev, priv);
- pm_runtime_set_autosuspend_delay(&pdev->dev, RNG_AUTOSUSPEND_TIMEOUT);
- pm_runtime_use_autosuspend(&pdev->dev);
- ret = devm_pm_runtime_enable(&pdev->dev);
- if (ret)
- return ret;
+ if (!(priv->flags & MTK_RNG_SMC)) {
+ dev_set_drvdata(&pdev->dev, priv);
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ RNG_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
+ }
dev_info(&pdev->dev, "registered RNG driver\n");
@@ -182,8 +263,12 @@ static const struct dev_pm_ops mtk_rng_p
#endif /* CONFIG_PM */
static const struct of_device_id mtk_rng_match[] = {
- { .compatible = "mediatek,mt7986-rng" },
{ .compatible = "mediatek,mt7623-rng" },
+ { .compatible = "mediatek,mt7622-rng" },
+ { .compatible = "mediatek,mt7981-rng", .data = (void *)MTK_RNG_SMC },
+ { .compatible = "mediatek,mt7986-rng" },
+ { .compatible = "mediatek,mt7987-rng", .data = (void *)MTK_RNG_SMC },
+ { .compatible = "mediatek,mt7988-rng", .data = (void *)MTK_RNG_SMC },
{},
};
MODULE_DEVICE_TABLE(of, mtk_rng_match);
@@ -201,4 +286,5 @@ module_platform_driver(mtk_rng_driver);
MODULE_DESCRIPTION("Mediatek Random Number Generator Driver");
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
MODULE_LICENSE("GPL");