summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/gk20a/gk20a_scale.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a/gk20a_scale.c')
-rw-r--r--drivers/gpu/nvgpu/gk20a/gk20a_scale.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/gk20a_scale.c b/drivers/gpu/nvgpu/gk20a/gk20a_scale.c
new file mode 100644
index 00000000..d1fd71fe
--- /dev/null
+++ b/drivers/gpu/nvgpu/gk20a/gk20a_scale.c
@@ -0,0 +1,358 @@
1/*
2 * gk20a clock scaling profile
3 *
4 * Copyright (c) 2013-2014, 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/devfreq.h>
20#include <linux/debugfs.h>
21#include <linux/types.h>
22#include <linux/clk.h>
23#include <linux/export.h>
24#include <linux/slab.h>
25#include <linux/clk/tegra.h>
26#include <linux/tegra-soc.h>
27#include <linux/platform_data/tegra_edp.h>
28#include <linux/pm_qos.h>
29
30#include <governor.h>
31
32#include "gk20a.h"
33#include "pmu_gk20a.h"
34#include "clk_gk20a.h"
35#include "gk20a_scale.h"
36
37static ssize_t gk20a_scale_load_show(struct device *dev,
38 struct device_attribute *attr,
39 char *buf)
40{
41 struct platform_device *pdev = to_platform_device(dev);
42 struct gk20a *g = get_gk20a(pdev);
43 u32 busy_time;
44 ssize_t res;
45
46 if (!g->power_on) {
47 busy_time = 0;
48 } else {
49 gk20a_busy(g->dev);
50 gk20a_pmu_load_norm(g, &busy_time);
51 gk20a_idle(g->dev);
52 }
53
54 res = snprintf(buf, PAGE_SIZE, "%u\n", busy_time);
55
56 return res;
57}
58
59static DEVICE_ATTR(load, S_IRUGO, gk20a_scale_load_show, NULL);
60
61/*
62 * gk20a_scale_qos_notify()
63 *
64 * This function is called when the minimum QoS requirement for the device
65 * has changed. The function calls postscaling callback if it is defined.
66 */
67
68static int gk20a_scale_qos_notify(struct notifier_block *nb,
69 unsigned long n, void *p)
70{
71 struct gk20a_scale_profile *profile =
72 container_of(nb, struct gk20a_scale_profile,
73 qos_notify_block);
74 struct gk20a_platform *platform = platform_get_drvdata(profile->pdev);
75 struct gk20a *g = get_gk20a(profile->pdev);
76 unsigned long freq;
77
78 if (!platform->postscale)
79 return NOTIFY_OK;
80
81 /* get the frequency requirement. if devfreq is enabled, check if it
82 * has higher demand than qos */
83 freq = gk20a_clk_round_rate(g, pm_qos_request(platform->qos_id));
84 if (g->devfreq)
85 freq = max(g->devfreq->previous_freq, freq);
86
87 platform->postscale(profile->pdev, freq);
88
89 return NOTIFY_OK;
90}
91
92/*
93 * gk20a_scale_make_freq_table(profile)
94 *
95 * This function initialises the frequency table for the given device profile
96 */
97
98static int gk20a_scale_make_freq_table(struct gk20a_scale_profile *profile)
99{
100 struct gk20a *g = get_gk20a(profile->pdev);
101 unsigned long *freqs;
102 int num_freqs, err;
103
104 /* make sure the clock is available */
105 if (!gk20a_clk_get(g))
106 return -ENOSYS;
107
108 /* get gpu dvfs table */
109 err = tegra_dvfs_get_freqs(clk_get_parent(g->clk.tegra_clk),
110 &freqs, &num_freqs);
111 if (err)
112 return -ENOSYS;
113
114 profile->devfreq_profile.freq_table = (unsigned long *)freqs;
115 profile->devfreq_profile.max_state = num_freqs;
116
117 return 0;
118}
119
120/*
121 * gk20a_scale_target(dev, *freq, flags)
122 *
123 * This function scales the clock
124 */
125
126static int gk20a_scale_target(struct device *dev, unsigned long *freq,
127 u32 flags)
128{
129 struct gk20a *g = get_gk20a(to_platform_device(dev));
130 struct gk20a_platform *platform = dev_get_drvdata(dev);
131 struct gk20a_scale_profile *profile = g->scale_profile;
132 unsigned long rounded_rate = gk20a_clk_round_rate(g, *freq);
133
134 if (gk20a_clk_get_rate(g) == rounded_rate) {
135 *freq = rounded_rate;
136 return 0;
137 }
138
139 gk20a_clk_set_rate(g, rounded_rate);
140 if (platform->postscale)
141 platform->postscale(profile->pdev, rounded_rate);
142 *freq = gk20a_clk_get_rate(g);
143
144 return 0;
145}
146
147/*
148 * update_load_estimate_gpmu(profile)
149 *
150 * Update load estimate using gpmu. The gpmu value is normalised
151 * based on the time it was asked last time.
152 */
153
154static void update_load_estimate_gpmu(struct platform_device *pdev)
155{
156 struct gk20a *g = get_gk20a(pdev);
157 struct gk20a_scale_profile *profile = g->scale_profile;
158 unsigned long dt;
159 u32 busy_time;
160 ktime_t t;
161
162 t = ktime_get();
163 dt = ktime_us_delta(t, profile->last_event_time);
164
165 profile->dev_stat.total_time = dt;
166 profile->last_event_time = t;
167 gk20a_pmu_load_norm(g, &busy_time);
168 profile->dev_stat.busy_time = (busy_time * dt) / 1000;
169}
170
171/*
172 * gk20a_scale_suspend(pdev)
173 *
174 * This function informs devfreq of suspend
175 */
176
177void gk20a_scale_suspend(struct platform_device *pdev)
178{
179 struct gk20a *g = get_gk20a(pdev);
180 struct devfreq *devfreq = g->devfreq;
181
182 if (!devfreq)
183 return;
184
185 devfreq_suspend_device(devfreq);
186}
187
188/*
189 * gk20a_scale_resume(pdev)
190 *
191 * This functions informs devfreq of resume
192 */
193
194void gk20a_scale_resume(struct platform_device *pdev)
195{
196 struct gk20a *g = get_gk20a(pdev);
197 struct devfreq *devfreq = g->devfreq;
198
199 if (!devfreq)
200 return;
201
202 devfreq_resume_device(devfreq);
203}
204
205/*
206 * gk20a_scale_notify(pdev, busy)
207 *
208 * Calling this function informs that the device is idling (..or busy). This
209 * data is used to estimate the current load
210 */
211
212static void gk20a_scale_notify(struct platform_device *pdev, bool busy)
213{
214 struct gk20a_platform *platform = platform_get_drvdata(pdev);
215 struct gk20a *g = get_gk20a(pdev);
216 struct gk20a_scale_profile *profile = g->scale_profile;
217 struct devfreq *devfreq = g->devfreq;
218
219 /* inform edp about new constraint */
220 if (platform->prescale)
221 platform->prescale(pdev);
222
223 /* Is the device profile initialised? */
224 if (!(profile && devfreq))
225 return;
226
227 mutex_lock(&devfreq->lock);
228 profile->dev_stat.busy = busy;
229 update_devfreq(devfreq);
230 mutex_unlock(&devfreq->lock);
231}
232
233void gk20a_scale_notify_idle(struct platform_device *pdev)
234{
235 gk20a_scale_notify(pdev, false);
236
237}
238
239void gk20a_scale_notify_busy(struct platform_device *pdev)
240{
241 gk20a_scale_notify(pdev, true);
242}
243
244/*
245 * gk20a_scale_get_dev_status(dev, *stat)
246 *
247 * This function queries the current device status.
248 */
249
250static int gk20a_scale_get_dev_status(struct device *dev,
251 struct devfreq_dev_status *stat)
252{
253 struct gk20a *g = get_gk20a(to_platform_device(dev));
254 struct gk20a_scale_profile *profile = g->scale_profile;
255
256 /* Make sure there are correct values for the current frequency */
257 profile->dev_stat.current_frequency = gk20a_clk_get_rate(g);
258
259 /* Update load estimate */
260 update_load_estimate_gpmu(to_platform_device(dev));
261
262 /* Copy the contents of the current device status */
263 *stat = profile->dev_stat;
264
265 /* Finally, clear out the local values */
266 profile->dev_stat.total_time = 0;
267 profile->dev_stat.busy_time = 0;
268
269 return 0;
270}
271
272/*
273 * gk20a_scale_init(pdev)
274 */
275
276void gk20a_scale_init(struct platform_device *pdev)
277{
278 struct gk20a_platform *platform = platform_get_drvdata(pdev);
279 struct gk20a *g = platform->g;
280 struct gk20a_scale_profile *profile;
281 int err;
282
283 if (g->scale_profile)
284 return;
285
286 profile = kzalloc(sizeof(*profile), GFP_KERNEL);
287
288 profile->pdev = pdev;
289 profile->dev_stat.busy = false;
290
291 /* Create frequency table */
292 err = gk20a_scale_make_freq_table(profile);
293 if (err || !profile->devfreq_profile.max_state)
294 goto err_get_freqs;
295
296 if (device_create_file(&pdev->dev, &dev_attr_load))
297 goto err_create_sysfs_entry;
298
299 /* Store device profile so we can access it if devfreq governor
300 * init needs that */
301 g->scale_profile = profile;
302
303 if (platform->devfreq_governor) {
304 struct devfreq *devfreq;
305
306 profile->devfreq_profile.initial_freq =
307 profile->devfreq_profile.freq_table[0];
308 profile->devfreq_profile.target = gk20a_scale_target;
309 profile->devfreq_profile.get_dev_status =
310 gk20a_scale_get_dev_status;
311
312 devfreq = devfreq_add_device(&pdev->dev,
313 &profile->devfreq_profile,
314 platform->devfreq_governor, NULL);
315
316 if (IS_ERR(devfreq))
317 devfreq = NULL;
318
319 g->devfreq = devfreq;
320 }
321
322 /* Should we register QoS callback for this device? */
323 if (platform->qos_id < PM_QOS_NUM_CLASSES &&
324 platform->qos_id != PM_QOS_RESERVED &&
325 platform->postscale) {
326 profile->qos_notify_block.notifier_call =
327 &gk20a_scale_qos_notify;
328 pm_qos_add_notifier(platform->qos_id,
329 &profile->qos_notify_block);
330 }
331
332 return;
333
334err_get_freqs:
335 device_remove_file(&pdev->dev, &dev_attr_load);
336err_create_sysfs_entry:
337 kfree(g->scale_profile);
338 g->scale_profile = NULL;
339}
340
341/*
342 * gk20a_scale_hw_init(dev)
343 *
344 * Initialize hardware portion of the device
345 */
346
347void gk20a_scale_hw_init(struct platform_device *pdev)
348{
349 struct gk20a_platform *platform = platform_get_drvdata(pdev);
350 struct gk20a_scale_profile *profile = platform->g->scale_profile;
351
352 /* make sure that scaling has bee initialised */
353 if (!profile)
354 return;
355
356 profile->dev_stat.total_time = 0;
357 profile->last_event_time = ktime_get();
358}