diff options
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c')
-rw-r--r-- | drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c b/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c new file mode 100644 index 00000000..f6b43f50 --- /dev/null +++ b/drivers/gpu/nvgpu/gk20a/gk20a_sysfs.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /* | ||
2 | * drivers/video/tegra/host/gk20a/gk20a_sysfs.c | ||
3 | * | ||
4 | * GK20A Graphics | ||
5 | * | ||
6 | * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
15 | * more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | */ | ||
20 | |||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/pm_runtime.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/fb.h> | ||
25 | |||
26 | #include <mach/clk.h> | ||
27 | |||
28 | #include "gk20a.h" | ||
29 | #include "gr_gk20a.h" | ||
30 | #include "fifo_gk20a.h" | ||
31 | |||
32 | |||
33 | #define PTIMER_FP_FACTOR 1000000 | ||
34 | /* PTIMER_REF_FREQ_HZ corresponds to a period of 32 nanoseconds. 32 ns is | ||
35 | the resolution of ptimer. */ | ||
36 | #define PTIMER_REF_FREQ_HZ 31250000 | ||
37 | |||
38 | |||
39 | static ssize_t elcg_enable_store(struct device *device, | ||
40 | struct device_attribute *attr, const char *buf, size_t count) | ||
41 | { | ||
42 | struct platform_device *ndev = to_platform_device(device); | ||
43 | struct gk20a *g = get_gk20a(ndev); | ||
44 | unsigned long val = 0; | ||
45 | |||
46 | if (kstrtoul(buf, 10, &val) < 0) | ||
47 | return -EINVAL; | ||
48 | |||
49 | gk20a_busy(g->dev); | ||
50 | if (val) { | ||
51 | g->elcg_enabled = true; | ||
52 | gr_gk20a_init_elcg_mode(g, ELCG_AUTO, ENGINE_GR_GK20A); | ||
53 | gr_gk20a_init_elcg_mode(g, ELCG_AUTO, ENGINE_CE2_GK20A); | ||
54 | } else { | ||
55 | g->elcg_enabled = false; | ||
56 | gr_gk20a_init_elcg_mode(g, ELCG_RUN, ENGINE_GR_GK20A); | ||
57 | gr_gk20a_init_elcg_mode(g, ELCG_RUN, ENGINE_CE2_GK20A); | ||
58 | } | ||
59 | gk20a_idle(g->dev); | ||
60 | |||
61 | dev_info(device, "ELCG is %s.\n", g->elcg_enabled ? "enabled" : | ||
62 | "disabled"); | ||
63 | |||
64 | return count; | ||
65 | } | ||
66 | |||
67 | static ssize_t elcg_enable_read(struct device *device, | ||
68 | struct device_attribute *attr, char *buf) | ||
69 | { | ||
70 | struct platform_device *ndev = to_platform_device(device); | ||
71 | struct gk20a *g = get_gk20a(ndev); | ||
72 | |||
73 | return sprintf(buf, "%d\n", g->elcg_enabled ? 1 : 0); | ||
74 | } | ||
75 | |||
76 | static DEVICE_ATTR(elcg_enable, S_IRWXUGO, elcg_enable_read, elcg_enable_store); | ||
77 | |||
78 | static ssize_t blcg_enable_store(struct device *device, | ||
79 | struct device_attribute *attr, const char *buf, size_t count) | ||
80 | { | ||
81 | struct platform_device *ndev = to_platform_device(device); | ||
82 | struct gk20a *g = get_gk20a(ndev); | ||
83 | unsigned long val = 0; | ||
84 | |||
85 | if (kstrtoul(buf, 10, &val) < 0) | ||
86 | return -EINVAL; | ||
87 | |||
88 | if (val) | ||
89 | g->blcg_enabled = true; | ||
90 | else | ||
91 | g->blcg_enabled = false; | ||
92 | |||
93 | gk20a_busy(g->dev); | ||
94 | g->ops.clock_gating.blcg_gr_load_gating_prod(g, g->blcg_enabled); | ||
95 | gk20a_idle(g->dev); | ||
96 | |||
97 | dev_info(device, "BLCG is %s.\n", g->blcg_enabled ? "enabled" : | ||
98 | "disabled"); | ||
99 | |||
100 | return count; | ||
101 | } | ||
102 | |||
103 | static ssize_t blcg_enable_read(struct device *device, | ||
104 | struct device_attribute *attr, char *buf) | ||
105 | { | ||
106 | struct platform_device *ndev = to_platform_device(device); | ||
107 | struct gk20a *g = get_gk20a(ndev); | ||
108 | |||
109 | return sprintf(buf, "%d\n", g->blcg_enabled ? 1 : 0); | ||
110 | } | ||
111 | |||
112 | static DEVICE_ATTR(blcg_enable, S_IRWXUGO, blcg_enable_read, blcg_enable_store); | ||
113 | |||
114 | static ssize_t slcg_enable_store(struct device *device, | ||
115 | struct device_attribute *attr, const char *buf, size_t count) | ||
116 | { | ||
117 | struct platform_device *ndev = to_platform_device(device); | ||
118 | struct gk20a *g = get_gk20a(ndev); | ||
119 | unsigned long val = 0; | ||
120 | |||
121 | if (kstrtoul(buf, 10, &val) < 0) | ||
122 | return -EINVAL; | ||
123 | |||
124 | if (val) | ||
125 | g->slcg_enabled = true; | ||
126 | else | ||
127 | g->slcg_enabled = false; | ||
128 | |||
129 | /* | ||
130 | * TODO: slcg_therm_load_gating is not enabled anywhere during | ||
131 | * init. Therefore, it would be incongruous to add it here. Once | ||
132 | * it is added to init, we should add it here too. | ||
133 | */ | ||
134 | gk20a_busy(g->dev); | ||
135 | g->ops.clock_gating.slcg_gr_load_gating_prod(g, g->slcg_enabled); | ||
136 | g->ops.clock_gating.slcg_perf_load_gating_prod(g, g->slcg_enabled); | ||
137 | gk20a_idle(g->dev); | ||
138 | |||
139 | dev_info(device, "SLCG is %s.\n", g->slcg_enabled ? "enabled" : | ||
140 | "disabled"); | ||
141 | |||
142 | return count; | ||
143 | } | ||
144 | |||
145 | static ssize_t slcg_enable_read(struct device *device, | ||
146 | struct device_attribute *attr, char *buf) | ||
147 | { | ||
148 | struct platform_device *ndev = to_platform_device(device); | ||
149 | struct gk20a *g = get_gk20a(ndev); | ||
150 | |||
151 | return sprintf(buf, "%d\n", g->slcg_enabled ? 1 : 0); | ||
152 | } | ||
153 | |||
154 | static DEVICE_ATTR(slcg_enable, S_IRWXUGO, slcg_enable_read, slcg_enable_store); | ||
155 | |||
156 | static ssize_t ptimer_scale_factor_show(struct device *dev, | ||
157 | struct device_attribute *attr, | ||
158 | char *buf) | ||
159 | { | ||
160 | u32 tsc_freq_hz = clk_get_rate(clk_get_sys(NULL, "clk_m")); | ||
161 | u32 scaling_factor_fp = (u32)(PTIMER_REF_FREQ_HZ) / | ||
162 | ((u32)(tsc_freq_hz) / | ||
163 | (u32)(PTIMER_FP_FACTOR)); | ||
164 | ssize_t res = snprintf(buf, | ||
165 | PAGE_SIZE, | ||
166 | "%u.%u\n", | ||
167 | scaling_factor_fp / PTIMER_FP_FACTOR, | ||
168 | scaling_factor_fp % PTIMER_FP_FACTOR); | ||
169 | |||
170 | return res; | ||
171 | } | ||
172 | |||
173 | static DEVICE_ATTR(ptimer_scale_factor, | ||
174 | S_IRUGO, | ||
175 | ptimer_scale_factor_show, | ||
176 | NULL); | ||
177 | |||
178 | static ssize_t railgate_delay_store(struct device *dev, | ||
179 | struct device_attribute *attr, | ||
180 | const char *buf, size_t count) | ||
181 | { | ||
182 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
183 | int railgate_delay = 0, ret = 0; | ||
184 | |||
185 | if (!platform->can_railgate) { | ||
186 | dev_info(dev, "does not support power-gating\n"); | ||
187 | return count; | ||
188 | } | ||
189 | |||
190 | ret = sscanf(buf, "%d", &railgate_delay); | ||
191 | if (ret == 1 && railgate_delay >= 0) { | ||
192 | struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain); | ||
193 | platform->railgate_delay = railgate_delay; | ||
194 | pm_genpd_set_poweroff_delay(genpd, platform->railgate_delay); | ||
195 | } else | ||
196 | dev_err(dev, "Invalid powergate delay\n"); | ||
197 | |||
198 | return count; | ||
199 | } | ||
200 | static ssize_t railgate_delay_show(struct device *dev, | ||
201 | struct device_attribute *attr, char *buf) | ||
202 | { | ||
203 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
204 | return snprintf(buf, PAGE_SIZE, "%d\n", platform->railgate_delay); | ||
205 | } | ||
206 | static DEVICE_ATTR(railgate_delay, S_IRWXUGO, railgate_delay_show, | ||
207 | railgate_delay_store); | ||
208 | |||
209 | static ssize_t clockgate_delay_store(struct device *dev, | ||
210 | struct device_attribute *attr, | ||
211 | const char *buf, size_t count) | ||
212 | { | ||
213 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
214 | int clockgate_delay = 0, ret = 0; | ||
215 | |||
216 | ret = sscanf(buf, "%d", &clockgate_delay); | ||
217 | if (ret == 1 && clockgate_delay >= 0) { | ||
218 | platform->clockgate_delay = clockgate_delay; | ||
219 | pm_runtime_set_autosuspend_delay(dev, | ||
220 | platform->clockgate_delay); | ||
221 | } else | ||
222 | dev_err(dev, "Invalid clockgate delay\n"); | ||
223 | |||
224 | return count; | ||
225 | } | ||
226 | static ssize_t clockgate_delay_show(struct device *dev, | ||
227 | struct device_attribute *attr, char *buf) | ||
228 | { | ||
229 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
230 | return snprintf(buf, PAGE_SIZE, "%d\n", platform->clockgate_delay); | ||
231 | } | ||
232 | static DEVICE_ATTR(clockgate_delay, S_IRWXUGO, clockgate_delay_show, | ||
233 | clockgate_delay_store); | ||
234 | |||
235 | static ssize_t counters_show(struct device *dev, | ||
236 | struct device_attribute *attr, char *buf) | ||
237 | { | ||
238 | struct platform_device *pdev = to_platform_device(dev); | ||
239 | struct gk20a *g = get_gk20a(pdev); | ||
240 | u32 busy_cycles, total_cycles; | ||
241 | ssize_t res; | ||
242 | |||
243 | gk20a_pmu_get_load_counters(g, &busy_cycles, &total_cycles); | ||
244 | |||
245 | res = snprintf(buf, PAGE_SIZE, "%u %u\n", busy_cycles, total_cycles); | ||
246 | |||
247 | return res; | ||
248 | } | ||
249 | |||
250 | static DEVICE_ATTR(counters, S_IRUGO, counters_show, NULL); | ||
251 | static ssize_t counters_show_reset(struct device *dev, | ||
252 | struct device_attribute *attr, char *buf) | ||
253 | { | ||
254 | ssize_t res = counters_show(dev, attr, buf); | ||
255 | struct platform_device *pdev = to_platform_device(dev); | ||
256 | struct gk20a *g = get_gk20a(pdev); | ||
257 | |||
258 | gk20a_pmu_reset_load_counters(g); | ||
259 | |||
260 | return res; | ||
261 | } | ||
262 | |||
263 | static DEVICE_ATTR(counters_reset, S_IRUGO, counters_show_reset, NULL); | ||
264 | |||
265 | static ssize_t elpg_enable_store(struct device *device, | ||
266 | struct device_attribute *attr, const char *buf, size_t count) | ||
267 | { | ||
268 | struct platform_device *ndev = to_platform_device(device); | ||
269 | struct gk20a *g = get_gk20a(ndev); | ||
270 | unsigned long val = 0; | ||
271 | |||
272 | if (kstrtoul(buf, 10, &val) < 0) | ||
273 | return -EINVAL; | ||
274 | |||
275 | /* | ||
276 | * Since elpg is refcounted, we should not unnecessarily call | ||
277 | * enable/disable if it is already so. | ||
278 | */ | ||
279 | gk20a_channel_busy(g->dev); | ||
280 | if (val && !g->elpg_enabled) { | ||
281 | g->elpg_enabled = true; | ||
282 | gk20a_pmu_enable_elpg(g); | ||
283 | } else if (!val && g->elpg_enabled) { | ||
284 | g->elpg_enabled = false; | ||
285 | gk20a_pmu_disable_elpg(g); | ||
286 | } | ||
287 | gk20a_channel_idle(g->dev); | ||
288 | |||
289 | dev_info(device, "ELPG is %s.\n", g->elpg_enabled ? "enabled" : | ||
290 | "disabled"); | ||
291 | |||
292 | return count; | ||
293 | } | ||
294 | |||
295 | static ssize_t elpg_enable_read(struct device *device, | ||
296 | struct device_attribute *attr, char *buf) | ||
297 | { | ||
298 | struct platform_device *ndev = to_platform_device(device); | ||
299 | struct gk20a *g = get_gk20a(ndev); | ||
300 | |||
301 | return sprintf(buf, "%d\n", g->elpg_enabled ? 1 : 0); | ||
302 | } | ||
303 | |||
304 | static DEVICE_ATTR(elpg_enable, S_IRWXUGO, elpg_enable_read, elpg_enable_store); | ||
305 | |||
306 | void gk20a_remove_sysfs(struct device *dev) | ||
307 | { | ||
308 | device_remove_file(dev, &dev_attr_elcg_enable); | ||
309 | device_remove_file(dev, &dev_attr_blcg_enable); | ||
310 | device_remove_file(dev, &dev_attr_slcg_enable); | ||
311 | device_remove_file(dev, &dev_attr_ptimer_scale_factor); | ||
312 | device_remove_file(dev, &dev_attr_elpg_enable); | ||
313 | device_remove_file(dev, &dev_attr_counters); | ||
314 | device_remove_file(dev, &dev_attr_counters_reset); | ||
315 | device_remove_file(dev, &dev_attr_railgate_delay); | ||
316 | device_remove_file(dev, &dev_attr_clockgate_delay); | ||
317 | } | ||
318 | |||
319 | void gk20a_create_sysfs(struct platform_device *dev) | ||
320 | { | ||
321 | int error = 0; | ||
322 | |||
323 | error |= device_create_file(&dev->dev, &dev_attr_elcg_enable); | ||
324 | error |= device_create_file(&dev->dev, &dev_attr_blcg_enable); | ||
325 | error |= device_create_file(&dev->dev, &dev_attr_slcg_enable); | ||
326 | error |= device_create_file(&dev->dev, &dev_attr_ptimer_scale_factor); | ||
327 | error |= device_create_file(&dev->dev, &dev_attr_elpg_enable); | ||
328 | error |= device_create_file(&dev->dev, &dev_attr_counters); | ||
329 | error |= device_create_file(&dev->dev, &dev_attr_counters_reset); | ||
330 | error |= device_create_file(&dev->dev, &dev_attr_railgate_delay); | ||
331 | error |= device_create_file(&dev->dev, &dev_attr_clockgate_delay); | ||
332 | |||
333 | if (error) | ||
334 | dev_err(&dev->dev, "Failed to create sysfs attributes!\n"); | ||
335 | } | ||