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);
297