linux/drivers/clk/qcom/common.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
   3 *
   4 * This software is licensed under the terms of the GNU General Public
   5 * License version 2, as published by the Free Software Foundation, and
   6 * may be copied, distributed, and modified under those terms.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 */
  13
  14#include <linux/export.h>
  15#include <linux/module.h>
  16#include <linux/regmap.h>
  17#include <linux/platform_device.h>
  18#include <linux/clk-provider.h>
  19#include <linux/reset-controller.h>
  20#include <linux/of.h>
  21
  22#include "common.h"
  23#include "clk-rcg.h"
  24#include "clk-regmap.h"
  25#include "reset.h"
  26#include "gdsc.h"
  27
  28struct qcom_cc {
  29        struct qcom_reset_controller reset;
  30        struct clk_onecell_data data;
  31        struct clk *clks[];
  32};
  33
  34const
  35struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate)
  36{
  37        if (!f)
  38                return NULL;
  39
  40        for (; f->freq; f++)
  41                if (rate <= f->freq)
  42                        return f;
  43
  44        /* Default to our fastest rate */
  45        return f - 1;
  46}
  47EXPORT_SYMBOL_GPL(qcom_find_freq);
  48
  49int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map, u8 src)
  50{
  51        int i, num_parents = clk_hw_get_num_parents(hw);
  52
  53        for (i = 0; i < num_parents; i++)
  54                if (src == map[i].src)
  55                        return i;
  56
  57        return -ENOENT;
  58}
  59EXPORT_SYMBOL_GPL(qcom_find_src_index);
  60
  61struct regmap *
  62qcom_cc_map(struct platform_device *pdev, const struct qcom_cc_desc *desc)
  63{
  64        void __iomem *base;
  65        struct resource *res;
  66        struct device *dev = &pdev->dev;
  67
  68        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  69        base = devm_ioremap_resource(dev, res);
  70        if (IS_ERR(base))
  71                return ERR_CAST(base);
  72
  73        return devm_regmap_init_mmio(dev, base, desc->config);
  74}
  75EXPORT_SYMBOL_GPL(qcom_cc_map);
  76
  77static void qcom_cc_del_clk_provider(void *data)
  78{
  79        of_clk_del_provider(data);
  80}
  81
  82static void qcom_cc_reset_unregister(void *data)
  83{
  84        reset_controller_unregister(data);
  85}
  86
  87static void qcom_cc_gdsc_unregister(void *data)
  88{
  89        gdsc_unregister(data);
  90}
  91
  92/*
  93 * Backwards compatibility with old DTs. Register a pass-through factor 1/1
  94 * clock to translate 'path' clk into 'name' clk and regsiter the 'path'
  95 * clk as a fixed rate clock if it isn't present.
  96 */
  97static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
  98                                       const char *name, unsigned long rate,
  99                                       bool add_factor)
 100{
 101        struct device_node *node = NULL;
 102        struct device_node *clocks_node;
 103        struct clk_fixed_factor *factor;
 104        struct clk_fixed_rate *fixed;
 105        struct clk *clk;
 106        struct clk_init_data init_data = { };
 107
 108        clocks_node = of_find_node_by_path("/clocks");
 109        if (clocks_node)
 110                node = of_find_node_by_name(clocks_node, path);
 111        of_node_put(clocks_node);
 112
 113        if (!node) {
 114                fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
 115                if (!fixed)
 116                        return -EINVAL;
 117
 118                fixed->fixed_rate = rate;
 119                fixed->hw.init = &init_data;
 120
 121                init_data.name = path;
 122                init_data.ops = &clk_fixed_rate_ops;
 123
 124                clk = devm_clk_register(dev, &fixed->hw);
 125                if (IS_ERR(clk))
 126                        return PTR_ERR(clk);
 127        }
 128        of_node_put(node);
 129
 130        if (add_factor) {
 131                factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
 132                if (!factor)
 133                        return -EINVAL;
 134
 135                factor->mult = factor->div = 1;
 136                factor->hw.init = &init_data;
 137
 138                init_data.name = name;
 139                init_data.parent_names = &path;
 140                init_data.num_parents = 1;
 141                init_data.flags = 0;
 142                init_data.ops = &clk_fixed_factor_ops;
 143
 144                clk = devm_clk_register(dev, &factor->hw);
 145                if (IS_ERR(clk))
 146                        return PTR_ERR(clk);
 147        }
 148
 149        return 0;
 150}
 151
 152int qcom_cc_register_board_clk(struct device *dev, const char *path,
 153                               const char *name, unsigned long rate)
 154{
 155        bool add_factor = true;
 156        struct device_node *node;
 157
 158        /* The RPM clock driver will add the factor clock if present */
 159        if (IS_ENABLED(CONFIG_QCOM_RPMCC)) {
 160                node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc");
 161                if (of_device_is_available(node))
 162                        add_factor = false;
 163                of_node_put(node);
 164        }
 165
 166        return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
 167}
 168EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk);
 169
 170int qcom_cc_register_sleep_clk(struct device *dev)
 171{
 172        return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src",
 173                                           32768, true);
 174}
 175EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
 176
 177int qcom_cc_really_probe(struct platform_device *pdev,
 178                         const struct qcom_cc_desc *desc, struct regmap *regmap)
 179{
 180        int i, ret;
 181        struct device *dev = &pdev->dev;
 182        struct clk *clk;
 183        struct clk_onecell_data *data;
 184        struct clk **clks;
 185        struct qcom_reset_controller *reset;
 186        struct qcom_cc *cc;
 187        struct gdsc_desc *scd;
 188        size_t num_clks = desc->num_clks;
 189        struct clk_regmap **rclks = desc->clks;
 190
 191        cc = devm_kzalloc(dev, sizeof(*cc) + sizeof(*clks) * num_clks,
 192                          GFP_KERNEL);
 193        if (!cc)
 194                return -ENOMEM;
 195
 196        clks = cc->clks;
 197        data = &cc->data;
 198        data->clks = clks;
 199        data->clk_num = num_clks;
 200
 201        for (i = 0; i < num_clks; i++) {
 202                if (!rclks[i]) {
 203                        clks[i] = ERR_PTR(-ENOENT);
 204                        continue;
 205                }
 206                clk = devm_clk_register_regmap(dev, rclks[i]);
 207                if (IS_ERR(clk))
 208                        return PTR_ERR(clk);
 209                clks[i] = clk;
 210        }
 211
 212        ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
 213        if (ret)
 214                return ret;
 215
 216        ret = devm_add_action_or_reset(dev, qcom_cc_del_clk_provider,
 217                                       pdev->dev.of_node);
 218
 219        if (ret)
 220                return ret;
 221
 222        reset = &cc->reset;
 223        reset->rcdev.of_node = dev->of_node;
 224        reset->rcdev.ops = &qcom_reset_ops;
 225        reset->rcdev.owner = dev->driver->owner;
 226        reset->rcdev.nr_resets = desc->num_resets;
 227        reset->regmap = regmap;
 228        reset->reset_map = desc->resets;
 229
 230        ret = reset_controller_register(&reset->rcdev);
 231        if (ret)
 232                return ret;
 233
 234        ret = devm_add_action_or_reset(dev, qcom_cc_reset_unregister,
 235                                       &reset->rcdev);
 236
 237        if (ret)
 238                return ret;
 239
 240        if (desc->gdscs && desc->num_gdscs) {
 241                scd = devm_kzalloc(dev, sizeof(*scd), GFP_KERNEL);
 242                if (!scd)
 243                        return -ENOMEM;
 244                scd->dev = dev;
 245                scd->scs = desc->gdscs;
 246                scd->num = desc->num_gdscs;
 247                ret = gdsc_register(scd, &reset->rcdev, regmap);
 248                if (ret)
 249                        return ret;
 250                ret = devm_add_action_or_reset(dev, qcom_cc_gdsc_unregister,
 251                                               scd);
 252                if (ret)
 253                        return ret;
 254        }
 255
 256        return 0;
 257}
 258EXPORT_SYMBOL_GPL(qcom_cc_really_probe);
 259
 260int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc)
 261{
 262        struct regmap *regmap;
 263
 264        regmap = qcom_cc_map(pdev, desc);
 265        if (IS_ERR(regmap))
 266                return PTR_ERR(regmap);
 267
 268        return qcom_cc_really_probe(pdev, desc, regmap);
 269}
 270EXPORT_SYMBOL_GPL(qcom_cc_probe);
 271
 272MODULE_LICENSE("GPL v2");
 273