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

clk-tegra210-emc.c (9126B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2015-2020, NVIDIA CORPORATION.  All rights reserved.
      4 */
      5
      6#include <linux/bitfield.h>
      7#include <linux/clk.h>
      8#include <linux/clk-provider.h>
      9#include <linux/clk/tegra.h>
     10#include <linux/device.h>
     11#include <linux/module.h>
     12#include <linux/io.h>
     13#include <linux/slab.h>
     14
     15#include "clk.h"
     16
     17#define CLK_SOURCE_EMC 0x19c
     18#define  CLK_SOURCE_EMC_2X_CLK_SRC GENMASK(31, 29)
     19#define  CLK_SOURCE_EMC_MC_EMC_SAME_FREQ BIT(16)
     20#define  CLK_SOURCE_EMC_2X_CLK_DIVISOR GENMASK(7, 0)
     21
     22#define CLK_SRC_PLLM 0
     23#define CLK_SRC_PLLC 1
     24#define CLK_SRC_PLLP 2
     25#define CLK_SRC_CLK_M 3
     26#define CLK_SRC_PLLM_UD 4
     27#define CLK_SRC_PLLMB_UD 5
     28#define CLK_SRC_PLLMB 6
     29#define CLK_SRC_PLLP_UD 7
     30
     31struct tegra210_clk_emc {
     32	struct clk_hw hw;
     33	void __iomem *regs;
     34
     35	struct tegra210_clk_emc_provider *provider;
     36
     37	struct clk *parents[8];
     38};
     39
     40static inline struct tegra210_clk_emc *
     41to_tegra210_clk_emc(struct clk_hw *hw)
     42{
     43	return container_of(hw, struct tegra210_clk_emc, hw);
     44}
     45
     46static const char *tegra210_clk_emc_parents[] = {
     47	"pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_mb_ud",
     48	"pll_mb", "pll_p_ud",
     49};
     50
     51static u8 tegra210_clk_emc_get_parent(struct clk_hw *hw)
     52{
     53	struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
     54	u32 value;
     55	u8 src;
     56
     57	value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
     58	src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, value);
     59
     60	return src;
     61}
     62
     63static unsigned long tegra210_clk_emc_recalc_rate(struct clk_hw *hw,
     64						  unsigned long parent_rate)
     65{
     66	struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
     67	u32 value, div;
     68
     69	/*
     70	 * CCF assumes that neither the parent nor its rate will change during
     71	 * ->set_rate(), so the parent rate passed in here was cached from the
     72	 * parent before the ->set_rate() call.
     73	 *
     74	 * This can lead to wrong results being reported for the EMC clock if
     75	 * the parent and/or parent rate have changed as part of the EMC rate
     76	 * change sequence. Fix this by overriding the parent clock with what
     77	 * we know to be the correct value after the rate change.
     78	 */
     79	parent_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
     80
     81	value = readl_relaxed(emc->regs + CLK_SOURCE_EMC);
     82
     83	div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, value);
     84	div += 2;
     85
     86	return DIV_ROUND_UP(parent_rate * 2, div);
     87}
     88
     89static long tegra210_clk_emc_round_rate(struct clk_hw *hw, unsigned long rate,
     90					unsigned long *prate)
     91{
     92	struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
     93	struct tegra210_clk_emc_provider *provider = emc->provider;
     94	unsigned int i;
     95
     96	if (!provider || !provider->configs || provider->num_configs == 0)
     97		return clk_hw_get_rate(hw);
     98
     99	for (i = 0; i < provider->num_configs; i++) {
    100		if (provider->configs[i].rate >= rate)
    101			return provider->configs[i].rate;
    102	}
    103
    104	return provider->configs[i - 1].rate;
    105}
    106
    107static struct clk *tegra210_clk_emc_find_parent(struct tegra210_clk_emc *emc,
    108						u8 index)
    109{
    110	struct clk_hw *parent = clk_hw_get_parent_by_index(&emc->hw, index);
    111	const char *name = clk_hw_get_name(parent);
    112
    113	/* XXX implement cache? */
    114
    115	return __clk_lookup(name);
    116}
    117
    118static int tegra210_clk_emc_set_rate(struct clk_hw *hw, unsigned long rate,
    119				     unsigned long parent_rate)
    120{
    121	struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
    122	struct tegra210_clk_emc_provider *provider = emc->provider;
    123	struct tegra210_clk_emc_config *config;
    124	struct device *dev = provider->dev;
    125	struct clk_hw *old, *new, *parent;
    126	u8 old_idx, new_idx, index;
    127	struct clk *clk;
    128	unsigned int i;
    129	int err;
    130
    131	if (!provider->configs || provider->num_configs == 0)
    132		return -EINVAL;
    133
    134	for (i = 0; i < provider->num_configs; i++) {
    135		if (provider->configs[i].rate >= rate) {
    136			config = &provider->configs[i];
    137			break;
    138		}
    139	}
    140
    141	if (i == provider->num_configs)
    142		config = &provider->configs[i - 1];
    143
    144	old_idx = tegra210_clk_emc_get_parent(hw);
    145	new_idx = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
    146
    147	old = clk_hw_get_parent_by_index(hw, old_idx);
    148	new = clk_hw_get_parent_by_index(hw, new_idx);
    149
    150	/* if the rate has changed... */
    151	if (config->parent_rate != clk_hw_get_rate(old)) {
    152		/* ... but the clock source remains the same ... */
    153		if (new_idx == old_idx) {
    154			/* ... switch to the alternative clock source. */
    155			switch (new_idx) {
    156			case CLK_SRC_PLLM:
    157				new_idx = CLK_SRC_PLLMB;
    158				break;
    159
    160			case CLK_SRC_PLLM_UD:
    161				new_idx = CLK_SRC_PLLMB_UD;
    162				break;
    163
    164			case CLK_SRC_PLLMB_UD:
    165				new_idx = CLK_SRC_PLLM_UD;
    166				break;
    167
    168			case CLK_SRC_PLLMB:
    169				new_idx = CLK_SRC_PLLM;
    170				break;
    171			}
    172
    173			/*
    174			 * This should never happen because we can't deal with
    175			 * it.
    176			 */
    177			if (WARN_ON(new_idx == old_idx))
    178				return -EINVAL;
    179
    180			new = clk_hw_get_parent_by_index(hw, new_idx);
    181		}
    182
    183		index = new_idx;
    184		parent = new;
    185	} else {
    186		index = old_idx;
    187		parent = old;
    188	}
    189
    190	clk = tegra210_clk_emc_find_parent(emc, index);
    191	if (IS_ERR(clk)) {
    192		err = PTR_ERR(clk);
    193		dev_err(dev, "failed to get parent clock for index %u: %d\n",
    194			index, err);
    195		return err;
    196	}
    197
    198	/* set the new parent clock to the required rate */
    199	if (clk_get_rate(clk) != config->parent_rate) {
    200		err = clk_set_rate(clk, config->parent_rate);
    201		if (err < 0) {
    202			dev_err(dev, "failed to set rate %lu Hz for %pC: %d\n",
    203				config->parent_rate, clk, err);
    204			return err;
    205		}
    206	}
    207
    208	/* enable the new parent clock */
    209	if (parent != old) {
    210		err = clk_prepare_enable(clk);
    211		if (err < 0) {
    212			dev_err(dev, "failed to enable parent clock %pC: %d\n",
    213				clk, err);
    214			return err;
    215		}
    216	}
    217
    218	/* update the EMC source configuration to reflect the new parent */
    219	config->value &= ~CLK_SOURCE_EMC_2X_CLK_SRC;
    220	config->value |= FIELD_PREP(CLK_SOURCE_EMC_2X_CLK_SRC, index);
    221
    222	/*
    223	 * Finally, switch the EMC programming with both old and new parent
    224	 * clocks enabled.
    225	 */
    226	err = provider->set_rate(dev, config);
    227	if (err < 0) {
    228		dev_err(dev, "failed to set EMC rate to %lu Hz: %d\n", rate,
    229			err);
    230
    231		/*
    232		 * If we're unable to switch to the new EMC frequency, we no
    233		 * longer need the new parent to be enabled.
    234		 */
    235		if (parent != old)
    236			clk_disable_unprepare(clk);
    237
    238		return err;
    239	}
    240
    241	/* reparent to new parent clock and disable the old parent clock */
    242	if (parent != old) {
    243		clk = tegra210_clk_emc_find_parent(emc, old_idx);
    244		if (IS_ERR(clk)) {
    245			err = PTR_ERR(clk);
    246			dev_err(dev,
    247				"failed to get parent clock for index %u: %d\n",
    248				old_idx, err);
    249			return err;
    250		}
    251
    252		clk_hw_reparent(hw, parent);
    253		clk_disable_unprepare(clk);
    254	}
    255
    256	return err;
    257}
    258
    259static const struct clk_ops tegra210_clk_emc_ops = {
    260	.get_parent = tegra210_clk_emc_get_parent,
    261	.recalc_rate = tegra210_clk_emc_recalc_rate,
    262	.round_rate = tegra210_clk_emc_round_rate,
    263	.set_rate = tegra210_clk_emc_set_rate,
    264};
    265
    266struct clk *tegra210_clk_register_emc(struct device_node *np,
    267				      void __iomem *regs)
    268{
    269	struct tegra210_clk_emc *emc;
    270	struct clk_init_data init;
    271	struct clk *clk;
    272
    273	emc = kzalloc(sizeof(*emc), GFP_KERNEL);
    274	if (!emc)
    275		return ERR_PTR(-ENOMEM);
    276
    277	emc->regs = regs;
    278
    279	init.name = "emc";
    280	init.ops = &tegra210_clk_emc_ops;
    281	init.flags = CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE;
    282	init.parent_names = tegra210_clk_emc_parents;
    283	init.num_parents = ARRAY_SIZE(tegra210_clk_emc_parents);
    284	emc->hw.init = &init;
    285
    286	clk = clk_register(NULL, &emc->hw);
    287	if (IS_ERR(clk)) {
    288		kfree(emc);
    289		return clk;
    290	}
    291
    292	return clk;
    293}
    294
    295int tegra210_clk_emc_attach(struct clk *clk,
    296			    struct tegra210_clk_emc_provider *provider)
    297{
    298	struct clk_hw *hw = __clk_get_hw(clk);
    299	struct tegra210_clk_emc *emc = to_tegra210_clk_emc(hw);
    300	struct device *dev = provider->dev;
    301	unsigned int i;
    302	int err;
    303
    304	if (!try_module_get(provider->owner))
    305		return -ENODEV;
    306
    307	for (i = 0; i < provider->num_configs; i++) {
    308		struct tegra210_clk_emc_config *config = &provider->configs[i];
    309		struct clk_hw *parent;
    310		bool same_freq;
    311		u8 div, src;
    312
    313		div = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_DIVISOR, config->value);
    314		src = FIELD_GET(CLK_SOURCE_EMC_2X_CLK_SRC, config->value);
    315
    316		/* do basic sanity checking on the EMC timings */
    317		if (div & 0x1) {
    318			dev_err(dev, "invalid odd divider %u for rate %lu Hz\n",
    319				div, config->rate);
    320			err = -EINVAL;
    321			goto put;
    322		}
    323
    324		same_freq = config->value & CLK_SOURCE_EMC_MC_EMC_SAME_FREQ;
    325
    326		if (same_freq != config->same_freq) {
    327			dev_err(dev,
    328				"ambiguous EMC to MC ratio for rate %lu Hz\n",
    329				config->rate);
    330			err = -EINVAL;
    331			goto put;
    332		}
    333
    334		parent = clk_hw_get_parent_by_index(hw, src);
    335		config->parent = src;
    336
    337		if (src == CLK_SRC_PLLM || src == CLK_SRC_PLLM_UD) {
    338			config->parent_rate = config->rate * (1 + div / 2);
    339		} else {
    340			unsigned long rate = config->rate * (1 + div / 2);
    341
    342			config->parent_rate = clk_hw_get_rate(parent);
    343
    344			if (config->parent_rate != rate) {
    345				dev_err(dev,
    346					"rate %lu Hz does not match input\n",
    347					config->rate);
    348				err = -EINVAL;
    349				goto put;
    350			}
    351		}
    352	}
    353
    354	emc->provider = provider;
    355
    356	return 0;
    357
    358put:
    359	module_put(provider->owner);
    360	return err;
    361}
    362EXPORT_SYMBOL_GPL(tegra210_clk_emc_attach);
    363
    364void tegra210_clk_emc_detach(struct clk *clk)
    365{
    366	struct tegra210_clk_emc *emc = to_tegra210_clk_emc(__clk_get_hw(clk));
    367
    368	module_put(emc->provider->owner);
    369	emc->provider = NULL;
    370}
    371EXPORT_SYMBOL_GPL(tegra210_clk_emc_detach);