* [PATCH v10 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100
2026-10-23 2:12 [PATCH v10 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
@ 2026-10-23 2:12 ` lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 2/6] hwrng: starfive - fix runtime PM and power teardown handling lianfeng.ouyang
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-10-23 2:12 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel, Lianfeng Ouyang
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
Update the StarFive TRNG DT bindings to reflect current SoC support.
The obsolete "starfive,jh8100-trng" compatible string is removed since
JH8100 SoC is no longer supported. A new "starfive,jhb100-trng"
compatible string is added for JHB100 SoC TRNG support.
The maintainer entry is also updated to reflect current ownership as the
previous maintainer has resigned.
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
.../devicetree/bindings/rng/starfive,jh7110-trng.yaml | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
index 4639247e9e51..d21769b7d54e 100644
--- a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
+++ b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
@@ -7,15 +7,13 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: StarFive SoC TRNG Module
maintainers:
- - Jia Jie Ho <jiajie.ho@starfivetech.com>
+ - Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
properties:
compatible:
- oneOf:
- - items:
- - const: starfive,jh8100-trng
- - const: starfive,jh7110-trng
- - const: starfive,jh7110-trng
+ enum:
+ - starfive,jh7110-trng
+ - starfive,jhb100-trng
reg:
maxItems: 1
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v10 2/6] hwrng: starfive - fix runtime PM and power teardown handling
2026-10-23 2:12 [PATCH v10 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100 lianfeng.ouyang
@ 2026-10-23 2:12 ` lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 3/6] hwrng: starfive - move the SEU reseed out of the interrupt handler lianfeng.ouyang
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-10-23 2:12 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel, Lianfeng Ouyang
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
The runtime PM bookkeeping of the driver does not match the hardware
state it leaves behind:
- probe enables the clocks and deasserts the reset, but then calls
pm_runtime_enable() without marking the device active. The first
pm_runtime_get_sync() in ->read() therefore runs the resume callback
and enables the clocks a second time, and the autosuspend only ever
drops that extra reference, so the clocks are never gated.
- the system sleep ops point at the runtime callbacks. When the device
is already runtime suspended, a system suspend gates the clocks a
second time.
- ->cleanup() asserts the reset and gates the clocks, but ->init()
never undoes that. Deselecting and reselecting the device through
rng_current reinitialises it with the clocks off. On remove, the
clocks taken in probe are only dropped if ->cleanup() happened to
run.
- ->init() and ->cleanup() access the registers without holding a
runtime PM reference.
Move the clock and reset handling into starfive_trng_power_on() and
starfive_trng_power_off(), which track what is currently enabled, and
tear the hardware down from a devm action so that every probe error
path and remove unwind it. Mark the device active before enabling
runtime PM with devm_pm_runtime_set_active_enabled() and let it
autosuspend from pm_runtime_idle(). Use DEFINE_RUNTIME_DEV_PM_OPS() so
that system sleep goes through pm_runtime_force_suspend() and
pm_runtime_force_resume(). Take a runtime PM reference in ->init() and
->cleanup(), and make ->cleanup() only quiesce the hardware; runtime PM
and the devm action take care of powering it down.
Fixes: 9b2b61126a06 ("hwrng: starfive - Add runtime pm ops")
Fixes: c388f458bc34 ("hwrng: starfive - Add TRNG driver for StarFive SoC")
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
drivers/char/hw_random/jh7110-trng.c | 150 ++++++++++++++++++---------
1 file changed, 103 insertions(+), 47 deletions(-)
diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
index 61eb4db7ac01..17932cc6d7aa 100644
--- a/drivers/char/hw_random/jh7110-trng.c
+++ b/drivers/char/hw_random/jh7110-trng.c
@@ -104,6 +104,8 @@ struct starfive_trng {
u32 mode;
u32 mission;
u32 reseed;
+ bool clk_on;
+ bool rst_deassert;
/* protects against concurrent write to ctrl register */
spinlock_t write_lock;
};
@@ -138,6 +140,60 @@ static inline void starfive_trng_irq_mask_clear(struct starfive_trng *trng)
writel(data, trng->base + STARFIVE_ISTAT);
}
+static int starfive_trng_power_on(struct starfive_trng *trng)
+{
+ int ret;
+
+ if (!trng->clk_on) {
+ ret = clk_prepare_enable(trng->hclk);
+ if (ret)
+ goto err_hclk;
+
+ ret = clk_prepare_enable(trng->ahb);
+ if (ret)
+ goto err_ahb;
+
+ trng->clk_on = true;
+ }
+
+ if (!trng->rst_deassert) {
+ ret = reset_control_deassert(trng->rst);
+ if (ret)
+ goto err_rst;
+
+ trng->rst_deassert = true;
+ }
+
+ return 0;
+
+err_rst:
+ clk_disable_unprepare(trng->ahb);
+err_ahb:
+ clk_disable_unprepare(trng->hclk);
+ trng->clk_on = false;
+err_hclk:
+ return ret;
+}
+
+static void starfive_trng_power_off(struct starfive_trng *trng, bool assert_rst)
+{
+ if (assert_rst && trng->rst_deassert) {
+ reset_control_assert(trng->rst);
+ trng->rst_deassert = false;
+ }
+
+ if (trng->clk_on) {
+ clk_disable_unprepare(trng->ahb);
+ clk_disable_unprepare(trng->hclk);
+ trng->clk_on = false;
+ }
+}
+
+static void starfive_trng_power_off_action(void *data)
+{
+ starfive_trng_power_off(data, true);
+}
+
static int starfive_trng_cmd(struct starfive_trng *trng, u32 cmd, bool wait)
{
int wait_time = 1000;
@@ -174,6 +230,13 @@ static int starfive_trng_init(struct hwrng *rng)
{
struct starfive_trng *trng = to_trng(rng);
u32 mode, intr = 0;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(trng->dev);
+ if (ret < 0) {
+ dev_err(trng->dev, "Failed to wake device for init: %d\n", ret);
+ return ret;
+ }
/* setup Auto Request/Age register */
writel(autoage, trng->base + STARFIVE_AUTO_AGE);
@@ -201,7 +264,11 @@ static int starfive_trng_init(struct hwrng *rng)
writel(mode, trng->base + STARFIVE_MODE);
- return starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED, 1);
+ ret = starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED, 1);
+
+ pm_runtime_put_autosuspend(trng->dev);
+
+ return ret;
}
static irqreturn_t starfive_trng_irq(int irq, void *priv)
@@ -234,12 +301,17 @@ static irqreturn_t starfive_trng_irq(int irq, void *priv)
static void starfive_trng_cleanup(struct hwrng *rng)
{
struct starfive_trng *trng = to_trng(rng);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(trng->dev);
+ if (ret < 0) {
+ dev_err(trng->dev, "Failed to wake device for cleanup: %d\n", ret);
+ return;
+ }
- writel(0, trng->base + STARFIVE_CTRL);
+ writel(STARFIVE_CTRL_EXEC_NOP, trng->base + STARFIVE_CTRL);
- reset_control_assert(trng->rst);
- clk_disable_unprepare(trng->hclk);
- clk_disable_unprepare(trng->ahb);
+ pm_runtime_put_sync_suspend(trng->dev);
}
static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
@@ -247,7 +319,11 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai
struct starfive_trng *trng = to_trng(rng);
int ret;
- pm_runtime_get_sync(trng->dev);
+ ret = pm_runtime_resume_and_get(trng->dev);
+ if (ret < 0) {
+ dev_warn_ratelimited(trng->dev, "Failed to wake device for read: %d\n", ret);
+ return ret;
+ }
if (trng->mode == PRNG_256BIT)
max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));
@@ -270,7 +346,7 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai
ret = max;
out_put:
- pm_runtime_put_sync_autosuspend(trng->dev);
+ pm_runtime_put_autosuspend(trng->dev);
return ret;
}
@@ -320,17 +396,13 @@ static int starfive_trng_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(trng->rst),
"Error getting hardware reset line\n");
- ret = clk_prepare_enable(trng->hclk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(trng->ahb);
+ ret = starfive_trng_power_on(trng);
if (ret)
- goto disable_hclk;
+ return dev_err_probe(&pdev->dev, ret, "Failed to power on\n");
- ret = reset_control_deassert(trng->rst);
+ ret = devm_add_action_or_reset(&pdev->dev, starfive_trng_power_off_action, trng);
if (ret)
- goto disable_ahb;
+ return ret;
trng->rng.name = dev_driver_string(&pdev->dev);
trng->rng.init = starfive_trng_init;
@@ -343,71 +415,55 @@ static int starfive_trng_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
- pm_runtime_enable(&pdev->dev);
+
+ ret = devm_pm_runtime_set_active_enabled(&pdev->dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to enable runtime PM\n");
ret = devm_hwrng_register(&pdev->dev, &trng->rng);
- if (ret) {
- pm_runtime_disable(&pdev->dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
- reset_control_assert(trng->rst);
- ret = dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
- goto disable_ahb;
- }
+ pm_runtime_idle(&pdev->dev);
return 0;
-
-disable_ahb:
- clk_disable_unprepare(trng->ahb);
-disable_hclk:
- clk_disable_unprepare(trng->hclk);
-
- return ret;
}
-static int __maybe_unused starfive_trng_suspend(struct device *dev)
+static int starfive_trng_suspend(struct device *dev)
{
struct starfive_trng *trng = dev_get_drvdata(dev);
- clk_disable_unprepare(trng->hclk);
- clk_disable_unprepare(trng->ahb);
+ starfive_trng_power_off(trng, false);
return 0;
}
-static int __maybe_unused starfive_trng_resume(struct device *dev)
+static int starfive_trng_resume(struct device *dev)
{
struct starfive_trng *trng = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(trng->hclk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(trng->ahb);
+ ret = starfive_trng_power_on(trng);
if (ret)
- clk_disable_unprepare(trng->hclk);
+ dev_err(dev, "Failed to power on: %d\n", ret);
return ret;
}
-static const struct dev_pm_ops starfive_trng_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(starfive_trng_suspend,
- starfive_trng_resume)
- SET_RUNTIME_PM_OPS(starfive_trng_suspend,
- starfive_trng_resume, NULL)
-};
-
static const struct of_device_id trng_dt_ids[] __maybe_unused = {
{ .compatible = "starfive,jh7110-trng" },
{ }
};
MODULE_DEVICE_TABLE(of, trng_dt_ids);
+static DEFINE_RUNTIME_DEV_PM_OPS(starfive_trng_pm_ops,
+ starfive_trng_suspend, starfive_trng_resume, NULL);
+
static struct platform_driver starfive_trng_driver = {
.probe = starfive_trng_probe,
.driver = {
.name = "jh7110-trng",
- .pm = &starfive_trng_pm_ops,
+ .pm = pm_ptr(&starfive_trng_pm_ops),
.of_match_table = of_match_ptr(trng_dt_ids),
},
};
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v10 3/6] hwrng: starfive - move the SEU reseed out of the interrupt handler
2026-10-23 2:12 [PATCH v10 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100 lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 2/6] hwrng: starfive - fix runtime PM and power teardown handling lianfeng.ouyang
@ 2026-10-23 2:12 ` lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 4/6] hwrng: starfive - tidy up includes and drop unused code lianfeng.ouyang
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-10-23 2:12 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel, Lianfeng Ouyang
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
When the hardware reports an LFSR lockup (a single event upset), the
interrupt handler writes the reseed command straight into the CTRL
register. The spinlock only serialises that register write: nothing
stops it from racing with a command ->read() has just issued, nothing
waits for the reseed to complete before the next command is issued, and
a sample taken from a locked up generator is handed out as random data.
The 40us timeout used for wait == 0 also rounds up to a jiffy and so is
not what it claims to be.
Rework the command handling around a mutex that serialises complete
command sequences instead:
- starfive_trng_cmd() checks that the hardware is idle before issuing
a command, takes an explicit timeout, and resynchronises with the
interrupt handler before declaring a timeout so that a late
completion is not left behind for the next command.
- The interrupt handler only records a lockup in trng->need_reseed and
queues a work item which performs the reseed under the mutex.
starfive_trng_irq_clear() records a lockup it clears for the same
reason, so that one observed across suspend/resume is not lost.
- ->read() reseeds before sampling when a lockup is pending, and
retries once when the lockup is reported together with RAND_RDY.
Non-blocking readers return no data instead and make sure the work
is queued, so they cannot stall when the lockup was observed outside
the interrupt handler or a previous reseed attempt failed.
- The interrupt is requested with IRQF_NO_AUTOEN and is only enabled
between ->init() and ->cleanup(). trng->initialized tracks that
state: the hwrng core may call ->init() again without an intervening
->cleanup() when the device is deselected and quickly reselected,
and it calls ->cleanup() after a failed ->init(), so neither may
enable or disable the line a second time.
- The suspend callback masks the interrupts, and asserts the reset
while the device is not initialised; resume re-enables the
interrupts when it is.
Fixes: c388f458bc34 ("hwrng: starfive - Add TRNG driver for StarFive SoC")
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
drivers/char/hw_random/jh7110-trng.c | 352 ++++++++++++++++++++-------
1 file changed, 261 insertions(+), 91 deletions(-)
diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
index 17932cc6d7aa..8e0e7865453b 100644
--- a/drivers/char/hw_random/jh7110-trng.c
+++ b/drivers/char/hw_random/jh7110-trng.c
@@ -8,18 +8,22 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/devm-helpers.h>
#include <linux/err.h>
#include <linux/hw_random.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <linux/reset.h>
+#include <linux/workqueue.h>
/* trng register offset */
#define STARFIVE_CTRL 0x00
@@ -78,7 +82,14 @@
#define STARFIVE_ISTAT_SEED_DONE BIT(1)
#define STARFIVE_ISTAT_LFSR_LOCKUP BIT(4)
+#define STARFIVE_ISTAT_ALL (STARFIVE_ISTAT_RAND_RDY | \
+ STARFIVE_ISTAT_SEED_DONE | \
+ STARFIVE_ISTAT_LFSR_LOCKUP)
+
#define STARFIVE_RAND_LEN sizeof(u32)
+#define STARFIVE_RANDRESEED_TIMEOUT_MS 50
+#define STARFIVE_RANDGEN_TIMEOUT_MS 20
+#define STARFIVE_RANDGEN_NOWAIT_TIMEOUT 1
#define to_trng(p) container_of(p, struct starfive_trng, rng)
@@ -95,21 +106,29 @@ enum mode {
struct starfive_trng {
struct device *dev;
void __iomem *base;
+ int irq;
struct clk *hclk;
struct clk *ahb;
struct reset_control *rst;
struct hwrng rng;
struct completion random_done;
struct completion reseed_done;
+ struct work_struct work;
u32 mode;
u32 mission;
u32 reseed;
+ bool need_reseed;
+ bool initialized;
bool clk_on;
bool rst_deassert;
- /* protects against concurrent write to ctrl register */
- spinlock_t write_lock;
+ struct mutex lock; /* protect trng cmd seq */
};
+static inline struct starfive_trng *work_to_trng(struct work_struct *work)
+{
+ return container_of(work, struct starfive_trng, work);
+}
+
static u16 autoreq;
module_param(autoreq, ushort, 0);
MODULE_PARM_DESC(autoreq, "Auto-reseeding after random number requests by host reaches specified counter:\n"
@@ -122,22 +141,38 @@ MODULE_PARM_DESC(autoage, "Auto-reseeding after specified timer countdowns to 0:
" 0 - disable timer\n"
" other - reload value for internal timer");
-static inline int starfive_trng_wait_idle(struct starfive_trng *trng)
+static bool starfive_trng_idle(struct starfive_trng *trng, bool wait)
{
- u32 stat;
+ u32 stat, busy = STARFIVE_STAT_RAND_GENERATING | STARFIVE_STAT_RAND_SEEDING;
- return readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat,
- !(stat & (STARFIVE_STAT_RAND_GENERATING |
- STARFIVE_STAT_RAND_SEEDING)),
- 10, 100000);
+ if (!wait)
+ return !(readl_relaxed(trng->base + STARFIVE_STAT) & busy);
+
+ return !readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat,
+ !(stat & busy),
+ 10, 100000);
}
-static inline void starfive_trng_irq_mask_clear(struct starfive_trng *trng)
+/*
+ * Record a pending LFSR lockup and make sure it gets serviced. queue_work()
+ * is a no-op while the work is already pending, so this is safe to call
+ * from any context that notices the lockup.
+ */
+static void starfive_trng_request_reseed(struct starfive_trng *trng)
{
- /* clear register: ISTAT */
- u32 data = readl(trng->base + STARFIVE_ISTAT);
+ WRITE_ONCE(trng->need_reseed, true);
+ queue_work(system_freezable_wq, &trng->work);
+}
+
+static void starfive_trng_irq_clear(struct starfive_trng *trng)
+{
+ u32 istat = readl(trng->base + STARFIVE_ISTAT);
+
+ /* Do not lose an LFSR lockup that has not been serviced yet. */
+ if (istat & STARFIVE_ISTAT_LFSR_LOCKUP)
+ starfive_trng_request_reseed(trng);
- writel(data, trng->base + STARFIVE_ISTAT);
+ writel(istat, trng->base + STARFIVE_ISTAT);
}
static int starfive_trng_power_on(struct starfive_trng *trng)
@@ -194,42 +229,71 @@ static void starfive_trng_power_off_action(void *data)
starfive_trng_power_off(data, true);
}
-static int starfive_trng_cmd(struct starfive_trng *trng, u32 cmd, bool wait)
+static int starfive_trng_cmd(struct starfive_trng *trng, u32 cmd, unsigned long timeout, bool wait)
{
- int wait_time = 1000;
+ struct completion *done;
- /* allow up to 40 us for wait == 0 */
- if (!wait)
- wait_time = 40;
+ lockdep_assert_held(&trng->lock);
+
+ if (!starfive_trng_idle(trng, wait))
+ return wait ? -ETIMEDOUT : -EBUSY;
switch (cmd) {
case STARFIVE_CTRL_GENE_RANDNUM:
- reinit_completion(&trng->random_done);
- spin_lock_irq(&trng->write_lock);
- writel(cmd, trng->base + STARFIVE_CTRL);
- spin_unlock_irq(&trng->write_lock);
- if (!wait_for_completion_timeout(&trng->random_done, usecs_to_jiffies(wait_time)))
- return -ETIMEDOUT;
+ done = &trng->random_done;
break;
case STARFIVE_CTRL_EXEC_RANDRESEED:
- reinit_completion(&trng->reseed_done);
- spin_lock_irq(&trng->write_lock);
- writel(cmd, trng->base + STARFIVE_CTRL);
- spin_unlock_irq(&trng->write_lock);
- if (!wait_for_completion_timeout(&trng->reseed_done, usecs_to_jiffies(wait_time)))
- return -ETIMEDOUT;
+ done = &trng->reseed_done;
break;
default:
return -EINVAL;
}
+ reinit_completion(done);
+ writel(cmd, trng->base + STARFIVE_CTRL);
+ if (!wait_for_completion_timeout(done, timeout)) {
+ synchronize_irq(trng->irq);
+ if (!try_wait_for_completion(done))
+ return -ETIMEDOUT;
+ }
+
return 0;
}
+static int starfive_trng_reseed(struct starfive_trng *trng)
+{
+ int ret;
+
+ lockdep_assert_held(&trng->lock);
+
+ WRITE_ONCE(trng->need_reseed, false);
+ ret = starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED,
+ msecs_to_jiffies(STARFIVE_RANDRESEED_TIMEOUT_MS), true);
+ if (ret)
+ WRITE_ONCE(trng->need_reseed, true);
+
+ return ret;
+}
+
+static void starfive_trng_hw_setup(struct starfive_trng *trng)
+{
+ u32 mode;
+
+ /* setup Auto Request/Age register */
+ writel(autoage, trng->base + STARFIVE_AUTO_AGE);
+ writel(autoreq, trng->base + STARFIVE_AUTO_RQSTS);
+
+ mode = readl(trng->base + STARFIVE_MODE);
+ if (trng->mode == PRNG_128BIT)
+ mode &= ~STARFIVE_MODE_R256;
+ else
+ mode |= STARFIVE_MODE_R256;
+ writel(mode, trng->base + STARFIVE_MODE);
+}
+
static int starfive_trng_init(struct hwrng *rng)
{
struct starfive_trng *trng = to_trng(rng);
- u32 mode, intr = 0;
int ret;
ret = pm_runtime_resume_and_get(trng->dev);
@@ -238,37 +302,62 @@ static int starfive_trng_init(struct hwrng *rng)
return ret;
}
- /* setup Auto Request/Age register */
- writel(autoage, trng->base + STARFIVE_AUTO_AGE);
- writel(autoreq, trng->base + STARFIVE_AUTO_RQSTS);
+ mutex_lock(&trng->lock);
+
+ /*
+ * The hwrng core can call init again without an intervening
+ * cleanup: deselecting and quickly reselecting the device makes
+ * cleanup_rng_work() skip ->cleanup() once the refcount is back up.
+ * Only bring up the interrupt line once in that case.
+ */
+ if (!trng->initialized) {
+ starfive_trng_irq_clear(trng);
+ starfive_trng_hw_setup(trng);
+
+ enable_irq(trng->irq);
+ writel(STARFIVE_IE_ALL, trng->base + STARFIVE_IE);
+ trng->initialized = true;
+ }
+
+ ret = starfive_trng_reseed(trng);
+ if (ret) {
+ trng->initialized = false;
+ writel(0, trng->base + STARFIVE_IE);
+ starfive_trng_irq_clear(trng);
+ disable_irq(trng->irq);
+ }
- /* clear register: ISTAT */
- starfive_trng_irq_mask_clear(trng);
+ mutex_unlock(&trng->lock);
- intr |= STARFIVE_IE_ALL;
- writel(intr, trng->base + STARFIVE_IE);
+ pm_runtime_put_autosuspend(trng->dev);
- mode = readl(trng->base + STARFIVE_MODE);
+ return ret;
+}
- switch (trng->mode) {
- case PRNG_128BIT:
- mode &= ~STARFIVE_MODE_R256;
- break;
- case PRNG_256BIT:
- mode |= STARFIVE_MODE_R256;
- break;
- default:
- mode |= STARFIVE_MODE_R256;
- break;
+static void starfive_trng_reseed_work(struct work_struct *work)
+{
+ struct starfive_trng *trng = work_to_trng(work);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(trng->dev);
+ if (ret < 0) {
+ dev_err(trng->dev, "Failed to wake device for reseed: %d\n", ret);
+ return;
}
- writel(mode, trng->base + STARFIVE_MODE);
+ mutex_lock(&trng->lock);
- ret = starfive_trng_cmd(trng, STARFIVE_CTRL_EXEC_RANDRESEED, 1);
+ if (!READ_ONCE(trng->need_reseed) || !trng->initialized)
+ goto unlock;
- pm_runtime_put_autosuspend(trng->dev);
+ ret = starfive_trng_reseed(trng);
+ if (ret)
+ dev_err(trng->dev, "Reseed after LFSR lockup failed: %d\n", ret);
- return ret;
+unlock:
+ mutex_unlock(&trng->lock);
+
+ pm_runtime_put_autosuspend(trng->dev);
}
static irqreturn_t starfive_trng_irq(int irq, void *priv)
@@ -276,24 +365,21 @@ static irqreturn_t starfive_trng_irq(int irq, void *priv)
u32 status;
struct starfive_trng *trng = (struct starfive_trng *)priv;
- status = readl(trng->base + STARFIVE_ISTAT);
- if (status & STARFIVE_ISTAT_RAND_RDY) {
- writel(STARFIVE_ISTAT_RAND_RDY, trng->base + STARFIVE_ISTAT);
+ status = readl(trng->base + STARFIVE_ISTAT) & STARFIVE_ISTAT_ALL;
+ if (!status)
+ return IRQ_NONE;
+
+ writel(status, trng->base + STARFIVE_ISTAT);
+
+ /* SEU occurred, reseeding required */
+ if (status & STARFIVE_ISTAT_LFSR_LOCKUP)
+ starfive_trng_request_reseed(trng);
+
+ if (status & STARFIVE_ISTAT_RAND_RDY)
complete(&trng->random_done);
- }
- if (status & STARFIVE_ISTAT_SEED_DONE) {
- writel(STARFIVE_ISTAT_SEED_DONE, trng->base + STARFIVE_ISTAT);
+ if (status & STARFIVE_ISTAT_SEED_DONE)
complete(&trng->reseed_done);
- }
-
- if (status & STARFIVE_ISTAT_LFSR_LOCKUP) {
- writel(STARFIVE_ISTAT_LFSR_LOCKUP, trng->base + STARFIVE_ISTAT);
- /* SEU occurred, reseeding required*/
- spin_lock(&trng->write_lock);
- writel(STARFIVE_CTRL_EXEC_RANDRESEED, trng->base + STARFIVE_CTRL);
- spin_unlock(&trng->write_lock);
- }
return IRQ_HANDLED;
}
@@ -301,15 +387,36 @@ static irqreturn_t starfive_trng_irq(int irq, void *priv)
static void starfive_trng_cleanup(struct hwrng *rng)
{
struct starfive_trng *trng = to_trng(rng);
+ bool initialized;
int ret;
ret = pm_runtime_resume_and_get(trng->dev);
- if (ret < 0) {
+ if (ret < 0)
dev_err(trng->dev, "Failed to wake device for cleanup: %d\n", ret);
- return;
+
+ mutex_lock(&trng->lock);
+ initialized = trng->initialized;
+ trng->initialized = false;
+ if (ret >= 0 && initialized) {
+ writel(0, trng->base + STARFIVE_IE);
+ starfive_trng_irq_clear(trng);
}
+ mutex_unlock(&trng->lock);
+
+ /*
+ * cleanup also runs after a failed init, which has already undone
+ * its enable_irq(). Only disable the line if init left it enabled.
+ */
+ if (initialized)
+ disable_irq(trng->irq);
+ cancel_work_sync(&trng->work);
+ if (ret < 0)
+ return;
+
+ mutex_lock(&trng->lock);
writel(STARFIVE_CTRL_EXEC_NOP, trng->base + STARFIVE_CTRL);
+ mutex_unlock(&trng->lock);
pm_runtime_put_sync_suspend(trng->dev);
}
@@ -317,7 +424,8 @@ static void starfive_trng_cleanup(struct hwrng *rng)
static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
{
struct starfive_trng *trng = to_trng(rng);
- int ret;
+ unsigned long timeout;
+ int ret, retry = 1;
ret = pm_runtime_resume_and_get(trng->dev);
if (ret < 0) {
@@ -325,35 +433,75 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai
return ret;
}
- if (trng->mode == PRNG_256BIT)
- max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));
- else
- max = min_t(size_t, max, (STARFIVE_RAND_LEN * 4));
-
if (wait) {
- ret = starfive_trng_wait_idle(trng);
- if (ret) {
- ret = -ETIMEDOUT;
+ mutex_lock(&trng->lock);
+ timeout = msecs_to_jiffies(STARFIVE_RANDGEN_TIMEOUT_MS);
+ } else {
+ /* allow up to 1 jiffy for wait == 0 */
+ if (!mutex_trylock(&trng->lock)) {
+ ret = 0;
goto out_put;
}
+ timeout = STARFIVE_RANDGEN_NOWAIT_TIMEOUT;
}
- ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait);
- if (ret)
- goto out_put;
+ for (;;) {
+ /*
+ * A pending LFSR lockup taints the next sample. Blocking
+ * callers reseed here; non-blocking callers return no data
+ * and leave it to the reseed work. Re-queue it explicitly:
+ * the lockup may have been seen by starfive_trng_irq_clear()
+ * or a previous reseed attempt may have failed.
+ */
+ if (READ_ONCE(trng->need_reseed)) {
+ if (!wait) {
+ starfive_trng_request_reseed(trng);
+ ret = 0;
+ goto out_unlock;
+ }
+ ret = starfive_trng_reseed(trng);
+ if (ret)
+ goto out_unlock;
+ }
+
+ ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, timeout, wait);
+ if (ret) {
+ if (!wait && (ret == -EBUSY || ret == -ETIMEDOUT))
+ ret = 0;
+ goto out_unlock;
+ }
+
+ /* Lockup reported together with RAND_RDY: reseed and retry once */
+ if (!READ_ONCE(trng->need_reseed))
+ break;
+
+ if (!wait || !retry--) {
+ ret = 0;
+ goto out_unlock;
+ }
+ }
+
+ if (trng->mode == PRNG_256BIT)
+ max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));
+ else
+ max = min_t(size_t, max, (STARFIVE_RAND_LEN * 4));
memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max);
+
ret = max;
+out_unlock:
+ mutex_unlock(&trng->lock);
+
out_put:
pm_runtime_put_autosuspend(trng->dev);
+
return ret;
}
static int starfive_trng_probe(struct platform_device *pdev)
{
int ret;
- int irq;
struct starfive_trng *trng;
trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
@@ -368,16 +516,14 @@ static int starfive_trng_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),
"Error remapping memory for platform device.\n");
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ trng->irq = platform_get_irq(pdev, 0);
+ if (trng->irq < 0)
+ return trng->irq;
init_completion(&trng->random_done);
init_completion(&trng->reseed_done);
- spin_lock_init(&trng->write_lock);
- ret = devm_request_irq(&pdev->dev, irq, starfive_trng_irq, 0, pdev->name,
- (void *)trng);
+ ret = devm_mutex_init(&pdev->dev, &trng->lock);
if (ret)
return ret;
@@ -420,6 +566,15 @@ static int starfive_trng_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to enable runtime PM\n");
+ ret = devm_work_autocancel(&pdev->dev, &trng->work, starfive_trng_reseed_work);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, trng->irq, starfive_trng_irq, IRQF_NO_AUTOEN,
+ pdev->name, (void *)trng);
+ if (ret)
+ return ret;
+
ret = devm_hwrng_register(&pdev->dev, &trng->rng);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
@@ -432,8 +587,16 @@ static int starfive_trng_probe(struct platform_device *pdev)
static int starfive_trng_suspend(struct device *dev)
{
struct starfive_trng *trng = dev_get_drvdata(dev);
+ bool assert_rst;
+
+ mutex_lock(&trng->lock);
+ assert_rst = !trng->initialized;
+ writel(0, trng->base + STARFIVE_IE);
+ starfive_trng_irq_clear(trng);
+ mutex_unlock(&trng->lock);
- starfive_trng_power_off(trng, false);
+ synchronize_irq(trng->irq);
+ starfive_trng_power_off(trng, assert_rst);
return 0;
}
@@ -444,10 +607,17 @@ static int starfive_trng_resume(struct device *dev)
int ret;
ret = starfive_trng_power_on(trng);
- if (ret)
+ if (ret) {
dev_err(dev, "Failed to power on: %d\n", ret);
+ return ret;
+ }
- return ret;
+ if (trng->initialized) {
+ starfive_trng_irq_clear(trng);
+ writel(STARFIVE_IE_ALL, trng->base + STARFIVE_IE);
+ }
+
+ return 0;
}
static const struct of_device_id trng_dt_ids[] __maybe_unused = {
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v10 4/6] hwrng: starfive - tidy up includes and drop unused code
2026-10-23 2:12 [PATCH v10 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
` (2 preceding siblings ...)
2026-10-23 2:12 ` [PATCH v10 3/6] hwrng: starfive - move the SEU reseed out of the interrupt handler lianfeng.ouyang
@ 2026-10-23 2:12 ` lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 5/6] hwrng: starfive - add JHB100 support lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 6/6] MAINTAINERS: update the StarFive TRNG driver maintainer lianfeng.ouyang
5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-10-23 2:12 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel, Lianfeng Ouyang
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
Make the driver include what it uses: add <linux/bits.h>,
<linux/container_of.h>, <linux/device.h>, <linux/errno.h>,
<linux/jiffies.h>, <linux/minmax.h> and <linux/types.h>, and drop
<linux/delay.h>, <linux/kernel.h>, <linux/of.h> and <linux/random.h>,
which nothing in the driver needs. struct of_device_id is already
provided through <linux/platform_device.h>, so <linux/mod_devicetable.h>
is not needed either.
With <linux/of.h> gone, stop wrapping the match table in of_match_ptr()
and drop its __maybe_unused annotation.
Also drop the unused reseed enum and struct member, name the mode enum
after the driver, use a local struct device pointer in probe, name the
interrupt after the device and replace min_t() with min().
No functional change.
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
drivers/char/hw_random/jh7110-trng.c | 81 ++++++++++++++--------------
1 file changed, 39 insertions(+), 42 deletions(-)
diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
index 8e0e7865453b..3bec7b53770e 100644
--- a/drivers/char/hw_random/jh7110-trng.c
+++ b/drivers/char/hw_random/jh7110-trng.c
@@ -5,24 +5,27 @@
* Copyright (C) 2022 StarFive Technology Co.
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/completion.h>
-#include <linux/delay.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
#include <linux/devm-helpers.h>
#include <linux/err.h>
+#include <linux/errno.h>
#include <linux/hw_random.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
-#include <linux/kernel.h>
+#include <linux/jiffies.h>
#include <linux/lockdep.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/random.h>
#include <linux/reset.h>
+#include <linux/types.h>
#include <linux/workqueue.h>
/* trng register offset */
@@ -93,12 +96,7 @@
#define to_trng(p) container_of(p, struct starfive_trng, rng)
-enum reseed {
- RANDOM_RESEED,
- NONCE_RESEED,
-};
-
-enum mode {
+enum starfive_trng_mode {
PRNG_128BIT,
PRNG_256BIT,
};
@@ -116,7 +114,6 @@ struct starfive_trng {
struct work_struct work;
u32 mode;
u32 mission;
- u32 reseed;
bool need_reseed;
bool initialized;
bool clk_on;
@@ -363,7 +360,7 @@ static void starfive_trng_reseed_work(struct work_struct *work)
static irqreturn_t starfive_trng_irq(int irq, void *priv)
{
u32 status;
- struct starfive_trng *trng = (struct starfive_trng *)priv;
+ struct starfive_trng *trng = priv;
status = readl(trng->base + STARFIVE_ISTAT) & STARFIVE_ISTAT_ALL;
if (!status)
@@ -482,9 +479,9 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai
}
if (trng->mode == PRNG_256BIT)
- max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));
+ max = min(max, STARFIVE_RAND_LEN * 8);
else
- max = min_t(size_t, max, (STARFIVE_RAND_LEN * 4));
+ max = min(max, STARFIVE_RAND_LEN * 4);
memcpy_fromio(buf, trng->base + STARFIVE_RAND0, max);
@@ -501,20 +498,21 @@ static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wai
static int starfive_trng_probe(struct platform_device *pdev)
{
- int ret;
+ struct device *dev = &pdev->dev;
struct starfive_trng *trng;
+ int ret;
- trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
+ trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL);
if (!trng)
return -ENOMEM;
platform_set_drvdata(pdev, trng);
- trng->dev = &pdev->dev;
+ trng->dev = dev;
trng->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(trng->base))
- return dev_err_probe(&pdev->dev, PTR_ERR(trng->base),
- "Error remapping memory for platform device.\n");
+ return dev_err_probe(dev, PTR_ERR(trng->base),
+ "Error remapping memory for platform device\n");
trng->irq = platform_get_irq(pdev, 0);
if (trng->irq < 0)
@@ -523,63 +521,62 @@ static int starfive_trng_probe(struct platform_device *pdev)
init_completion(&trng->random_done);
init_completion(&trng->reseed_done);
- ret = devm_mutex_init(&pdev->dev, &trng->lock);
+ ret = devm_mutex_init(dev, &trng->lock);
if (ret)
return ret;
- trng->hclk = devm_clk_get(&pdev->dev, "hclk");
+ trng->hclk = devm_clk_get(dev, "hclk");
if (IS_ERR(trng->hclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(trng->hclk),
+ return dev_err_probe(dev, PTR_ERR(trng->hclk),
"Error getting hardware reference clock\n");
- trng->ahb = devm_clk_get(&pdev->dev, "ahb");
+ trng->ahb = devm_clk_get(dev, "ahb");
if (IS_ERR(trng->ahb))
- return dev_err_probe(&pdev->dev, PTR_ERR(trng->ahb),
+ return dev_err_probe(dev, PTR_ERR(trng->ahb),
"Error getting ahb reference clock\n");
- trng->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
+ trng->rst = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(trng->rst))
- return dev_err_probe(&pdev->dev, PTR_ERR(trng->rst),
+ return dev_err_probe(dev, PTR_ERR(trng->rst),
"Error getting hardware reset line\n");
ret = starfive_trng_power_on(trng);
if (ret)
- return dev_err_probe(&pdev->dev, ret, "Failed to power on\n");
+ return dev_err_probe(dev, ret, "Failed to power on\n");
- ret = devm_add_action_or_reset(&pdev->dev, starfive_trng_power_off_action, trng);
+ ret = devm_add_action_or_reset(dev, starfive_trng_power_off_action, trng);
if (ret)
return ret;
- trng->rng.name = dev_driver_string(&pdev->dev);
+ trng->rng.name = dev_driver_string(dev);
trng->rng.init = starfive_trng_init;
trng->rng.cleanup = starfive_trng_cleanup;
trng->rng.read = starfive_trng_read;
trng->mode = PRNG_256BIT;
trng->mission = 1;
- trng->reseed = RANDOM_RESEED;
- pm_runtime_use_autosuspend(&pdev->dev);
- pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 100);
- ret = devm_pm_runtime_set_active_enabled(&pdev->dev);
+ ret = devm_pm_runtime_set_active_enabled(dev);
if (ret)
- return dev_err_probe(&pdev->dev, ret, "Failed to enable runtime PM\n");
+ return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
- ret = devm_work_autocancel(&pdev->dev, &trng->work, starfive_trng_reseed_work);
+ ret = devm_work_autocancel(dev, &trng->work, starfive_trng_reseed_work);
if (ret)
return ret;
- ret = devm_request_irq(&pdev->dev, trng->irq, starfive_trng_irq, IRQF_NO_AUTOEN,
- pdev->name, (void *)trng);
+ ret = devm_request_irq(dev, trng->irq, starfive_trng_irq, IRQF_NO_AUTOEN,
+ dev_name(dev), trng);
if (ret)
return ret;
- ret = devm_hwrng_register(&pdev->dev, &trng->rng);
+ ret = devm_hwrng_register(dev, &trng->rng);
if (ret)
- return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
+ return dev_err_probe(dev, ret, "Failed to register hwrng\n");
- pm_runtime_idle(&pdev->dev);
+ pm_runtime_idle(dev);
return 0;
}
@@ -620,7 +617,7 @@ static int starfive_trng_resume(struct device *dev)
return 0;
}
-static const struct of_device_id trng_dt_ids[] __maybe_unused = {
+static const struct of_device_id trng_dt_ids[] = {
{ .compatible = "starfive,jh7110-trng" },
{ }
};
@@ -634,7 +631,7 @@ static struct platform_driver starfive_trng_driver = {
.driver = {
.name = "jh7110-trng",
.pm = pm_ptr(&starfive_trng_pm_ops),
- .of_match_table = of_match_ptr(trng_dt_ids),
+ .of_match_table = trng_dt_ids,
},
};
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v10 5/6] hwrng: starfive - add JHB100 support
2026-10-23 2:12 [PATCH v10 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
` (3 preceding siblings ...)
2026-10-23 2:12 ` [PATCH v10 4/6] hwrng: starfive - tidy up includes and drop unused code lianfeng.ouyang
@ 2026-10-23 2:12 ` lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 6/6] MAINTAINERS: update the StarFive TRNG driver maintainer lianfeng.ouyang
5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-10-23 2:12 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel, Lianfeng Ouyang
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
The JHB100 SoC has the same TRNG as the JH7110, but asserting its reset
while the clocks are still running causes reset-domain-crossing
glitches that can affect downstream IPs. It needs the clocks gated
before the reset is asserted, whereas the JH7110 keeps the original
reset-first order.
Describe the power-down ordering in per-compatible match data, select
it with device_get_match_data(), and add the "starfive,jhb100-trng"
compatible.
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
drivers/char/hw_random/jh7110-trng.c | 45 ++++++++++++++++++++++++++--
1 file changed, 42 insertions(+), 3 deletions(-)
diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
index 3bec7b53770e..1018a28f291d 100644
--- a/drivers/char/hw_random/jh7110-trng.c
+++ b/drivers/char/hw_random/jh7110-trng.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * TRNG driver for the StarFive JH7110 SoC
+ * TRNG driver for the StarFive JH7110/JHB100 SoCs
*
* Copyright (C) 2022 StarFive Technology Co.
*/
@@ -24,6 +24,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/reset.h>
#include <linux/types.h>
#include <linux/workqueue.h>
@@ -101,6 +102,22 @@ enum starfive_trng_mode {
PRNG_256BIT,
};
+/*
+ * Power-down ordering of reset assertion vs. clock gating:
+ * SEQ_RST_FIRST - assert reset, then gate the clocks (JH7110).
+ * SEQ_CLK_FIRST - gate the clocks, then assert reset (JHB100). Asserting
+ * reset with the clocks running causes reset-domain-crossing
+ * glitches that can affect downstream IPs.
+ */
+enum seq_rst_clk {
+ SEQ_RST_FIRST,
+ SEQ_CLK_FIRST,
+};
+
+struct starfive_trng_data {
+ enum seq_rst_clk seq_rst_clk;
+};
+
struct starfive_trng {
struct device *dev;
void __iomem *base;
@@ -112,6 +129,7 @@ struct starfive_trng {
struct completion random_done;
struct completion reseed_done;
struct work_struct work;
+ const struct starfive_trng_data *data;
u32 mode;
u32 mission;
bool need_reseed;
@@ -209,7 +227,10 @@ static int starfive_trng_power_on(struct starfive_trng *trng)
static void starfive_trng_power_off(struct starfive_trng *trng, bool assert_rst)
{
- if (assert_rst && trng->rst_deassert) {
+ bool clk_first = trng->data->seq_rst_clk == SEQ_CLK_FIRST;
+ bool do_rst = assert_rst && trng->rst_deassert;
+
+ if (do_rst && !clk_first) {
reset_control_assert(trng->rst);
trng->rst_deassert = false;
}
@@ -219,6 +240,11 @@ static void starfive_trng_power_off(struct starfive_trng *trng, bool assert_rst)
clk_disable_unprepare(trng->hclk);
trng->clk_on = false;
}
+
+ if (do_rst && clk_first) {
+ reset_control_assert(trng->rst);
+ trng->rst_deassert = false;
+ }
}
static void starfive_trng_power_off_action(void *data)
@@ -507,7 +533,11 @@ static int starfive_trng_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, trng);
+
trng->dev = dev;
+ trng->data = device_get_match_data(dev);
+ if (!trng->data)
+ return dev_err_probe(dev, -ENODEV, "No match data\n");
trng->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(trng->base))
@@ -617,8 +647,17 @@ static int starfive_trng_resume(struct device *dev)
return 0;
}
+static const struct starfive_trng_data jh7110_data = {
+ .seq_rst_clk = SEQ_RST_FIRST,
+};
+
+static const struct starfive_trng_data jhb100_data = {
+ .seq_rst_clk = SEQ_CLK_FIRST,
+};
+
static const struct of_device_id trng_dt_ids[] = {
- { .compatible = "starfive,jh7110-trng" },
+ { .compatible = "starfive,jh7110-trng", .data = &jh7110_data },
+ { .compatible = "starfive,jhb100-trng", .data = &jhb100_data },
{ }
};
MODULE_DEVICE_TABLE(of, trng_dt_ids);
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v10 6/6] MAINTAINERS: update the StarFive TRNG driver maintainer
2026-10-23 2:12 [PATCH v10 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
` (4 preceding siblings ...)
2026-10-23 2:12 ` [PATCH v10 5/6] hwrng: starfive - add JHB100 support lianfeng.ouyang
@ 2026-10-23 2:12 ` lianfeng.ouyang
5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-10-23 2:12 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel, Lianfeng Ouyang
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
Take over maintenance of the StarFive TRNG driver from Jia Jie Ho.
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
MAINTAINERS | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 6d998ed051a8..5211aa88048f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26239,7 +26239,7 @@ F: Documentation/devicetree/bindings/perf/starfive,jh8100-starlink-pmu.yaml
F: drivers/perf/starfive_starlink_pmu.c
STARFIVE TRNG DRIVER
-M: Jia Jie Ho <jiajie.ho@starfivetech.com>
+M: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
S: Supported
F: Documentation/devicetree/bindings/rng/starfive*
F: drivers/char/hw_random/jh7110-trng.c
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread