linux/drivers/clk/clk-fixed-factor.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
   4 */
   5#include <linux/module.h>
   6#include <linux/clk-provider.h>
   7#include <linux/slab.h>
   8#include <linux/err.h>
   9#include <linux/of.h>
  10#include <linux/platform_device.h>
  11
  12/*
  13 * DOC: basic fixed multiplier and divider clock that cannot gate
  14 *
  15 * Traits of this clock:
  16 * prepare - clk_prepare only ensures that parents are prepared
  17 * enable - clk_enable only ensures that parents are enabled
  18 * rate - rate is fixed.  clk->rate = parent->rate / div * mult
  19 * parent - fixed parent.  No clk_set_parent support
  20 */
  21
  22static unsigned long clk_factor_recalc_rate(struct clk_hw *hw,
  23                unsigned long parent_rate)
  24{
  25        struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
  26        unsigned long long int rate;
  27
  28        rate = (unsigned long long int)parent_rate * fix->mult;
  29        do_div(rate, fix->div);
  30        return (unsigned long)rate;
  31}
  32
  33static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate,
  34                                unsigned long *prate)
  35{
  36        struct clk_fixed_factor *fix = to_clk_fixed_factor(hw);
  37
  38        if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
  39                unsigned long best_parent;
  40
  41                best_parent = (rate / fix->mult) * fix->div;
  42                *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
  43        }
  44
  45        return (*prate / fix->div) * fix->mult;
  46}
  47
  48static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate,
  49                                unsigned long parent_rate)
  50{
  51        /*
  52         * We must report success but we can do so unconditionally because
  53         * clk_factor_round_rate returns values that ensure this call is a
  54         * nop.
  55         */
  56
  57        return 0;
  58}
  59
  60const struct clk_ops clk_fixed_factor_ops = {
  61        .round_rate = clk_factor_round_rate,
  62        .set_rate = clk_factor_set_rate,
  63        .recalc_rate = clk_factor_recalc_rate,
  64};
  65EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
  66
  67static struct clk_hw *
  68__clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
  69                const char *name, const char *parent_name, int index,
  70                unsigned long flags, unsigned int mult, unsigned int div)
  71{
  72        struct clk_fixed_factor *fix;
  73        struct clk_init_data init = { };
  74        struct clk_parent_data pdata = { .index = index };
  75        struct clk_hw *hw;
  76        int ret;
  77
  78        fix = kmalloc(sizeof(*fix), GFP_KERNEL);
  79        if (!fix)
  80                return ERR_PTR(-ENOMEM);
  81
  82        /* struct clk_fixed_factor assignments */
  83        fix->mult = mult;
  84        fix->div = div;
  85        fix->hw.init = &init;
  86
  87        init.name = name;
  88        init.ops = &clk_fixed_factor_ops;
  89        init.flags = flags;
  90        if (parent_name)
  91                init.parent_names = &parent_name;
  92        else
  93                init.parent_data = &pdata;
  94        init.num_parents = 1;
  95
  96        hw = &fix->hw;
  97        if (dev)
  98                ret = clk_hw_register(dev, hw);
  99        else
 100                ret = of_clk_hw_register(np, hw);
 101        if (ret) {
 102                kfree(fix);
 103                hw = ERR_PTR(ret);
 104        }
 105
 106        return hw;
 107}
 108
 109struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
 110                const char *name, const char *parent_name, unsigned long flags,
 111                unsigned int mult, unsigned int div)
 112{
 113        return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1,
 114                                              flags, mult, div);
 115}
 116EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);
 117
 118struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
 119                const char *parent_name, unsigned long flags,
 120                unsigned int mult, unsigned int div)
 121{
 122        struct clk_hw *hw;
 123
 124        hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
 125                                          div);
 126        if (IS_ERR(hw))
 127                return ERR_CAST(hw);
 128        return hw->clk;
 129}
 130EXPORT_SYMBOL_GPL(clk_register_fixed_factor);
 131
 132void clk_unregister_fixed_factor(struct clk *clk)
 133{
 134        struct clk_hw *hw;
 135
 136        hw = __clk_get_hw(clk);
 137        if (!hw)
 138                return;
 139
 140        clk_unregister(clk);
 141        kfree(to_clk_fixed_factor(hw));
 142}
 143EXPORT_SYMBOL_GPL(clk_unregister_fixed_factor);
 144
 145void clk_hw_unregister_fixed_factor(struct clk_hw *hw)
 146{
 147        struct clk_fixed_factor *fix;
 148
 149        fix = to_clk_fixed_factor(hw);
 150
 151        clk_hw_unregister(hw);
 152        kfree(fix);
 153}
 154EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor);
 155
 156#ifdef CONFIG_OF
 157static const struct of_device_id set_rate_parent_matches[] = {
 158        { .compatible = "allwinner,sun4i-a10-pll3-2x-clk" },
 159        { /* Sentinel */ },
 160};
 161
 162static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node)
 163{
 164        struct clk_hw *hw;
 165        const char *clk_name = node->name;
 166        unsigned long flags = 0;
 167        u32 div, mult;
 168        int ret;
 169
 170        if (of_property_read_u32(node, "clock-div", &div)) {
 171                pr_err("%s Fixed factor clock <%pOFn> must have a clock-div property\n",
 172                        __func__, node);
 173                return ERR_PTR(-EIO);
 174        }
 175
 176        if (of_property_read_u32(node, "clock-mult", &mult)) {
 177                pr_err("%s Fixed factor clock <%pOFn> must have a clock-mult property\n",
 178                        __func__, node);
 179                return ERR_PTR(-EIO);
 180        }
 181
 182        of_property_read_string(node, "clock-output-names", &clk_name);
 183
 184        if (of_match_node(set_rate_parent_matches, node))
 185                flags |= CLK_SET_RATE_PARENT;
 186
 187        hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, 0,
 188                                            flags, mult, div);
 189        if (IS_ERR(hw)) {
 190                /*
 191                 * Clear OF_POPULATED flag so that clock registration can be
 192                 * attempted again from probe function.
 193                 */
 194                of_node_clear_flag(node, OF_POPULATED);
 195                return ERR_CAST(hw);
 196        }
 197
 198        ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
 199        if (ret) {
 200                clk_hw_unregister_fixed_factor(hw);
 201                return ERR_PTR(ret);
 202        }
 203
 204        return hw;
 205}
 206
 207/**
 208 * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
 209 * @node:       device node for the clock
 210 */
 211void __init of_fixed_factor_clk_setup(struct device_node *node)
 212{
 213        _of_fixed_factor_clk_setup(node);
 214}
 215CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
 216                of_fixed_factor_clk_setup);
 217
 218static int of_fixed_factor_clk_remove(struct platform_device *pdev)
 219{
 220        struct clk_hw *clk = platform_get_drvdata(pdev);
 221
 222        of_clk_del_provider(pdev->dev.of_node);
 223        clk_hw_unregister_fixed_factor(clk);
 224
 225        return 0;
 226}
 227
 228static int of_fixed_factor_clk_probe(struct platform_device *pdev)
 229{
 230        struct clk_hw *clk;
 231
 232        /*
 233         * This function is not executed when of_fixed_factor_clk_setup
 234         * succeeded.
 235         */
 236        clk = _of_fixed_factor_clk_setup(pdev->dev.of_node);
 237        if (IS_ERR(clk))
 238                return PTR_ERR(clk);
 239
 240        platform_set_drvdata(pdev, clk);
 241
 242        return 0;
 243}
 244
 245static const struct of_device_id of_fixed_factor_clk_ids[] = {
 246        { .compatible = "fixed-factor-clock" },
 247        { }
 248};
 249MODULE_DEVICE_TABLE(of, of_fixed_factor_clk_ids);
 250
 251static struct platform_driver of_fixed_factor_clk_driver = {
 252        .driver = {
 253                .name = "of_fixed_factor_clk",
 254                .of_match_table = of_fixed_factor_clk_ids,
 255        },
 256        .probe = of_fixed_factor_clk_probe,
 257        .remove = of_fixed_factor_clk_remove,
 258};
 259builtin_platform_driver(of_fixed_factor_clk_driver);
 260#endif
 261