diff options
Diffstat (limited to 'drivers/clk/clk-fixed-factor.c')
| -rw-r--r-- | drivers/clk/clk-fixed-factor.c | 287 |
1 files changed, 234 insertions, 53 deletions
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index a5d402de5584..de658c9e4c53 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -1,11 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Standard functionality for the common clock API. */ #include <linux/module.h> #include <linux/clk-provider.h> @@ -35,19 +30,21 @@ static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, return (unsigned long)rate; } -static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +static int clk_factor_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { unsigned long best_parent; - best_parent = (rate / fix->mult) * fix->div; - *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent); + best_parent = (req->rate / fix->mult) * fix->div; + req->best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent); } - return (*prate / fix->div) * fix->mult; + req->rate = (req->best_parent_rate / fix->div) * fix->mult; + + return 0; } static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, @@ -55,30 +52,65 @@ static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, { /* * We must report success but we can do so unconditionally because - * clk_factor_round_rate returns values that ensure this call is a + * clk_factor_determine_rate returns values that ensure this call is a * nop. */ return 0; } +static unsigned long clk_factor_recalc_accuracy(struct clk_hw *hw, + unsigned long parent_accuracy) +{ + struct clk_fixed_factor *fix = to_clk_fixed_factor(hw); + + if (fix->flags & CLK_FIXED_FACTOR_FIXED_ACCURACY) + return fix->acc; + + return parent_accuracy; +} + const struct clk_ops clk_fixed_factor_ops = { - .round_rate = clk_factor_round_rate, + .determine_rate = clk_factor_determine_rate, .set_rate = clk_factor_set_rate, .recalc_rate = clk_factor_recalc_rate, + .recalc_accuracy = clk_factor_recalc_accuracy, }; EXPORT_SYMBOL_GPL(clk_fixed_factor_ops); -struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, - const char *name, const char *parent_name, unsigned long flags, - unsigned int mult, unsigned int div) +static void devm_clk_hw_register_fixed_factor_release(struct device *dev, void *res) +{ + struct clk_fixed_factor *fix = res; + + /* + * We can not use clk_hw_unregister_fixed_factor, since it will kfree() + * the hw, resulting in double free. Just unregister the hw and let + * devres code kfree() it. + */ + clk_hw_unregister(&fix->hw); +} + +static struct clk_hw * +__clk_hw_register_fixed_factor(struct device *dev, struct device_node *np, + const char *name, const char *parent_name, + const struct clk_hw *parent_hw, const struct clk_parent_data *pdata, + unsigned long flags, unsigned int mult, unsigned int div, + unsigned long acc, unsigned int fixflags, bool devm) { struct clk_fixed_factor *fix; - struct clk_init_data init; + struct clk_init_data init = { }; struct clk_hw *hw; int ret; - fix = kmalloc(sizeof(*fix), GFP_KERNEL); + /* You can't use devm without a dev */ + if (devm && !dev) + return ERR_PTR(-EINVAL); + + if (devm) + fix = devres_alloc(devm_clk_hw_register_fixed_factor_release, + sizeof(*fix), GFP_KERNEL); + else + fix = kmalloc(sizeof(*fix), GFP_KERNEL); if (!fix) return ERR_PTR(-ENOMEM); @@ -86,24 +118,142 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, fix->mult = mult; fix->div = div; fix->hw.init = &init; + fix->acc = acc; + fix->flags = fixflags; init.name = name; init.ops = &clk_fixed_factor_ops; - init.flags = flags | CLK_IS_BASIC; - init.parent_names = &parent_name; + init.flags = flags; + if (parent_name) + init.parent_names = &parent_name; + else if (parent_hw) + init.parent_hws = &parent_hw; + else + init.parent_data = pdata; init.num_parents = 1; hw = &fix->hw; - ret = clk_hw_register(dev, hw); + if (dev) + ret = clk_hw_register(dev, hw); + else + ret = of_clk_hw_register(np, hw); if (ret) { - kfree(fix); + if (devm) + devres_free(fix); + else + kfree(fix); hw = ERR_PTR(ret); - } + } else if (devm) + devres_add(dev, fix); return hw; } + +/** + * devm_clk_hw_register_fixed_factor_index - Register a fixed factor clock with + * parent from DT index + * @dev: device that is registering this clock + * @name: name of this clock + * @index: index of phandle in @dev 'clocks' property + * @flags: fixed factor flags + * @mult: multiplier + * @div: divider + * + * Return: Pointer to fixed factor clk_hw structure that was registered or + * an error pointer. + */ +struct clk_hw *devm_clk_hw_register_fixed_factor_index(struct device *dev, + const char *name, unsigned int index, unsigned long flags, + unsigned int mult, unsigned int div) +{ + const struct clk_parent_data pdata = { .index = index }; + + return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, NULL, &pdata, + flags, mult, div, 0, 0, true); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_index); + +/** + * devm_clk_hw_register_fixed_factor_parent_hw - Register a fixed factor clock with + * pointer to parent clock + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_hw: pointer to parent clk + * @flags: fixed factor flags + * @mult: multiplier + * @div: divider + * + * Return: Pointer to fixed factor clk_hw structure that was registered or + * an error pointer. + */ +struct clk_hw *devm_clk_hw_register_fixed_factor_parent_hw(struct device *dev, + const char *name, const struct clk_hw *parent_hw, + unsigned long flags, unsigned int mult, unsigned int div) +{ + const struct clk_parent_data pdata = { .index = -1 }; + + return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, parent_hw, + &pdata, flags, mult, div, 0, 0, true); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_parent_hw); + +struct clk_hw *clk_hw_register_fixed_factor_parent_hw(struct device *dev, + const char *name, const struct clk_hw *parent_hw, + unsigned long flags, unsigned int mult, unsigned int div) +{ + const struct clk_parent_data pdata = { .index = -1 }; + + return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, parent_hw, + &pdata, flags, mult, div, 0, 0, false); +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_parent_hw); + +struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + const struct clk_parent_data pdata = { .index = -1 }; + + return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL, + &pdata, flags, mult, div, 0, 0, false); +} EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor); +struct clk_hw *clk_hw_register_fixed_factor_fwname(struct device *dev, + struct device_node *np, const char *name, const char *fw_name, + unsigned long flags, unsigned int mult, unsigned int div) +{ + const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name }; + + return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL, + &pdata, flags, mult, div, 0, 0, false); +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_fwname); + +struct clk_hw *clk_hw_register_fixed_factor_with_accuracy_fwname(struct device *dev, + struct device_node *np, const char *name, const char *fw_name, + unsigned long flags, unsigned int mult, unsigned int div, + unsigned long acc) +{ + const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name }; + + return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL, + &pdata, flags, mult, div, acc, + CLK_FIXED_FACTOR_FIXED_ACCURACY, false); +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_with_accuracy_fwname); + +struct clk_hw *clk_hw_register_fixed_factor_index(struct device *dev, + const char *name, unsigned int index, unsigned long flags, + unsigned int mult, unsigned int div) +{ + const struct clk_parent_data pdata = { .index = index }; + + return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, NULL, &pdata, + flags, mult, div, 0, 0, false); +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_index); + struct clk *clk_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) @@ -142,55 +292,87 @@ void clk_hw_unregister_fixed_factor(struct clk_hw *hw) } EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor); -#ifdef CONFIG_OF -static const struct of_device_id set_rate_parent_matches[] = { - { .compatible = "allwinner,sun4i-a10-pll3-2x-clk" }, - { /* Sentinel */ }, -}; +struct clk_hw *devm_clk_hw_register_fixed_factor(struct device *dev, + const char *name, const char *parent_name, unsigned long flags, + unsigned int mult, unsigned int div) +{ + const struct clk_parent_data pdata = { .index = -1 }; + + return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL, + &pdata, flags, mult, div, 0, 0, true); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor); -static struct clk *_of_fixed_factor_clk_setup(struct device_node *node) +struct clk_hw *devm_clk_hw_register_fixed_factor_fwname(struct device *dev, + struct device_node *np, const char *name, const char *fw_name, + unsigned long flags, unsigned int mult, unsigned int div) { - struct clk *clk; + const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name }; + + return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL, + &pdata, flags, mult, div, 0, 0, true); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_fwname); + +struct clk_hw *devm_clk_hw_register_fixed_factor_with_accuracy_fwname(struct device *dev, + struct device_node *np, const char *name, const char *fw_name, + unsigned long flags, unsigned int mult, unsigned int div, + unsigned long acc) +{ + const struct clk_parent_data pdata = { .index = -1, .fw_name = fw_name }; + + return __clk_hw_register_fixed_factor(dev, np, name, NULL, NULL, + &pdata, flags, mult, div, acc, + CLK_FIXED_FACTOR_FIXED_ACCURACY, true); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_with_accuracy_fwname); + +#ifdef CONFIG_OF +static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node) +{ + struct clk_hw *hw; const char *clk_name = node->name; - const char *parent_name; - unsigned long flags = 0; + const struct clk_parent_data pdata = { .index = 0 }; u32 div, mult; int ret; if (of_property_read_u32(node, "clock-div", &div)) { - pr_err("%s Fixed factor clock <%s> must have a clock-div property\n", - __func__, node->name); + pr_err("%s Fixed factor clock <%pOFn> must have a clock-div property\n", + __func__, node); return ERR_PTR(-EIO); } if (of_property_read_u32(node, "clock-mult", &mult)) { - pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n", - __func__, node->name); + pr_err("%s Fixed factor clock <%pOFn> must have a clock-mult property\n", + __func__, node); return ERR_PTR(-EIO); } of_property_read_string(node, "clock-output-names", &clk_name); - parent_name = of_clk_get_parent_name(node, 0); - if (of_match_node(set_rate_parent_matches, node)) - flags |= CLK_SET_RATE_PARENT; - - clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags, - mult, div); - if (IS_ERR(clk)) - return clk; + hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, NULL, + &pdata, 0, mult, div, 0, 0, false); + if (IS_ERR(hw)) { + /* + * Clear OF_POPULATED flag so that clock registration can be + * attempted again from probe function. + */ + of_node_clear_flag(node, OF_POPULATED); + return ERR_CAST(hw); + } - ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); if (ret) { - clk_unregister(clk); + clk_hw_unregister_fixed_factor(hw); return ERR_PTR(ret); } - return clk; + return hw; } /** * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock + * @node: device node for the clock */ void __init of_fixed_factor_clk_setup(struct device_node *node) { @@ -199,18 +381,17 @@ void __init of_fixed_factor_clk_setup(struct device_node *node) CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock", of_fixed_factor_clk_setup); -static int of_fixed_factor_clk_remove(struct platform_device *pdev) +static void of_fixed_factor_clk_remove(struct platform_device *pdev) { - struct clk *clk = platform_get_drvdata(pdev); + struct clk_hw *clk = platform_get_drvdata(pdev); - clk_unregister_fixed_factor(clk); - - return 0; + of_clk_del_provider(pdev->dev.of_node); + clk_hw_unregister_fixed_factor(clk); } static int of_fixed_factor_clk_probe(struct platform_device *pdev) { - struct clk *clk; + struct clk_hw *clk; /* * This function is not executed when of_fixed_factor_clk_setup |
