linux/drivers/clk/qcom/common.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/export.h>
   7#include <linux/module.h>
   8#include <linux/regmap.h>
   9#include <linux/platform_device.h>
  10#include <linux/clk-provider.h>
  11#include <linux/reset-controller.h>
  12#include <linux/of.h>
  13
  14#include "common.h"
  15#include "clk-rcg.h"
  16#include "clk-regmap.h"
  17#include "reset.h"
  18#include "gdsc.h"
  19
  20struct qcom_cc {
  21        struct qcom_reset_controller reset;
  22        struct clk_regmap **rclks;
  23        size_t num_rclks;
  24};
  25
  26const
  27struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
  28{
  29        if (!f)
  30                return NULL;
  31
  32        if (!f->freq)
  33                return f;
  34
  35        for (; f->freq; f++)
  36                if (rate <= f->freq)
  37                        return f;
  38
  39        /* Default to our fastest rate */
  40        return f - 1;
  41}
  42EXPORT_SYMBOL_GPL(qcom_find_freq);
  43
  44const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
  45                                            unsigned long rate)
  46{
  47        const struct freq_tbl *best = NULL;
  48
  49        for ( ; f->freq; f++) {
  50                if (rate >= f->freq)
  51                        best = f;
  52                else
  53                        break;
  54        }
  55
  56        return best;
  57}
  58EXPORT_SYMBOL_GPL(qcom_find_freq_floor);
  59
  60int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
  61{
  62        int i, num_parents = clk_hw_get_num_parents(hw);
  63
  64        for (i = 0; i < num_parents; i++)
  65                if (src == map[i].src)
  66                        return i;
  67
  68        return -ENOENT;
  69}
  70EXPORT_SYMBOL_GPL(qcom_find_src_index);
  71
  72struct regmap *
  73qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
  74{
  75        void __iomem *base;
  76        struct resource *res;
  77        struct device *dev = &pdev->dev;
  78
  79        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  80        base = devm_ioremap_resource(dev, res);
  81        if (IS_ERR(base))
  82                return ERR_CAST(base);
  83
  84        return devm_regmap_init_mmio(dev, base, desc->config);
  85}
  86EXPORT_SYMBOL_GPL(qcom_cc_map);
  87
  88void
  89qcom_pll_set_fsm_mode(struct regmap *map, u32 reg, u8 bias_count, u8 lock_count)
  90{
  91        u32 val;
  92        u32 mask;
  93
  94        /* De-assert reset to FSM */
  95        regmap_update_bits(map, reg, PLL_VOTE_FSM_RESET, 0);
  96
  97        /* Program bias count and lock count */
  98        val = bias_count << PLL_BIAS_COUNT_SHIFT |
  99                lock_count << PLL_LOCK_COUNT_SHIFT;
 100        mask = PLL_BIAS_COUNT_MASK << PLL_BIAS_COUNT_SHIFT;
 101        mask |= PLL_LOCK_COUNT_MASK << PLL_LOCK_COUNT_SHIFT;
 102        regmap_update_bits(map, reg, mask, val);
 103
 104        /* Enable PLL FSM voting */
 105        regmap_update_bits(map, reg, PLL_VOTE_FSM_ENA, PLL_VOTE_FSM_ENA);
 106}
 107EXPORT_SYMBOL_GPL(qcom_pll_set_fsm_mode);
 108
 109static void qcom_cc_gdsc_unregister(void *data)
 110{
 111        gdsc_unregister(data);
 112}
 113
 114/*
 115 * Backwards compatibility with old DTs. Register a pass-through factor 1/1
 116 * clock to translate 'path' clk into 'name' clk and register the 'path'
 117 * clk as a fixed rate clock if it isn't present.
 118 */
 119static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
 120                                       const char *name, unsigned long rate,
 121                                       bool add_factor)
 122{
 123        struct device_node *node = NULL;
 124        struct device_node *clocks_node;
 125        struct clk_fixed_factor *factor;
 126        struct clk_fixed_rate *fixed;
 127        struct clk_init_data init_data = { };
 128        int ret;
 129
 130        clocks_node = of_find_node_by_path("/clocks");
 131        if (clocks_node) {
 132                node = of_get_child_by_name(clocks_node, path);
 133                of_node_put(clocks_node);
 134        }
 135
 136        if (!node) {
 137                fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
 138                if (!fixed)
 139                        return -EINVAL;
 140
 141                fixed->fixed_rate = rate;
 142                fixed->hw.init = &init_data;
 143
 144                init_data.name = path;
 145                init_data.ops = &clk_fixed_rate_ops;
 146
 147                ret = devm_clk_hw_register(dev, &fixed->hw);
 148                if (ret)
 149                        return ret;
 150        }
 151        of_node_put(node);
 152
 153        if (add_factor) {
 154                factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
 155                if (!factor)
 156                        return -EINVAL;
 157
 158                factor->mult = factor->div = 1;
 159                factor->hw.init = &init_data;
 160
 161                init_data.name = name;
 162                init_data.parent_names = &path;
 163                init_data.num_parents = 1;
 164                init_data.flags = 0;
 165                init_data.ops = &clk_fixed_factor_ops;
 166
 167                ret = devm_clk_hw_register(dev, &factor->hw);
 168                if (ret)
 169                        return ret;
 170        }
 171
 172        return 0;
 173}
 174
 175int qcom_cc_register_board_clk(struct device *dev, const char *path,
 176                               const char *name, unsigned long rate)
 177{
 178        bool add_factor = true;
 179
 180        /*
 181         * TODO: The RPM clock driver currently does not support the xo clock.
 182         * When xo is added to the RPM clock driver, we should change this
 183         * function to skip registration of xo factor clocks.
 184         */
 185
 186        return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
 187}
 188EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk);
 189
 190int qcom_cc_register_sleep_clk(struct device *dev)
 191{
 192        return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src",
 193                                           32768, true);
 194}
 195EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
 196
 197/* Drop 'protected-clocks' from the list of clocks to register */
 198static void qcom_cc_drop_protected(struct device *dev, struct qcom_cc *cc)
 199{
 200        struct device_node *np = dev->of_node;
 201        struct property *prop;
 202        const __be32 *p;
 203        u32 i;
 204
 205        of_property_for_each_u32(np, "protected-clocks", prop, p, i) {
 206                if (i >= cc->num_rclks)
 207                        continue;
 208
 209                cc->rclks[i] = NULL;
 210        }
 211}
 212
 213static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec,
 214                                         void *data)
 215{
 216        struct qcom_cc *cc = data;
 217        unsigned int idx = clkspec->args[0];
 218
 219        if (idx >= cc->num_rclks) {
 220                pr_err("%s: invalid index %u\n", __func__, idx);
 221                return ERR_PTR(-EINVAL);
 222        }
 223
 224        return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL;
 225}
 226
 227int qcom_cc_really_probe(struct platform_device *pdev,
 228                         const struct qcom_cc_desc *desc, struct regmap *regmap)
 229{
 230        int i, ret;
 231        struct device *dev = &pdev->dev;
 232        struct qcom_reset_controller *reset;
 233        struct qcom_cc *cc;
 234        struct gdsc_desc *scd;
 235        size_t num_clks = desc->num_clks;
 236        struct clk_regmap **rclks = desc->clks;
 237        size_t num_clk_hws = desc->num_clk_hws;
 238        struct clk_hw **clk_hws = desc->clk_hws;
 239
 240        cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
 241        if (!cc)
 242                return -ENOMEM;
 243
 244        reset = &cc->reset;
 245        reset->rcdev.of_node = dev->of_node;
 246        reset->rcdev.ops = &qcom_reset_ops;
 247        reset->rcdev.owner = dev->driver->owner;
 248        reset->rcdev.nr_resets = desc->num_resets;
 249        reset->regmap = regmap;
 250        reset->reset_map = desc->resets;
 251
 252        ret = devm_reset_controller_register(dev, &reset->rcdev);
 253        if (ret)
 254                return ret;
 255
 256        if (desc->gdscs && desc->num_gdscs) {
 257                scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
 258                if (!scd)
 259                        return -ENOMEM;
 260                scd->dev = dev;
 261                scd->scs = desc->gdscs;
 262                scd->num = desc->num_gdscs;
 263                ret = gdsc_register(scd, &reset->rcdev, regmap);
 264                if (ret)
 265                        return ret;
 266                ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister,
 267                                               scd);
 268                if (ret)
 269                        return ret;
 270        }
 271
 272        cc->rclks = rclks;
 273        cc->num_rclks = num_clks;
 274
 275        qcom_cc_drop_protected(dev, cc);
 276
 277        for (i = 0; i < num_clk_hws; i++) {
 278                ret = devm_clk_hw_register(dev, clk_hws[i]);
 279                if (ret)
 280                        return ret;
 281        }
 282
 283        for (i = 0; i < num_clks; i++) {
 284                if (!rclks[i])
 285                        continue;
 286
 287                ret = devm_clk_register_regmap(dev, rclks[i]);
 288                if (ret)
 289                        return ret;
 290        }
 291
 292        ret = devm_of_clk_add_hw_provider(dev, qcom_cc_clk_hw_get, cc);
 293        if (ret)
 294                return ret;
 295
 296        return 0;
 297}
 298EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
 299
 300int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
 301{
 302        struct regmap *regmap;
 303
 304        regmap = qcom_cc_map(pdev, desc);
 305        if (IS_ERR(regmap))
 306                return PTR_ERR(regmap);
 307
 308        return qcom_cc_really_probe(pdev, desc, regmap);
 309}
 310EXPORT_SYMBOL_GPL(qcom_cc_probe);
 311
 312int qcom_cc_probe_by_index(struct platform_device *pdev, int index,
 313                           const struct qcom_cc_desc *desc)
 314{
 315        struct regmap *regmap;
 316        struct resource *res;
 317        void __iomem *base;
 318
 319        res = platform_get_resource(pdev, IORESOURCE_MEM, index);
 320        base = devm_ioremap_resource(&pdev->dev, res);
 321        if (IS_ERR(base))
 322                return -ENOMEM;
 323
 324        regmap = devm_regmap_init_mmio(&pdev->dev, base, desc->config);
 325        if (IS_ERR(regmap))
 326                return PTR_ERR(regmap);
 327
 328        return qcom_cc_really_probe(pdev, desc, regmap);
 329}
 330EXPORT_SYMBOL_GPL(qcom_cc_probe_by_index);
 331
 332MODULE_LICENSE("GPL v2");
 333