summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Sverdlin <alexander.sverdlin@siemens.com>2026-08-28 18:44:39 +0200
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2026-10-29 11:23:57 +0100
commita451b6bb922ede877b141beb09c3d2eb3cc9edfe (patch)
tree3da2fbcc433b0b6b83a12dc4f6f4721a44230295
parentf6550426f21ec5293feddb74368be6f4e9eefac1 (diff)
rtc: pcf85063: use devm_of_clk_add_hw_provider() for clkout
pcf85063_clkout_register_clk() registered the OF clock provider with the deprecated of_clk_add_provider() and never removed it, so the provider kept pointing at freed data after unbind, leading to a use-after-free the next time the device-tree clock was resolved. Switch to devm_clk_hw_register() and devm_of_clk_add_hw_provider(): both the clock and its OF provider are managed resources now, and the provider is torn down before the clock on unbind. Register the clock on the parent i2c device instead of the rtc device. devm_of_clk_add_hw_provider() must use the parent, which owns the of_node, and the clkout_hw it points at lives in the driver data allocated on the parent. Tying both to the parent releases them together on unbind, before the driver data is freed, and keeps their teardown ordering guaranteed by a single devres list. Registering the clock on the rtc device instead could defer its unregistration past unbind (e.g. while a /dev/rtcN fd is open), after clkout_hw has already been freed. Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com> Link: https://patch.msgid.link/20260828164445.3907839-2-alexander.sverdlin@siemens.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
-rw-r--r--drivers/rtc/rtc-pcf85063.c19
1 files changed, 10 insertions, 9 deletions
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
index 8cb9ffc73f6d..ac9ab1c376c0 100644
--- a/drivers/rtc/rtc-pcf85063.c
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -495,12 +495,13 @@ static const struct clk_ops pcf85063_clkout_ops = {
.set_rate = pcf85063_clkout_set_rate,
};
-static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
+static int pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
{
- struct clk *clk;
+ struct device *dev = pcf85063->rtc->dev.parent;
struct clk_init_data init = {};
- struct device_node *node = pcf85063->rtc->dev.parent->of_node;
+ struct device_node *node = dev->of_node;
struct device_node *fixed_clock;
+ int ret;
fixed_clock = of_get_child_by_name(node, "clock");
if (fixed_clock) {
@@ -510,7 +511,7 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
* registered automatically when being referenced.
*/
of_node_put(fixed_clock);
- return NULL;
+ return 0;
}
init.name = "pcf85063-clkout";
@@ -524,12 +525,12 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
of_property_read_string(node, "clock-output-names", &init.name);
/* register the clock */
- clk = devm_clk_register(&pcf85063->rtc->dev, &pcf85063->clkout_hw);
-
- if (!IS_ERR(clk))
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ ret = devm_clk_hw_register(dev, &pcf85063->clkout_hw);
+ if (ret)
+ return ret;
- return clk;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &pcf85063->clkout_hw);
}
#endif