From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
There are clock users in the kernel that can't use
devm_clk_get_optional_enabled() as they need to set rate after getting
the clock and before enabling it. Provide a managed helper that wraps
these operations in the correct order.
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
---
drivers/clk/clk-devres.c | 28 ++++++++++++++++++++++++++++
include/linux/clk.h | 26 ++++++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
index 90e6078fb6e1..34e800525e21 100644
--- a/drivers/clk/clk-devres.c
+++ b/drivers/clk/clk-devres.c
@@ -99,6 +99,34 @@ struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id)
}
EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled);
+struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev,
+ const char *id,
+ unsigned long rate)
+{
+ struct clk *clk;
+ int ret;
+
+ clk = __devm_clk_get(dev, id, clk_get_optional, NULL,
+ clk_disable_unprepare);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+
+ ret = clk_set_rate(clk, rate);
+ if (ret)
+ goto out_put_clk;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_put_clk;
+
+ return clk;
+
+out_put_clk:
+ devm_clk_put(dev, clk);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(devm_clk_get_optional_enabled_with_rate);
+
struct clk_bulk_devres {
struct clk_bulk_data *clks;
int num_clks;
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 0fa56d672532..c8d237e9b8d4 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -640,6 +640,32 @@ struct clk *devm_clk_get_optional_prepared(struct device *dev, const char *id);
*/
struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id);
+/**
+ * devm_clk_get_optional_enabled_with_rate - devm_clk_get_optional() +
+ * clk_set_rate() +
+ * clk_prepare_enable()
+ * @dev: device for clock "consumer"
+ * @id: clock consumer ID
+ * @rate: new clock rate
+ *
+ * Context: May sleep.
+ *
+ * Return: a struct clk corresponding to the clock producer, or
+ * valid IS_ERR() condition containing errno. The implementation
+ * uses @dev and @id to determine the clock consumer, and thereby
+ * the clock producer. If no such clk is found, it returns NULL
+ * which serves as a dummy clk. That's the only difference compared
+ * to devm_clk_get_enabled().
+ *
+ * The returned clk (if valid) is prepared and enabled and rate was set.
+ *
+ * The clock will automatically be disabled, unprepared and freed
+ * when the device is unbound from the bus.
+ */
+struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev,
+ const char *id,
+ unsigned long rate);
+
/**
* devm_get_clk_from_child - lookup and obtain a managed reference to a
* clock producer from child node.
--
2.43.0
Quoting Bartosz Golaszewski (2024-08-01 08:58:49) > diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c > index 90e6078fb6e1..34e800525e21 100644 > --- a/drivers/clk/clk-devres.c > +++ b/drivers/clk/clk-devres.c > @@ -99,6 +99,34 @@ struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id) > } > EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled); > > +struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, > + const char *id, > + unsigned long rate) > +{ > + struct clk *clk; > + int ret; > + > + clk = __devm_clk_get(dev, id, clk_get_optional, NULL, > + clk_disable_unprepare); > + if (IS_ERR(clk)) > + return ERR_CAST(clk); > + > + ret = clk_set_rate(clk, rate); > + if (ret) > + goto out_put_clk; > + > + ret = clk_prepare_enable(clk); > + if (ret) > + goto out_put_clk; > + > + return clk; > + > +out_put_clk: > + devm_clk_put(dev, clk); > + return ERR_PTR(ret); > +} > +EXPORT_SYMBOL(devm_clk_get_optional_enabled_with_rate); GPL
© 2016 - 2024 Red Hat, Inc.