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