From 19f7def2646f2ec2926e8a0fcff50d4b754eec92 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 25 May 2026 13:45:42 +0200 Subject: reset: Add reset_reset() and reset_reset_bulk() API Add reset_reset() and reset_reset_bulk() functions to the reset controller API. These functions assert and then deassert reset signals in a single call, providing a convenient way to pulse/toggle a reset line. This mimics the Linux kernel's reset_control_reset() and reset_control_bulk_reset() APIs. The new functions are useful for drivers that need to cycle a reset line during initialization or error recovery but with also passing delay parameter. If a driver implements the rst_reset op, it will be called directly with the delay parameter. Otherwise, the reset core performs reset_assert(), optional udelay(), and reset_deassert() as fallback. Reviewed-by: Simon Glass Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/55ddd313c9e7b2d4dc79ab36bdd0040f871610f6.1779709539.git.michal.simek@amd.com --- drivers/reset/reset-uclass.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/reset') diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c index fe4cebf54f1..c199e3e5da7 100644 --- a/drivers/reset/reset-uclass.c +++ b/drivers/reset/reset-uclass.c @@ -13,6 +13,7 @@ #include #include #include +#include static inline struct reset_ops *reset_dev_ops(struct udevice *dev) { @@ -225,6 +226,39 @@ int reset_deassert_bulk(struct reset_ctl_bulk *bulk) return 0; } +int reset_reset(struct reset_ctl *reset_ctl, ulong delay_us) +{ + struct reset_ops *ops = reset_dev_ops(reset_ctl->dev); + int ret; + + debug("%s(reset_ctl=%p, delay_us=%lu)\n", __func__, reset_ctl, + delay_us); + + if (ops->rst_reset) + return ops->rst_reset(reset_ctl, delay_us); + + ret = reset_assert(reset_ctl); + if (ret < 0) + return ret; + + udelay(delay_us); + + return reset_deassert(reset_ctl); +} + +int reset_reset_bulk(struct reset_ctl_bulk *bulk, ulong delay_us) +{ + int i, ret; + + for (i = 0; i < bulk->count; i++) { + ret = reset_reset(&bulk->resets[i], delay_us); + if (ret < 0) + return ret; + } + + return 0; +} + int reset_status(struct reset_ctl *reset_ctl) { struct reset_ops *ops = reset_dev_ops(reset_ctl->dev); -- cgit v1.3.1 From 724d3cafe3ba8a2b3007c579bf52cd0612e6c565 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 25 May 2026 13:45:43 +0200 Subject: reset: Add sandbox tests for reset_reset() and reset_reset_bulk() Add DM test coverage for the new reset_reset() and reset_reset_bulk() API functions. The sandbox reset driver implements rst_reset so these tests exercise that op (not the assert/udelay/deassert fallback in reset_reset()). reset_reset_bulk() calls reset_reset() on each bulk entry in order, so each line's rst_reset runs in sequence. Reviewed-by: Simon Glass Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/be5411daf0de8eb64fbddf06e8ad82f50066e811.1779709539.git.michal.simek@amd.com --- arch/sandbox/include/asm/reset.h | 3 ++ drivers/reset/sandbox-reset-test.c | 14 ++++++++ drivers/reset/sandbox-reset.c | 31 ++++++++++++++++++ test/dm/reset.c | 67 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+) (limited to 'drivers/reset') diff --git a/arch/sandbox/include/asm/reset.h b/arch/sandbox/include/asm/reset.h index f0709b41c09..2890e0dc09b 100644 --- a/arch/sandbox/include/asm/reset.h +++ b/arch/sandbox/include/asm/reset.h @@ -10,6 +10,7 @@ struct udevice; int sandbox_reset_query(struct udevice *dev, unsigned long id); int sandbox_reset_is_requested(struct udevice *dev, unsigned long id); +int sandbox_reset_get_count(struct udevice *dev, unsigned long id); int sandbox_reset_test_get(struct udevice *dev); int sandbox_reset_test_get_devm(struct udevice *dev); @@ -19,6 +20,8 @@ int sandbox_reset_test_assert(struct udevice *dev); int sandbox_reset_test_assert_bulk(struct udevice *dev); int sandbox_reset_test_deassert(struct udevice *dev); int sandbox_reset_test_deassert_bulk(struct udevice *dev); +int sandbox_reset_test_reset(struct udevice *dev); +int sandbox_reset_test_reset_bulk(struct udevice *dev); int sandbox_reset_test_free(struct udevice *dev); int sandbox_reset_test_release_bulk(struct udevice *dev); diff --git a/drivers/reset/sandbox-reset-test.c b/drivers/reset/sandbox-reset-test.c index dfacb764bc7..64c205596c5 100644 --- a/drivers/reset/sandbox-reset-test.c +++ b/drivers/reset/sandbox-reset-test.c @@ -96,6 +96,20 @@ int sandbox_reset_test_deassert_bulk(struct udevice *dev) return reset_deassert_bulk(sbrt->bulkp); } +int sandbox_reset_test_reset(struct udevice *dev) +{ + struct sandbox_reset_test *sbrt = dev_get_priv(dev); + + return reset_reset(sbrt->ctlp, 0); +} + +int sandbox_reset_test_reset_bulk(struct udevice *dev) +{ + struct sandbox_reset_test *sbrt = dev_get_priv(dev); + + return reset_reset_bulk(sbrt->bulkp, 0); +} + int sandbox_reset_test_free(struct udevice *dev) { struct sandbox_reset_test *sbrt = dev_get_priv(dev); diff --git a/drivers/reset/sandbox-reset.c b/drivers/reset/sandbox-reset.c index 1c0ea7390df..458c332071f 100644 --- a/drivers/reset/sandbox-reset.c +++ b/drivers/reset/sandbox-reset.c @@ -9,12 +9,14 @@ #include #include #include +#include #define SANDBOX_RESET_SIGNALS 101 struct sandbox_reset_signal { bool asserted; bool requested; + int reset_count; }; struct sandbox_reset { @@ -31,6 +33,7 @@ static int sandbox_reset_request(struct reset_ctl *reset_ctl) return -EINVAL; sbr->signals[reset_ctl->id].requested = true; + sbr->signals[reset_ctl->id].reset_count = 0; return 0; } @@ -66,6 +69,21 @@ static int sandbox_reset_deassert(struct reset_ctl *reset_ctl) return 0; } +static int sandbox_reset_reset(struct reset_ctl *reset_ctl, ulong delay_us) +{ + struct sandbox_reset *sbr = dev_get_priv(reset_ctl->dev); + + debug("%s(reset_ctl=%p, delay_us=%lu)\n", __func__, reset_ctl, + delay_us); + + sbr->signals[reset_ctl->id].asserted = true; + udelay(delay_us); + sbr->signals[reset_ctl->id].asserted = false; + sbr->signals[reset_ctl->id].reset_count++; + + return 0; +} + static int sandbox_reset_bind(struct udevice *dev) { debug("%s(dev=%p)\n", __func__, dev); @@ -90,6 +108,7 @@ static const struct reset_ops sandbox_reset_reset_ops = { .rfree = sandbox_reset_free, .rst_assert = sandbox_reset_assert, .rst_deassert = sandbox_reset_deassert, + .rst_reset = sandbox_reset_reset, }; U_BOOT_DRIVER(sandbox_reset) = { @@ -125,3 +144,15 @@ int sandbox_reset_is_requested(struct udevice *dev, unsigned long id) return sbr->signals[id].requested; } + +int sandbox_reset_get_count(struct udevice *dev, unsigned long id) +{ + struct sandbox_reset *sbr = dev_get_priv(dev); + + debug("%s(dev=%p, id=%ld)\n", __func__, dev, id); + + if (id >= SANDBOX_RESET_SIGNALS) + return -EINVAL; + + return sbr->signals[id].reset_count; +} diff --git a/test/dm/reset.c b/test/dm/reset.c index dceb6a1dad3..043d7cb7e0f 100644 --- a/test/dm/reset.c +++ b/test/dm/reset.c @@ -120,6 +120,73 @@ static int dm_test_reset_devm(struct unit_test_state *uts) } DM_TEST(dm_test_reset_devm, UTF_SCAN_FDT); +static int dm_test_reset_reset(struct unit_test_state *uts) +{ + struct udevice *dev_reset; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl", + &dev_reset)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev_test)); + ut_assertok(sandbox_reset_test_get(dev_test)); + + /* Verify reset_count starts at 0 */ + ut_asserteq(0, sandbox_reset_get_count(dev_reset, TEST_RESET_ID)); + + ut_assertok(sandbox_reset_test_assert(dev_test)); + ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + + ut_assertok(sandbox_reset_test_reset(dev_test)); + + /* Verify reset was pulsed (count incremented) */ + ut_asserteq(1, sandbox_reset_get_count(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + + ut_assertok(sandbox_reset_test_free(dev_test)); + + return 0; +} +DM_TEST(dm_test_reset_reset, UTF_SCAN_FDT); + +static int dm_test_reset_reset_bulk(struct unit_test_state *uts) +{ + struct udevice *dev_reset; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl", + &dev_reset)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev_test)); + ut_assertok(sandbox_reset_test_get_bulk(dev_test)); + + /* Verify reset_count starts at 0 */ + ut_asserteq(0, sandbox_reset_get_count(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_get_count(dev_reset, OTHER_RESET_ID)); + + ut_assertok(sandbox_reset_test_assert_bulk(dev_test)); + ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(sandbox_reset_test_reset_bulk(dev_test)); + + /* Verify resets were pulsed (counts incremented) */ + ut_asserteq(1, sandbox_reset_get_count(dev_reset, TEST_RESET_ID)); + ut_asserteq(1, sandbox_reset_get_count(dev_reset, OTHER_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(sandbox_reset_test_release_bulk(dev_test)); + + return 0; +} +DM_TEST(dm_test_reset_reset_bulk, UTF_SCAN_FDT); + static int dm_test_reset_bulk(struct unit_test_state *uts) { struct udevice *dev_reset; -- cgit v1.3.1 From 4e3f64c7cc0c6a5defdceb485313b8a33f231f10 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 25 May 2026 13:45:44 +0200 Subject: reset: sandbox: Cover reset_reset() fallback with second sandbox provider Add a sandbox reset controller compatible string "sandbox,reset-ctl-fallback-only" that reuses the existing sandbox assert, deassert, request, and free helpers but omits rst_reset. That forces reset_reset() through the core assert / udelay / deassert fallback. Extend the reset-ctl-test DT node with a fifth reset line named "fallback" that points at the new provider, and add dm_test_reset_reset_fallback_path which verifies sandbox_reset_get_count() stays zero (rst_reset is never invoked) while the line ends deasserted after reset_reset(). This complements the existing rst_reset coverage on sandbox,reset-ctl and matches the approach of using a separate controller to exercise the fallback path in unit tests. Reviewed-by: Simon Glass Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/c1d40db6e2332a8b23ba842385b3f8c3d0290109.1779709539.git.michal.simek@amd.com --- arch/sandbox/dts/test.dts | 10 ++++++++-- drivers/reset/sandbox-reset.c | 27 +++++++++++++++++++++++++++ test/dm/reset.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) (limited to 'drivers/reset') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 0887de4333b..074e5c06ec8 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1530,10 +1530,16 @@ #reset-cells = <1>; }; + resetc_fb: reset-ctl-fallback { + compatible = "sandbox,reset-ctl-fallback-only"; + #reset-cells = <1>; + }; + reset-ctl-test { compatible = "sandbox,reset-ctl-test"; - resets = <&resetc 100>, <&resetc 2>, <&resetc 20>, <&resetc 40>; - reset-names = "other", "test", "test2", "test3"; + resets = <&resetc 100>, <&resetc 2>, <&resetc 20>, <&resetc 40>, + <&resetc_fb 5>; + reset-names = "other", "test", "test2", "test3", "fallback"; }; rng { diff --git a/drivers/reset/sandbox-reset.c b/drivers/reset/sandbox-reset.c index 458c332071f..12812f0f340 100644 --- a/drivers/reset/sandbox-reset.c +++ b/drivers/reset/sandbox-reset.c @@ -121,6 +121,33 @@ U_BOOT_DRIVER(sandbox_reset) = { .ops = &sandbox_reset_reset_ops, }; +/* + * Second sandbox reset controller for tests: same assert/deassert + * behaviour as sandbox_reset, but no rst_reset so reset_reset() uses + * the core assert / udelay / deassert fallback (reset_count never bumps). + */ +static const struct udevice_id sandbox_reset_fallback_ids[] = { + { .compatible = "sandbox,reset-ctl-fallback-only" }, + { } +}; + +static const struct reset_ops sandbox_reset_fallback_reset_ops = { + .request = sandbox_reset_request, + .rfree = sandbox_reset_free, + .rst_assert = sandbox_reset_assert, + .rst_deassert = sandbox_reset_deassert, +}; + +U_BOOT_DRIVER(sandbox_reset_fallback) = { + .name = "sandbox_reset_fallback", + .id = UCLASS_RESET, + .of_match = sandbox_reset_fallback_ids, + .bind = sandbox_reset_bind, + .probe = sandbox_reset_probe, + .priv_auto = sizeof(struct sandbox_reset), + .ops = &sandbox_reset_fallback_reset_ops, +}; + int sandbox_reset_query(struct udevice *dev, unsigned long id) { struct sandbox_reset *sbr = dev_get_priv(dev); diff --git a/test/dm/reset.c b/test/dm/reset.c index 043d7cb7e0f..91fa7ff723b 100644 --- a/test/dm/reset.c +++ b/test/dm/reset.c @@ -19,6 +19,9 @@ /* This is the other reset phandle specifier handled by bulk */ #define OTHER_RESET_ID 2 +/* Line on reset-ctl-fallback (sandbox,reset-ctl-fallback-only); see test.dts */ +#define FALLBACK_RESET_ID 5 + /* Base test of the reset uclass */ static int dm_test_reset_base(struct unit_test_state *uts) { @@ -151,6 +154,43 @@ static int dm_test_reset_reset(struct unit_test_state *uts) } DM_TEST(dm_test_reset_reset, UTF_SCAN_FDT); +/* + * reset_reset() fallback path: controller has no rst_reset op, so the + * core does assert -> udelay -> deassert. rst_reset-only accounting + * (reset_count) stays zero. Leave the line asserted before reset_reset() + * so we verify the fallback actually pulses it back to deasserted. + */ +static int dm_test_reset_reset_fallback_path(struct unit_test_state *uts) +{ + struct udevice *dev_reset_fb; + struct udevice *dev_test; + struct reset_ctl ctl; + + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl-fallback", + &dev_reset_fb)); + ut_asserteq(0, sandbox_reset_query(dev_reset_fb, FALLBACK_RESET_ID)); + ut_asserteq(0, sandbox_reset_get_count(dev_reset_fb, FALLBACK_RESET_ID)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev_test)); + ut_assertok(reset_get_by_name(dev_test, "fallback", &ctl)); + ut_asserteq_ptr(ctl.dev, dev_reset_fb); + ut_asserteq(FALLBACK_RESET_ID, ctl.id); + + ut_assertok(reset_assert(&ctl)); + ut_asserteq(1, sandbox_reset_query(dev_reset_fb, FALLBACK_RESET_ID)); + ut_asserteq(0, sandbox_reset_get_count(dev_reset_fb, FALLBACK_RESET_ID)); + + ut_assertok(reset_reset(&ctl, 1)); + ut_asserteq(0, sandbox_reset_get_count(dev_reset_fb, FALLBACK_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset_fb, FALLBACK_RESET_ID)); + + ut_assertok(reset_free(&ctl)); + + return 0; +} +DM_TEST(dm_test_reset_reset_fallback_path, UTF_SCAN_FDT); + static int dm_test_reset_reset_bulk(struct unit_test_state *uts) { struct udevice *dev_reset; -- cgit v1.3.1 From 8efa173b389e5cef6eece991351442baea0264fd Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Mon, 25 May 2026 13:45:46 +0200 Subject: reset: zynqmp: Implement rst_reset using PM_RESET_ACTION_PULSE Implement the rst_reset operation in the ZynqMP reset driver to use PM_RESET_ACTION_PULSE. This allows the reset controller to perform a reset pulse in a single firmware call instead of separate assert and deassert calls. This matches the Linux kernel implementation of zynqmp_reset_reset(). Reviewed-by: Simon Glass Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/be77d6f1b60f591ef626c14229d85c5cab867967.1779709539.git.michal.simek@amd.com --- drivers/reset/reset-zynqmp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/reset') diff --git a/drivers/reset/reset-zynqmp.c b/drivers/reset/reset-zynqmp.c index d04e8eef3bb..2b58f3a75b4 100644 --- a/drivers/reset/reset-zynqmp.c +++ b/drivers/reset/reset-zynqmp.c @@ -45,6 +45,16 @@ static int zynqmp_reset_deassert(struct reset_ctl *rst) PM_RESET_ACTION_RELEASE); } +static int zynqmp_reset_reset(struct reset_ctl *rst, ulong delay_us) +{ + struct zynqmp_reset_priv *priv = dev_get_priv(rst->dev); + + dev_dbg(rst->dev, "%s(rst=%p) (id=%lu)\n", __func__, rst, rst->id); + + return zynqmp_pm_reset_assert(priv->reset_id + rst->id, + PM_RESET_ACTION_PULSE); +} + static int zynqmp_reset_request(struct reset_ctl *rst) { struct zynqmp_reset_priv *priv = dev_get_priv(rst->dev); @@ -74,6 +84,7 @@ const struct reset_ops zynqmp_reset_ops = { .request = zynqmp_reset_request, .rst_assert = zynqmp_reset_assert, .rst_deassert = zynqmp_reset_deassert, + .rst_reset = zynqmp_reset_reset, }; static const struct udevice_id zynqmp_reset_ids[] = { -- cgit v1.3.1