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