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-tegra20-emc.c (6629B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Based on drivers/clk/tegra/clk-emc.c
      4 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
      5 *
      6 * Author: Dmitry Osipenko <digetx@gmail.com>
      7 * Copyright (C) 2019 GRATE-DRIVER project
      8 */
      9
     10#define pr_fmt(fmt)	"tegra-emc-clk: " fmt
     11
     12#include <linux/bits.h>
     13#include <linux/clk-provider.h>
     14#include <linux/clk/tegra.h>
     15#include <linux/err.h>
     16#include <linux/export.h>
     17#include <linux/io.h>
     18#include <linux/kernel.h>
     19#include <linux/slab.h>
     20
     21#include "clk.h"
     22
     23#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK	GENMASK(7, 0)
     24#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK		GENMASK(31, 30)
     25#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT		30
     26
     27#define MC_EMC_SAME_FREQ	BIT(16)
     28#define USE_PLLM_UD		BIT(29)
     29
     30#define EMC_SRC_PLL_M		0
     31#define EMC_SRC_PLL_C		1
     32#define EMC_SRC_PLL_P		2
     33#define EMC_SRC_CLK_M		3
     34
     35static const char * const emc_parent_clk_names[] = {
     36	"pll_m", "pll_c", "pll_p", "clk_m",
     37};
     38
     39struct tegra_clk_emc {
     40	struct clk_hw hw;
     41	void __iomem *reg;
     42	bool mc_same_freq;
     43	bool want_low_jitter;
     44
     45	tegra20_clk_emc_round_cb *round_cb;
     46	void *cb_arg;
     47};
     48
     49static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw)
     50{
     51	return container_of(hw, struct tegra_clk_emc, hw);
     52}
     53
     54static unsigned long emc_recalc_rate(struct clk_hw *hw,
     55				     unsigned long parent_rate)
     56{
     57	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
     58	u32 val, div;
     59
     60	val = readl_relaxed(emc->reg);
     61	div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
     62
     63	return DIV_ROUND_UP(parent_rate * 2, div + 2);
     64}
     65
     66static u8 emc_get_parent(struct clk_hw *hw)
     67{
     68	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
     69
     70	return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
     71}
     72
     73static int emc_set_parent(struct clk_hw *hw, u8 index)
     74{
     75	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
     76	u32 val, div;
     77
     78	val = readl_relaxed(emc->reg);
     79	val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
     80	val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
     81
     82	div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
     83
     84	if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
     85		val |= USE_PLLM_UD;
     86	else
     87		val &= ~USE_PLLM_UD;
     88
     89	if (emc->mc_same_freq)
     90		val |= MC_EMC_SAME_FREQ;
     91	else
     92		val &= ~MC_EMC_SAME_FREQ;
     93
     94	writel_relaxed(val, emc->reg);
     95
     96	fence_udelay(1, emc->reg);
     97
     98	return 0;
     99}
    100
    101static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
    102			unsigned long parent_rate)
    103{
    104	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
    105	unsigned int index;
    106	u32 val, div;
    107
    108	div = div_frac_get(rate, parent_rate, 8, 1, 0);
    109
    110	val = readl_relaxed(emc->reg);
    111	val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
    112	val |= div;
    113
    114	index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
    115
    116	if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
    117		val |= USE_PLLM_UD;
    118	else
    119		val &= ~USE_PLLM_UD;
    120
    121	if (emc->mc_same_freq)
    122		val |= MC_EMC_SAME_FREQ;
    123	else
    124		val &= ~MC_EMC_SAME_FREQ;
    125
    126	writel_relaxed(val, emc->reg);
    127
    128	fence_udelay(1, emc->reg);
    129
    130	return 0;
    131}
    132
    133static int emc_set_rate_and_parent(struct clk_hw *hw,
    134				   unsigned long rate,
    135				   unsigned long parent_rate,
    136				   u8 index)
    137{
    138	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
    139	u32 val, div;
    140
    141	div = div_frac_get(rate, parent_rate, 8, 1, 0);
    142
    143	val = readl_relaxed(emc->reg);
    144
    145	val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
    146	val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
    147
    148	val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
    149	val |= div;
    150
    151	if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
    152		val |= USE_PLLM_UD;
    153	else
    154		val &= ~USE_PLLM_UD;
    155
    156	if (emc->mc_same_freq)
    157		val |= MC_EMC_SAME_FREQ;
    158	else
    159		val &= ~MC_EMC_SAME_FREQ;
    160
    161	writel_relaxed(val, emc->reg);
    162
    163	fence_udelay(1, emc->reg);
    164
    165	return 0;
    166}
    167
    168static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
    169{
    170	struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
    171	struct clk_hw *parent_hw;
    172	unsigned long divided_rate;
    173	unsigned long parent_rate;
    174	unsigned int i;
    175	long emc_rate;
    176	int div;
    177
    178	emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate,
    179				 emc->cb_arg);
    180	if (emc_rate < 0)
    181		return emc_rate;
    182
    183	for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
    184		parent_hw = clk_hw_get_parent_by_index(hw, i);
    185
    186		if (req->best_parent_hw == parent_hw)
    187			parent_rate = req->best_parent_rate;
    188		else
    189			parent_rate = clk_hw_get_rate(parent_hw);
    190
    191		if (emc_rate > parent_rate)
    192			continue;
    193
    194		div = div_frac_get(emc_rate, parent_rate, 8, 1, 0);
    195		divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2);
    196
    197		if (divided_rate != emc_rate)
    198			continue;
    199
    200		req->best_parent_rate = parent_rate;
    201		req->best_parent_hw = parent_hw;
    202		req->rate = emc_rate;
    203		break;
    204	}
    205
    206	if (i == ARRAY_SIZE(emc_parent_clk_names)) {
    207		pr_err_once("can't find parent for rate %lu emc_rate %lu\n",
    208			    req->rate, emc_rate);
    209		return -EINVAL;
    210	}
    211
    212	return 0;
    213}
    214
    215static const struct clk_ops tegra_clk_emc_ops = {
    216	.recalc_rate = emc_recalc_rate,
    217	.get_parent = emc_get_parent,
    218	.set_parent = emc_set_parent,
    219	.set_rate = emc_set_rate,
    220	.set_rate_and_parent = emc_set_rate_and_parent,
    221	.determine_rate = emc_determine_rate,
    222};
    223
    224void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
    225					void *cb_arg)
    226{
    227	struct clk *clk = __clk_lookup("emc");
    228	struct tegra_clk_emc *emc;
    229	struct clk_hw *hw;
    230
    231	if (clk) {
    232		hw = __clk_get_hw(clk);
    233		emc = to_tegra_clk_emc(hw);
    234
    235		emc->round_cb = round_cb;
    236		emc->cb_arg = cb_arg;
    237	}
    238}
    239EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
    240
    241bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
    242{
    243	return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
    244}
    245
    246struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter)
    247{
    248	struct tegra_clk_emc *emc;
    249	struct clk_init_data init;
    250	struct clk *clk;
    251
    252	emc = kzalloc(sizeof(*emc), GFP_KERNEL);
    253	if (!emc)
    254		return NULL;
    255
    256	/*
    257	 * EMC stands for External Memory Controller.
    258	 *
    259	 * We don't want EMC clock to be disabled ever by gating its
    260	 * parent and whatnot because system is busted immediately in that
    261	 * case, hence the clock is marked as critical.
    262	 */
    263	init.name = "emc";
    264	init.ops = &tegra_clk_emc_ops;
    265	init.flags = CLK_IS_CRITICAL;
    266	init.parent_names = emc_parent_clk_names;
    267	init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
    268
    269	emc->reg = ioaddr;
    270	emc->hw.init = &init;
    271	emc->want_low_jitter = low_jitter;
    272
    273	clk = clk_register(NULL, &emc->hw);
    274	if (IS_ERR(clk)) {
    275		kfree(emc);
    276		return NULL;
    277	}
    278
    279	return clk;
    280}
    281
    282int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
    283{
    284	struct tegra_clk_emc *emc;
    285	struct clk_hw *hw;
    286
    287	if (!emc_clk)
    288		return -EINVAL;
    289
    290	hw = __clk_get_hw(emc_clk);
    291	emc = to_tegra_clk_emc(hw);
    292	emc->mc_same_freq = same;
    293
    294	return 0;
    295}
    296EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);