* [PATCH v10 0/6] hwrng: starfive - fixes and JHB100 support
@ 2026-10-23 2:12 lianfeng.ouyang
2026-10-23 2:12 ` [PATCH v10 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100 lianfeng.ouyang
` (5 more replies)
0 siblings, 6 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>
This series adds support for the TRNG on the StarFive JHB100 SoC. The
JHB100 needs the clocks gated before the reset is asserted, so the
driver grows per-compatible match data describing the power-down
ordering.
Earlier versions of this series carried all of that in the
JHB100 patch, which made it hard to review. v10 splits it up:
1: dt-bindings for the new compatible (unchanged)
2: runtime PM / power teardown fix (backportable)
3: SEU reseed moved out of the interrupt handler, command
serialisation, IRQ lifecycle (backportable)
4: include and cleanup tidy-up, no functional change
5: match data and the JHB100 compatible
6: MAINTAINERS
This patch series adds support for the JHB100 SoC TRNG and fixes
clock/reset teardown ordering issues.
The first patch updates the device tree bindings by removing the
obsolete JH8100 compatible string and adding JHB100 support
while updating the maintainer.
The second patch reworks the driver to ensure proper clock gating
before reset assertion for JHB100 to avoid reset-domain crossing
glitches, fixes RPM usage count handling, and improves error
path cleanup through devm actions.
Changes in v1:
- Remove jh8100-trng and Fix the compatible description in .yaml
- add JHB100 and fix clk/reset teardown logic code
Changes in v2:
- Balance the PM via pm_runtime_set_active() in ->probe
- Add devm_add_action_or_reset() to register a resource rollback function
Changes in v3:
- Supplement .yaml commit message
- Add pm_runtime_get_if_active() to check PM in irq
- Balances pm_runtime_get/put calls in init, read, and cleanup paths
- Add per-compatible match data for the teardown order
Changes in v4:
- Modify .yaml commit information to non point description
- serialise the command sequences with a mutex.
Changes in v5:
- Run the reseed from a workqueue instead of hard IRQ context.
- Balance the probe reset_control_deassert() via a ->cleanup flag in the
PM suspend/resume callbacks (was skipped when already suspended).
- Use devm_pm_runtime_set_active_enabled() instead of manual PM enable.
- Register the IRQ after clk/reset/PM setup; reuse
starfive_trng_release() on request_irq() failure.
- Check reset_control_deassert();
- drop of_match_ptr() from the match table.
Changes in v6:
- check lock in starfive_trng_read() using mutex_trylock when wait is 0
Changes in v7:
- using mutex_trylock when wait is 0, otherwise use mutex_lock()
Changes in v8:
- rebase patches against the latest tree
Changes in v9:
- Fixed the PM runtime reference leak in starfive_trng_read()
- Drop redundant pm_runtime_get/put calls from the interrupt
handler (starfive_trng_irq).
- Use DEFINE_RUNTIME_DEV_PM_OPS() and pm_ptr() for power
management operations.
- Split clock/reset control into starfive_trng_power_on/off()
- Track power state with clk_on/rst_deassert/initialized/need_reseed
instead of open-coded clk control.
- IRQ now requested with IRQF_NO_AUTOEN and enabled only after init
succeeds.
- LFSR_LOCKUP no longer schedules work from hard IRQ: set need_reseed
flag and let starfive_trng_reseed_work() handle reseed on a
freezable workqueue.
- starfive_trng_cmd() takes timeout + wait args; read path checks idle,
reseeds on need_reseed, retries once on lockup, returns 0 for
non-blocking.
- Protect ctrl-cmd sequences with mutex + lockdep_assert_held.
- Cleanup/suspend disables IE, clears ISTAT, synchronize_irq() before
gating clocks/reset.
Changes in v10:
- Split the driver change into fixes, cleanup and the JHB100 addition.
- starfive_trng_init() only enables the interrupt line once and clears
trng->initialized on failure; starfive_trng_cleanup() only disables
the line if init left it enabled. Fixes the unbalanced enable_irq()
when the device is deselected and quickly reselected, and the double
disable_irq() after a failed init.
- Non-blocking reads re-queue the reseed work instead of assuming it
is pending, and starfive_trng_irq_clear() queues it when it clears a
lockup. Fixes the stall of non-blocking reads after a lockup seen
outside the interrupt handler or a failed reseed.
- Do not include <linux/mod_devicetable.h>, <linux/platform_device.h>
already provides struct of_device_id.
- Describe the header rearrangement in the commit log.
- Rebased on top of ace145efdd80.
Lianfeng Ouyang (6):
dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100
hwrng: starfive - fix runtime PM and power teardown handling
hwrng: starfive - move the SEU reseed out of the interrupt handler
hwrng: starfive - tidy up includes and drop unused code
hwrng: starfive - add JHB100 support
MAINTAINERS: update the StarFive TRNG driver maintainer
.../bindings/rng/starfive,jh7110-trng.yaml | 10 +-
MAINTAINERS | 2 +-
drivers/char/hw_random/jh7110-trng.c | 578 +++++++++++++-----
3 files changed, 425 insertions(+), 165 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [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
end of thread, other threads:[~2026-10-23 2:13 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH v10 3/6] hwrng: starfive - move the SEU reseed out of the interrupt handler lianfeng.ouyang
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 ` [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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®