aboutsummaryrefslogtreecommitdiffstats
path: root/include/os/linux/clk.c
diff options
context:
space:
mode:
Diffstat (limited to 'include/os/linux/clk.c')
-rw-r--r--include/os/linux/clk.c286
1 files changed, 0 insertions, 286 deletions
diff --git a/include/os/linux/clk.c b/include/os/linux/clk.c
deleted file mode 100644
index e9796ea..0000000
--- a/include/os/linux/clk.c
+++ /dev/null
@@ -1,286 +0,0 @@
1/*
2 * Linux clock support
3 *
4 * Copyright (c) 2017-2019, NVIDIA CORPORATION. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <linux/clk.h>
20
21#include <soc/tegra/tegra-dvfs.h>
22#include <soc/tegra/tegra-bpmp-dvfs.h>
23
24#include "clk.h"
25#include "os_linux.h"
26#include "platform_gk20a.h"
27
28#include <nvgpu/gk20a.h>
29#include <nvgpu/clk_arb.h>
30
31#define HZ_TO_MHZ(x) ((x) / 1000000)
32
33static unsigned long nvgpu_linux_clk_get_rate(struct gk20a *g, u32 api_domain)
34{
35 struct gk20a_platform *platform = gk20a_get_platform(dev_from_gk20a(g));
36 unsigned long ret;
37
38 switch (api_domain) {
39 case CTRL_CLK_DOMAIN_GPCCLK:
40 if (g->clk.tegra_clk)
41 ret = clk_get_rate(g->clk.tegra_clk);
42 else
43 ret = clk_get_rate(platform->clk[0]);
44 break;
45 case CTRL_CLK_DOMAIN_PWRCLK:
46 ret = clk_get_rate(platform->clk[1]);
47 break;
48 default:
49 nvgpu_err(g, "unknown clock: %u", api_domain);
50 ret = 0;
51 break;
52 }
53
54 return ret;
55}
56
57static int nvgpu_linux_clk_set_rate(struct gk20a *g,
58 u32 api_domain, unsigned long rate)
59{
60 struct gk20a_platform *platform = gk20a_get_platform(dev_from_gk20a(g));
61 int ret;
62
63 switch (api_domain) {
64 case CTRL_CLK_DOMAIN_GPCCLK:
65 if (g->clk.tegra_clk)
66 ret = clk_set_rate(g->clk.tegra_clk, rate);
67 else
68 ret = clk_set_rate(platform->clk[0], rate);
69 break;
70 case CTRL_CLK_DOMAIN_PWRCLK:
71 ret = clk_set_rate(platform->clk[1], rate);
72 break;
73 default:
74 nvgpu_err(g, "unknown clock: %u", api_domain);
75 ret = -EINVAL;
76 break;
77 }
78
79 return ret;
80}
81
82static unsigned long nvgpu_linux_get_fmax_at_vmin_safe(struct gk20a *g)
83{
84 struct gk20a_platform *platform = gk20a_get_platform(dev_from_gk20a(g));
85
86 /*
87 * On Tegra platforms with GPCPLL bus (gbus) GPU tegra_clk clock exposed
88 * to frequency governor is a shared user on the gbus. The latter can be
89 * accessed as GPU clock parent, and incorporate DVFS related data.
90 */
91 if (g->clk.tegra_clk)
92 return tegra_dvfs_get_fmax_at_vmin_safe_t(
93 g->clk.tegra_clk_parent);
94
95 if (platform->maxmin_clk_id)
96 return tegra_bpmp_dvfs_get_fmax_at_vmin(
97 platform->maxmin_clk_id);
98
99 return 0;
100}
101
102static u32 nvgpu_linux_get_ref_clock_rate(struct gk20a *g)
103{
104 struct clk *c;
105
106 c = clk_get_sys("gpu_ref", "gpu_ref");
107 if (IS_ERR(c)) {
108 nvgpu_err(g, "failed to get GPCPLL reference clock");
109 return 0;
110 }
111
112 return clk_get_rate(c);
113}
114
115static int nvgpu_linux_predict_mv_at_hz_cur_tfloor(struct clk_gk20a *clk,
116 unsigned long rate)
117{
118 return tegra_dvfs_predict_mv_at_hz_cur_tfloor(
119 clk->tegra_clk_parent, rate);
120}
121
122static unsigned long nvgpu_linux_get_maxrate(struct gk20a *g, u32 api_domain)
123{
124 int ret;
125 u16 min_mhz, max_mhz;
126
127 switch (api_domain) {
128 case CTRL_CLK_DOMAIN_GPCCLK:
129 ret = tegra_dvfs_get_maxrate(g->clk.tegra_clk_parent);
130 /* If dvfs not supported */
131 if (ret == 0) {
132 int err = nvgpu_clk_arb_get_arbiter_clk_range(g,
133 NVGPU_CLK_DOMAIN_GPCCLK,
134 &min_mhz, &max_mhz);
135 if (err == 0) {
136 ret = max_mhz * 1000000L;
137 }
138 }
139 break;
140 default:
141 nvgpu_err(g, "unknown clock: %u", api_domain);
142 ret = 0;
143 break;
144 }
145
146 return ret;
147}
148
149/*
150 * This API is used to return a list of supported frequencies by igpu.
151 * Set *num_points as 0 to get the size of the freqs list, returned
152 * by *num_points itself. freqs array must be provided by caller.
153 * If *num_points is non-zero, then freqs array size must atleast
154 * equal *num_points.
155 */
156static int nvgpu_linux_clk_get_f_points(struct gk20a *g,
157 u32 api_domain, u32 *num_points, u16 *freqs)
158{
159 struct device *dev = dev_from_gk20a(g);
160 struct gk20a_platform *platform = gk20a_get_platform(dev);
161 unsigned long *gpu_freq_table;
162 int ret = 0;
163 int num_supported_freq = 0;
164 u32 i;
165
166 switch (api_domain) {
167 case CTRL_CLK_DOMAIN_GPCCLK:
168 ret = platform->get_clk_freqs(dev, &gpu_freq_table,
169 &num_supported_freq);
170
171 if (ret) {
172 return ret;
173 }
174
175 if (num_points == NULL) {
176 return -EINVAL;
177 }
178
179 if (*num_points != 0U) {
180 if (freqs == NULL || (*num_points > (u32)num_supported_freq)) {
181 return -EINVAL;
182 }
183 }
184
185 if (*num_points == 0) {
186 *num_points = num_supported_freq;
187 } else {
188 for (i = 0; i < *num_points; i++) {
189 freqs[i] = HZ_TO_MHZ(gpu_freq_table[i]);
190 }
191 }
192 break;
193 default:
194 nvgpu_err(g, "unknown clock: %u", api_domain);
195 ret = -EINVAL;
196 break;
197 }
198
199 return ret;
200}
201
202static int nvgpu_clk_get_range(struct gk20a *g, u32 api_domain,
203 u16 *min_mhz, u16 *max_mhz)
204{
205 struct device *dev = dev_from_gk20a(g);
206 struct gk20a_platform *platform = gk20a_get_platform(dev);
207 unsigned long *freqs;
208 int num_freqs;
209 int ret;
210
211 switch (api_domain) {
212 case CTRL_CLK_DOMAIN_GPCCLK:
213 ret = platform->get_clk_freqs(dev, &freqs, &num_freqs);
214
215 if (!ret) {
216 *min_mhz = HZ_TO_MHZ(freqs[0]);
217 *max_mhz = HZ_TO_MHZ(freqs[num_freqs - 1]);
218 }
219 break;
220 default:
221 nvgpu_err(g, "unknown clock: %u", api_domain);
222 ret = -EINVAL;
223 break;
224 }
225
226 return ret;
227}
228
229/* rate_target should be passed in as Hz
230 rounded_rate is returned in Hz */
231static int nvgpu_clk_get_round_rate(struct gk20a *g,
232 u32 api_domain, unsigned long rate_target,
233 unsigned long *rounded_rate)
234{
235 struct device *dev = dev_from_gk20a(g);
236 struct gk20a_platform *platform = gk20a_get_platform(dev);
237 unsigned long *freqs;
238 int num_freqs;
239 int i, ret = 0;
240
241 switch (api_domain) {
242 case CTRL_CLK_DOMAIN_GPCCLK:
243 ret = platform->get_clk_freqs(dev, &freqs, &num_freqs);
244
245 for (i = 0; i < num_freqs; ++i) {
246 if (freqs[i] >= rate_target) {
247 *rounded_rate = freqs[i];
248 return 0;
249 }
250 }
251 *rounded_rate = freqs[num_freqs - 1];
252 break;
253 default:
254 nvgpu_err(g, "unknown clock: %u", api_domain);
255 ret = -EINVAL;
256 break;
257 }
258
259 return ret;
260}
261
262static int nvgpu_linux_prepare_enable(struct clk_gk20a *clk)
263{
264 return clk_prepare_enable(clk->tegra_clk);
265}
266
267static void nvgpu_linux_disable_unprepare(struct clk_gk20a *clk)
268{
269 clk_disable_unprepare(clk->tegra_clk);
270}
271
272void nvgpu_linux_init_clk_support(struct gk20a *g)
273{
274 g->ops.clk.get_rate = nvgpu_linux_clk_get_rate;
275 g->ops.clk.set_rate = nvgpu_linux_clk_set_rate;
276 g->ops.clk.get_fmax_at_vmin_safe = nvgpu_linux_get_fmax_at_vmin_safe;
277 g->ops.clk.get_ref_clock_rate = nvgpu_linux_get_ref_clock_rate;
278 g->ops.clk.predict_mv_at_hz_cur_tfloor = nvgpu_linux_predict_mv_at_hz_cur_tfloor;
279 g->ops.clk.get_maxrate = nvgpu_linux_get_maxrate;
280 g->ops.clk.prepare_enable = nvgpu_linux_prepare_enable;
281 g->ops.clk.disable_unprepare = nvgpu_linux_disable_unprepare;
282 g->ops.clk.clk_domain_get_f_points = nvgpu_linux_clk_get_f_points;
283 g->ops.clk.get_clk_range = nvgpu_clk_get_range;
284 g->ops.clk.clk_get_round_rate = nvgpu_clk_get_round_rate;
285 g->ops.clk.measure_freq = nvgpu_clk_measure_freq;
286}