brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · 33cc1f7 Raw
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