From e0cf84109bc6c6768337123f1de24ff56b41c91b Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Wed, 4 Feb 2026 15:34:27 +0200 Subject: reset: rzg2l-usbphy-ctrl: Check pwrrdy is valid before using it The pwrrdy regmap_filed is allocated in rzg2l_usbphy_ctrl_pwrrdy_init() only if the driver data is set to RZG2L_USBPHY_CTRL_PWRRDY. Check that pwrrdy is valid before using it to avoid "Unable to handle kernel NULL pointer dereference at virtual address" errors. Fixes: c5b7cd9adefc ("reset: rzg2l-usbphy-ctrl: Add suspend/resume support") Signed-off-by: Claudiu Beznea Reviewed-by: Biju Das Signed-off-by: Philipp Zabel --- drivers/reset/reset-rzg2l-usbphy-ctrl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index 32bc268c9149..05dd9b4a02df 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -136,6 +136,9 @@ static int rzg2l_usbphy_ctrl_set_pwrrdy(struct regmap_field *pwrrdy, { u32 val = power_on ? 0 : 1; + if (!pwrrdy) + return 0; + /* The initialization path guarantees that the mask is 1 bit long. */ return regmap_field_update_bits(pwrrdy, 1, val); } -- cgit v1.2.3 From 59ad2fb0be75d984e78c9e9ceb6c371f656ac74d Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 13 Aug 2025 16:41:33 -0500 Subject: reset: ath79: Use devm_register_restart_handler() Function register_restart_handler() is deprecated. Using this new API removes our need to keep and manage a struct notifier_block and to later unregister the handler. Signed-off-by: Andrew Davis Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-ath79.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/reset/reset-ath79.c b/drivers/reset/reset-ath79.c index b5d620132052..4c4e69eb32bb 100644 --- a/drivers/reset/reset-ath79.c +++ b/drivers/reset/reset-ath79.c @@ -15,7 +15,6 @@ struct ath79_reset { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; void __iomem *base; spinlock_t lock; }; @@ -72,11 +71,9 @@ static const struct reset_control_ops ath79_reset_ops = { .status = ath79_reset_status, }; -static int ath79_reset_restart_handler(struct notifier_block *nb, - unsigned long action, void *data) +static int ath79_reset_restart_handler(struct sys_off_data *data) { - struct ath79_reset *ath79_reset = - container_of(nb, struct ath79_reset, restart_nb); + struct ath79_reset *ath79_reset = data->cb_data; ath79_reset_assert(&ath79_reset->rcdev, FULL_CHIP_RESET); @@ -108,10 +105,7 @@ static int ath79_reset_probe(struct platform_device *pdev) if (err) return err; - ath79_reset->restart_nb.notifier_call = ath79_reset_restart_handler; - ath79_reset->restart_nb.priority = 128; - - err = register_restart_handler(&ath79_reset->restart_nb); + err = devm_register_restart_handler(&pdev->dev, ath79_reset_restart_handler, ath79_reset); if (err) dev_warn(&pdev->dev, "Failed to register restart handler\n"); -- cgit v1.2.3 From 8bfef0c2438978d4e246f239b3e5dcd0ab9df07d Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 13 Aug 2025 16:41:34 -0500 Subject: reset: intel: Use devm_register_restart_handler() Function register_restart_handler() is deprecated. Using this new API removes our need to keep and manage a struct notifier_block and to later unregister the handler. Signed-off-by: Andrew Davis Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-intel-gw.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/reset/reset-intel-gw.c b/drivers/reset/reset-intel-gw.c index a5ce3350cb5e..0db64cc8a282 100644 --- a/drivers/reset/reset-intel-gw.c +++ b/drivers/reset/reset-intel-gw.c @@ -28,7 +28,6 @@ struct intel_reset_soc { struct intel_reset_data { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; const struct intel_reset_soc *soc_data; struct regmap *regmap; struct device *dev; @@ -153,12 +152,10 @@ static int intel_reset_xlate(struct reset_controller_dev *rcdev, return id; } -static int intel_reset_restart_handler(struct notifier_block *nb, - unsigned long action, void *data) +static int intel_reset_restart_handler(struct sys_off_data *data) { - struct intel_reset_data *reset_data; + struct intel_reset_data *reset_data = data->cb_data; - reset_data = container_of(nb, struct intel_reset_data, restart_nb); intel_assert_device(&reset_data->rcdev, reset_data->reboot_id); return NOTIFY_DONE; @@ -215,9 +212,7 @@ static int intel_reset_probe(struct platform_device *pdev) if (data->soc_data->legacy) data->reboot_id |= FIELD_PREP(STAT_BIT_OFFSET_MASK, rb_id[2]); - data->restart_nb.notifier_call = intel_reset_restart_handler; - data->restart_nb.priority = 128; - register_restart_handler(&data->restart_nb); + devm_register_restart_handler(&pdev->dev, intel_reset_restart_handler, data); return 0; } -- cgit v1.2.3 From 09f166128aeae20706611d009e3c3d39e285e368 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 13 Aug 2025 16:41:35 -0500 Subject: reset: lpc18xx: Use devm_register_sys_off_handler() Function register_restart_handler() is deprecated. Using this new API removes our need to keep and manage a struct notifier_block and to later unregister the handler. Signed-off-by: Andrew Davis Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-lpc18xx.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/reset/reset-lpc18xx.c b/drivers/reset/reset-lpc18xx.c index e42b2f24a93d..8ac9f237e1ce 100644 --- a/drivers/reset/reset-lpc18xx.c +++ b/drivers/reset/reset-lpc18xx.c @@ -31,7 +31,6 @@ struct lpc18xx_rgu_data { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; struct clk *clk_delay; struct clk *clk_reg; void __iomem *base; @@ -41,11 +40,9 @@ struct lpc18xx_rgu_data { #define to_rgu_data(p) container_of(p, struct lpc18xx_rgu_data, rcdev) -static int lpc18xx_rgu_restart(struct notifier_block *nb, unsigned long mode, - void *cmd) +static int lpc18xx_rgu_restart(struct sys_off_data *data) { - struct lpc18xx_rgu_data *rc = container_of(nb, struct lpc18xx_rgu_data, - restart_nb); + struct lpc18xx_rgu_data *rc = data->cb_data; writel(BIT(LPC18XX_RGU_CORE_RST), rc->base + LPC18XX_RGU_CTRL0); mdelay(2000); @@ -178,9 +175,8 @@ static int lpc18xx_rgu_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "unable to register device\n"); - rc->restart_nb.priority = 192, - rc->restart_nb.notifier_call = lpc18xx_rgu_restart, - ret = register_restart_handler(&rc->restart_nb); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, 192, + lpc18xx_rgu_restart, rc); if (ret) dev_warn(&pdev->dev, "failed to register restart handler\n"); -- cgit v1.2.3 From 42d03696a05de8a79383d9ed29ca286846133c40 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 13 Aug 2025 16:41:36 -0500 Subject: reset: ma35d1: Use devm_register_sys_off_handler() Function register_restart_handler() is deprecated. Using this new API removes our need to keep and manage a struct notifier_block and to later unregister the handler. Signed-off-by: Andrew Davis Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-ma35d1.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/reset/reset-ma35d1.c b/drivers/reset/reset-ma35d1.c index 54e53863c98a..4ee901f00132 100644 --- a/drivers/reset/reset-ma35d1.c +++ b/drivers/reset/reset-ma35d1.c @@ -19,7 +19,6 @@ struct ma35d1_reset_data { struct reset_controller_dev rcdev; - struct notifier_block restart_handler; void __iomem *base; /* protect registers against concurrent read-modify-write */ spinlock_t lock; @@ -125,10 +124,9 @@ static const struct { [MA35D1_RESET_SSPCC] = {0x2C, 31} }; -static int ma35d1_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd) +static int ma35d1_restart_handler(struct sys_off_data *sys_off_data) { - struct ma35d1_reset_data *data = - container_of(this, struct ma35d1_reset_data, restart_handler); + struct ma35d1_reset_data *data = sys_off_data->cb_data; u32 id = MA35D1_RESET_CHIP; writel_relaxed(BIT(ma35d1_reset_map[id].bit), @@ -213,11 +211,10 @@ static int ma35d1_reset_probe(struct platform_device *pdev) reset_data->rcdev.nr_resets = MA35D1_RESET_COUNT; reset_data->rcdev.ops = &ma35d1_reset_ops; reset_data->rcdev.of_node = dev->of_node; - reset_data->restart_handler.notifier_call = ma35d1_restart_handler; - reset_data->restart_handler.priority = 192; spin_lock_init(&reset_data->lock); - err = register_restart_handler(&reset_data->restart_handler); + err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, 192, + ma35d1_restart_handler, reset_data); if (err) dev_warn(&pdev->dev, "failed to register restart handler\n"); -- cgit v1.2.3 From 71c021cd8875ad96c01fa9263b8a4868d2635a0d Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 13 Aug 2025 16:41:37 -0500 Subject: reset: npcm: Use devm_register_sys_off_handler() Function register_restart_handler() is deprecated. Using this new API removes our need to keep and manage a struct notifier_block and to later unregister the handler. Signed-off-by: Andrew Davis Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-npcm.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/reset/reset-npcm.c b/drivers/reset/reset-npcm.c index 4070a4b6663f..a762d0215e76 100644 --- a/drivers/reset/reset-npcm.c +++ b/drivers/reset/reset-npcm.c @@ -89,7 +89,6 @@ static const struct npcm_reset_info npxm8xx_reset_info[] = { struct npcm_rc_data { struct reset_controller_dev rcdev; - struct notifier_block restart_nb; const struct npcm_reset_info *info; struct regmap *gcr_regmap; u32 sw_reset_number; @@ -100,11 +99,9 @@ struct npcm_rc_data { #define to_rc_data(p) container_of(p, struct npcm_rc_data, rcdev) -static int npcm_rc_restart(struct notifier_block *nb, unsigned long mode, - void *cmd) +static int npcm_rc_restart(struct sys_off_data *data) { - struct npcm_rc_data *rc = container_of(nb, struct npcm_rc_data, - restart_nb); + struct npcm_rc_data *rc = data->cb_data; writel(NPCM_SWRST << rc->sw_reset_number, rc->base + NPCM_SWRSTR); mdelay(1000); @@ -472,9 +469,8 @@ static int npcm_rc_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "nuvoton,sw-reset-number", &rc->sw_reset_number)) { if (rc->sw_reset_number && rc->sw_reset_number < 5) { - rc->restart_nb.priority = 192; - rc->restart_nb.notifier_call = npcm_rc_restart; - ret = register_restart_handler(&rc->restart_nb); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, 192, + npcm_rc_restart, rc); if (ret) { dev_warn(&pdev->dev, "failed to register restart handler\n"); return ret; -- cgit v1.2.3 From 9d93e13bf5c4d7800a6168a145984adda9bf3c0e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 13 Aug 2025 16:41:38 -0500 Subject: reset: sunplus: Use devm_register_sys_off_handler() Function register_restart_handler() is deprecated. Using this new API removes our need to keep and manage a struct notifier_block and to later unregister the handler. Signed-off-by: Andrew Davis Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-sunplus.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/reset/reset-sunplus.c b/drivers/reset/reset-sunplus.c index df58decab64d..58b087433759 100644 --- a/drivers/reset/reset-sunplus.c +++ b/drivers/reset/reset-sunplus.c @@ -100,7 +100,6 @@ static const u32 sp_resets[] = { struct sp_reset { struct reset_controller_dev rcdev; - struct notifier_block notifier; void __iomem *base; }; @@ -154,10 +153,9 @@ static const struct reset_control_ops sp_reset_ops = { .status = sp_reset_status, }; -static int sp_restart(struct notifier_block *nb, unsigned long mode, - void *cmd) +static int sp_restart(struct sys_off_data *data) { - struct sp_reset *reset = container_of(nb, struct sp_reset, notifier); + struct sp_reset *reset = data->cb_data; sp_reset_assert(&reset->rcdev, 0); sp_reset_deassert(&reset->rcdev, 0); @@ -189,10 +187,8 @@ static int sp_reset_probe(struct platform_device *pdev) if (ret) return ret; - reset->notifier.notifier_call = sp_restart; - reset->notifier.priority = 192; - - return register_restart_handler(&reset->notifier); + return devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, + 192, sp_restart, reset); } static const struct of_device_id sp_reset_dt_ids[] = { -- cgit v1.2.3 From fe3da77f2f946e84d13e5183ae0fa223b968766b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:45 +0100 Subject: reset: gpio: remove unneeded OF-node put priv->rc.of_node is never set in reset core. Even if it were: tasking the reset-gpio driver with controlling the reference count of an OF node set up in reset core is a weird inversion of responsability. But it's also wrong in that the underlying device never actually gets removed so the node should not be put at all and especially not at driver detach. Remove the devres action. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/reset-gpio.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index 0a1610d9e78a..ad5bfe27aaef 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -52,18 +52,12 @@ static int reset_gpio_of_xlate(struct reset_controller_dev *rcdev, return reset_spec->args[0]; } -static void reset_gpio_of_node_put(void *data) -{ - of_node_put(data); -} - static int reset_gpio_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { struct device *dev = &adev->dev; struct of_phandle_args *platdata = dev_get_platdata(dev); struct reset_gpio_priv *priv; - int ret; if (!platdata) return -EINVAL; @@ -83,10 +77,6 @@ static int reset_gpio_probe(struct auxiliary_device *adev, priv->rc.owner = THIS_MODULE; priv->rc.dev = dev; priv->rc.of_args = platdata; - ret = devm_add_action_or_reset(dev, reset_gpio_of_node_put, - priv->rc.of_node); - if (ret) - return ret; /* Cells to match GPIO specifier, but it's not really used */ priv->rc.of_reset_n_cells = 2; -- cgit v1.2.3 From a9b95ce36de4422761dc2a2afc01e1781801800c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:46 +0100 Subject: reset: gpio: add a devlink between reset-gpio and its consumer The device that requests the reset control managed by the reset-gpio device is effectively its consumer but the devlink is only established between it and the GPIO controller exposing the reset pin. Add a devlink between the consumer of the reset control and its supplier. This will allow us to simplify the GPIOLIB code managing shared GPIOs when handling the corner case of reset-gpio and gpiolib-shared interacting. While at it and since we need to store the address of the auxiliary device: don't allocate memory for the device separately but fold it into struct reset_gpio_lookup instead. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 79 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 24 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index fceec45c8afc..3845e77a8d32 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -77,11 +77,13 @@ struct reset_control_array { * @of_args: phandle to the reset controller with all the args like GPIO number * @swnode: Software node containing the reference to the GPIO provider * @list: list entry for the reset_gpio_lookup_list + * @adev: Auxiliary device representing the reset controller */ struct reset_gpio_lookup { struct of_phandle_args of_args; struct fwnode_handle *swnode; struct list_head list; + struct auxiliary_device adev; }; static const char *rcdev_name(struct reset_controller_dev *rcdev) @@ -824,49 +826,72 @@ static void __reset_control_put_internal(struct reset_control *rstc) static void reset_gpio_aux_device_release(struct device *dev) { - struct auxiliary_device *adev = to_auxiliary_dev(dev); - kfree(adev); } -static int reset_add_gpio_aux_device(struct device *parent, - struct fwnode_handle *swnode, - int id, void *pdata) +static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev, + struct device *parent, int id) { - struct auxiliary_device *adev; + struct auxiliary_device *adev = &rgpio_dev->adev; int ret; - adev = kzalloc_obj(*adev); - if (!adev) - return -ENOMEM; - adev->id = id; adev->name = "gpio"; adev->dev.parent = parent; - adev->dev.platform_data = pdata; + adev->dev.platform_data = &rgpio_dev->of_args; adev->dev.release = reset_gpio_aux_device_release; - device_set_node(&adev->dev, swnode); + device_set_node(&adev->dev, rgpio_dev->swnode); ret = auxiliary_device_init(adev); - if (ret) { - kfree(adev); + if (ret) return ret; - } ret = __auxiliary_device_add(adev, "reset"); if (ret) { auxiliary_device_uninit(adev); - kfree(adev); return ret; } - return ret; + return 0; +} + +static void reset_gpio_add_devlink(struct device_node *np, + struct reset_gpio_lookup *rgpio_dev) +{ + struct device *consumer; + + /* + * We must use get_dev_from_fwnode() and not of_find_device_by_node() + * because the latter only considers the platform bus while we want to + * get consumers of any kind that can be associated with firmware + * nodes: auxiliary, soundwire, etc. + */ + consumer = get_dev_from_fwnode(of_fwnode_handle(np)); + if (consumer) { + if (!device_link_add(consumer, &rgpio_dev->adev.dev, + DL_FLAG_AUTOREMOVE_CONSUMER)) + pr_warn("Failed to create a device link between reset-gpio and its consumer"); + + put_device(consumer); + } + /* + * else { } + * + * TODO: If ever there's a case where we need to support shared + * reset-gpios retrieved from a device node for which there's no + * device present yet, this is where we'd set up a notifier waiting + * for the device to appear in the system. This would be a lot of code + * that would go unused for now so let's cross that bridge when and if + * we get there. + */ } /* - * @args: phandle to the GPIO provider with all the args like GPIO number + * @np: OF-node associated with the consumer + * @args: phandle to the GPIO provider with all the args like GPIO number */ -static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) +static int __reset_add_reset_gpio_device(struct device_node *np, + const struct of_phandle_args *args) { struct property_entry properties[3] = { }; unsigned int offset, of_flags, lflags; @@ -916,8 +941,14 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) { if (args->np == rgpio_dev->of_args.np) { - if (of_phandle_args_equal(args, &rgpio_dev->of_args)) - return 0; /* Already on the list, done */ + if (of_phandle_args_equal(args, &rgpio_dev->of_args)) { + /* + * Already on the list, create the device link + * and stop here. + */ + reset_gpio_add_devlink(np, rgpio_dev); + return 0; + } } } @@ -951,11 +982,11 @@ static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) goto err_put_of_node; } - ret = reset_add_gpio_aux_device(parent, rgpio_dev->swnode, id, - &rgpio_dev->of_args); + ret = reset_create_gpio_aux_device(rgpio_dev, parent, id); if (ret) goto err_del_swnode; + reset_gpio_add_devlink(np, rgpio_dev); list_add(&rgpio_dev->list, &reset_gpio_lookup_list); return 0; @@ -1035,7 +1066,7 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, gpio_fallback = true; - ret = __reset_add_reset_gpio_device(&args); + ret = __reset_add_reset_gpio_device(node, &args); if (ret) { rstc = ERR_PTR(ret); goto out_put; -- cgit v1.2.3 From ad9d28e68f4f9d15b9bde15e1ab79a4f85eff60e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:47 +0100 Subject: reset: gpio: simplify fallback device matching The of_args field of struct reset_controller_dev was introduced to allow the reset-gpio driver to pass the phandle arguments back to reset core. The thing is: it doesn't even have to do it. The core sets the platform data of the auxiliary device *AND* has access to it later on during the lookup. This means the field is unneeded and all can happen entirely in reset core. Remove the field from the public header and don't set it in reset-gpio.c. Retrieve the platform data in reset core when needed instead. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 15 ++++++--------- drivers/reset/reset-gpio.c | 5 ----- include/linux/reset-controller.h | 4 ---- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 3845e77a8d32..954df36a242e 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -94,9 +94,6 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) if (rcdev->of_node) return rcdev->of_node->full_name; - if (rcdev->of_args) - return rcdev->of_args->np->full_name; - return NULL; } @@ -125,9 +122,6 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, */ int reset_controller_register(struct reset_controller_dev *rcdev) { - if (rcdev->of_node && rcdev->of_args) - return -EINVAL; - if (!rcdev->of_xlate) { rcdev->of_reset_n_cells = 1; rcdev->of_xlate = of_reset_simple_xlate; @@ -1006,13 +1000,16 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a bool gpio_fallback) { struct reset_controller_dev *rcdev; + struct of_phandle_args *rc_args; lockdep_assert_held(&reset_list_mutex); list_for_each_entry(rcdev, &reset_controller_list, list) { - if (gpio_fallback) { - if (rcdev->of_args && of_phandle_args_equal(args, - rcdev->of_args)) + if (gpio_fallback && rcdev->dev && + device_is_compatible(rcdev->dev, "reset-gpio")) { + rc_args = dev_get_platdata(rcdev->dev); + + if (of_phandle_args_equal(args, rc_args)) return rcdev; } else { if (args->np == rcdev->of_node) diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index ad5bfe27aaef..6e1c4f990bc0 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -56,12 +56,8 @@ static int reset_gpio_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { struct device *dev = &adev->dev; - struct of_phandle_args *platdata = dev_get_platdata(dev); struct reset_gpio_priv *priv; - if (!platdata) - return -EINVAL; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -76,7 +72,6 @@ static int reset_gpio_probe(struct auxiliary_device *adev, priv->rc.ops = &reset_gpio_ops; priv->rc.owner = THIS_MODULE; priv->rc.dev = dev; - priv->rc.of_args = platdata; /* Cells to match GPIO specifier, but it's not really used */ priv->rc.of_reset_n_cells = 2; diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index 46514cb1b9e0..aa95b460fdf8 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -35,9 +35,6 @@ struct of_phandle_args; * @reset_control_head: head of internal list of requested reset controls * @dev: corresponding driver model device struct * @of_node: corresponding device tree node as phandle target - * @of_args: for reset-gpios controllers: corresponding phandle args with - * of_node and GPIO number complementing of_node; either this or - * of_node should be present * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the * device tree to id as given to the reset control ops, defaults @@ -51,7 +48,6 @@ struct reset_controller_dev { struct list_head reset_control_head; struct device *dev; struct device_node *of_node; - const struct of_phandle_args *of_args; int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec); -- cgit v1.2.3 From 1acd4663840849d3371ab8734b55cd184a2a5c5b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:48 +0100 Subject: reset: gpio: remove unneeded auxiliary_set_drvdata() There's no user of the driver data so don't needlessly assign it. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/reset-gpio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index 6e1c4f990bc0..5044f809d0e5 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -62,8 +62,6 @@ static int reset_gpio_probe(struct auxiliary_device *adev, if (!priv) return -ENOMEM; - auxiliary_set_drvdata(adev, &priv->rc); - priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(priv->reset)) return dev_err_probe(dev, PTR_ERR(priv->reset), -- cgit v1.2.3 From 20adbf3b8f5c5787da29f8cdd7cfc4fa87854bd5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:49 +0100 Subject: reset: warn on reset-gpio release While we implement an empty .release() callback for reset-gpio (driver core requires it), this function will never actually be called as nobody ever removes the device and the last reference is not dropped anywhere. This is by design - once created, the reset-gpio device stays in memory. Make the .release() callback emit a warning, should it ever be called due to a programming bug. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 954df36a242e..3fa0d49eb494 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -820,7 +820,7 @@ static void __reset_control_put_internal(struct reset_control *rstc) static void reset_gpio_aux_device_release(struct device *dev) { - + WARN(1, "reset-gpio device %s should never have been removed", dev_name(dev)); } static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev, -- cgit v1.2.3 From 6703784ab9a8039bd3f476db2643414c45690c68 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:50 +0100 Subject: reset: fold ida_alloc() into reset_create_gpio_aux_device() We don't need to know the IDA value outside of the function that creates the auxiliary reset-gpio device. Simplify error handling by folding it into reset_create_gpio_aux_device(). Signed-off-by: Bartosz Golaszewski Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 3fa0d49eb494..3b67e727c84e 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -824,10 +824,14 @@ static void reset_gpio_aux_device_release(struct device *dev) } static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev, - struct device *parent, int id) + struct device *parent) { struct auxiliary_device *adev = &rgpio_dev->adev; - int ret; + int ret, id; + + id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); + if (id < 0) + return -ENOMEM; adev->id = id; adev->name = "gpio"; @@ -837,12 +841,15 @@ static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev, device_set_node(&adev->dev, rgpio_dev->swnode); ret = auxiliary_device_init(adev); - if (ret) + if (ret) { + ida_free(&reset_gpio_ida, id); return ret; + } ret = __auxiliary_device_add(adev, "reset"); if (ret) { auxiliary_device_uninit(adev); + ida_free(&reset_gpio_ida, id); return ret; } @@ -891,7 +898,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np, unsigned int offset, of_flags, lflags; struct reset_gpio_lookup *rgpio_dev; struct device *parent; - int id, ret, prop = 0; + int ret, prop = 0; /* * Currently only #gpio-cells=2 is supported with the meaning of: @@ -951,16 +958,10 @@ static int __reset_add_reset_gpio_device(struct device_node *np, properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio"); properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags); - id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); - if (id < 0) - return id; - /* Not freed on success, because it is persisent subsystem data. */ rgpio_dev = kzalloc_obj(*rgpio_dev); - if (!rgpio_dev) { - ret = -ENOMEM; - goto err_ida_free; - } + if (!rgpio_dev) + return -ENOMEM; rgpio_dev->of_args = *args; /* @@ -976,7 +977,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np, goto err_put_of_node; } - ret = reset_create_gpio_aux_device(rgpio_dev, parent, id); + ret = reset_create_gpio_aux_device(rgpio_dev, parent); if (ret) goto err_del_swnode; @@ -990,8 +991,6 @@ err_del_swnode: err_put_of_node: of_node_put(rgpio_dev->of_args.np); kfree(rgpio_dev); -err_ida_free: - ida_free(&reset_gpio_ida, id); return ret; } -- cgit v1.2.3 From 1f10008aff7190e300e93ed33a0eb2d236c0b87d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:51 +0100 Subject: reset: use lock guards in reset core Simplify the locking code in reset core by using lock guard from linux/cleanup.h. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 55 ++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 3b67e727c84e..2102093624aa 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -129,9 +129,9 @@ int reset_controller_register(struct reset_controller_dev *rcdev) INIT_LIST_HEAD(&rcdev->reset_control_head); - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + list_add(&rcdev->list, &reset_controller_list); - mutex_unlock(&reset_list_mutex); return 0; } @@ -143,9 +143,9 @@ EXPORT_SYMBOL_GPL(reset_controller_register); */ void reset_controller_unregister(struct reset_controller_dev *rcdev) { - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + list_del(&rcdev->list); - mutex_unlock(&reset_list_mutex); } EXPORT_SYMBOL_GPL(reset_controller_unregister); @@ -646,25 +646,20 @@ int reset_control_acquire(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_acquire(rstc_to_array(rstc)); - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); - if (rstc->acquired) { - mutex_unlock(&reset_list_mutex); + if (rstc->acquired) return 0; - } list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) { if (rstc != rc && rstc->id == rc->id) { - if (rc->acquired) { - mutex_unlock(&reset_list_mutex); + if (rc->acquired) return -EBUSY; - } } } rstc->acquired = true; - mutex_unlock(&reset_list_mutex); return 0; } EXPORT_SYMBOL_GPL(reset_control_acquire); @@ -1064,27 +1059,28 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, ret = __reset_add_reset_gpio_device(node, &args); if (ret) { - rstc = ERR_PTR(ret); - goto out_put; + of_node_put(args.np); + return ERR_PTR(ret); } } - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + rcdev = __reset_find_rcdev(&args, gpio_fallback); if (!rcdev) { rstc = ERR_PTR(-EPROBE_DEFER); - goto out_unlock; + goto out_put; } if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { rstc = ERR_PTR(-EINVAL); - goto out_unlock; + goto out_put; } rstc_id = rcdev->of_xlate(rcdev, &args); if (rstc_id < 0) { rstc = ERR_PTR(rstc_id); - goto out_unlock; + goto out_put; } flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL; @@ -1092,8 +1088,6 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, /* reset_list_mutex also protects the rcdev's reset_control list */ rstc = __reset_control_get_internal(rcdev, rstc_id, flags); -out_unlock: - mutex_unlock(&reset_list_mutex); out_put: of_node_put(args.np); @@ -1135,10 +1129,11 @@ int __reset_control_bulk_get(struct device *dev, int num_rstcs, return 0; err: - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + while (i--) __reset_control_put_internal(rstcs[i].rstc); - mutex_unlock(&reset_list_mutex); + return ret; } EXPORT_SYMBOL_GPL(__reset_control_bulk_get); @@ -1147,10 +1142,10 @@ static void reset_control_array_put(struct reset_control_array *resets) { int i; - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + for (i = 0; i < resets->num_rstcs; i++) __reset_control_put_internal(resets->rstc[i]); - mutex_unlock(&reset_list_mutex); kfree(resets); } @@ -1168,9 +1163,9 @@ void reset_control_put(struct reset_control *rstc) return; } - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + __reset_control_put_internal(rstc); - mutex_unlock(&reset_list_mutex); } EXPORT_SYMBOL_GPL(reset_control_put); @@ -1181,10 +1176,10 @@ EXPORT_SYMBOL_GPL(reset_control_put); */ void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs) { - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + while (num_rstcs--) __reset_control_put_internal(rstcs[num_rstcs].rstc); - mutex_unlock(&reset_list_mutex); } EXPORT_SYMBOL_GPL(reset_control_bulk_put); @@ -1403,10 +1398,10 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag return &resets->base; err_rst: - mutex_lock(&reset_list_mutex); + guard(mutex)(&reset_list_mutex); + while (--i >= 0) __reset_control_put_internal(resets->rstc[i]); - mutex_unlock(&reset_list_mutex); kfree(resets); -- cgit v1.2.3 From 78ebbff6d1a05ffc2062d90231b4720618a57147 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:52 +0100 Subject: reset: handle removing supplier before consumers Except for the reset-gpio, all reset drivers use device tree - and as such - benefit from the device links set up by driver core. This means, that no reset supplier will be unbound before all its consumers have been. For this reason, nobody bothered making the reset core resiliant to the object life-time issues that are plagueing the kernel. In this case: reset control handles referencing the reset provider device with no serialization or NULL-pointer checking. We now want to make the reset core fwnode-agnostic but before we do, we must make sure it can survive unbinding of suppliers with consumers still holding reset control handles. To that end: use SRCU to protect the rcdev pointer inside struct reset_control. We protect all sections using the pointer with SRCU read-only critical sections and synchronize SRCU after every modification of the pointer. This is in line with what the GPIO subsystem does and what the proposed revocable API tries to generalize. When and if the latter makes its way into the kernel, reset core could potentially also be generalized to use it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 108 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 17 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 2102093624aa..e6c12fbebca9 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -23,6 +23,7 @@ #include #include #include +#include static DEFINE_MUTEX(reset_list_mutex); static LIST_HEAD(reset_controller_list); @@ -36,6 +37,7 @@ static DEFINE_IDA(reset_gpio_ida); * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device * this reset control belongs to + * @srcu: protects the rcdev pointer from removal during consumer access * @list: list entry for the rcdev's reset controller list * @id: ID of the reset controller in the reset * controller device @@ -49,7 +51,8 @@ static DEFINE_IDA(reset_gpio_ida); * will be either 0 or 1. */ struct reset_control { - struct reset_controller_dev *rcdev; + struct reset_controller_dev __rcu *rcdev; + struct srcu_struct srcu; struct list_head list; unsigned int id; struct kref refcnt; @@ -137,15 +140,35 @@ int reset_controller_register(struct reset_controller_dev *rcdev) } EXPORT_SYMBOL_GPL(reset_controller_register); +static void reset_controller_remove(struct reset_controller_dev *rcdev, + struct reset_control *rstc) +{ + list_del(&rstc->list); + module_put(rcdev->owner); + put_device(rcdev->dev); +} + /** * reset_controller_unregister - unregister a reset controller device * @rcdev: a pointer to the reset controller device */ void reset_controller_unregister(struct reset_controller_dev *rcdev) { + struct reset_control *rstc, *pos; + guard(mutex)(&reset_list_mutex); list_del(&rcdev->list); + + /* + * Numb but don't free the remaining reset control handles that are + * still held by consumers. + */ + list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) { + rcu_assign_pointer(rstc->rcdev, NULL); + synchronize_srcu(&rstc->srcu); + reset_controller_remove(rcdev, rstc); + } } EXPORT_SYMBOL_GPL(reset_controller_unregister); @@ -322,6 +345,7 @@ static inline bool reset_control_is_array(struct reset_control *rstc) */ int reset_control_reset(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; int ret; if (!rstc) @@ -333,7 +357,13 @@ int reset_control_reset(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_reset(rstc_to_array(rstc)); - if (!rstc->rcdev->ops->reset) + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + + if (!rcdev->ops->reset) return -ENOTSUPP; if (rstc->shared) { @@ -347,7 +377,7 @@ int reset_control_reset(struct reset_control *rstc) return -EPERM; } - ret = rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); + ret = rcdev->ops->reset(rcdev, rstc->id); if (rstc->shared && ret) atomic_dec(&rstc->triggered_count); @@ -437,6 +467,8 @@ EXPORT_SYMBOL_GPL(reset_control_rearm); */ int reset_control_assert(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; + if (!rstc) return 0; @@ -446,6 +478,12 @@ int reset_control_assert(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_assert(rstc_to_array(rstc)); + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + if (rstc->shared) { if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) return -EINVAL; @@ -460,7 +498,7 @@ int reset_control_assert(struct reset_control *rstc) * Shared reset controls allow the reset line to be in any state * after this call, so doing nothing is a valid option. */ - if (!rstc->rcdev->ops->assert) + if (!rcdev->ops->assert) return 0; } else { /* @@ -468,17 +506,17 @@ int reset_control_assert(struct reset_control *rstc) * is no way to guarantee that the reset line is asserted after * this call. */ - if (!rstc->rcdev->ops->assert) + if (!rcdev->ops->assert) return -ENOTSUPP; if (!rstc->acquired) { WARN(1, "reset %s (ID: %u) is not acquired\n", - rcdev_name(rstc->rcdev), rstc->id); + rcdev_name(rcdev), rstc->id); return -EPERM; } } - return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); + return rcdev->ops->assert(rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_assert); @@ -525,6 +563,8 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_assert); */ int reset_control_deassert(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; + if (!rstc) return 0; @@ -534,6 +574,12 @@ int reset_control_deassert(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_deassert(rstc_to_array(rstc)); + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + if (rstc->shared) { if (WARN_ON(atomic_read(&rstc->triggered_count) != 0)) return -EINVAL; @@ -543,7 +589,7 @@ int reset_control_deassert(struct reset_control *rstc) } else { if (!rstc->acquired) { WARN(1, "reset %s (ID: %u) is not acquired\n", - rcdev_name(rstc->rcdev), rstc->id); + rcdev_name(rcdev), rstc->id); return -EPERM; } } @@ -555,10 +601,10 @@ int reset_control_deassert(struct reset_control *rstc) * case, the reset controller driver should implement .deassert() and * return -ENOTSUPP. */ - if (!rstc->rcdev->ops->deassert) + if (!rcdev->ops->deassert) return 0; - return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); + return rcdev->ops->deassert(rcdev, rstc->id); } EXPORT_SYMBOL_GPL(reset_control_deassert); @@ -600,14 +646,22 @@ EXPORT_SYMBOL_GPL(reset_control_bulk_deassert); */ int reset_control_status(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; + if (!rstc) return 0; if (WARN_ON(IS_ERR(rstc)) || reset_control_is_array(rstc)) return -EINVAL; - if (rstc->rcdev->ops->status) - return rstc->rcdev->ops->status(rstc->rcdev, rstc->id); + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + + if (rcdev->ops->status) + return rcdev->ops->status(rcdev, rstc->id); return -ENOTSUPP; } @@ -635,6 +689,7 @@ EXPORT_SYMBOL_GPL(reset_control_status); */ int reset_control_acquire(struct reset_control *rstc) { + struct reset_controller_dev *rcdev; struct reset_control *rc; if (!rstc) @@ -651,7 +706,13 @@ int reset_control_acquire(struct reset_control *rstc) if (rstc->acquired) return 0; - list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) { + guard(srcu)(&rstc->srcu); + + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + return -ENODEV; + + list_for_each_entry(rc, &rcdev->reset_control_head, list) { if (rstc != rc && rstc->id == rc->id) { if (rc->acquired) return -EBUSY; @@ -743,6 +804,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev, bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED; bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED; struct reset_control *rstc; + int ret; lockdep_assert_held(&reset_list_mutex); @@ -773,12 +835,19 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev, if (!rstc) return ERR_PTR(-ENOMEM); + ret = init_srcu_struct(&rstc->srcu); + if (ret) { + kfree(rstc); + return ERR_PTR(ret); + } + if (!try_module_get(rcdev->owner)) { + cleanup_srcu_struct(&rstc->srcu); kfree(rstc); return ERR_PTR(-ENODEV); } - rstc->rcdev = rcdev; + rcu_assign_pointer(rstc->rcdev, rcdev); list_add(&rstc->list, &rcdev->reset_control_head); rstc->id = index; kref_init(&rstc->refcnt); @@ -793,13 +862,18 @@ static void __reset_control_release(struct kref *kref) { struct reset_control *rstc = container_of(kref, struct reset_control, refcnt); + struct reset_controller_dev *rcdev; lockdep_assert_held(&reset_list_mutex); - module_put(rstc->rcdev->owner); + scoped_guard(srcu, &rstc->srcu) { + rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true); + if (rcdev) + reset_controller_remove(rcdev, rstc); + } - list_del(&rstc->list); - put_device(rstc->rcdev->dev); + synchronize_srcu(&rstc->srcu); + cleanup_srcu_struct(&rstc->srcu); kfree(rstc); } -- cgit v1.2.3 From 44a0acb2caca3bfd0ca459fbf0b19be75f1819c0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:53 +0100 Subject: reset: protect struct reset_controller_dev with its own mutex Currently we use a single, global mutex - misleadingly names reset_list_mutex - to protect the global list of reset devices, per-controller list of reset control handles and also internal fields of struct reset_control. Locking can be made a lot more fine-grained if we use a separate mutex for serializing operations on the list AND accessing the reset controller device. Signed-off-by: Bartosz Golaszewski Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 44 ++++++++++++++++++++++++---------------- include/linux/reset-controller.h | 3 +++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index e6c12fbebca9..acd9d10b1ceb 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -131,6 +131,7 @@ int reset_controller_register(struct reset_controller_dev *rcdev) } INIT_LIST_HEAD(&rcdev->reset_control_head); + mutex_init(&rcdev->lock); guard(mutex)(&reset_list_mutex); @@ -143,6 +144,8 @@ EXPORT_SYMBOL_GPL(reset_controller_register); static void reset_controller_remove(struct reset_controller_dev *rcdev, struct reset_control *rstc) { + lockdep_assert_held(&rcdev->lock); + list_del(&rstc->list); module_put(rcdev->owner); put_device(rcdev->dev); @@ -156,19 +159,22 @@ void reset_controller_unregister(struct reset_controller_dev *rcdev) { struct reset_control *rstc, *pos; - guard(mutex)(&reset_list_mutex); - - list_del(&rcdev->list); + scoped_guard(mutex, &reset_list_mutex) + list_del(&rcdev->list); - /* - * Numb but don't free the remaining reset control handles that are - * still held by consumers. - */ - list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) { - rcu_assign_pointer(rstc->rcdev, NULL); - synchronize_srcu(&rstc->srcu); - reset_controller_remove(rcdev, rstc); + scoped_guard(mutex, &rcdev->lock) { + /* + * Numb but don't free the remaining reset control handles that are + * still held by consumers. + */ + list_for_each_entry_safe(rstc, pos, &rcdev->reset_control_head, list) { + rcu_assign_pointer(rstc->rcdev, NULL); + synchronize_srcu(&rstc->srcu); + reset_controller_remove(rcdev, rstc); + } } + + mutex_destroy(&rcdev->lock); } EXPORT_SYMBOL_GPL(reset_controller_unregister); @@ -712,10 +718,12 @@ int reset_control_acquire(struct reset_control *rstc) if (!rcdev) return -ENODEV; - list_for_each_entry(rc, &rcdev->reset_control_head, list) { - if (rstc != rc && rstc->id == rc->id) { - if (rc->acquired) - return -EBUSY; + scoped_guard(mutex, &rcdev->lock) { + list_for_each_entry(rc, &rcdev->reset_control_head, list) { + if (rstc != rc && rstc->id == rc->id) { + if (rc->acquired) + return -EBUSY; + } } } @@ -806,7 +814,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev, struct reset_control *rstc; int ret; - lockdep_assert_held(&reset_list_mutex); + lockdep_assert_held(&rcdev->lock); /* Expect callers to filter out OPTIONAL and DEASSERTED bits */ if (WARN_ON(flags & ~(RESET_CONTROL_FLAGS_BIT_SHARED | @@ -868,8 +876,10 @@ static void __reset_control_release(struct kref *kref) scoped_guard(srcu, &rstc->srcu) { rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true); - if (rcdev) + if (rcdev) { + guard(mutex)(&rcdev->lock); reset_controller_remove(rcdev, rstc); + } } synchronize_srcu(&rstc->srcu); diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index aa95b460fdf8..185d2a9bd7cd 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -3,6 +3,7 @@ #define _LINUX_RESET_CONTROLLER_H_ #include +#include struct reset_controller_dev; @@ -40,6 +41,7 @@ struct of_phandle_args; * device tree to id as given to the reset control ops, defaults * to :c:func:`of_reset_simple_xlate`. * @nr_resets: number of reset controls in this reset controller device + * @lock: protects the reset control list from concurrent access */ struct reset_controller_dev { const struct reset_control_ops *ops; @@ -52,6 +54,7 @@ struct reset_controller_dev { int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec); unsigned int nr_resets; + struct mutex lock; }; #if IS_ENABLED(CONFIG_RESET_CONTROLLER) -- cgit v1.2.3 From 8c91302a29bc1b710c7a164d4b81b5bb432f4eb5 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:54 +0100 Subject: reset: protect struct reset_control with its own mutex Currently we use a single, global mutex - misleadingly names reset_list_mutex - to protect the global list of reset devices, per-controller list of reset control handles and also internal fields of struct reset_control. Locking can be made a lot more fine-grained if we use a separate mutex for serializing operations on the list AND accessing the reset control handle. Signed-off-by: Bartosz Golaszewski Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 68 +++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index acd9d10b1ceb..edb672ad7e40 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -49,6 +49,7 @@ static DEFINE_IDA(reset_gpio_ida); * @triggered_count: Number of times this reset line has been reset. Currently * only used for shared resets, which means that the value * will be either 0 or 1. + * @lock: serializes the internals of reset_control_acquire() */ struct reset_control { struct reset_controller_dev __rcu *rcdev; @@ -61,6 +62,7 @@ struct reset_control { bool array; atomic_t deassert_count; atomic_t triggered_count; + struct mutex lock; }; /** @@ -707,7 +709,7 @@ int reset_control_acquire(struct reset_control *rstc) if (reset_control_is_array(rstc)) return reset_control_array_acquire(rstc_to_array(rstc)); - guard(mutex)(&reset_list_mutex); + guard(mutex)(&rstc->lock); if (rstc->acquired) return 0; @@ -859,6 +861,7 @@ __reset_control_get_internal(struct reset_controller_dev *rcdev, list_add(&rstc->list, &rcdev->reset_control_head); rstc->id = index; kref_init(&rstc->refcnt); + mutex_init(&rstc->lock); rstc->acquired = acquired; rstc->shared = shared; get_device(rcdev->dev); @@ -872,29 +875,40 @@ static void __reset_control_release(struct kref *kref) refcnt); struct reset_controller_dev *rcdev; - lockdep_assert_held(&reset_list_mutex); + lockdep_assert_held(&rstc->srcu); - scoped_guard(srcu, &rstc->srcu) { - rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true); - if (rcdev) { - guard(mutex)(&rcdev->lock); - reset_controller_remove(rcdev, rstc); - } + rcdev = rcu_replace_pointer(rstc->rcdev, NULL, true); + if (rcdev) { + lockdep_assert_held(&rcdev->lock); + reset_controller_remove(rcdev, rstc); } - synchronize_srcu(&rstc->srcu); - cleanup_srcu_struct(&rstc->srcu); - kfree(rstc); + mutex_destroy(&rstc->lock); } -static void __reset_control_put_internal(struct reset_control *rstc) +static void reset_control_put_internal(struct reset_control *rstc) { - lockdep_assert_held(&reset_list_mutex); + struct reset_controller_dev *rcdev; + int ret = 0; if (IS_ERR_OR_NULL(rstc)) return; - kref_put(&rstc->refcnt, __reset_control_release); + scoped_guard(srcu, &rstc->srcu) { + rcdev = srcu_dereference(rstc->rcdev, &rstc->srcu); + if (!rcdev) + /* Already released. */ + return; + + guard(mutex)(&rcdev->lock); + ret = kref_put(&rstc->refcnt, __reset_control_release); + } + + if (ret) { + synchronize_srcu(&rstc->srcu); + cleanup_srcu_struct(&rstc->srcu); + kfree(rstc); + } } static void reset_gpio_aux_device_release(struct device *dev) @@ -1104,7 +1118,7 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; bool gpio_fallback = false; - struct reset_control *rstc; + struct reset_control *rstc = ERR_PTR(-EINVAL); struct reset_controller_dev *rcdev; struct of_phandle_args args; int rstc_id; @@ -1169,8 +1183,8 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL; - /* reset_list_mutex also protects the rcdev's reset_control list */ - rstc = __reset_control_get_internal(rcdev, rstc_id, flags); + scoped_guard(mutex, &rcdev->lock) + rstc = __reset_control_get_internal(rcdev, rstc_id, flags); out_put: of_node_put(args.np); @@ -1213,10 +1227,8 @@ int __reset_control_bulk_get(struct device *dev, int num_rstcs, return 0; err: - guard(mutex)(&reset_list_mutex); - while (i--) - __reset_control_put_internal(rstcs[i].rstc); + reset_control_put_internal(rstcs[i].rstc); return ret; } @@ -1226,10 +1238,8 @@ static void reset_control_array_put(struct reset_control_array *resets) { int i; - guard(mutex)(&reset_list_mutex); - for (i = 0; i < resets->num_rstcs; i++) - __reset_control_put_internal(resets->rstc[i]); + reset_control_put_internal(resets->rstc[i]); kfree(resets); } @@ -1247,9 +1257,7 @@ void reset_control_put(struct reset_control *rstc) return; } - guard(mutex)(&reset_list_mutex); - - __reset_control_put_internal(rstc); + reset_control_put_internal(rstc); } EXPORT_SYMBOL_GPL(reset_control_put); @@ -1260,10 +1268,8 @@ EXPORT_SYMBOL_GPL(reset_control_put); */ void reset_control_bulk_put(int num_rstcs, struct reset_control_bulk_data *rstcs) { - guard(mutex)(&reset_list_mutex); - while (num_rstcs--) - __reset_control_put_internal(rstcs[num_rstcs].rstc); + reset_control_put_internal(rstcs[num_rstcs].rstc); } EXPORT_SYMBOL_GPL(reset_control_bulk_put); @@ -1482,10 +1488,8 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag return &resets->base; err_rst: - guard(mutex)(&reset_list_mutex); - while (--i >= 0) - __reset_control_put_internal(resets->rstc[i]); + reset_control_put_internal(resets->rstc[i]); kfree(resets); -- cgit v1.2.3 From 9d52054a4fc3d6f95f4aa81d5355505c98a3e4ee Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:55 +0100 Subject: reset: convert of_reset_control_get_count() to using firmware nodes Start the conversion of reset core to using firmware nodes by reworking of_reset_control_get_count(). Unfortunately there is no fwnode-based alternative to of_count_phandle_with_args() so we have to hand-code it. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index edb672ad7e40..f1b644a86ad0 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1430,21 +1432,35 @@ EXPORT_SYMBOL_GPL(__device_reset); */ /** - * of_reset_control_get_count - Count number of resets available with a device + * fwnode_reset_control_get_count - Count number of resets available with a device * - * @node: device node that contains 'resets'. + * @fwnode: firmware node that contains 'resets'. * * Returns positive reset count on success, or error number on failure and * on count being zero. */ -static int of_reset_control_get_count(struct device_node *node) +static int fwnode_reset_control_get_count(struct fwnode_handle *fwnode) { - int count; + struct fwnode_reference_args args; + int count = 0, ret; - if (!node) + if (!fwnode) return -EINVAL; - count = of_count_phandle_with_args(node, "resets", "#reset-cells"); + for (;;) { + ret = fwnode_property_get_reference_args(fwnode, "resets", "#reset-cells", + 0, count, &args); + if (ret) { + if (ret == -ENOENT) + break; + + return ret; + } + + fwnode_handle_put(args.fwnode); + count++; + } + if (count == 0) count = -ENOENT; @@ -1468,7 +1484,7 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag struct reset_control *rstc; int num, i; - num = of_reset_control_get_count(np); + num = fwnode_reset_control_get_count(of_fwnode_handle(np)); if (num < 0) return optional ? NULL : ERR_PTR(num); @@ -1542,8 +1558,10 @@ EXPORT_SYMBOL_GPL(devm_reset_control_array_get); */ int reset_control_get_count(struct device *dev) { - if (dev->of_node) - return of_reset_control_get_count(dev->of_node); + struct fwnode_handle *fwnode = dev_fwnode(dev); + + if (fwnode) + return fwnode_reset_control_get_count(fwnode); return -ENOENT; } -- cgit v1.2.3 From ba8dbbb14b7e6734afbb5ba37d0679831aa3d590 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:56 +0100 Subject: reset: convert the core API to using firmware nodes In order to simplify the commit converting the internals of reset core to using firmware nodes, first convert the user-facing API. Modify the signature of the core consumer functions but leave the specialized wrappers as is to avoid modifying users for now. No functional change intended. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- Documentation/driver-api/reset.rst | 1 - drivers/reset/core.c | 33 ++++++++++++++++------------- include/linux/reset.h | 43 +++++++++++++++++++++++++------------- 3 files changed, 46 insertions(+), 31 deletions(-) diff --git a/Documentation/driver-api/reset.rst b/Documentation/driver-api/reset.rst index f773100daaa4..7a6571849664 100644 --- a/Documentation/driver-api/reset.rst +++ b/Documentation/driver-api/reset.rst @@ -198,7 +198,6 @@ query the reset line status using reset_control_status(). reset_control_rearm reset_control_put of_reset_control_get_count - of_reset_control_array_get devm_reset_control_array_get reset_control_get_count diff --git a/drivers/reset/core.c b/drivers/reset/core.c index f1b644a86ad0..0da5079ea9db 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -1061,7 +1061,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np, rgpio_dev->of_args = *args; /* * We keep the device_node reference, but of_args.np is put at the end - * of __of_reset_control_get(), so get it one more time. + * of __fwnode_reset_control_get(), so get it one more time. * Hold reference as long as rgpio_dev memory is valid. */ of_node_get(rgpio_dev->of_args.np); @@ -1115,18 +1115,19 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a } struct reset_control * -__of_reset_control_get(struct device_node *node, const char *id, int index, - enum reset_control_flags flags) +__fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int index, + enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; bool gpio_fallback = false; + struct device_node *node = to_of_node(fwnode); struct reset_control *rstc = ERR_PTR(-EINVAL); struct reset_controller_dev *rcdev; struct of_phandle_args args; int rstc_id; int ret; - if (!node) + if (!fwnode) return ERR_PTR(-EINVAL); if (id) { @@ -1193,7 +1194,7 @@ out_put: return rstc; } -EXPORT_SYMBOL_GPL(__of_reset_control_get); +EXPORT_SYMBOL_GPL(__fwnode_reset_control_get); struct reset_control *__reset_control_get(struct device *dev, const char *id, int index, enum reset_control_flags flags) @@ -1201,12 +1202,13 @@ struct reset_control *__reset_control_get(struct device *dev, const char *id, bool shared = flags & RESET_CONTROL_FLAGS_BIT_SHARED; bool acquired = flags & RESET_CONTROL_FLAGS_BIT_ACQUIRED; bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; + struct fwnode_handle *fwnode = dev_fwnode(dev); if (WARN_ON(shared && acquired)) return ERR_PTR(-EINVAL); - if (dev->of_node) - return __of_reset_control_get(dev->of_node, id, index, flags); + if (fwnode) + return __fwnode_reset_control_get(fwnode, id, index, flags); return optional ? NULL : ERR_PTR(-ENOENT); } @@ -1468,23 +1470,24 @@ static int fwnode_reset_control_get_count(struct fwnode_handle *fwnode) } /** - * of_reset_control_array_get - Get a list of reset controls using - * device node. + * fwnode_reset_control_array_get - Get a list of reset controls using + * a firmware node. * - * @np: device node for the device that requests the reset controls array + * @fwnode: firmware node for the device that requests the reset controls array * @flags: whether reset controls are shared, optional, acquired * * Returns pointer to allocated reset_control on success or error on failure */ struct reset_control * -of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags) +fwnode_reset_control_array_get(struct fwnode_handle *fwnode, + enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; struct reset_control_array *resets; struct reset_control *rstc; int num, i; - num = fwnode_reset_control_get_count(of_fwnode_handle(np)); + num = fwnode_reset_control_get_count(fwnode); if (num < 0) return optional ? NULL : ERR_PTR(num); @@ -1494,7 +1497,7 @@ of_reset_control_array_get(struct device_node *np, enum reset_control_flags flag resets->num_rstcs = num; for (i = 0; i < num; i++) { - rstc = __of_reset_control_get(np, NULL, i, flags); + rstc = __fwnode_reset_control_get(fwnode, NULL, i, flags); if (IS_ERR(rstc)) goto err_rst; resets->rstc[i] = rstc; @@ -1511,7 +1514,7 @@ err_rst: return rstc; } -EXPORT_SYMBOL_GPL(of_reset_control_array_get); +EXPORT_SYMBOL_GPL(fwnode_reset_control_array_get); /** * devm_reset_control_array_get - Resource managed reset control array get @@ -1535,7 +1538,7 @@ devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags) if (!ptr) return ERR_PTR(-ENOMEM); - rstc = of_reset_control_array_get(dev->of_node, flags); + rstc = fwnode_reset_control_array_get(dev_fwnode(dev), flags); if (IS_ERR_OR_NULL(rstc)) { devres_free(ptr); return rstc; diff --git a/include/linux/reset.h b/include/linux/reset.h index 44f9e3415f92..9c391cf0c822 100644 --- a/include/linux/reset.h +++ b/include/linux/reset.h @@ -5,10 +5,12 @@ #include #include #include +#include #include struct device; struct device_node; +struct fwnode_handle; struct reset_control; /** @@ -84,7 +86,7 @@ int reset_control_bulk_deassert(int num_rstcs, struct reset_control_bulk_data *r int reset_control_bulk_acquire(int num_rstcs, struct reset_control_bulk_data *rstcs); void reset_control_bulk_release(int num_rstcs, struct reset_control_bulk_data *rstcs); -struct reset_control *__of_reset_control_get(struct device_node *node, +struct reset_control *__fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int index, enum reset_control_flags flags); struct reset_control *__reset_control_get(struct device *dev, const char *id, int index, enum reset_control_flags flags); @@ -103,7 +105,8 @@ int __devm_reset_control_bulk_get(struct device *dev, int num_rstcs, struct reset_control *devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags); -struct reset_control *of_reset_control_array_get(struct device_node *np, enum reset_control_flags); +struct reset_control *fwnode_reset_control_array_get(struct fwnode_handle *fwnode, + enum reset_control_flags); int reset_control_get_count(struct device *dev); @@ -152,8 +155,8 @@ static inline int __device_reset(struct device *dev, bool optional) return optional ? 0 : -ENOTSUPP; } -static inline struct reset_control *__of_reset_control_get( - struct device_node *node, +static inline struct reset_control *__fwnode_reset_control_get( + struct fwnode_handle *fwnode, const char *id, int index, enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; @@ -242,7 +245,7 @@ devm_reset_control_array_get(struct device *dev, enum reset_control_flags flags) } static inline struct reset_control * -of_reset_control_array_get(struct device_node *np, enum reset_control_flags flags) +fwnode_reset_control_array_get(struct fwnode_handle *fwnode, enum reset_control_flags flags) { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; @@ -500,7 +503,8 @@ reset_control_bulk_get_optional_shared(struct device *dev, int num_rstcs, static inline struct reset_control *of_reset_control_get_exclusive( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, RESET_CONTROL_EXCLUSIVE); + return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0, + RESET_CONTROL_EXCLUSIVE); } /** @@ -520,7 +524,8 @@ static inline struct reset_control *of_reset_control_get_exclusive( static inline struct reset_control *of_reset_control_get_optional_exclusive( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, RESET_CONTROL_OPTIONAL_EXCLUSIVE); + return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0, + RESET_CONTROL_OPTIONAL_EXCLUSIVE); } /** @@ -545,7 +550,8 @@ static inline struct reset_control *of_reset_control_get_optional_exclusive( static inline struct reset_control *of_reset_control_get_shared( struct device_node *node, const char *id) { - return __of_reset_control_get(node, id, 0, RESET_CONTROL_SHARED); + return __fwnode_reset_control_get(of_fwnode_handle(node), id, 0, + RESET_CONTROL_SHARED); } /** @@ -562,7 +568,8 @@ static inline struct reset_control *of_reset_control_get_shared( static inline struct reset_control *of_reset_control_get_exclusive_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index, RESET_CONTROL_EXCLUSIVE); + return __fwnode_reset_control_get(of_fwnode_handle(node), NULL, index, + RESET_CONTROL_EXCLUSIVE); } /** @@ -590,7 +597,8 @@ static inline struct reset_control *of_reset_control_get_exclusive_by_index( static inline struct reset_control *of_reset_control_get_shared_by_index( struct device_node *node, int index) { - return __of_reset_control_get(node, NULL, index, RESET_CONTROL_SHARED); + return __fwnode_reset_control_get(of_fwnode_handle(node), NULL, index, + RESET_CONTROL_SHARED); } /** @@ -1032,30 +1040,35 @@ devm_reset_control_array_get_optional_shared(struct device *dev) static inline struct reset_control * of_reset_control_array_get_exclusive(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_EXCLUSIVE); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_EXCLUSIVE); } static inline struct reset_control * of_reset_control_array_get_exclusive_released(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_EXCLUSIVE_RELEASED); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_EXCLUSIVE_RELEASED); } static inline struct reset_control * of_reset_control_array_get_shared(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_SHARED); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_SHARED); } static inline struct reset_control * of_reset_control_array_get_optional_exclusive(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_OPTIONAL_EXCLUSIVE); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_OPTIONAL_EXCLUSIVE); } static inline struct reset_control * of_reset_control_array_get_optional_shared(struct device_node *node) { - return of_reset_control_array_get(node, RESET_CONTROL_OPTIONAL_SHARED); + return fwnode_reset_control_array_get(of_fwnode_handle(node), + RESET_CONTROL_OPTIONAL_SHARED); } #endif -- cgit v1.2.3 From 9035073d0ef1de813c6335239250248bfe0a64aa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:57 +0100 Subject: reset: convert reset core to using firmware nodes With everything else now in place, we can convert the remaining parts of the reset subsystem to becoming fwnode-agnostic - meaning it will work with all kinds of firmware nodes, not only devicetree. To that end: extend struct reset_controller_dev with fields taking information relevant for using firmware nodes (which mirrors what we already do for OF-nodes) and limit using of_ APIs only to where it's absolutely necessary (mostly around the of_xlate callback). For backward compatibility of existing drivers we still support OF-nodes but firmware nodes become the preferred method. Signed-off-by: Bartosz Golaszewski Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 166 +++++++++++++++++++++++---------------- include/linux/reset-controller.h | 14 +++- 2 files changed, 112 insertions(+), 68 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 0da5079ea9db..e625cf59cfb0 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -81,13 +81,13 @@ struct reset_control_array { /** * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices - * @of_args: phandle to the reset controller with all the args like GPIO number + * @ref_args: Reference to the reset controller with all the args like GPIO number * @swnode: Software node containing the reference to the GPIO provider * @list: list entry for the reset_gpio_lookup_list * @adev: Auxiliary device representing the reset controller */ struct reset_gpio_lookup { - struct of_phandle_args of_args; + struct fwnode_reference_args ref_args; struct fwnode_handle *swnode; struct list_head list; struct auxiliary_device adev; @@ -98,24 +98,24 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) if (rcdev->dev) return dev_name(rcdev->dev); - if (rcdev->of_node) - return rcdev->of_node->full_name; + if (rcdev->fwnode) + return fwnode_get_name(rcdev->fwnode); return NULL; } /** - * of_reset_simple_xlate - translate reset_spec to the reset line number + * fwnode_reset_simple_xlate - translate reset_spec to the reset line number * @rcdev: a pointer to the reset controller device - * @reset_spec: reset line specifier as found in the device tree + * @reset_spec: reset line specifier as found in firmware * - * This static translation function is used by default if of_xlate in - * :c:type:`reset_controller_dev` is not set. It is useful for all reset - * controllers with 1:1 mapping, where reset lines can be indexed by number - * without gaps. + * This static translation function is used by default if neither fwnode_xlate + * not of_xlate in :c:type:`reset_controller_dev` is not set. It is useful for + * all reset controllers with 1:1 mapping, where reset lines can be indexed by + * number without gaps. */ -static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, - const struct of_phandle_args *reset_spec) +static int fwnode_reset_simple_xlate(struct reset_controller_dev *rcdev, + const struct fwnode_reference_args *reset_spec) { if (reset_spec->args[0] >= rcdev->nr_resets) return -EINVAL; @@ -129,9 +129,23 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, */ int reset_controller_register(struct reset_controller_dev *rcdev) { - if (!rcdev->of_xlate) { - rcdev->of_reset_n_cells = 1; - rcdev->of_xlate = of_reset_simple_xlate; + if ((rcdev->of_node && rcdev->fwnode) || (rcdev->of_xlate && rcdev->fwnode_xlate)) + return -EINVAL; + + if (!rcdev->of_node && !rcdev->fwnode) { + rcdev->fwnode = dev_fwnode(rcdev->dev); + if (!rcdev->fwnode) + return -EINVAL; + } + + if (rcdev->of_node) { + rcdev->fwnode = of_fwnode_handle(rcdev->of_node); + rcdev->fwnode_reset_n_cells = rcdev->of_reset_n_cells; + } + + if (rcdev->fwnode && !rcdev->fwnode_xlate) { + rcdev->fwnode_reset_n_cells = 1; + rcdev->fwnode_xlate = fwnode_reset_simple_xlate; } INIT_LIST_HEAD(&rcdev->reset_control_head); @@ -931,7 +945,7 @@ static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev, adev->id = id; adev->name = "gpio"; adev->dev.parent = parent; - adev->dev.platform_data = &rgpio_dev->of_args; + adev->dev.platform_data = &rgpio_dev->ref_args; adev->dev.release = reset_gpio_aux_device_release; device_set_node(&adev->dev, rgpio_dev->swnode); @@ -951,18 +965,18 @@ static int reset_create_gpio_aux_device(struct reset_gpio_lookup *rgpio_dev, return 0; } -static void reset_gpio_add_devlink(struct device_node *np, +static void reset_gpio_add_devlink(struct fwnode_handle *fwnode, struct reset_gpio_lookup *rgpio_dev) { struct device *consumer; /* - * We must use get_dev_from_fwnode() and not of_find_device_by_node() + * We must use get_dev_from_fwnode() and not ref_find_device_by_node() * because the latter only considers the platform bus while we want to * get consumers of any kind that can be associated with firmware * nodes: auxiliary, soundwire, etc. */ - consumer = get_dev_from_fwnode(of_fwnode_handle(np)); + consumer = get_dev_from_fwnode(fwnode); if (consumer) { if (!device_link_add(consumer, &rgpio_dev->adev.dev, DL_FLAG_AUTOREMOVE_CONSUMER)) @@ -982,15 +996,23 @@ static void reset_gpio_add_devlink(struct device_node *np, */ } +/* TODO: move it out into drivers/base/ */ +static bool fwnode_reference_args_equal(const struct fwnode_reference_args *left, + const struct fwnode_reference_args *right) +{ + return left->fwnode == right->fwnode && left->nargs == right->nargs && + !memcmp(left->args, right->args, sizeof(left->args[0]) * left->nargs); +} + /* * @np: OF-node associated with the consumer - * @args: phandle to the GPIO provider with all the args like GPIO number + * @args: Reference to the GPIO provider with all the args like GPIO number */ -static int __reset_add_reset_gpio_device(struct device_node *np, - const struct of_phandle_args *args) +static int __reset_add_reset_gpio_device(struct fwnode_handle *fwnode, + const struct fwnode_reference_args *args) { struct property_entry properties[3] = { }; - unsigned int offset, of_flags, lflags; + unsigned int offset, flags, lflags; struct reset_gpio_lookup *rgpio_dev; struct device *parent; int ret, prop = 0; @@ -1001,7 +1023,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np, * args[1]: GPIO flags * TODO: Handle other cases. */ - if (args->args_count != 2) + if (args->nargs != 2) return -ENOENT; /* @@ -1012,7 +1034,7 @@ static int __reset_add_reset_gpio_device(struct device_node *np, lockdep_assert_not_held(&reset_list_mutex); offset = args->args[0]; - of_flags = args->args[1]; + flags = args->args[1]; /* * Later we map GPIO flags between OF and Linux, however not all @@ -1022,33 +1044,31 @@ static int __reset_add_reset_gpio_device(struct device_node *np, * FIXME: Find a better way of translating OF flags to GPIO lookup * flags. */ - if (of_flags > GPIO_ACTIVE_LOW) { + if (flags > GPIO_ACTIVE_LOW) { pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n", - of_flags, offset); + flags, offset); return -EINVAL; } struct gpio_device *gdev __free(gpio_device_put) = - gpio_device_find_by_fwnode(of_fwnode_handle(args->np)); + gpio_device_find_by_fwnode(args->fwnode); if (!gdev) return -EPROBE_DEFER; guard(mutex)(&reset_gpio_lookup_mutex); list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) { - if (args->np == rgpio_dev->of_args.np) { - if (of_phandle_args_equal(args, &rgpio_dev->of_args)) { - /* - * Already on the list, create the device link - * and stop here. - */ - reset_gpio_add_devlink(np, rgpio_dev); - return 0; - } + if (fwnode_reference_args_equal(args, &rgpio_dev->ref_args)) { + /* + * Already on the list, create the device link + * and stop here. + */ + reset_gpio_add_devlink(fwnode, rgpio_dev); + return 0; } } - lflags = GPIO_PERSISTENT | (of_flags & GPIO_ACTIVE_LOW); + lflags = GPIO_PERSISTENT | (flags & GPIO_ACTIVE_LOW); parent = gpio_device_to_device(gdev); properties[prop++] = PROPERTY_ENTRY_STRING("compatible", "reset-gpio"); properties[prop++] = PROPERTY_ENTRY_GPIO("reset-gpios", parent->fwnode, offset, lflags); @@ -1058,43 +1078,43 @@ static int __reset_add_reset_gpio_device(struct device_node *np, if (!rgpio_dev) return -ENOMEM; - rgpio_dev->of_args = *args; + rgpio_dev->ref_args = *args; /* - * We keep the device_node reference, but of_args.np is put at the end - * of __fwnode_reset_control_get(), so get it one more time. + * We keep the fwnode_handle reference, but ref_args.fwnode is put at + * the end of __fwnode_reset_control_get(), so get it one more time. * Hold reference as long as rgpio_dev memory is valid. */ - of_node_get(rgpio_dev->of_args.np); + fwnode_handle_get(rgpio_dev->ref_args.fwnode); rgpio_dev->swnode = fwnode_create_software_node(properties, NULL); if (IS_ERR(rgpio_dev->swnode)) { ret = PTR_ERR(rgpio_dev->swnode); - goto err_put_of_node; + goto err_put_fwnode; } ret = reset_create_gpio_aux_device(rgpio_dev, parent); if (ret) goto err_del_swnode; - reset_gpio_add_devlink(np, rgpio_dev); + reset_gpio_add_devlink(fwnode, rgpio_dev); list_add(&rgpio_dev->list, &reset_gpio_lookup_list); return 0; err_del_swnode: fwnode_remove_software_node(rgpio_dev->swnode); -err_put_of_node: - of_node_put(rgpio_dev->of_args.np); +err_put_fwnode: + fwnode_handle_put(rgpio_dev->ref_args.fwnode); kfree(rgpio_dev); return ret; } -static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args, - bool gpio_fallback) +static struct reset_controller_dev * +__reset_find_rcdev(const struct fwnode_reference_args *args, bool gpio_fallback) { + struct fwnode_reference_args *rc_args; struct reset_controller_dev *rcdev; - struct of_phandle_args *rc_args; lockdep_assert_held(&reset_list_mutex); @@ -1103,10 +1123,10 @@ static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_a device_is_compatible(rcdev->dev, "reset-gpio")) { rc_args = dev_get_platdata(rcdev->dev); - if (of_phandle_args_equal(args, rc_args)) + if (fwnode_reference_args_equal(args, rc_args)) return rcdev; } else { - if (args->np == rcdev->of_node) + if (args->fwnode == rcdev->fwnode) return rcdev; } } @@ -1120,27 +1140,26 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind { bool optional = flags & RESET_CONTROL_FLAGS_BIT_OPTIONAL; bool gpio_fallback = false; - struct device_node *node = to_of_node(fwnode); struct reset_control *rstc = ERR_PTR(-EINVAL); struct reset_controller_dev *rcdev; - struct of_phandle_args args; - int rstc_id; + struct fwnode_reference_args args; + struct of_phandle_args of_args; + int rstc_id = -EINVAL; int ret; if (!fwnode) return ERR_PTR(-EINVAL); if (id) { - index = of_property_match_string(node, - "reset-names", id); + index = fwnode_property_match_string(fwnode, "reset-names", id); if (index == -EILSEQ) return ERR_PTR(index); if (index < 0) return optional ? NULL : ERR_PTR(-ENOENT); } - ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", - index, &args); + ret = fwnode_property_get_reference_args(fwnode, "resets", "#reset-cells", + 0, index, &args); if (ret == -EINVAL) return ERR_PTR(ret); if (ret) { @@ -1151,16 +1170,16 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind * There can be only one reset-gpio for regular devices, so * don't bother with the "reset-gpios" phandle index. */ - ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells", - 0, &args); + ret = fwnode_property_get_reference_args(fwnode, "reset-gpios", + "#gpio-cells", 0, 0, &args); if (ret) return optional ? NULL : ERR_PTR(ret); gpio_fallback = true; - ret = __reset_add_reset_gpio_device(node, &args); + ret = __reset_add_reset_gpio_device(fwnode, &args); if (ret) { - of_node_put(args.np); + fwnode_handle_put(args.fwnode); return ERR_PTR(ret); } } @@ -1173,15 +1192,30 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind goto out_put; } - if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { + if (WARN_ON(args.nargs != rcdev->fwnode_reset_n_cells)) { rstc = ERR_PTR(-EINVAL); goto out_put; } - rstc_id = rcdev->of_xlate(rcdev, &args); + if (rcdev->of_xlate && is_of_node(fwnode)) { + ret = of_parse_phandle_with_args(to_of_node(fwnode), + gpio_fallback ? "reset-gpios" : "resets", + gpio_fallback ? "#gpio-cells" : "#reset-cells", + gpio_fallback ? 0 : index, + &of_args); + if (ret) { + rstc = ERR_PTR(ret); + goto out_put; + } + + rstc_id = rcdev->of_xlate(rcdev, &of_args); + of_node_put(of_args.np); + } else if (rcdev->fwnode_xlate) { + rstc_id = rcdev->fwnode_xlate(rcdev, &args); + } if (rstc_id < 0) { rstc = ERR_PTR(rstc_id); - goto out_put; + goto out_put; } flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL; @@ -1190,7 +1224,7 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind rstc = __reset_control_get_internal(rcdev, rstc_id, flags); out_put: - of_node_put(args.np); + fwnode_handle_put(args.fwnode); return rstc; } diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h index 185d2a9bd7cd..52a5a4e81f18 100644 --- a/include/linux/reset-controller.h +++ b/include/linux/reset-controller.h @@ -5,6 +5,8 @@ #include #include +struct fwnode_handle; +struct fwnode_reference_args; struct reset_controller_dev; /** @@ -38,8 +40,12 @@ struct of_phandle_args; * @of_node: corresponding device tree node as phandle target * @of_reset_n_cells: number of cells in reset line specifiers * @of_xlate: translation function to translate from specifier as found in the - * device tree to id as given to the reset control ops, defaults - * to :c:func:`of_reset_simple_xlate`. + * device tree to id as given to the reset control ops + * @fwnode: firmware node associated with this device + * @fwnode_reset_n_cells: number of cells in reset line specifiers + * @fwnode_xlate: translation function to translate from firmware specifier to + * id as given to the reset control ops, defaults to + * :c:func:`fwnode_reset_simple_xlate` * @nr_resets: number of reset controls in this reset controller device * @lock: protects the reset control list from concurrent access */ @@ -53,6 +59,10 @@ struct reset_controller_dev { int of_reset_n_cells; int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec); + struct fwnode_handle *fwnode; + int fwnode_reset_n_cells; + int (*fwnode_xlate)(struct reset_controller_dev *rcdev, + const struct fwnode_reference_args *reset_spec); unsigned int nr_resets; struct mutex lock; }; -- cgit v1.2.3 From faaad5006e5835c3b9228906232fc6b17b01512b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Mar 2026 18:22:58 +0100 Subject: reset: gpio: make the driver fwnode-agnostic With reset core now being able to work with firmware nodes, we can make reset-gpio node-agnostic and drop any OF dependencies. Reviewed-by: Philipp Zabel Signed-off-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/reset-gpio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c index 5044f809d0e5..26aa2c3a2e68 100644 --- a/drivers/reset/reset-gpio.c +++ b/drivers/reset/reset-gpio.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include struct reset_gpio_priv { @@ -46,8 +46,8 @@ static const struct reset_control_ops reset_gpio_ops = { .status = reset_gpio_status, }; -static int reset_gpio_of_xlate(struct reset_controller_dev *rcdev, - const struct of_phandle_args *reset_spec) +static int reset_gpio_fwnode_xlate(struct reset_controller_dev *rcdev, + const struct fwnode_reference_args *reset_spec) { return reset_spec->args[0]; } @@ -72,8 +72,8 @@ static int reset_gpio_probe(struct auxiliary_device *adev, priv->rc.dev = dev; /* Cells to match GPIO specifier, but it's not really used */ - priv->rc.of_reset_n_cells = 2; - priv->rc.of_xlate = reset_gpio_of_xlate; + priv->rc.fwnode_reset_n_cells = 2; + priv->rc.fwnode_xlate = reset_gpio_fwnode_xlate; priv->rc.nr_resets = 1; return devm_reset_controller_register(dev, &priv->rc); -- cgit v1.2.3 From c76350e7add86344beae4cd69fffdf63284a4bf5 Mon Sep 17 00:00:00 2001 From: Gary Yang Date: Mon, 2 Mar 2026 14:44:05 +0800 Subject: dt-bindings: soc: cix: document the syscon on Sky1 SoC There are two system control on Cix sky1 Soc. One is located in S0 domain, and the other is located in S5 domain. The system control contains resets, usb typeC and more. At this point, only the reset controller is embedded as usb typeC uses it by phandle. Signed-off-by: Gary Yang Reviewed-by: Krzysztof Kozlowski Signed-off-by: Philipp Zabel --- .../bindings/soc/cix/cix,sky1-system-control.yaml | 42 ++++++ .../dt-bindings/reset/cix,sky1-s5-system-control.h | 163 +++++++++++++++++++++ .../dt-bindings/reset/cix,sky1-system-control.h | 41 ++++++ 3 files changed, 246 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml create mode 100644 include/dt-bindings/reset/cix,sky1-s5-system-control.h create mode 100644 include/dt-bindings/reset/cix,sky1-system-control.h diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml new file mode 100644 index 000000000000..a01a515222c6 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/cix/cix,sky1-system-control.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cix Sky1 SoC system control register region + +maintainers: + - Gary Yang + +description: + An wide assortment of registers of the system controller on Sky1 SoC, + including resets, usb, wakeup sources and so on. + +properties: + compatible: + items: + - enum: + - cix,sky1-system-control + - cix,sky1-s5-system-control + - const: syscon + + reg: + maxItems: 1 + + '#reset-cells': + const: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + syscon@4160000 { + compatible = "cix,sky1-system-control", "syscon"; + reg = <0x4160000 0x100>; + #reset-cells = <1>; + }; diff --git a/include/dt-bindings/reset/cix,sky1-s5-system-control.h b/include/dt-bindings/reset/cix,sky1-s5-system-control.h new file mode 100644 index 000000000000..808bbcbe0c98 --- /dev/null +++ b/include/dt-bindings/reset/cix,sky1-s5-system-control.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Author: Jerry Zhu */ +#ifndef DT_BINDING_RESET_CIX_SKY1_S5_SYSTEM_CONTROL_H +#define DT_BINDING_RESET_CIX_SKY1_S5_SYSTEM_CONTROL_H + +/* reset for csu_pm */ +#define SKY1_CSU_PM_RESET_N 0 +#define SKY1_SENSORFUSION_RESET_N 1 +#define SKY1_SENSORFUSION_NOC_RESET_N 2 + +/* reset group0 for s0 domain modules */ +#define SKY1_DDRC_RESET_N 3 +#define SKY1_GIC_RESET_N 4 +#define SKY1_CI700_RESET_N 5 +#define SKY1_SYS_NI700_RESET_N 6 +#define SKY1_MM_NI700_RESET_N 7 +#define SKY1_PCIE_NI700_RESET_N 8 +#define SKY1_GPU_RESET_N 9 +#define SKY1_NPUTOP_RESET_N 10 +#define SKY1_NPUCORE0_RESET_N 11 +#define SKY1_NPUCORE1_RESET_N 12 +#define SKY1_NPUCORE2_RESET_N 13 +#define SKY1_VPU_RESET_N 14 +#define SKY1_ISP_SRESET_N 15 +#define SKY1_ISP_ARESET_N 16 +#define SKY1_ISP_HRESET_N 17 +#define SKY1_ISP_GDCRESET_N 18 +#define SKY1_DPU_RESET0_N 19 +#define SKY1_DPU_RESET1_N 20 +#define SKY1_DPU_RESET2_N 21 +#define SKY1_DPU_RESET3_N 22 +#define SKY1_DPU_RESET4_N 23 +#define SKY1_DP_RESET0_N 24 +#define SKY1_DP_RESET1_N 25 +#define SKY1_DP_RESET2_N 26 +#define SKY1_DP_RESET3_N 27 +#define SKY1_DP_RESET4_N 28 +#define SKY1_DP_PHY_RST_N 29 + +/* reset group1 for s0 domain modules */ +#define SKY1_AUDIO_HIFI5_RESET_N 30 +#define SKY1_AUDIO_HIFI5_NOC_RESET_N 31 +#define SKY1_CSIDPHY_PRST0_N 32 +#define SKY1_CSIDPHY_CMNRST0_N 33 +#define SKY1_CSI0_RST_N 34 +#define SKY1_CSIDPHY_PRST1_N 35 +#define SKY1_CSIDPHY_CMNRST1_N 36 +#define SKY1_CSI1_RST_N 37 +#define SKY1_CSI2_RST_N 38 +#define SKY1_CSI3_RST_N 39 +#define SKY1_CSIBRDGE0_RST_N 40 +#define SKY1_CSIBRDGE1_RST_N 41 +#define SKY1_CSIBRDGE2_RST_N 42 +#define SKY1_CSIBRDGE3_RST_N 43 +#define SKY1_GMAC0_RST_N 44 +#define SKY1_GMAC1_RST_N 45 +#define SKY1_PCIE0_RESET_N 46 +#define SKY1_PCIE1_RESET_N 47 +#define SKY1_PCIE2_RESET_N 48 +#define SKY1_PCIE3_RESET_N 49 +#define SKY1_PCIE4_RESET_N 50 + +/* reset group1 for usb phys */ +#define SKY1_USB_DP_PHY0_PRST_N 51 +#define SKY1_USB_DP_PHY1_PRST_N 52 +#define SKY1_USB_DP_PHY2_PRST_N 53 +#define SKY1_USB_DP_PHY3_PRST_N 54 +#define SKY1_USB_DP_PHY0_RST_N 55 +#define SKY1_USB_DP_PHY1_RST_N 56 +#define SKY1_USB_DP_PHY2_RST_N 57 +#define SKY1_USB_DP_PHY3_RST_N 58 +#define SKY1_USBPHY_SS_PST_N 59 +#define SKY1_USBPHY_SS_RST_N 60 +#define SKY1_USBPHY_HS0_PRST_N 61 +#define SKY1_USBPHY_HS1_PRST_N 62 +#define SKY1_USBPHY_HS2_PRST_N 63 +#define SKY1_USBPHY_HS3_PRST_N 64 +#define SKY1_USBPHY_HS4_PRST_N 65 +#define SKY1_USBPHY_HS5_PRST_N 66 +#define SKY1_USBPHY_HS6_PRST_N 67 +#define SKY1_USBPHY_HS7_PRST_N 68 +#define SKY1_USBPHY_HS8_PRST_N 69 +#define SKY1_USBPHY_HS9_PRST_N 70 + +/* reset group1 for usb controllers */ +#define SKY1_USBC_SS0_PRST_N 71 +#define SKY1_USBC_SS1_PRST_N 72 +#define SKY1_USBC_SS2_PRST_N 73 +#define SKY1_USBC_SS3_PRST_N 74 +#define SKY1_USBC_SS4_PRST_N 75 +#define SKY1_USBC_SS5_PRST_N 76 +#define SKY1_USBC_SS0_RST_N 77 +#define SKY1_USBC_SS1_RST_N 78 +#define SKY1_USBC_SS2_RST_N 79 +#define SKY1_USBC_SS3_RST_N 80 +#define SKY1_USBC_SS4_RST_N 81 +#define SKY1_USBC_SS5_RST_N 82 +#define SKY1_USBC_HS0_PRST_N 83 +#define SKY1_USBC_HS1_PRST_N 84 +#define SKY1_USBC_HS2_PRST_N 85 +#define SKY1_USBC_HS3_PRST_N 86 +#define SKY1_USBC_HS0_RST_N 87 +#define SKY1_USBC_HS1_RST_N 88 +#define SKY1_USBC_HS2_RST_N 89 +#define SKY1_USBC_HS3_RST_N 90 + +/* reset group0 for rcsu */ +#define SKY1_AUDIO_RCSU_RESET_N 91 +#define SKY1_CI700_RCSU_RESET_N 92 +#define SKY1_CSI_RCSU0_RESET_N 93 +#define SKY1_CSI_RCSU1_RESET_N 94 +#define SKY1_CSU_PM_RCSU_RESET_N 95 +#define SKY1_DDR_BROADCAST_RCSU_RESET_N 96 +#define SKY1_DDR_CTRL_RCSU_0_RESET_N 97 +#define SKY1_DDR_CTRL_RCSU_1_RESET_N 98 +#define SKY1_DDR_CTRL_RCSU_2_RESET_N 99 +#define SKY1_DDR_CTRL_RCSU_3_RESET_N 100 +#define SKY1_DDR_TZC400_RCSU_0_RESET_N 101 +#define SKY1_DDR_TZC400_RCSU_1_RESET_N 102 +#define SKY1_DDR_TZC400_RCSU_2_RESET_N 103 +#define SKY1_DDR_TZC400_RCSU_3_RESET_N 104 +#define SKY1_DP0_RCSU_RESET_N 105 +#define SKY1_DP1_RCSU_RESET_N 106 +#define SKY1_DP2_RCSU_RESET_N 107 +#define SKY1_DP3_RCSU_RESET_N 108 +#define SKY1_DP4_RCSU_RESET_N 109 +#define SKY1_DPU0_RCSU_RESET_N 110 +#define SKY1_DPU1_RCSU_RESET_N 111 +#define SKY1_DPU2_RCSU_RESET_N 112 +#define SKY1_DPU3_RCSU_RESET_N 113 +#define SKY1_DPU4_RCSU_RESET_N 114 +#define SKY1_DSU_RCSU_RESET_N 115 +#define SKY1_FCH_RCSU_RESET_N 116 +#define SKY1_GICD_RCSU_RESET_N 117 +#define SKY1_GMAC_RCSU_RESET_N 118 +#define SKY1_GPU_RCSU_RESET_N 119 +#define SKY1_ISP_RCSU0_RESET_N 120 +#define SKY1_ISP_RCSU1_RESET_N 121 +#define SKY1_NI700_MMHUB_RCSU_RESET_N 122 + +/* reset group1 for rcsu */ +#define SKY1_NPU_RCSU_RESET_N 123 +#define SKY1_NI700_PCIE_RCSU_RESET_N 124 +#define SKY1_PCIE_X421_RCSU_RESET_N 125 +#define SKY1_PCIE_X8_RCSU_RESET_N 126 +#define SKY1_SF_RCSU_RESET_N 127 +#define SKY1_RCSU_SMMU_MMHUB_RESET_N 128 +#define SKY1_RCSU_SMMU_PCIEHUB_RESET_N 129 +#define SKY1_RCSU_SYSHUB_RESET_N 130 +#define SKY1_NI700_SMN_RCSU_RESET_N 131 +#define SKY1_NI700_SYSHUB_RCSU_RESET_N 132 +#define SKY1_RCSU_USB2_HOST0_RESET_N 133 +#define SKY1_RCSU_USB2_HOST1_RESET_N 134 +#define SKY1_RCSU_USB2_HOST2_RESET_N 135 +#define SKY1_RCSU_USB2_HOST3_RESET_N 136 +#define SKY1_RCSU_USB3_TYPEA_DRD_RESET_N 137 +#define SKY1_RCSU_USB3_TYPEC_DRD_RESET_N 138 +#define SKY1_RCSU_USB3_TYPEC_HOST0_RESET_N 139 +#define SKY1_RCSU_USB3_TYPEC_HOST1_RESET_N 140 +#define SKY1_RCSU_USB3_TYPEC_HOST2_RESET_N 141 +#define SKY1_VPU_RCSU_RESET_N 142 + +#endif diff --git a/include/dt-bindings/reset/cix,sky1-system-control.h b/include/dt-bindings/reset/cix,sky1-system-control.h new file mode 100644 index 000000000000..7a16fc4ef3b5 --- /dev/null +++ b/include/dt-bindings/reset/cix,sky1-system-control.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* Author: Jerry Zhu */ +#ifndef DT_BINDING_RESET_CIX_SKY1_SYSTEM_CONTROL_H +#define DT_BINDING_RESET_CIX_SKY1_SYSTEM_CONTROL_H + +/* func reset for sky1 fch */ +#define SW_I3C0_RST_FUNC_G_N 0 +#define SW_I3C0_RST_FUNC_I_N 1 +#define SW_I3C1_RST_FUNC_G_N 2 +#define SW_I3C1_RST_FUNC_I_N 3 +#define SW_UART0_RST_FUNC_N 4 +#define SW_UART1_RST_FUNC_N 5 +#define SW_UART2_RST_FUNC_N 6 +#define SW_UART3_RST_FUNC_N 7 +#define SW_TIMER_RST_FUNC_N 8 + +/* apb reset for sky1 fch */ +#define SW_I3C0_RST_APB_N 9 +#define SW_I3C1_RST_APB_N 10 +#define SW_DMA_RST_AXI_N 11 +#define SW_UART0_RST_APB_N 12 +#define SW_UART1_RST_APB_N 13 +#define SW_UART2_RST_APB_N 14 +#define SW_UART3_RST_APB_N 15 +#define SW_SPI0_RST_APB_N 16 +#define SW_SPI1_RST_APB_N 17 +#define SW_I2C0_RST_APB_N 18 +#define SW_I2C1_RST_APB_N 19 +#define SW_I2C2_RST_APB_N 20 +#define SW_I2C3_RST_APB_N 21 +#define SW_I2C4_RST_APB_N 22 +#define SW_I2C5_RST_APB_N 23 +#define SW_I2C6_RST_APB_N 24 +#define SW_I2C7_RST_APB_N 25 +#define SW_GPIO_RST_APB_N 26 + +/* fch rst for xspi */ +#define SW_XSPI_REG_RST_N 27 +#define SW_XSPI_SYS_RST_N 28 + +#endif -- cgit v1.2.3 From 7cd3ca90d92110cb6a57fcc286c193cf7fd048d2 Mon Sep 17 00:00:00 2001 From: Gary Yang Date: Mon, 2 Mar 2026 14:44:06 +0800 Subject: reset: add Sky1 soc reset support Add support for the resets on Cix's Sky1 SoC. There are two reset controllers on Cix Sky1 Soc. One is located in S0 domain, and the other is located in S5 domain. Signed-off-by: Gary Yang Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/Kconfig | 7 + drivers/reset/Makefile | 1 + drivers/reset/reset-sky1.c | 367 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 375 insertions(+) create mode 100644 drivers/reset/reset-sky1.c diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 7ce151f6a7e4..c73145749455 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -291,6 +291,13 @@ config RESET_SIMPLE - SiFive FU740 SoCs - Sophgo SoCs +config RESET_SKY1 + bool "Cix Sky1 reset controller" + depends on ARCH_CIX || COMPILE_TEST + select REGMAP_MMIO + help + This enables the reset controller for Cix Sky1. + config RESET_SOCFPGA bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA) default ARM && ARCH_INTEL_SOCFPGA diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index fc0cc99f8514..d1b8c66e5086 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o +obj-$(CONFIG_RESET_SKY1) += reset-sky1.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o diff --git a/drivers/reset/reset-sky1.c b/drivers/reset/reset-sky1.c new file mode 100644 index 000000000000..78e80a533c39 --- /dev/null +++ b/drivers/reset/reset-sky1.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * CIX System Reset Controller (SRC) driver + * + * Author: Jerry Zhu + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SKY1_RESET_SLEEP_MIN_US 50 +#define SKY1_RESET_SLEEP_MAX_US 100 + +struct sky1_src_signal { + unsigned int offset; + unsigned int bit; +}; + +struct sky1_src_variant { + const struct sky1_src_signal *signals; + unsigned int signals_num; +}; + +struct sky1_src { + struct reset_controller_dev rcdev; + const struct sky1_src_signal *signals; + struct regmap *regmap; +}; + +enum { + CSU_PM_RESET = 0x304, + SENSORFUSION_RESET = 0x308, + SENSORFUSION_NOC_RESET = 0x30c, + RESET_GROUP0_S0_DOMAIN_0 = 0x400, + RESET_GROUP0_S0_DOMAIN_1 = 0x404, + RESET_GROUP1_USB_PHYS = 0x408, + RESET_GROUP1_USB_CONTROLLERS = 0x40c, + RESET_GROUP0_RCSU = 0x800, + RESET_GROUP1_RCSU = 0x804, +}; + +static const struct sky1_src_signal sky1_src_signals[] = { + /* reset group1 for s0 domain modules */ + [SKY1_CSU_PM_RESET_N] = { CSU_PM_RESET, BIT(0) }, + [SKY1_SENSORFUSION_RESET_N] = { SENSORFUSION_RESET, BIT(0) }, + [SKY1_SENSORFUSION_NOC_RESET_N] = { SENSORFUSION_NOC_RESET, BIT(0) }, + [SKY1_DDRC_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(0) }, + [SKY1_GIC_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(1) }, + [SKY1_CI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(2) }, + [SKY1_SYS_NI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(3) }, + [SKY1_MM_NI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(4) }, + [SKY1_PCIE_NI700_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(5) }, + [SKY1_GPU_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(6) }, + [SKY1_NPUTOP_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(7) }, + [SKY1_NPUCORE0_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(8) }, + [SKY1_NPUCORE1_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(9) }, + [SKY1_NPUCORE2_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(10) }, + [SKY1_VPU_RESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(11) }, + [SKY1_ISP_SRESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(12) }, + [SKY1_ISP_ARESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(13) }, + [SKY1_ISP_HRESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(14) }, + [SKY1_ISP_GDCRESET_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(15) }, + [SKY1_DPU_RESET0_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(16) }, + [SKY1_DPU_RESET1_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(17) }, + [SKY1_DPU_RESET2_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(18) }, + [SKY1_DPU_RESET3_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(19) }, + [SKY1_DPU_RESET4_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(20) }, + [SKY1_DP_RESET0_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(21) }, + [SKY1_DP_RESET1_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(22) }, + [SKY1_DP_RESET2_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(23) }, + [SKY1_DP_RESET3_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(24) }, + [SKY1_DP_RESET4_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(25) }, + [SKY1_DP_PHY_RST_N] = { RESET_GROUP0_S0_DOMAIN_0, BIT(26) }, + + /* reset group1 for s0 domain modules */ + [SKY1_AUDIO_HIFI5_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(0) }, + [SKY1_AUDIO_HIFI5_NOC_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(1) }, + [SKY1_CSIDPHY_PRST0_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(2) }, + [SKY1_CSIDPHY_CMNRST0_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(3) }, + [SKY1_CSI0_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(4) }, + [SKY1_CSIDPHY_PRST1_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(5) }, + [SKY1_CSIDPHY_CMNRST1_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(6) }, + [SKY1_CSI1_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(7) }, + [SKY1_CSI2_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(8) }, + [SKY1_CSI3_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(9) }, + [SKY1_CSIBRDGE0_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(10) }, + [SKY1_CSIBRDGE1_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(11) }, + [SKY1_CSIBRDGE2_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(12) }, + [SKY1_CSIBRDGE3_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(13) }, + [SKY1_GMAC0_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(14) }, + [SKY1_GMAC1_RST_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(15) }, + [SKY1_PCIE0_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(16) }, + [SKY1_PCIE1_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(17) }, + [SKY1_PCIE2_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(18) }, + [SKY1_PCIE3_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(19) }, + [SKY1_PCIE4_RESET_N] = { RESET_GROUP0_S0_DOMAIN_1, BIT(20) }, + + /* reset group1 for usb phys */ + [SKY1_USB_DP_PHY0_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(0) }, + [SKY1_USB_DP_PHY1_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(1) }, + [SKY1_USB_DP_PHY2_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(2) }, + [SKY1_USB_DP_PHY3_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(3) }, + [SKY1_USB_DP_PHY0_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(4) }, + [SKY1_USB_DP_PHY1_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(5) }, + [SKY1_USB_DP_PHY2_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(6) }, + [SKY1_USB_DP_PHY3_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(7) }, + [SKY1_USBPHY_SS_PST_N] = { RESET_GROUP1_USB_PHYS, BIT(8) }, + [SKY1_USBPHY_SS_RST_N] = { RESET_GROUP1_USB_PHYS, BIT(9) }, + [SKY1_USBPHY_HS0_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(10) }, + [SKY1_USBPHY_HS1_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(11) }, + [SKY1_USBPHY_HS2_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(12) }, + [SKY1_USBPHY_HS3_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(13) }, + [SKY1_USBPHY_HS4_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(14) }, + [SKY1_USBPHY_HS5_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(15) }, + [SKY1_USBPHY_HS6_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(16) }, + [SKY1_USBPHY_HS7_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(17) }, + [SKY1_USBPHY_HS8_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(18) }, + [SKY1_USBPHY_HS9_PRST_N] = { RESET_GROUP1_USB_PHYS, BIT(19) }, + + /* reset group1 for usb controllers */ + [SKY1_USBC_SS0_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(0) }, + [SKY1_USBC_SS1_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(1) }, + [SKY1_USBC_SS2_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(2) }, + [SKY1_USBC_SS3_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(3) }, + [SKY1_USBC_SS4_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(4) }, + [SKY1_USBC_SS5_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(5) }, + [SKY1_USBC_SS0_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(6) }, + [SKY1_USBC_SS1_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(7) }, + [SKY1_USBC_SS2_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(8) }, + [SKY1_USBC_SS3_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(9) }, + [SKY1_USBC_SS4_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(10) }, + [SKY1_USBC_SS5_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(11) }, + [SKY1_USBC_HS0_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(12) }, + [SKY1_USBC_HS1_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(13) }, + [SKY1_USBC_HS2_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(14) }, + [SKY1_USBC_HS3_PRST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(15) }, + [SKY1_USBC_HS0_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(16) }, + [SKY1_USBC_HS1_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(17) }, + [SKY1_USBC_HS2_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(18) }, + [SKY1_USBC_HS3_RST_N] = { RESET_GROUP1_USB_CONTROLLERS, BIT(19) }, + + /* reset group0 for rcsu */ + [SKY1_AUDIO_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(0) }, + [SKY1_CI700_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(1) }, + [SKY1_CSI_RCSU0_RESET_N] = { RESET_GROUP0_RCSU, BIT(2) }, + [SKY1_CSI_RCSU1_RESET_N] = { RESET_GROUP0_RCSU, BIT(3) }, + [SKY1_CSU_PM_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(4) }, + [SKY1_DDR_BROADCAST_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(5) }, + [SKY1_DDR_CTRL_RCSU_0_RESET_N] = { RESET_GROUP0_RCSU, BIT(6) }, + [SKY1_DDR_CTRL_RCSU_1_RESET_N] = { RESET_GROUP0_RCSU, BIT(7) }, + [SKY1_DDR_CTRL_RCSU_2_RESET_N] = { RESET_GROUP0_RCSU, BIT(8) }, + [SKY1_DDR_CTRL_RCSU_3_RESET_N] = { RESET_GROUP0_RCSU, BIT(9) }, + [SKY1_DDR_TZC400_RCSU_0_RESET_N] = { RESET_GROUP0_RCSU, BIT(10) }, + [SKY1_DDR_TZC400_RCSU_1_RESET_N] = { RESET_GROUP0_RCSU, BIT(11) }, + [SKY1_DDR_TZC400_RCSU_2_RESET_N] = { RESET_GROUP0_RCSU, BIT(12) }, + [SKY1_DDR_TZC400_RCSU_3_RESET_N] = { RESET_GROUP0_RCSU, BIT(13) }, + [SKY1_DP0_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(14) }, + [SKY1_DP1_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(15) }, + [SKY1_DP2_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(16) }, + [SKY1_DP3_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(17) }, + [SKY1_DP4_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(18) }, + [SKY1_DPU0_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(19) }, + [SKY1_DPU1_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(20) }, + [SKY1_DPU2_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(21) }, + [SKY1_DPU3_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(22) }, + [SKY1_DPU4_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(23) }, + [SKY1_DSU_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(24) }, + [SKY1_FCH_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(25) }, + [SKY1_GICD_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(26) }, + [SKY1_GMAC_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(27) }, + [SKY1_GPU_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(28) }, + [SKY1_ISP_RCSU0_RESET_N] = { RESET_GROUP0_RCSU, BIT(29) }, + [SKY1_ISP_RCSU1_RESET_N] = { RESET_GROUP0_RCSU, BIT(30) }, + [SKY1_NI700_MMHUB_RCSU_RESET_N] = { RESET_GROUP0_RCSU, BIT(31) }, + + /* reset group1 for rcsu */ + [SKY1_NPU_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(0) }, + [SKY1_NI700_PCIE_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(1) }, + [SKY1_PCIE_X421_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(2) }, + [SKY1_PCIE_X8_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(3) }, + [SKY1_SF_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(4) }, + [SKY1_RCSU_SMMU_MMHUB_RESET_N] = { RESET_GROUP1_RCSU, BIT(5) }, + [SKY1_RCSU_SMMU_PCIEHUB_RESET_N] = { RESET_GROUP1_RCSU, BIT(6) }, + [SKY1_RCSU_SYSHUB_RESET_N] = { RESET_GROUP1_RCSU, BIT(7) }, + [SKY1_NI700_SMN_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(8) }, + [SKY1_NI700_SYSHUB_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(9) }, + [SKY1_RCSU_USB2_HOST0_RESET_N] = { RESET_GROUP1_RCSU, BIT(10) }, + [SKY1_RCSU_USB2_HOST1_RESET_N] = { RESET_GROUP1_RCSU, BIT(11) }, + [SKY1_RCSU_USB2_HOST2_RESET_N] = { RESET_GROUP1_RCSU, BIT(12) }, + [SKY1_RCSU_USB2_HOST3_RESET_N] = { RESET_GROUP1_RCSU, BIT(13) }, + [SKY1_RCSU_USB3_TYPEA_DRD_RESET_N] = { RESET_GROUP1_RCSU, BIT(14) }, + [SKY1_RCSU_USB3_TYPEC_DRD_RESET_N] = { RESET_GROUP1_RCSU, BIT(15) }, + [SKY1_RCSU_USB3_TYPEC_HOST0_RESET_N] = { RESET_GROUP1_RCSU, BIT(16) }, + [SKY1_RCSU_USB3_TYPEC_HOST1_RESET_N] = { RESET_GROUP1_RCSU, BIT(17) }, + [SKY1_RCSU_USB3_TYPEC_HOST2_RESET_N] = { RESET_GROUP1_RCSU, BIT(18) }, + [SKY1_VPU_RCSU_RESET_N] = { RESET_GROUP1_RCSU, BIT(19) }, +}; + +static const struct sky1_src_variant variant_sky1 = { + .signals = sky1_src_signals, + .signals_num = ARRAY_SIZE(sky1_src_signals), +}; + +enum { + FCH_SW_RST_FUNC = 0x8, + FCH_SW_RST_BUS = 0xc, + FCH_SW_XSPI = 0x10, +}; + +static const struct sky1_src_signal sky1_src_fch_signals[] = { + /* resets for fch_sw_rst_func */ + [SW_I3C0_RST_FUNC_G_N] = { FCH_SW_RST_FUNC, BIT(0) }, + [SW_I3C0_RST_FUNC_I_N] = { FCH_SW_RST_FUNC, BIT(1) }, + [SW_I3C1_RST_FUNC_G_N] = { FCH_SW_RST_FUNC, BIT(2) }, + [SW_I3C1_RST_FUNC_I_N] = { FCH_SW_RST_FUNC, BIT(3) }, + [SW_UART0_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(4) }, + [SW_UART1_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(5) }, + [SW_UART2_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(6) }, + [SW_UART3_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(7) }, + [SW_TIMER_RST_FUNC_N] = { FCH_SW_RST_FUNC, BIT(20) }, + + /* resets for fch_sw_rst_bus */ + [SW_I3C0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(0) }, + [SW_I3C1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(1) }, + [SW_DMA_RST_AXI_N] = { FCH_SW_RST_BUS, BIT(2) }, + [SW_UART0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(4) }, + [SW_UART1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(5) }, + [SW_UART2_RST_APB_N] = { FCH_SW_RST_BUS, BIT(6) }, + [SW_UART3_RST_APB_N] = { FCH_SW_RST_BUS, BIT(7) }, + [SW_SPI0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(8) }, + [SW_SPI1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(9) }, + [SW_I2C0_RST_APB_N] = { FCH_SW_RST_BUS, BIT(12) }, + [SW_I2C1_RST_APB_N] = { FCH_SW_RST_BUS, BIT(13) }, + [SW_I2C2_RST_APB_N] = { FCH_SW_RST_BUS, BIT(14) }, + [SW_I2C3_RST_APB_N] = { FCH_SW_RST_BUS, BIT(15) }, + [SW_I2C4_RST_APB_N] = { FCH_SW_RST_BUS, BIT(16) }, + [SW_I2C5_RST_APB_N] = { FCH_SW_RST_BUS, BIT(17) }, + [SW_I2C6_RST_APB_N] = { FCH_SW_RST_BUS, BIT(18) }, + [SW_I2C7_RST_APB_N] = { FCH_SW_RST_BUS, BIT(19) }, + [SW_GPIO_RST_APB_N] = { FCH_SW_RST_BUS, BIT(21) }, + + /* resets for fch_sw_xspi */ + [SW_XSPI_REG_RST_N] = { FCH_SW_XSPI, BIT(0) }, + [SW_XSPI_SYS_RST_N] = { FCH_SW_XSPI, BIT(1) }, +}; + +static const struct sky1_src_variant variant_sky1_fch = { + .signals = sky1_src_fch_signals, + .signals_num = ARRAY_SIZE(sky1_src_fch_signals), +}; + +static struct sky1_src *to_sky1_src(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct sky1_src, rcdev); +} + +static int sky1_reset_set(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct sky1_src *sky1src = to_sky1_src(rcdev); + const struct sky1_src_signal *signal = &sky1src->signals[id]; + unsigned int value = assert ? 0 : signal->bit; + + return regmap_update_bits(sky1src->regmap, + signal->offset, signal->bit, value); +} + +static int sky1_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + sky1_reset_set(rcdev, id, true); + usleep_range(SKY1_RESET_SLEEP_MIN_US, + SKY1_RESET_SLEEP_MAX_US); + return 0; +} + +static int sky1_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + sky1_reset_set(rcdev, id, false); + usleep_range(SKY1_RESET_SLEEP_MIN_US, + SKY1_RESET_SLEEP_MAX_US); + return 0; +} + +static int sky1_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + sky1_reset_assert(rcdev, id); + sky1_reset_deassert(rcdev, id); + return 0; +} + +static int sky1_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + unsigned int value = 0; + struct sky1_src *sky1src = to_sky1_src(rcdev); + const struct sky1_src_signal *signal = &sky1src->signals[id]; + + regmap_read(sky1src->regmap, signal->offset, &value); + return !(value & signal->bit); +} + +static const struct reset_control_ops sky1_src_ops = { + .reset = sky1_reset, + .assert = sky1_reset_assert, + .deassert = sky1_reset_deassert, + .status = sky1_reset_status +}; + +static int sky1_reset_probe(struct platform_device *pdev) +{ + struct sky1_src *sky1src; + struct device *dev = &pdev->dev; + const struct sky1_src_variant *variant; + + sky1src = devm_kzalloc(dev, sizeof(*sky1src), GFP_KERNEL); + if (!sky1src) + return -ENOMEM; + + variant = of_device_get_match_data(dev); + + sky1src->regmap = device_node_to_regmap(dev->of_node); + if (IS_ERR(sky1src->regmap)) { + return dev_err_probe(dev, PTR_ERR(sky1src->regmap), + "Unable to get sky1-src regmap"); + } + + sky1src->signals = variant->signals; + sky1src->rcdev.owner = THIS_MODULE; + sky1src->rcdev.nr_resets = variant->signals_num; + sky1src->rcdev.ops = &sky1_src_ops; + sky1src->rcdev.of_node = dev->of_node; + sky1src->rcdev.dev = dev; + + return devm_reset_controller_register(dev, &sky1src->rcdev); +} + +static const struct of_device_id sky1_sysreg_of_match[] = { + { .compatible = "cix,sky1-system-control", .data = &variant_sky1_fch}, + { .compatible = "cix,sky1-s5-system-control", .data = &variant_sky1}, + {}, +}; +MODULE_DEVICE_TABLE(of, sky1_sysreg_of_match); + +static struct platform_driver sky1_reset_driver = { + .probe = sky1_reset_probe, + .driver = { + .name = "cix,sky1-rst", + .of_match_table = sky1_sysreg_of_match, + }, +}; +module_platform_driver(sky1_reset_driver) + +MODULE_AUTHOR("Jerry Zhu "); +MODULE_DESCRIPTION("Cix Sky1 reset driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2737dcb3c467824cd55921fba3fbd1375fc01695 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 10 Mar 2026 10:00:05 +0100 Subject: reset: core: Fix indentation Correct an accidental whitespace change. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603100730.J3pi4xqi-lkp@intel.com/ Fixes: 9035073d0ef1 ("reset: convert reset core to using firmware nodes") Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index e625cf59cfb0..726b7346b148 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -1215,7 +1215,7 @@ __fwnode_reset_control_get(struct fwnode_handle *fwnode, const char *id, int ind } if (rstc_id < 0) { rstc = ERR_PTR(rstc_id); - goto out_put; + goto out_put; } flags &= ~RESET_CONTROL_FLAGS_BIT_OPTIONAL; -- cgit v1.2.3 From 62d11b80ea5d118ba4a1b24e55ca13d77a4cf161 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Mar 2026 16:15:15 +0100 Subject: reset: don't overwrite fwnode_reset_n_cells Fix a logic bug in reset_controller_register() where we set fwnode_reset_n_cells to 1 if fwnode is set and fwnode_xlate is not but we do it after assigning of_fwnode_handle(of_node) to fwnode. Modify the logic to: assign fwnode from of_node if applicable, if fwnode is still not set, try to get it from the device and only then check of_xlate and fwnode_xlate and either assign fwnode_reset_n_cells from OF or default to fwnode_reset_simple_xlate and fwnode_reset_n_cells = 1. Fixes: ba8dbbb14b7e ("reset: convert the core API to using firmware nodes") Reported-by: Mark Brown Closes: https://lore.kernel.org/all/0b72286b-33dd-4bc9-8c0e-161c2f4baed8@sirena.org.uk/ Signed-off-by: Bartosz Golaszewski Tested-by: Mark Brown Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 726b7346b148..a0d8c68f2afc 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -132,20 +132,21 @@ int reset_controller_register(struct reset_controller_dev *rcdev) if ((rcdev->of_node && rcdev->fwnode) || (rcdev->of_xlate && rcdev->fwnode_xlate)) return -EINVAL; - if (!rcdev->of_node && !rcdev->fwnode) { + if (rcdev->of_node && !rcdev->fwnode) + rcdev->fwnode = of_fwnode_handle(rcdev->of_node); + + if (!rcdev->fwnode) { rcdev->fwnode = dev_fwnode(rcdev->dev); if (!rcdev->fwnode) return -EINVAL; } - if (rcdev->of_node) { - rcdev->fwnode = of_fwnode_handle(rcdev->of_node); + if (rcdev->of_xlate) rcdev->fwnode_reset_n_cells = rcdev->of_reset_n_cells; - } - if (rcdev->fwnode && !rcdev->fwnode_xlate) { - rcdev->fwnode_reset_n_cells = 1; + if (!rcdev->fwnode_xlate && !rcdev->of_xlate) { rcdev->fwnode_xlate = fwnode_reset_simple_xlate; + rcdev->fwnode_reset_n_cells = 1; } INIT_LIST_HEAD(&rcdev->reset_control_head); -- cgit v1.2.3 From 223af4a569d1f491e55d4de8ed40333c938622d0 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 19 Mar 2026 18:05:17 -0700 Subject: reset: sti: kzalloc + kcalloc to kzalloc Simplify allocation. Signed-off-by: Rosen Penev Reviewed-by: Patrice Chotard Signed-off-by: Philipp Zabel --- drivers/reset/sti/reset-syscfg.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/reset/sti/reset-syscfg.c b/drivers/reset/sti/reset-syscfg.c index 2324060b747c..38f78d78fa4f 100644 --- a/drivers/reset/sti/reset-syscfg.c +++ b/drivers/reset/sti/reset-syscfg.c @@ -41,7 +41,7 @@ struct syscfg_reset_channel { struct syscfg_reset_controller { struct reset_controller_dev rst; bool active_low; - struct syscfg_reset_channel *channels; + struct syscfg_reset_channel channels[]; }; #define to_syscfg_reset_controller(_rst) \ @@ -135,15 +135,10 @@ static int syscfg_reset_controller_register(struct device *dev, struct syscfg_reset_controller *rc; int i, err; - rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); + rc = devm_kzalloc(dev, struct_size(rc, channels, data->nr_channels), GFP_KERNEL); if (!rc) return -ENOMEM; - rc->channels = devm_kcalloc(dev, data->nr_channels, - sizeof(*rc->channels), GFP_KERNEL); - if (!rc->channels) - return -ENOMEM; - rc->rst.ops = &syscfg_reset_ops; rc->rst.of_node = dev->of_node; rc->rst.nr_resets = data->nr_channels; -- cgit v1.2.3 From c961cc86af01246a7ce706bbc29072d314e00880 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 18 Mar 2026 12:08:52 +0000 Subject: reset: rzg2l-usbphy-ctrl: Fix malformed MODULE_AUTHOR string Fix a malformed MODULE_AUTHOR macro in the RZ/G2L USBPHY control driver where the author's name and opening angle bracket were missing, leaving only the email address with a stray closing >. Correct it to the standard Name format. Signed-off-by: Biju Das Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-rzg2l-usbphy-ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index 05dd9b4a02df..fd75d9601a3b 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -350,4 +350,4 @@ module_platform_driver(rzg2l_usbphy_ctrl_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Renesas RZ/G2L USBPHY Control"); -MODULE_AUTHOR("biju.das.jz@bp.renesas.com>"); +MODULE_AUTHOR("Biju Das "); -- cgit v1.2.3 From fbffb8c7c7bb4d38e9f65e0bee446685011de5d8 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 21 Mar 2026 15:42:40 +0800 Subject: reset: gpio: fix double free in reset_add_gpio_aux_device() error path When __auxiliary_device_add() fails, reset_add_gpio_aux_device() calls auxiliary_device_uninit(adev). The device release callback reset_gpio_aux_device_release() frees adev, but the current error path then calls kfree(adev) again, causing a double free. Keep kfree(adev) for the auxiliary_device_init() failure path, but avoid freeing adev after auxiliary_device_uninit(). Fixes: 5fc4e4cf7a22 ("reset: gpio: use software nodes to setup the GPIO lookup") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Bartosz Golaszewski Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index fceec45c8afc..352c2360603b 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -856,7 +856,6 @@ static int reset_add_gpio_aux_device(struct device *parent, ret = __auxiliary_device_add(adev, "reset"); if (ret) { auxiliary_device_uninit(adev); - kfree(adev); return ret; } -- cgit v1.2.3 From a0e0c2f8c5f32b675f58e25a9338283cedb5ad2b Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Fri, 20 Mar 2026 11:06:17 +0000 Subject: reset: spacemit: k3: Decouple composite reset lines Instead of grouping several different reset lines into one composite reset, decouple them to individual ones which make it more aligned with underlying hardware. And for DWC USB driver, it will match well with the number of the reset property in the DT bindings. The DWC3 USB host controller in K3 SoC has three reset lines - AHB, VCC, PHY. The PCIe controller also has three reset lines - DBI, Slave, Master. Also three reset lines each for UCIE and RCPU block. As an agreement with maintainer, the reset IDs has been rearranged as contiguous number but keep most part unchanged to avoid break patches which already sent to mailing list. The changes of DT binding header file and reset driver are merged together as one single commit to avoid git-bisect breakage. Fixes: 938ce3b16582 ("reset: spacemit: Add SpacemiT K3 reset driver") Fixes: 216e0a5e98e5 ("dt-bindings: soc: spacemit: Add K3 reset support and IDs") Signed-off-by: Yixun Lan Reviewed-by: Philipp Zabel Acked-by: Conor Dooley Signed-off-by: Philipp Zabel --- drivers/reset/spacemit/reset-spacemit-k3.c | 60 +++++++++++++++----------- include/dt-bindings/reset/spacemit,k3-resets.h | 48 +++++++++++++++------ 2 files changed, 72 insertions(+), 36 deletions(-) diff --git a/drivers/reset/spacemit/reset-spacemit-k3.c b/drivers/reset/spacemit/reset-spacemit-k3.c index e9e32e4c1ba5..9841f5e057b2 100644 --- a/drivers/reset/spacemit/reset-spacemit-k3.c +++ b/drivers/reset/spacemit/reset-spacemit-k3.c @@ -112,16 +112,21 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_SDH0] = RESET_DATA(APMU_SDH0_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH1] = RESET_DATA(APMU_SDH1_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_SDH2] = RESET_DATA(APMU_SDH2_CLK_RES_CTRL, 0, BIT(1)), - [RESET_APMU_USB2] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(1)|BIT(2)|BIT(3)), - [RESET_APMU_USB3_PORTA] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(5)|BIT(6)|BIT(7)), - [RESET_APMU_USB3_PORTB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(9)|BIT(10)|BIT(11)), - [RESET_APMU_USB3_PORTC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(13)|BIT(14)|BIT(15)), - [RESET_APMU_USB3_PORTD] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, - BIT(17)|BIT(18)|BIT(19)), + [RESET_APMU_USB2_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(1)), + [RESET_APMU_USB2_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(2)), + [RESET_APMU_USB2_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(3)), + [RESET_APMU_USB3_A_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(5)), + [RESET_APMU_USB3_A_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(6)), + [RESET_APMU_USB3_A_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(7)), + [RESET_APMU_USB3_B_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(9)), + [RESET_APMU_USB3_B_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(10)), + [RESET_APMU_USB3_B_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(11)), + [RESET_APMU_USB3_C_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(13)), + [RESET_APMU_USB3_C_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(14)), + [RESET_APMU_USB3_C_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(15)), + [RESET_APMU_USB3_D_AHB] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(17)), + [RESET_APMU_USB3_D_VCC] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(18)), + [RESET_APMU_USB3_D_PHY] = RESET_DATA(APMU_USB_CLK_RES_CTRL, 0, BIT(19)), [RESET_APMU_QSPI] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_QSPI_BUS] = RESET_DATA(APMU_QSPI_CLK_RES_CTRL, 0, BIT(0)), [RESET_APMU_DMA] = RESET_DATA(APMU_DMA_CLK_RES_CTRL, 0, BIT(0)), @@ -151,10 +156,12 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_CPU7_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(26), 0), [RESET_APMU_C1_MPSUB_SW] = RESET_DATA(APMU_PMU_CC2_AP, BIT(28), 0), [RESET_APMU_MPSUB_DBG] = RESET_DATA(APMU_PMU_CC2_AP, BIT(29), 0), - [RESET_APMU_UCIE] = RESET_DATA(APMU_UCIE_CTRL, - BIT(1) | BIT(2) | BIT(3), 0), - [RESET_APMU_RCPU] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, - BIT(3) | BIT(2) | BIT(0)), + [RESET_APMU_UCIE_IP] = RESET_DATA(APMU_UCIE_CTRL, BIT(1), 0), + [RESET_APMU_UCIE_HOT] = RESET_DATA(APMU_UCIE_CTRL, BIT(2), 0), + [RESET_APMU_UCIE_MON] = RESET_DATA(APMU_UCIE_CTRL, BIT(3), 0), + [RESET_APMU_RCPU_AUDIO_SYS] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, BIT(0)), + [RESET_APMU_RCPU_MCU_CORE] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, BIT(2)), + [RESET_APMU_RCPU_AUDIO_APMU] = RESET_DATA(APMU_RCPU_CLK_RES_CTRL, 0, BIT(3)), [RESET_APMU_DSI4LN2_ESCCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(3)), [RESET_APMU_DSI4LN2_LCD_SW] = RESET_DATA(APMU_LCD_CLK_RES_CTRL3, 0, BIT(4)), [RESET_APMU_DSI4LN2_LCD_MCLK] = RESET_DATA(APMU_LCD_CLK_RES_CTRL4, 0, BIT(9)), @@ -164,16 +171,21 @@ static const struct ccu_reset_data k3_apmu_resets[] = { [RESET_APMU_UFS_ACLK] = RESET_DATA(APMU_UFS_CLK_RES_CTRL, 0, BIT(0)), [RESET_APMU_EDP0] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(0)), [RESET_APMU_EDP1] = RESET_DATA(APMU_LCD_EDP_CTRL, 0, BIT(16)), - [RESET_APMU_PCIE_PORTA] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTB] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTC] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTD] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, - BIT(5) | BIT(4) | BIT(3)), - [RESET_APMU_PCIE_PORTE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, - BIT(5) | BIT(4) | BIT(3)), + [RESET_APMU_PCIE_A_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, BIT(3)), + [RESET_APMU_PCIE_A_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, BIT(4)), + [RESET_APMU_PCIE_A_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_A, 0, BIT(5)), + [RESET_APMU_PCIE_B_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, BIT(3)), + [RESET_APMU_PCIE_B_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, BIT(4)), + [RESET_APMU_PCIE_B_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_B, 0, BIT(5)), + [RESET_APMU_PCIE_C_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, BIT(3)), + [RESET_APMU_PCIE_C_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, BIT(4)), + [RESET_APMU_PCIE_C_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_C, 0, BIT(5)), + [RESET_APMU_PCIE_D_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, BIT(3)), + [RESET_APMU_PCIE_D_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, BIT(4)), + [RESET_APMU_PCIE_D_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_D, 0, BIT(5)), + [RESET_APMU_PCIE_E_DBI] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, BIT(3)), + [RESET_APMU_PCIE_E_SLAVE] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, BIT(4)), + [RESET_APMU_PCIE_E_MASTER] = RESET_DATA(APMU_PCIE_CLK_RES_CTRL_E, 0, BIT(5)), [RESET_APMU_EMAC0] = RESET_DATA(APMU_EMAC0_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_EMAC1] = RESET_DATA(APMU_EMAC1_CLK_RES_CTRL, 0, BIT(1)), [RESET_APMU_EMAC2] = RESET_DATA(APMU_EMAC2_CLK_RES_CTRL, 0, BIT(1)), diff --git a/include/dt-bindings/reset/spacemit,k3-resets.h b/include/dt-bindings/reset/spacemit,k3-resets.h index 79ac1c22b7b5..dc1ef009ba79 100644 --- a/include/dt-bindings/reset/spacemit,k3-resets.h +++ b/include/dt-bindings/reset/spacemit,k3-resets.h @@ -97,11 +97,11 @@ #define RESET_APMU_SDH0 13 #define RESET_APMU_SDH1 14 #define RESET_APMU_SDH2 15 -#define RESET_APMU_USB2 16 -#define RESET_APMU_USB3_PORTA 17 -#define RESET_APMU_USB3_PORTB 18 -#define RESET_APMU_USB3_PORTC 19 -#define RESET_APMU_USB3_PORTD 20 +#define RESET_APMU_USB2_AHB 16 +#define RESET_APMU_USB2_VCC 17 +#define RESET_APMU_USB2_PHY 18 +#define RESET_APMU_USB3_A_AHB 19 +#define RESET_APMU_USB3_A_VCC 20 #define RESET_APMU_QSPI 21 #define RESET_APMU_QSPI_BUS 22 #define RESET_APMU_DMA 23 @@ -132,8 +132,8 @@ #define RESET_APMU_CPU7_SW 48 #define RESET_APMU_C1_MPSUB_SW 49 #define RESET_APMU_MPSUB_DBG 50 -#define RESET_APMU_UCIE 51 -#define RESET_APMU_RCPU 52 +#define RESET_APMU_USB3_A_PHY 51 /* USB3 A */ +#define RESET_APMU_USB3_B_AHB 52 #define RESET_APMU_DSI4LN2_ESCCLK 53 #define RESET_APMU_DSI4LN2_LCD_SW 54 #define RESET_APMU_DSI4LN2_LCD_MCLK 55 @@ -143,16 +143,40 @@ #define RESET_APMU_UFS_ACLK 59 #define RESET_APMU_EDP0 60 #define RESET_APMU_EDP1 61 -#define RESET_APMU_PCIE_PORTA 62 -#define RESET_APMU_PCIE_PORTB 63 -#define RESET_APMU_PCIE_PORTC 64 -#define RESET_APMU_PCIE_PORTD 65 -#define RESET_APMU_PCIE_PORTE 66 +#define RESET_APMU_USB3_B_VCC 62 /* USB3 B */ +#define RESET_APMU_USB3_B_PHY 63 +#define RESET_APMU_USB3_C_AHB 64 +#define RESET_APMU_USB3_C_VCC 65 +#define RESET_APMU_USB3_C_PHY 66 #define RESET_APMU_EMAC0 67 #define RESET_APMU_EMAC1 68 #define RESET_APMU_EMAC2 69 #define RESET_APMU_ESPI_MCLK 70 #define RESET_APMU_ESPI_SCLK 71 +#define RESET_APMU_USB3_D_AHB 72 /* USB3 D */ +#define RESET_APMU_USB3_D_VCC 73 +#define RESET_APMU_USB3_D_PHY 74 +#define RESET_APMU_UCIE_IP 75 +#define RESET_APMU_UCIE_HOT 76 +#define RESET_APMU_UCIE_MON 77 +#define RESET_APMU_RCPU_AUDIO_SYS 78 +#define RESET_APMU_RCPU_MCU_CORE 79 +#define RESET_APMU_RCPU_AUDIO_APMU 80 +#define RESET_APMU_PCIE_A_DBI 81 +#define RESET_APMU_PCIE_A_SLAVE 82 +#define RESET_APMU_PCIE_A_MASTER 83 +#define RESET_APMU_PCIE_B_DBI 84 +#define RESET_APMU_PCIE_B_SLAVE 85 +#define RESET_APMU_PCIE_B_MASTER 86 +#define RESET_APMU_PCIE_C_DBI 87 +#define RESET_APMU_PCIE_C_SLAVE 88 +#define RESET_APMU_PCIE_C_MASTER 89 +#define RESET_APMU_PCIE_D_DBI 90 +#define RESET_APMU_PCIE_D_SLAVE 91 +#define RESET_APMU_PCIE_D_MASTER 92 +#define RESET_APMU_PCIE_E_DBI 93 +#define RESET_APMU_PCIE_E_SLAVE 94 +#define RESET_APMU_PCIE_E_MASTER 95 /* DCIU resets*/ #define RESET_DCIU_HDMA 0 -- cgit v1.2.3 From 8889b289ce1bd11a5102b9617742a1b93bb4843e Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Thu, 12 Mar 2026 15:50:38 +0100 Subject: reset: rzv2h-usb2phy: Keep PHY clock enabled for entire device lifetime The driver was disabling the USB2 PHY clock immediately after register initialization in probe() and after each reset operation. This left the PHY unclocked even though it must remain active for USB functionality. The behavior appeared to work only when another driver (e.g., USB controller) had already enabled the clock, making operation unreliable and hardware-dependent. In configurations where this driver is the sole clock user, USB functionality would fail. Fix this by: - Enabling the clock once in probe() via pm_runtime_resume_and_get() - Removing all pm_runtime_put() calls from assert/deassert/status - Registering a devm cleanup action to release the clock at removal - Removed rzv2h_usbphy_assert_helper() and its call in rzv2h_usb2phy_reset_probe() This ensures the PHY clock remains enabled for the entire device lifetime, preventing instability and aligning with hardware requirements. Cc: stable@vger.kernel.org Fixes: e3911d7f865b ("reset: Add USB2PHY port reset driver for Renesas RZ/V2H(P)") Signed-off-by: Tommaso Merciai Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/reset-rzv2h-usb2phy.c | 64 +++++++++++-------------------------- 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c index ae643575b067..5bdd39274612 100644 --- a/drivers/reset/reset-rzv2h-usb2phy.c +++ b/drivers/reset/reset-rzv2h-usb2phy.c @@ -49,9 +49,10 @@ static inline struct rzv2h_usb2phy_reset_priv return container_of(rcdev, struct rzv2h_usb2phy_reset_priv, rcdev); } -/* This function must be called only after pm_runtime_resume_and_get() has been called */ -static void rzv2h_usbphy_assert_helper(struct rzv2h_usb2phy_reset_priv *priv) +static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) { + struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); const struct rzv2h_usb2phy_reset_of_data *data = priv->data; scoped_guard(spinlock, &priv->lock) { @@ -60,24 +61,6 @@ static void rzv2h_usbphy_assert_helper(struct rzv2h_usb2phy_reset_priv *priv) } usleep_range(11, 20); -} - -static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); - struct device *dev = priv->dev; - int ret; - - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(dev, "pm_runtime_resume_and_get failed\n"); - return ret; - } - - rzv2h_usbphy_assert_helper(priv); - - pm_runtime_put(dev); return 0; } @@ -87,14 +70,6 @@ static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev, { struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); const struct rzv2h_usb2phy_reset_of_data *data = priv->data; - struct device *dev = priv->dev; - int ret; - - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(dev, "pm_runtime_resume_and_get failed\n"); - return ret; - } scoped_guard(spinlock, &priv->lock) { writel(data->reset_deassert_val, priv->base + data->reset_reg); @@ -102,8 +77,6 @@ static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev, writel(data->reset_release_val, priv->base + data->reset_reg); } - pm_runtime_put(dev); - return 0; } @@ -111,20 +84,10 @@ static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); - struct device *dev = priv->dev; - int ret; u32 reg; - ret = pm_runtime_resume_and_get(dev); - if (ret) { - dev_err(dev, "pm_runtime_resume_and_get failed\n"); - return ret; - } - reg = readl(priv->base + priv->data->reset_reg); - pm_runtime_put(dev); - return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits; } @@ -141,6 +104,11 @@ static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev, return 0; } +static void rzv2h_usb2phy_reset_pm_runtime_put(void *data) +{ + pm_runtime_put(data); +} + static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) { const struct rzv2h_usb2phy_reset_of_data *data; @@ -175,14 +143,14 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) if (error) return dev_err_probe(dev, error, "pm_runtime_resume_and_get failed\n"); + error = devm_add_action_or_reset(dev, rzv2h_usb2phy_reset_pm_runtime_put, + dev); + if (error) + return dev_err_probe(dev, error, "unable to register cleanup action\n"); + for (unsigned int i = 0; i < data->init_val_count; i++) writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg); - /* keep usb2phy in asserted state */ - rzv2h_usbphy_assert_helper(priv); - - pm_runtime_put(dev); - priv->rcdev.ops = &rzv2h_usbphy_reset_ops; priv->rcdev.of_reset_n_cells = 0; priv->rcdev.nr_resets = 1; @@ -190,7 +158,11 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) priv->rcdev.of_node = dev->of_node; priv->rcdev.dev = dev; - return devm_reset_controller_register(dev, &priv->rcdev); + error = devm_reset_controller_register(dev, &priv->rcdev); + if (error) + return dev_err_probe(dev, error, "could not register reset controller\n"); + + return 0; } /* -- cgit v1.2.3 From ccef9ef0aa2b9a7814e4def6f18b25211b8ae530 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Thu, 26 Mar 2026 21:29:24 +0200 Subject: reset: core: Drop unnecessary double quote Drop unnecessary double quote. Reported-by: Pavel Machek Closes: https://lore.kernel.org/all/acJbYxKGAB4lxGQr@duo.ucw.cz Signed-off-by: Claudiu Beznea Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/reset/core.c b/drivers/reset/core.c index a0d8c68f2afc..38e189d04d09 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -435,7 +435,7 @@ int reset_control_bulk_reset(int num_rstcs, EXPORT_SYMBOL_GPL(reset_control_bulk_reset); /** - * reset_control_rearm - allow shared reset line to be re-triggered" + * reset_control_rearm - allow shared reset line to be re-triggered * @rstc: reset controller * * On a shared reset line the actual reset pulse is only triggered once for the -- cgit v1.2.3 From 6a1b6f7e56dc72ff8b4a83c728a2bed6d834e4cb Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 1 Apr 2026 17:16:08 +0200 Subject: dt-bindings: reset: renesas,rzv2h-usb2phy: Add '#mux-state-cells' property Add the '#mux-state-cells' property to support describing the USB VBUS_SEL multiplexer as a mux-controller in the Renesas RZ/V2H(P) USB2PHY binding. The mux-controller cannot be integrated into the parent USB2PHY node because the VBUS source selector is part of a separate hardware block, not the USB2PHY block itself. This is required to properly configure USB PHY power selection on RZ/V2H(P) and RZ/G3E SoCs. Acked-by: Krzysztof Kozlowski Signed-off-by: Tommaso Merciai Signed-off-by: Philipp Zabel --- .../devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml index c1b800a10b53..7ed0980b9ee1 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml +++ b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml @@ -37,6 +37,9 @@ properties: '#reset-cells': const: 0 + '#mux-state-cells': + const: 1 + required: - compatible - reg @@ -44,6 +47,7 @@ required: - resets - power-domains - '#reset-cells' + - '#mux-state-cells' additionalProperties: false @@ -58,4 +62,5 @@ examples: resets = <&cpg 0xaf>; power-domains = <&cpg>; #reset-cells = <0>; + #mux-state-cells = <1>; }; -- cgit v1.2.3 From 63be00249dd9f42a51fa164fa4eab920cb44f122 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 1 Apr 2026 17:16:09 +0200 Subject: dt-bindings: reset: renesas,rzv2h-usb2phy: Document RZ/G3E USB2PHY reset Document USB2PHY reset controller bindings for RZ/G3E ("R9A09G047") SoC. The RZ/G3E USB2PHY reset controller is functionally identical to the one found on the RZ/V2H(P), so no driver changes are needed. The existing "renesas,r9a09g057-usb2phy-reset" will be used as a fallback compatible for this IP. Acked-by: Conor Dooley Signed-off-by: Tommaso Merciai Signed-off-by: Philipp Zabel --- .../devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml index 7ed0980b9ee1..66650ef8f772 100644 --- a/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml +++ b/Documentation/devicetree/bindings/reset/renesas,rzv2h-usb2phy-reset.yaml @@ -17,7 +17,9 @@ properties: compatible: oneOf: - items: - - const: renesas,r9a09g056-usb2phy-reset # RZ/V2N + - enum: + - renesas,r9a09g047-usb2phy-reset # RZ/G3E + - renesas,r9a09g056-usb2phy-reset # RZ/V2N - const: renesas,r9a09g057-usb2phy-reset - const: renesas,r9a09g057-usb2phy-reset # RZ/V2H(P) -- cgit v1.2.3 From 890628c8d0f156a80a0aea02803d31fd8dd83fc0 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 1 Apr 2026 17:16:10 +0200 Subject: reset: rzv2h-usb2phy: Convert to regmap API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace raw MMIO accesses (readl/writel) with regmap_read() and regmap_multi_reg_write() via devm_regmap_init_mmio(). Drop the manual spinlock as regmap provides internal locking. Replace the custom rzv2h_usb2phy_regval struct with the standard reg_sequence, and encode assert/deassert sequences as reg_sequence arrays rather than individual scalar fields in the of_data descriptor. Use the reg_sequence .delay_us field to encode the 11 µs post-assert delay, replacing the explicit usleep_range(11, 20) call in rzv2h_usbphy_reset_assert(). Select REGMAP_MMIO in Kconfig. Signed-off-by: Tommaso Merciai Reviewed-by: Philipp Zabel Signed-off-by: Philipp Zabel --- drivers/reset/Kconfig | 1 + drivers/reset/reset-rzv2h-usb2phy.c | 108 ++++++++++++++++++------------------ 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index c73145749455..a2f56ea470c5 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -257,6 +257,7 @@ config RESET_RZG2L_USBPHY_CTRL config RESET_RZV2H_USB2PHY tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver" depends on ARCH_RENESAS || COMPILE_TEST + select REGMAP_MMIO help Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC (and similar SoCs). diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c index 5bdd39274612..c79bf72602e8 100644 --- a/drivers/reset/reset-rzv2h-usb2phy.c +++ b/drivers/reset/reset-rzv2h-usb2phy.c @@ -5,42 +5,35 @@ * Copyright (C) 2025 Renesas Electronics Corporation */ -#include #include #include #include #include #include #include +#include #include #include -struct rzv2h_usb2phy_regval { - u16 reg; - u16 val; -}; - struct rzv2h_usb2phy_reset_of_data { - const struct rzv2h_usb2phy_regval *init_vals; - unsigned int init_val_count; + const struct reg_sequence *init_seq; + unsigned int init_nseq; + + const struct reg_sequence *assert_seq; + unsigned int assert_nseq; + + const struct reg_sequence *deassert_seq; + unsigned int deassert_nseq; u16 reset_reg; - u16 reset_assert_val; - u16 reset_deassert_val; u16 reset_status_bits; - u16 reset_release_val; - - u16 reset2_reg; - u16 reset2_acquire_val; - u16 reset2_release_val; }; struct rzv2h_usb2phy_reset_priv { const struct rzv2h_usb2phy_reset_of_data *data; - void __iomem *base; + struct regmap *regmap; struct device *dev; struct reset_controller_dev rcdev; - spinlock_t lock; /* protects register accesses */ }; static inline struct rzv2h_usb2phy_reset_priv @@ -53,31 +46,18 @@ static int rzv2h_usbphy_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) { struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); - const struct rzv2h_usb2phy_reset_of_data *data = priv->data; - scoped_guard(spinlock, &priv->lock) { - writel(data->reset2_acquire_val, priv->base + data->reset2_reg); - writel(data->reset_assert_val, priv->base + data->reset_reg); - } - - usleep_range(11, 20); - - return 0; + return regmap_multi_reg_write(priv->regmap, priv->data->assert_seq, + priv->data->assert_nseq); } static int rzv2h_usbphy_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) { struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); - const struct rzv2h_usb2phy_reset_of_data *data = priv->data; - scoped_guard(spinlock, &priv->lock) { - writel(data->reset_deassert_val, priv->base + data->reset_reg); - writel(data->reset2_release_val, priv->base + data->reset2_reg); - writel(data->reset_release_val, priv->base + data->reset_reg); - } - - return 0; + return regmap_multi_reg_write(priv->regmap, priv->data->deassert_seq, + priv->data->deassert_nseq); } static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev, @@ -86,7 +66,7 @@ static int rzv2h_usbphy_reset_status(struct reset_controller_dev *rcdev, struct rzv2h_usb2phy_reset_priv *priv = rzv2h_usbphy_rcdev_to_priv(rcdev); u32 reg; - reg = readl(priv->base + priv->data->reset_reg); + regmap_read(priv->regmap, priv->data->reset_reg, ®); return (reg & priv->data->reset_status_bits) == priv->data->reset_status_bits; } @@ -104,6 +84,13 @@ static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev, return 0; } +static const struct regmap_config rzv2h_usb2phy_reset_regconf = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .can_sleep = true, +}; + static void rzv2h_usb2phy_reset_pm_runtime_put(void *data) { pm_runtime_put(data); @@ -115,6 +102,7 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) struct rzv2h_usb2phy_reset_priv *priv; struct device *dev = &pdev->dev; struct reset_control *rstc; + void __iomem *base; int error; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -124,17 +112,19 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) data = of_device_get_match_data(dev); priv->data = data; priv->dev = dev; - priv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, &rzv2h_usb2phy_reset_regconf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); rstc = devm_reset_control_get_shared_deasserted(dev, NULL); if (IS_ERR(rstc)) return dev_err_probe(dev, PTR_ERR(rstc), "failed to get deasserted reset\n"); - spin_lock_init(&priv->lock); - error = devm_pm_runtime_enable(dev); if (error) return dev_err_probe(dev, error, "Failed to enable pm_runtime\n"); @@ -148,8 +138,9 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) if (error) return dev_err_probe(dev, error, "unable to register cleanup action\n"); - for (unsigned int i = 0; i < data->init_val_count; i++) - writel(data->init_vals[i].val, priv->base + data->init_vals[i].reg); + error = regmap_multi_reg_write(priv->regmap, data->init_seq, data->init_nseq); + if (error) + return dev_err_probe(dev, error, "failed to initialize PHY registers\n"); priv->rcdev.ops = &rzv2h_usbphy_reset_ops; priv->rcdev.of_reset_n_cells = 0; @@ -169,23 +160,32 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) * initialization values required to prepare the PHY to receive * assert and deassert requests. */ -static const struct rzv2h_usb2phy_regval rzv2h_init_vals[] = { - { .reg = 0xc10, .val = 0x67c }, - { .reg = 0xc14, .val = 0x1f }, - { .reg = 0x600, .val = 0x909 }, +static const struct reg_sequence rzv2h_init_seq[] = { + { .reg = 0xc10, .def = 0x67c }, + { .reg = 0xc14, .def = 0x01f }, + { .reg = 0x600, .def = 0x909 }, +}; + +static const struct reg_sequence rzv2h_assert_seq[] = { + { .reg = 0xb04, .def = 0x303 }, + { .reg = 0x000, .def = 0x206, .delay_us = 11 }, +}; + +static const struct reg_sequence rzv2h_deassert_seq[] = { + { .reg = 0x000, .def = 0x200 }, + { .reg = 0xb04, .def = 0x003 }, + { .reg = 0x000, .def = 0x000 }, }; static const struct rzv2h_usb2phy_reset_of_data rzv2h_reset_of_data = { - .init_vals = rzv2h_init_vals, - .init_val_count = ARRAY_SIZE(rzv2h_init_vals), + .init_seq = rzv2h_init_seq, + .init_nseq = ARRAY_SIZE(rzv2h_init_seq), + .assert_seq = rzv2h_assert_seq, + .assert_nseq = ARRAY_SIZE(rzv2h_assert_seq), + .deassert_seq = rzv2h_deassert_seq, + .deassert_nseq = ARRAY_SIZE(rzv2h_deassert_seq), .reset_reg = 0, - .reset_assert_val = 0x206, .reset_status_bits = BIT(2), - .reset_deassert_val = 0x200, - .reset_release_val = 0x0, - .reset2_reg = 0xb04, - .reset2_acquire_val = 0x303, - .reset2_release_val = 0x3, }; static const struct of_device_id rzv2h_usb2phy_reset_of_match[] = { -- cgit v1.2.3 From f62fcdf8ab826ffc811552e29a6dd8544281fd97 Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 1 Apr 2026 17:16:11 +0200 Subject: reset: rzv2h-usb2phy: Add support for VBUS mux controller registration The RZ/V2H USB2 PHY requires control of the VBUS selection line (VBENCTL) through a mux controller described in the device tree as "mux-controller". This change adds support for registering the rzv2h-usb-vbenctl auxiliary driver during probe. This enables proper management of USB2.0 VBUS source selection on platforms using the RZ/V2H SoC. Reviewed-by: Philipp Zabel Signed-off-by: Tommaso Merciai Signed-off-by: Philipp Zabel --- drivers/reset/Kconfig | 1 + drivers/reset/reset-rzv2h-usb2phy.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index a2f56ea470c5..2fda1d9622f4 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -257,6 +257,7 @@ config RESET_RZG2L_USBPHY_CTRL config RESET_RZV2H_USB2PHY tristate "Renesas RZ/V2H(P) (and similar SoCs) USB2PHY Reset driver" depends on ARCH_RENESAS || COMPILE_TEST + select AUXILIARY_BUS select REGMAP_MMIO help Support for USB2PHY Port reset Control found on the RZ/V2H(P) SoC diff --git a/drivers/reset/reset-rzv2h-usb2phy.c b/drivers/reset/reset-rzv2h-usb2phy.c index c79bf72602e8..d96042e28cd5 100644 --- a/drivers/reset/reset-rzv2h-usb2phy.c +++ b/drivers/reset/reset-rzv2h-usb2phy.c @@ -5,7 +5,9 @@ * Copyright (C) 2025 Renesas Electronics Corporation */ +#include #include +#include #include #include #include @@ -15,6 +17,8 @@ #include #include +static DEFINE_IDA(auxiliary_ids); + struct rzv2h_usb2phy_reset_of_data { const struct reg_sequence *init_seq; unsigned int init_nseq; @@ -84,6 +88,33 @@ static int rzv2h_usb2phy_reset_of_xlate(struct reset_controller_dev *rcdev, return 0; } +static void rzv2h_usb2phy_reset_ida_free(void *data) +{ + struct auxiliary_device *adev = data; + + ida_free(&auxiliary_ids, adev->id); +} + +static int rzv2h_usb2phy_reset_mux_register(struct device *dev, + const char *mux_name) +{ + struct auxiliary_device *adev; + int id; + + id = ida_alloc(&auxiliary_ids, GFP_KERNEL); + if (id < 0) + return id; + + adev = __devm_auxiliary_device_create(dev, dev->driver->name, + mux_name, NULL, id); + if (!adev) { + ida_free(&auxiliary_ids, id); + return -ENOMEM; + } + + return devm_add_action_or_reset(dev, rzv2h_usb2phy_reset_ida_free, adev); +} + static const struct regmap_config rzv2h_usb2phy_reset_regconf = { .reg_bits = 32, .val_bits = 32, @@ -153,6 +184,10 @@ static int rzv2h_usb2phy_reset_probe(struct platform_device *pdev) if (error) return dev_err_probe(dev, error, "could not register reset controller\n"); + error = rzv2h_usb2phy_reset_mux_register(dev, "vbenctl"); + if (error) + return dev_err_probe(dev, error, "could not register aux mux\n"); + return 0; } -- cgit v1.2.3