diff options
Diffstat (limited to 'drivers/clk/clk-composite.c')
| -rw-r--r-- | drivers/clk/clk-composite.c | 206 |
1 files changed, 176 insertions, 30 deletions
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 46604214bba0..66759fe28fad 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -3,8 +3,8 @@ * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. */ -#include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/device.h> #include <linux/err.h> #include <linux/slab.h> @@ -42,6 +42,29 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, return rate_ops->recalc_rate(rate_hw, parent_rate); } +static int clk_composite_determine_rate_for_parent(struct clk_hw *rate_hw, + struct clk_rate_request *req, + struct clk_hw *parent_hw, + const struct clk_ops *rate_ops) +{ + long rate; + + req->best_parent_hw = parent_hw; + req->best_parent_rate = clk_hw_get_rate(parent_hw); + + if (rate_ops->determine_rate) + return rate_ops->determine_rate(rate_hw, req); + + rate = rate_ops->round_rate(rate_hw, req->rate, + &req->best_parent_rate); + if (rate < 0) + return rate; + + req->rate = rate; + + return 0; +} + static int clk_composite_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { @@ -51,54 +74,62 @@ static int clk_composite_determine_rate(struct clk_hw *hw, struct clk_hw *rate_hw = composite->rate_hw; struct clk_hw *mux_hw = composite->mux_hw; struct clk_hw *parent; - unsigned long parent_rate; - long tmp_rate, best_rate = 0; unsigned long rate_diff; unsigned long best_rate_diff = ULONG_MAX; - long rate; - int i; + unsigned long best_rate = 0; + int i, ret; - if (rate_hw && rate_ops && rate_ops->determine_rate) { - __clk_hw_set_clk(rate_hw, hw); - return rate_ops->determine_rate(rate_hw, req); - } else if (rate_hw && rate_ops && rate_ops->round_rate && - mux_hw && mux_ops && mux_ops->set_parent) { + if (rate_hw && rate_ops && + (rate_ops->determine_rate || rate_ops->round_rate) && + mux_hw && mux_ops && mux_ops->set_parent) { req->best_parent_hw = NULL; if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) { + struct clk_rate_request tmp_req; + parent = clk_hw_get_parent(mux_hw); - req->best_parent_hw = parent; - req->best_parent_rate = clk_hw_get_rate(parent); - rate = rate_ops->round_rate(rate_hw, req->rate, - &req->best_parent_rate); - if (rate < 0) - return rate; + clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate); + ret = clk_composite_determine_rate_for_parent(rate_hw, + &tmp_req, + parent, + rate_ops); + if (ret) + return ret; + + req->rate = tmp_req.rate; + req->best_parent_hw = tmp_req.best_parent_hw; + req->best_parent_rate = tmp_req.best_parent_rate; - req->rate = rate; return 0; } for (i = 0; i < clk_hw_get_num_parents(mux_hw); i++) { + struct clk_rate_request tmp_req; + parent = clk_hw_get_parent_by_index(mux_hw, i); if (!parent) continue; - parent_rate = clk_hw_get_rate(parent); - - tmp_rate = rate_ops->round_rate(rate_hw, req->rate, - &parent_rate); - if (tmp_rate < 0) + clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate); + ret = clk_composite_determine_rate_for_parent(rate_hw, + &tmp_req, + parent, + rate_ops); + if (ret) continue; - rate_diff = abs(req->rate - tmp_rate); + if (req->rate >= tmp_req.rate) + rate_diff = req->rate - tmp_req.rate; + else + rate_diff = tmp_req.rate - req->rate; if (!rate_diff || !req->best_parent_hw || best_rate_diff > rate_diff) { req->best_parent_hw = parent; - req->best_parent_rate = parent_rate; + req->best_parent_rate = tmp_req.best_parent_rate; best_rate_diff = rate_diff; - best_rate = tmp_rate; + best_rate = tmp_req.rate; } if (!rate_diff) @@ -107,6 +138,9 @@ static int clk_composite_determine_rate(struct clk_hw *hw, req->rate = best_rate; return 0; + } else if (rate_hw && rate_ops && rate_ops->determine_rate) { + __clk_hw_set_clk(rate_hw, hw); + return rate_ops->determine_rate(rate_hw, req); } else if (mux_hw && mux_ops && mux_ops->determine_rate) { __clk_hw_set_clk(mux_hw, hw); return mux_ops->determine_rate(mux_hw, req); @@ -200,15 +234,16 @@ static void clk_composite_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } -struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, - const char * const *parent_names, int num_parents, +static struct clk_hw *__clk_hw_register_composite(struct device *dev, + const char *name, const char * const *parent_names, + const struct clk_parent_data *pdata, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, unsigned long flags) { struct clk_hw *hw; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_composite *composite; struct clk_ops *clk_composite_ops; int ret; @@ -218,8 +253,11 @@ struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, return ERR_PTR(-ENOMEM); init.name = name; - init.flags = flags | CLK_IS_BASIC; - init.parent_names = parent_names; + init.flags = flags; + if (parent_names) + init.parent_names = parent_names; + else + init.parent_data = pdata; init.num_parents = num_parents; hw = &composite->hw; @@ -313,6 +351,35 @@ err: return hw; } +struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, + const char * const *parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + return __clk_hw_register_composite(dev, name, parent_names, NULL, + num_parents, mux_hw, mux_ops, + rate_hw, rate_ops, gate_hw, + gate_ops, flags); +} +EXPORT_SYMBOL_GPL(clk_hw_register_composite); + +struct clk_hw *clk_hw_register_composite_pdata(struct device *dev, + const char *name, + const struct clk_parent_data *parent_data, + int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + return __clk_hw_register_composite(dev, name, NULL, parent_data, + num_parents, mux_hw, mux_ops, + rate_hw, rate_ops, gate_hw, + gate_ops, flags); +} + struct clk *clk_register_composite(struct device *dev, const char *name, const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, @@ -329,6 +396,25 @@ struct clk *clk_register_composite(struct device *dev, const char *name, return ERR_CAST(hw); return hw->clk; } +EXPORT_SYMBOL_GPL(clk_register_composite); + +struct clk *clk_register_composite_pdata(struct device *dev, const char *name, + const struct clk_parent_data *parent_data, + int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk_hw *hw; + + hw = clk_hw_register_composite_pdata(dev, name, parent_data, + num_parents, mux_hw, mux_ops, rate_hw, rate_ops, + gate_hw, gate_ops, flags); + if (IS_ERR(hw)) + return ERR_CAST(hw); + return hw->clk; +} void clk_unregister_composite(struct clk *clk) { @@ -344,3 +430,63 @@ void clk_unregister_composite(struct clk *clk) clk_unregister(clk); kfree(composite); } + +void clk_hw_unregister_composite(struct clk_hw *hw) +{ + struct clk_composite *composite; + + composite = to_clk_composite(hw); + + clk_hw_unregister(hw); + kfree(composite); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_composite); + +static void devm_clk_hw_release_composite(struct device *dev, void *res) +{ + clk_hw_unregister_composite(*(struct clk_hw **)res); +} + +static struct clk_hw *__devm_clk_hw_register_composite(struct device *dev, + const char *name, const char * const *parent_names, + const struct clk_parent_data *pdata, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk_hw **ptr, *hw; + + ptr = devres_alloc(devm_clk_hw_release_composite, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + hw = __clk_hw_register_composite(dev, name, parent_names, pdata, + num_parents, mux_hw, mux_ops, rate_hw, + rate_ops, gate_hw, gate_ops, flags); + + if (!IS_ERR(hw)) { + *ptr = hw; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return hw; +} + +struct clk_hw *devm_clk_hw_register_composite_pdata(struct device *dev, + const char *name, + const struct clk_parent_data *parent_data, + int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *rate_hw, const struct clk_ops *rate_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + return __devm_clk_hw_register_composite(dev, name, NULL, parent_data, + num_parents, mux_hw, mux_ops, + rate_hw, rate_ops, gate_hw, + gate_ops, flags); +} |
