mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support
@ 2026-11-03  7:32 lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 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-11-03  7:32 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. Since v10 it is split up:

  1: dt-bindings for the new compatible (unchanged)
  2: runtime PM / power teardown fix
  3: SEU reseed moved out of the interrupt handler, command
     serialisation, IRQ lifecycle
  4: include and cleanup tidy-up, no functional change
  5: match data and the JHB100 compatible
  6: MAINTAINERS

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.

Changes in v11:
 - starfive_trng_cmd() acks RAND_RDY/SEED_DONE in ISTAT and calls
   synchronize_irq() before re-arming its completion, so that the late
   completion interrupt of a command that timed out cannot complete
   the next command. The v10 commit log claimed this case was already
   covered; it was not.
 - starfive_trng_irq_clear() only queues the reseed work while the
   device is initialised, so the suspend run from ->cleanup() and from
   the runtime PM teardown on unbind can no longer queue the work after
   it has been cancelled.

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          | 587 +++++++++++++-----
 3 files changed, 434 insertions(+), 165 deletions(-)

--
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v11 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100
  2026-11-03  7:32 [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
@ 2026-11-03  7:32 ` lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 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-11-03  7:32 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 v11 2/6] hwrng: starfive - fix runtime PM and power teardown handling
  2026-11-03  7:32 [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100 lianfeng.ouyang
@ 2026-11-03  7:32 ` lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 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-11-03  7:32 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.

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 v11 3/6] hwrng: starfive - move the SEU reseed out of the interrupt handler
  2026-11-03  7:32 [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100 lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 2/6] hwrng: starfive - fix runtime PM and power teardown handling lianfeng.ouyang
@ 2026-11-03  7:32 ` lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 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-11-03  7:32 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 and takes an explicit timeout. A command that timed out
   may still raise its completion interrupt later, after the next
   command has been issued, so the next command acks RAND_RDY and
   SEED_DONE in ISTAT (write-1-to-clear) and synchronises with the
   interrupt handler before it re-arms its completion. A late interrupt
   then finds no status and cannot complete the wrong command. On
   timeout it synchronises with the handler once more, so that a
   completion that has already been signalled is not reported as a
   timeout. A SEED_DONE raised by a reseed that the hardware starts on
   its own (autoreq/autoage) still cannot be told apart from one for a
   host-issued reseed, but the next command waits for the reseed to
   finish either way.

 - 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() does the same for a lockup it clears while
   the device is initialised, so that one observed across
   suspend/resume is not lost. Once the device is no longer
   initialised it leaves the work alone: ->init() reseeds anyway, and
   the suspend that runs from ->cleanup() and from the runtime PM
   teardown on unbind would otherwise queue the work after it has been
   cancelled, and it could then run after the driver data is freed.

 - ->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.

Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
 drivers/char/hw_random/jh7110-trng.c | 361 ++++++++++++++++++++-------
 1 file changed, 270 insertions(+), 91 deletions(-)

diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
index 17932cc6d7aa..e370a49bba83 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) && trng->initialized)
+		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,80 @@ 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;
 	}

+	/*
+	 * A command that timed out earlier may still have its completion
+	 * interrupt in flight. Ack the status bits and flush the handler so
+	 * a late RAND_RDY/SEED_DONE cannot complete this command.
+	 */
+	writel(STARFIVE_ISTAT_RAND_RDY | STARFIVE_ISTAT_SEED_DONE,
+	       trng->base + STARFIVE_ISTAT);
+	synchronize_irq(trng->irq);
+
+	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 +311,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 +374,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 +396,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 +433,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 +442,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 +525,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 +575,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 +596,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 +616,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 v11 4/6] hwrng: starfive - tidy up includes and drop unused code
  2026-11-03  7:32 [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
                   ` (2 preceding siblings ...)
  2026-11-03  7:32 ` [PATCH v11 3/6] hwrng: starfive - move the SEU reseed out of the interrupt handler lianfeng.ouyang
@ 2026-11-03  7:32 ` lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 5/6] hwrng: starfive - add JHB100 support lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 6/6] MAINTAINERS: update the StarFive TRNG driver maintainer lianfeng.ouyang
  5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-11-03  7:32 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().

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 e370a49bba83..82c5d1491912 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;
@@ -372,7 +369,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)
@@ -491,9 +488,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);
 
@@ -510,20 +507,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)
@@ -532,63 +530,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;
 }
@@ -629,7 +626,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" },
 	{ }
 };
@@ -643,7 +640,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 v11 5/6] hwrng: starfive - add JHB100 support
  2026-11-03  7:32 [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
                   ` (3 preceding siblings ...)
  2026-11-03  7:32 ` [PATCH v11 4/6] hwrng: starfive - tidy up includes and drop unused code lianfeng.ouyang
@ 2026-11-03  7:32 ` lianfeng.ouyang
  2026-11-03  7:32 ` [PATCH v11 6/6] MAINTAINERS: update the StarFive TRNG driver maintainer lianfeng.ouyang
  5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-11-03  7:32 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 82c5d1491912..763aacf7dfac 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)
@@ -516,7 +542,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))
@@ -626,8 +656,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 v11 6/6] MAINTAINERS: update the StarFive TRNG driver maintainer
  2026-11-03  7:32 [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
                   ` (4 preceding siblings ...)
  2026-11-03  7:32 ` [PATCH v11 5/6] hwrng: starfive - add JHB100 support lianfeng.ouyang
@ 2026-11-03  7:32 ` lianfeng.ouyang
  5 siblings, 0 replies; 7+ messages in thread
From: lianfeng.ouyang @ 2026-11-03  7:32 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-11-03  7:33 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-03  7:32 [PATCH v11 0/6] hwrng: starfive - fixes and JHB100 support lianfeng.ouyang
2026-11-03  7:32 ` [PATCH v11 1/6] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100 lianfeng.ouyang
2026-11-03  7:32 ` [PATCH v11 2/6] hwrng: starfive - fix runtime PM and power teardown handling lianfeng.ouyang
2026-11-03  7:32 ` [PATCH v11 3/6] hwrng: starfive - move the SEU reseed out of the interrupt handler lianfeng.ouyang
2026-11-03  7:32 ` [PATCH v11 4/6] hwrng: starfive - tidy up includes and drop unused code lianfeng.ouyang
2026-11-03  7:32 ` [PATCH v11 5/6] hwrng: starfive - add JHB100 support lianfeng.ouyang
2026-11-03  7:32 ` [PATCH v11 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®