|
@@ -2941,6 +2941,7 @@ struct of_clk_provider {
|
|
|
|
|
|
struct device_node *node;
|
|
|
struct clk *(*get)(struct of_phandle_args *clkspec, void *data);
|
|
|
+ struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data);
|
|
|
void *data;
|
|
|
};
|
|
|
|
|
@@ -2957,6 +2958,12 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(of_clk_src_simple_get);
|
|
|
|
|
|
+struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data)
|
|
|
+{
|
|
|
+ return data;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(of_clk_hw_simple_get);
|
|
|
+
|
|
|
struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
|
|
|
{
|
|
|
struct clk_onecell_data *clk_data = data;
|
|
@@ -2971,6 +2978,21 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(of_clk_src_onecell_get);
|
|
|
|
|
|
+struct clk_hw *
|
|
|
+of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data)
|
|
|
+{
|
|
|
+ struct clk_hw_onecell_data *hw_data = data;
|
|
|
+ unsigned int idx = clkspec->args[0];
|
|
|
+
|
|
|
+ if (idx >= hw_data->num) {
|
|
|
+ pr_err("%s: invalid index %u\n", __func__, idx);
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+ }
|
|
|
+
|
|
|
+ return hw_data->hws[idx];
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(of_clk_hw_onecell_get);
|
|
|
+
|
|
|
/**
|
|
|
* of_clk_add_provider() - Register a clock provider for a node
|
|
|
* @np: Device node pointer associated with clock provider
|
|
@@ -3006,6 +3028,41 @@ int of_clk_add_provider(struct device_node *np,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(of_clk_add_provider);
|
|
|
|
|
|
+/**
|
|
|
+ * of_clk_add_hw_provider() - Register a clock provider for a node
|
|
|
+ * @np: Device node pointer associated with clock provider
|
|
|
+ * @get: callback for decoding clk_hw
|
|
|
+ * @data: context pointer for @get callback.
|
|
|
+ */
|
|
|
+int of_clk_add_hw_provider(struct device_node *np,
|
|
|
+ struct clk_hw *(*get)(struct of_phandle_args *clkspec,
|
|
|
+ void *data),
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ struct of_clk_provider *cp;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ cp = kzalloc(sizeof(*cp), GFP_KERNEL);
|
|
|
+ if (!cp)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ cp->node = of_node_get(np);
|
|
|
+ cp->data = data;
|
|
|
+ cp->get_hw = get;
|
|
|
+
|
|
|
+ mutex_lock(&of_clk_mutex);
|
|
|
+ list_add(&cp->link, &of_clk_providers);
|
|
|
+ mutex_unlock(&of_clk_mutex);
|
|
|
+ pr_debug("Added clk_hw provider from %s\n", np->full_name);
|
|
|
+
|
|
|
+ ret = of_clk_set_defaults(np, true);
|
|
|
+ if (ret < 0)
|
|
|
+ of_clk_del_provider(np);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(of_clk_add_hw_provider);
|
|
|
+
|
|
|
/**
|
|
|
* of_clk_del_provider() - Remove a previously registered clock provider
|
|
|
* @np: Device node pointer associated with clock provider
|
|
@@ -3027,11 +3084,32 @@ void of_clk_del_provider(struct device_node *np)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(of_clk_del_provider);
|
|
|
|
|
|
+static struct clk_hw *
|
|
|
+__of_clk_get_hw_from_provider(struct of_clk_provider *provider,
|
|
|
+ struct of_phandle_args *clkspec)
|
|
|
+{
|
|
|
+ struct clk *clk;
|
|
|
+ struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER);
|
|
|
+
|
|
|
+ if (provider->get_hw) {
|
|
|
+ hw = provider->get_hw(clkspec, provider->data);
|
|
|
+ } else if (provider->get) {
|
|
|
+ clk = provider->get(clkspec, provider->data);
|
|
|
+ if (!IS_ERR(clk))
|
|
|
+ hw = __clk_get_hw(clk);
|
|
|
+ else
|
|
|
+ hw = ERR_CAST(clk);
|
|
|
+ }
|
|
|
+
|
|
|
+ return hw;
|
|
|
+}
|
|
|
+
|
|
|
struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
|
|
|
const char *dev_id, const char *con_id)
|
|
|
{
|
|
|
struct of_clk_provider *provider;
|
|
|
struct clk *clk = ERR_PTR(-EPROBE_DEFER);
|
|
|
+ struct clk_hw *hw = ERR_PTR(-EPROBE_DEFER);
|
|
|
|
|
|
if (!clkspec)
|
|
|
return ERR_PTR(-EINVAL);
|
|
@@ -3040,10 +3118,9 @@ struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,
|
|
|
mutex_lock(&of_clk_mutex);
|
|
|
list_for_each_entry(provider, &of_clk_providers, link) {
|
|
|
if (provider->node == clkspec->np)
|
|
|
- clk = provider->get(clkspec, provider->data);
|
|
|
- if (!IS_ERR(clk)) {
|
|
|
- clk = __clk_create_clk(__clk_get_hw(clk), dev_id,
|
|
|
- con_id);
|
|
|
+ hw = __of_clk_get_hw_from_provider(provider, clkspec);
|
|
|
+ if (!IS_ERR(hw)) {
|
|
|
+ clk = __clk_create_clk(hw, dev_id, con_id);
|
|
|
|
|
|
if (!IS_ERR(clk) && !__clk_get(clk)) {
|
|
|
__clk_free_clk(clk);
|