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