cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

common.c (7900B)


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