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