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 void devm_clk_hw_register_fixed_factor_release(struct device *dev, void *res)
  68{
  69        struct clk_fixed_factor *fix = res;
  70
  71        /*
  72         * We can not use clk_hw_unregister_fixed_factor, since it will kfree()
  73         * the hw, resulting in double free. Just unregister the hw and let
  74         * devres code kfree() it.
  75         */
  76        clk_hw_unregister(&fix->hw);
  77}
  78
  79static struct clk_hw *
  80__clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
  81                const char *name, const char *parent_name, int index,
  82                unsigned long flags, unsigned int mult, unsigned int div,
  83                bool devm)
  84{
  85        struct clk_fixed_factor *fix;
  86        struct clk_init_data init = { };
  87        struct clk_parent_data pdata = { .index = index };
  88        struct clk_hw *hw;
  89        int ret;
  90
  91        /* You can't use devm without a dev */
  92        if (devm && !dev)
  93                return ERR_PTR(-EINVAL);
  94
  95        if (devm)
  96                fix = devres_alloc(devm_clk_hw_register_fixed_factor_release,
  97                                sizeof(*fix), GFP_KERNEL);
  98        else
  99                fix = kmalloc(sizeof(*fix), GFP_KERNEL);
 100        if (!fix)
 101                return ERR_PTR(-ENOMEM);
 102
 103        /* struct clk_fixed_factor assignments */
 104        fix->mult = mult;
 105        fix->div = div;
 106        fix->hw.init = &init;
 107
 108        init.name = name;
 109        init.ops = &clk_fixed_factor_ops;
 110        init.flags = flags;
 111        if (parent_name)
 112                init.parent_names = &parent_name;
 113        else
 114                init.parent_data = &pdata;
 115        init.num_parents = 1;
 116
 117        hw = &fix->hw;
 118        if (dev)
 119                ret = clk_hw_register(dev, hw);
 120        else
 121                ret = of_clk_hw_register(np, hw);
 122        if (ret) {
 123                if (devm)
 124                        devres_free(fix);
 125                else
 126                        kfree(fix);
 127                hw = ERR_PTR(ret);
 128        } else if (devm)
 129                devres_add(dev, fix);
 130
 131        return hw;
 132}
 133
 134struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
 135                const char *name, const char *parent_name, unsigned long flags,
 136                unsigned int mult, unsigned int div)
 137{
 138        return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1,
 139                                              flags, mult, div, false);
 140}
 141EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);
 142
 143struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
 144                const char *parent_name, unsigned long flags,
 145                unsigned int mult, unsigned int div)
 146{
 147        struct clk_hw *hw;
 148
 149        hw = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
 150                                          div);
 151        if (IS_ERR(hw))
 152                return ERR_CAST(hw);
 153        return hw->clk;
 154}
 155EXPORT_SYMBOL_GPL(clk_register_fixed_factor);
 156
 157void clk_unregister_fixed_factor(struct clk *clk)
 158{
 159        struct clk_hw *hw;
 160
 161        hw = __clk_get_hw(clk);
 162        if (!hw)
 163                return;
 164
 165        clk_unregister(clk);
 166        kfree(to_clk_fixed_factor(hw));
 167}
 168EXPORT_SYMBOL_GPL(clk_unregister_fixed_factor);
 169
 170void clk_hw_unregister_fixed_factor(struct clk_hw *hw)
 171{
 172        struct clk_fixed_factor *fix;
 173
 174        fix = to_clk_fixed_factor(hw);
 175
 176        clk_hw_unregister(hw);
 177        kfree(fix);
 178}
 179EXPORT_SYMBOL_GPL(clk_hw_unregister_fixed_factor);
 180
 181struct clk_hw *devm_clk_hw_register_fixed_factor(struct device *dev,
 182                const char *name, const char *parent_name, unsigned long flags,
 183                unsigned int mult, unsigned int div)
 184{
 185        return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1,
 186                        flags, mult, div, true);
 187}
 188EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor);
 189
 190#ifdef CONFIG_OF
 191static const struct of_device_id set_rate_parent_matches[] = {
 192        { .compatible = "allwinner,sun4i-a10-pll3-2x-clk" },
 193        { /* Sentinel */ },
 194};
 195
 196static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node)
 197{
 198        struct clk_hw *hw;
 199        const char *clk_name = node->name;
 200        unsigned long flags = 0;
 201        u32 div, mult;
 202        int ret;
 203
 204        if (of_property_read_u32(node, "clock-div", &div)) {
 205                pr_err("%s Fixed factor clock <%pOFn> must have a clock-div property\n",
 206                        __func__, node);
 207                return ERR_PTR(-EIO);
 208        }
 209
 210        if (of_property_read_u32(node, "clock-mult", &mult)) {
 211                pr_err("%s Fixed factor clock <%pOFn> must have a clock-mult property\n",
 212                        __func__, node);
 213                return ERR_PTR(-EIO);
 214        }
 215
 216        of_property_read_string(node, "clock-output-names", &clk_name);
 217
 218        if (of_match_node(set_rate_parent_matches, node))
 219                flags |= CLK_SET_RATE_PARENT;
 220
 221        hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, 0,
 222                                            flags, mult, div, false);
 223        if (IS_ERR(hw)) {
 224                /*
 225                 * Clear OF_POPULATED flag so that clock registration can be
 226                 * attempted again from probe function.
 227                 */
 228                of_node_clear_flag(node, OF_POPULATED);
 229                return ERR_CAST(hw);
 230        }
 231
 232        ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
 233        if (ret) {
 234                clk_hw_unregister_fixed_factor(hw);
 235                return ERR_PTR(ret);
 236        }
 237
 238        return hw;
 239}
 240
 241/**
 242 * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
 243 * @node:       device node for the clock
 244 */
 245void __init of_fixed_factor_clk_setup(struct device_node *node)
 246{
 247        _of_fixed_factor_clk_setup(node);
 248}
 249CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
 250                of_fixed_factor_clk_setup);
 251
 252static int of_fixed_factor_clk_remove(struct platform_device *pdev)
 253{
 254        struct clk_hw *clk = platform_get_drvdata(pdev);
 255
 256        of_clk_del_provider(pdev->dev.of_node);
 257        clk_hw_unregister_fixed_factor(clk);
 258
 259        return 0;
 260}
 261
 262static int of_fixed_factor_clk_probe(struct platform_device *pdev)
 263{
 264        struct clk_hw *clk;
 265
 266        /*
 267         * This function is not executed when of_fixed_factor_clk_setup
 268         * succeeded.
 269         */
 270        clk = _of_fixed_factor_clk_setup(pdev->dev.of_node);
 271        if (IS_ERR(clk))
 272                return PTR_ERR(clk);
 273
 274        platform_set_drvdata(pdev, clk);
 275
 276        return 0;
 277}
 278
 279static const struct of_device_id of_fixed_factor_clk_ids[] = {
 280        { .compatible = "fixed-factor-clock" },
 281        { }
 282};
 283MODULE_DEVICE_TABLE(of, of_fixed_factor_clk_ids);
 284
 285static struct platform_driver of_fixed_factor_clk_driver = {
 286        .driver = {
 287                .name = "of_fixed_factor_clk",
 288                .of_match_table = of_fixed_factor_clk_ids,
 289        },
 290        .probe = of_fixed_factor_clk_probe,
 291        .remove = of_fixed_factor_clk_remove,
 292};
 293builtin_platform_driver(of_fixed_factor_clk_driver);
 294#endif
 295