• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
35 static const char * const emc_parent_clk_names[] = {
36 	"pll_m", "pll_c", "pll_p", "clk_m",
37 };
38 
39 struct 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 
to_tegra_clk_emc(struct clk_hw * hw)49 static 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 
emc_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)54 static 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 
emc_get_parent(struct clk_hw * hw)66 static 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 
emc_set_parent(struct clk_hw * hw,u8 index)73 static 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 
emc_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)101 static 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 
emc_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)133 static 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 
emc_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)168 static 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 
215 static 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 
tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb * round_cb,void * cb_arg)224 void 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 }
239 EXPORT_SYMBOL_GPL(tegra20_clk_set_emc_round_callback);
240 
tegra20_clk_emc_driver_available(struct clk_hw * emc_hw)241 bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
242 {
243 	return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
244 }
245 
tegra20_clk_register_emc(void __iomem * ioaddr,bool low_jitter)246 struct 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 
tegra20_clk_prepare_emc_mc_same_freq(struct clk * emc_clk,bool same)282 int 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 }
296 EXPORT_SYMBOL_GPL(tegra20_clk_prepare_emc_mc_same_freq);
297