brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · dd74b85 Raw
297 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Based on drivers/clk/tegra/clk-emc.c4 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.5 *6 * Author: Dmitry Osipenko <digetx@gmail.com>7 * Copyright (C) 2019 GRATE-DRIVER project8 */9 10#define pr_fmt(fmt)	"tegra-emc-clk: " fmt11 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		3026 27#define MC_EMC_SAME_FREQ	BIT(16)28#define USE_PLLM_UD		BIT(29)29 30#define EMC_SRC_PLL_M		031#define EMC_SRC_PLL_C		132#define EMC_SRC_PLL_P		233#define EMC_SRC_CLK_M		334 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	else87		val &= ~USE_PLLM_UD;88 89	if (emc->mc_same_freq)90		val |= MC_EMC_SAME_FREQ;91	else92		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	else119		val &= ~USE_PLLM_UD;120 121	if (emc->mc_same_freq)122		val |= MC_EMC_SAME_FREQ;123	else124		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	else154		val &= ~USE_PLLM_UD;155 156	if (emc->mc_same_freq)157		val |= MC_EMC_SAME_FREQ;158	else159		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		else189			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 its260	 * parent and whatnot because system is busted immediately in that261	 * 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);297