diff options
Diffstat (limited to 'drivers/gpu/nvgpu/common/linux')
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/driver_common.c | 3 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/module.c | 4 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/pci.c | 4 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/platform_gp10b_tegra.c | 6 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/sysfs.c | 1023 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/sysfs.h | 24 |
6 files changed, 1053 insertions, 11 deletions
diff --git a/drivers/gpu/nvgpu/common/linux/driver_common.c b/drivers/gpu/nvgpu/common/linux/driver_common.c index c4d40c35..a7bc230c 100644 --- a/drivers/gpu/nvgpu/common/linux/driver_common.c +++ b/drivers/gpu/nvgpu/common/linux/driver_common.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "gk20a/platform_gk20a.h" | 28 | #include "gk20a/platform_gk20a.h" |
29 | #include "module.h" | 29 | #include "module.h" |
30 | #include "os_linux.h" | 30 | #include "os_linux.h" |
31 | #include "sysfs.h" | ||
31 | 32 | ||
32 | #define EMC3D_DEFAULT_RATIO 750 | 33 | #define EMC3D_DEFAULT_RATIO 750 |
33 | 34 | ||
@@ -190,7 +191,7 @@ int nvgpu_probe(struct gk20a *g, | |||
190 | 191 | ||
191 | nvgpu_init_mm_vars(g); | 192 | nvgpu_init_mm_vars(g); |
192 | 193 | ||
193 | gk20a_create_sysfs(g->dev); | 194 | nvgpu_create_sysfs(g->dev); |
194 | gk20a_debug_init(g, debugfs_symlink); | 195 | gk20a_debug_init(g, debugfs_symlink); |
195 | 196 | ||
196 | g->dbg_regops_tmp_buf = nvgpu_kzalloc(g, SZ_4K); | 197 | g->dbg_regops_tmp_buf = nvgpu_kzalloc(g, SZ_4K); |
diff --git a/drivers/gpu/nvgpu/common/linux/module.c b/drivers/gpu/nvgpu/common/linux/module.c index bfd2c790..99bbc25e 100644 --- a/drivers/gpu/nvgpu/common/linux/module.c +++ b/drivers/gpu/nvgpu/common/linux/module.c | |||
@@ -31,8 +31,8 @@ | |||
31 | #include <nvgpu/enabled.h> | 31 | #include <nvgpu/enabled.h> |
32 | #include <nvgpu/debug.h> | 32 | #include <nvgpu/debug.h> |
33 | 33 | ||
34 | #include "gk20a/gk20a.h" | ||
35 | #include "gk20a/platform_gk20a.h" | 34 | #include "gk20a/platform_gk20a.h" |
35 | #include "sysfs.h" | ||
36 | #include "vgpu/vgpu.h" | 36 | #include "vgpu/vgpu.h" |
37 | #include "gk20a/gk20a_scale.h" | 37 | #include "gk20a/gk20a_scale.h" |
38 | #include "gk20a/ctxsw_trace_gk20a.h" | 38 | #include "gk20a/ctxsw_trace_gk20a.h" |
@@ -985,7 +985,7 @@ static int __exit gk20a_remove(struct platform_device *pdev) | |||
985 | 985 | ||
986 | gk20a_debug_deinit(g); | 986 | gk20a_debug_deinit(g); |
987 | 987 | ||
988 | gk20a_remove_sysfs(dev); | 988 | nvgpu_remove_sysfs(dev); |
989 | 989 | ||
990 | if (platform->secure_buffer.destroy) | 990 | if (platform->secure_buffer.destroy) |
991 | platform->secure_buffer.destroy(g, | 991 | platform->secure_buffer.destroy(g, |
diff --git a/drivers/gpu/nvgpu/common/linux/pci.c b/drivers/gpu/nvgpu/common/linux/pci.c index cb315973..acb1bb9f 100644 --- a/drivers/gpu/nvgpu/common/linux/pci.c +++ b/drivers/gpu/nvgpu/common/linux/pci.c | |||
@@ -29,7 +29,7 @@ | |||
29 | #include "module.h" | 29 | #include "module.h" |
30 | #include "intr.h" | 30 | #include "intr.h" |
31 | #include "gp106/pmu_mclk_gp106.h" | 31 | #include "gp106/pmu_mclk_gp106.h" |
32 | 32 | #include "sysfs.h" | |
33 | #include "pci.h" | 33 | #include "pci.h" |
34 | 34 | ||
35 | #include "os_linux.h" | 35 | #include "os_linux.h" |
@@ -491,7 +491,7 @@ static void nvgpu_pci_remove(struct pci_dev *pdev) | |||
491 | debugfs_remove_recursive(platform->debugfs_alias); | 491 | debugfs_remove_recursive(platform->debugfs_alias); |
492 | #endif | 492 | #endif |
493 | 493 | ||
494 | gk20a_remove_sysfs(g->dev); | 494 | nvgpu_remove_sysfs(g->dev); |
495 | 495 | ||
496 | if (platform->remove) | 496 | if (platform->remove) |
497 | platform->remove(g->dev); | 497 | platform->remove(g->dev); |
diff --git a/drivers/gpu/nvgpu/common/linux/platform_gp10b_tegra.c b/drivers/gpu/nvgpu/common/linux/platform_gp10b_tegra.c index 5980c592..ab2aa1c5 100644 --- a/drivers/gpu/nvgpu/common/linux/platform_gp10b_tegra.c +++ b/drivers/gpu/nvgpu/common/linux/platform_gp10b_tegra.c | |||
@@ -38,7 +38,6 @@ | |||
38 | #include "gk20a/gk20a_scale.h" | 38 | #include "gk20a/gk20a_scale.h" |
39 | 39 | ||
40 | #include "platform_gk20a_tegra.h" | 40 | #include "platform_gk20a_tegra.h" |
41 | #include "gp10b/gp10b_sysfs.h" | ||
42 | #include "gp10b/platform_gp10b.h" | 41 | #include "gp10b/platform_gp10b.h" |
43 | #include "platform_gp10b_tegra.h" | 42 | #include "platform_gp10b_tegra.h" |
44 | 43 | ||
@@ -161,9 +160,6 @@ static int gp10b_tegra_late_probe(struct device *dev) | |||
161 | /* Cause early VPR resize */ | 160 | /* Cause early VPR resize */ |
162 | gk20a_tegra_secure_page_alloc(dev); | 161 | gk20a_tegra_secure_page_alloc(dev); |
163 | 162 | ||
164 | /*Create GP10B specific sysfs*/ | ||
165 | gp10b_create_sysfs(dev); | ||
166 | |||
167 | /* Initialise tegra specific scaling quirks */ | 163 | /* Initialise tegra specific scaling quirks */ |
168 | gp10b_tegra_scale_init(dev); | 164 | gp10b_tegra_scale_init(dev); |
169 | return 0; | 165 | return 0; |
@@ -172,8 +168,6 @@ static int gp10b_tegra_late_probe(struct device *dev) | |||
172 | int gp10b_tegra_remove(struct device *dev) | 168 | int gp10b_tegra_remove(struct device *dev) |
173 | { | 169 | { |
174 | gr_gp10b_remove_sysfs(dev); | 170 | gr_gp10b_remove_sysfs(dev); |
175 | /*Remove GP10B specific sysfs*/ | ||
176 | gp10b_remove_sysfs(dev); | ||
177 | 171 | ||
178 | /* deinitialise tegra specific scaling quirks */ | 172 | /* deinitialise tegra specific scaling quirks */ |
179 | gp10b_tegra_scale_exit(dev); | 173 | gp10b_tegra_scale_exit(dev); |
diff --git a/drivers/gpu/nvgpu/common/linux/sysfs.c b/drivers/gpu/nvgpu/common/linux/sysfs.c new file mode 100644 index 00000000..e0da4661 --- /dev/null +++ b/drivers/gpu/nvgpu/common/linux/sysfs.c | |||
@@ -0,0 +1,1023 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011-2017, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <linux/version.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | #include <linux/fb.h> | ||
21 | #include <soc/tegra/tegra-dvfs.h> | ||
22 | |||
23 | #include <nvgpu/kmem.h> | ||
24 | #include <nvgpu/nvhost.h> | ||
25 | |||
26 | #include "sysfs.h" | ||
27 | #include "gk20a/platform_gk20a.h" | ||
28 | #include "gk20a/pmu_gk20a.h" | ||
29 | #include "gk20a/gr_gk20a.h" | ||
30 | |||
31 | #define PTIMER_FP_FACTOR 1000000 | ||
32 | |||
33 | #define ROOTRW (S_IRWXU|S_IRGRP|S_IROTH) | ||
34 | |||
35 | static ssize_t elcg_enable_store(struct device *dev, | ||
36 | struct device_attribute *attr, const char *buf, size_t count) | ||
37 | { | ||
38 | struct gk20a *g = get_gk20a(dev); | ||
39 | unsigned long val = 0; | ||
40 | int err; | ||
41 | |||
42 | if (kstrtoul(buf, 10, &val) < 0) | ||
43 | return -EINVAL; | ||
44 | |||
45 | err = gk20a_busy(g); | ||
46 | if (err) | ||
47 | return err; | ||
48 | |||
49 | if (val) { | ||
50 | g->elcg_enabled = true; | ||
51 | gr_gk20a_init_cg_mode(g, ELCG_MODE, ELCG_AUTO); | ||
52 | } else { | ||
53 | g->elcg_enabled = false; | ||
54 | gr_gk20a_init_cg_mode(g, ELCG_MODE, ELCG_RUN); | ||
55 | } | ||
56 | |||
57 | gk20a_idle(g); | ||
58 | |||
59 | nvgpu_info(g, "ELCG is %s.", g->elcg_enabled ? "enabled" : | ||
60 | "disabled"); | ||
61 | |||
62 | return count; | ||
63 | } | ||
64 | |||
65 | static ssize_t elcg_enable_read(struct device *dev, | ||
66 | struct device_attribute *attr, char *buf) | ||
67 | { | ||
68 | struct gk20a *g = get_gk20a(dev); | ||
69 | |||
70 | return snprintf(buf, PAGE_SIZE, "%d\n", g->elcg_enabled ? 1 : 0); | ||
71 | } | ||
72 | |||
73 | static DEVICE_ATTR(elcg_enable, ROOTRW, elcg_enable_read, elcg_enable_store); | ||
74 | |||
75 | static ssize_t blcg_enable_store(struct device *dev, | ||
76 | struct device_attribute *attr, const char *buf, size_t count) | ||
77 | { | ||
78 | struct gk20a *g = get_gk20a(dev); | ||
79 | unsigned long val = 0; | ||
80 | int err; | ||
81 | |||
82 | if (kstrtoul(buf, 10, &val) < 0) | ||
83 | return -EINVAL; | ||
84 | |||
85 | if (val) | ||
86 | g->blcg_enabled = true; | ||
87 | else | ||
88 | g->blcg_enabled = false; | ||
89 | |||
90 | err = gk20a_busy(g); | ||
91 | if (err) | ||
92 | return err; | ||
93 | |||
94 | if (g->ops.clock_gating.blcg_bus_load_gating_prod) | ||
95 | g->ops.clock_gating.blcg_bus_load_gating_prod(g, | ||
96 | g->blcg_enabled); | ||
97 | if (g->ops.clock_gating.blcg_ce_load_gating_prod) | ||
98 | g->ops.clock_gating.blcg_ce_load_gating_prod(g, | ||
99 | g->blcg_enabled); | ||
100 | if (g->ops.clock_gating.blcg_ctxsw_firmware_load_gating_prod) | ||
101 | g->ops.clock_gating.blcg_ctxsw_firmware_load_gating_prod(g, | ||
102 | g->blcg_enabled); | ||
103 | if (g->ops.clock_gating.blcg_fb_load_gating_prod) | ||
104 | g->ops.clock_gating.blcg_fb_load_gating_prod(g, | ||
105 | g->blcg_enabled); | ||
106 | if (g->ops.clock_gating.blcg_fifo_load_gating_prod) | ||
107 | g->ops.clock_gating.blcg_fifo_load_gating_prod(g, | ||
108 | g->blcg_enabled); | ||
109 | if (g->ops.clock_gating.blcg_gr_load_gating_prod) | ||
110 | g->ops.clock_gating.blcg_gr_load_gating_prod(g, | ||
111 | g->blcg_enabled); | ||
112 | if (g->ops.clock_gating.blcg_ltc_load_gating_prod) | ||
113 | g->ops.clock_gating.blcg_ltc_load_gating_prod(g, | ||
114 | g->blcg_enabled); | ||
115 | if (g->ops.clock_gating.blcg_pmu_load_gating_prod) | ||
116 | g->ops.clock_gating.blcg_pmu_load_gating_prod(g, | ||
117 | g->blcg_enabled); | ||
118 | if (g->ops.clock_gating.blcg_xbar_load_gating_prod) | ||
119 | g->ops.clock_gating.blcg_xbar_load_gating_prod(g, | ||
120 | g->blcg_enabled); | ||
121 | gk20a_idle(g); | ||
122 | |||
123 | nvgpu_info(g, "BLCG is %s.", g->blcg_enabled ? "enabled" : | ||
124 | "disabled"); | ||
125 | |||
126 | return count; | ||
127 | } | ||
128 | |||
129 | static ssize_t blcg_enable_read(struct device *dev, | ||
130 | struct device_attribute *attr, char *buf) | ||
131 | { | ||
132 | struct gk20a *g = get_gk20a(dev); | ||
133 | |||
134 | return snprintf(buf, PAGE_SIZE, "%d\n", g->blcg_enabled ? 1 : 0); | ||
135 | } | ||
136 | |||
137 | |||
138 | static DEVICE_ATTR(blcg_enable, ROOTRW, blcg_enable_read, blcg_enable_store); | ||
139 | |||
140 | static ssize_t slcg_enable_store(struct device *dev, | ||
141 | struct device_attribute *attr, const char *buf, size_t count) | ||
142 | { | ||
143 | struct gk20a *g = get_gk20a(dev); | ||
144 | unsigned long val = 0; | ||
145 | int err; | ||
146 | |||
147 | if (kstrtoul(buf, 10, &val) < 0) | ||
148 | return -EINVAL; | ||
149 | |||
150 | if (val) | ||
151 | g->slcg_enabled = true; | ||
152 | else | ||
153 | g->slcg_enabled = false; | ||
154 | |||
155 | /* | ||
156 | * TODO: slcg_therm_load_gating is not enabled anywhere during | ||
157 | * init. Therefore, it would be incongruous to add it here. Once | ||
158 | * it is added to init, we should add it here too. | ||
159 | */ | ||
160 | err = gk20a_busy(g); | ||
161 | if (err) | ||
162 | return err; | ||
163 | |||
164 | if (g->ops.clock_gating.slcg_bus_load_gating_prod) | ||
165 | g->ops.clock_gating.slcg_bus_load_gating_prod(g, | ||
166 | g->slcg_enabled); | ||
167 | if (g->ops.clock_gating.slcg_ce2_load_gating_prod) | ||
168 | g->ops.clock_gating.slcg_ce2_load_gating_prod(g, | ||
169 | g->slcg_enabled); | ||
170 | if (g->ops.clock_gating.slcg_chiplet_load_gating_prod) | ||
171 | g->ops.clock_gating.slcg_chiplet_load_gating_prod(g, | ||
172 | g->slcg_enabled); | ||
173 | if (g->ops.clock_gating.slcg_ctxsw_firmware_load_gating_prod) | ||
174 | g->ops.clock_gating.slcg_ctxsw_firmware_load_gating_prod(g, | ||
175 | g->slcg_enabled); | ||
176 | if (g->ops.clock_gating.slcg_fb_load_gating_prod) | ||
177 | g->ops.clock_gating.slcg_fb_load_gating_prod(g, | ||
178 | g->slcg_enabled); | ||
179 | if (g->ops.clock_gating.slcg_fifo_load_gating_prod) | ||
180 | g->ops.clock_gating.slcg_fifo_load_gating_prod(g, | ||
181 | g->slcg_enabled); | ||
182 | if (g->ops.clock_gating.slcg_gr_load_gating_prod) | ||
183 | g->ops.clock_gating.slcg_gr_load_gating_prod(g, | ||
184 | g->slcg_enabled); | ||
185 | if (g->ops.clock_gating.slcg_ltc_load_gating_prod) | ||
186 | g->ops.clock_gating.slcg_ltc_load_gating_prod(g, | ||
187 | g->slcg_enabled); | ||
188 | if (g->ops.clock_gating.slcg_perf_load_gating_prod) | ||
189 | g->ops.clock_gating.slcg_perf_load_gating_prod(g, | ||
190 | g->slcg_enabled); | ||
191 | if (g->ops.clock_gating.slcg_priring_load_gating_prod) | ||
192 | g->ops.clock_gating.slcg_priring_load_gating_prod(g, | ||
193 | g->slcg_enabled); | ||
194 | if (g->ops.clock_gating.slcg_pmu_load_gating_prod) | ||
195 | g->ops.clock_gating.slcg_pmu_load_gating_prod(g, | ||
196 | g->slcg_enabled); | ||
197 | if (g->ops.clock_gating.slcg_xbar_load_gating_prod) | ||
198 | g->ops.clock_gating.slcg_xbar_load_gating_prod(g, | ||
199 | g->slcg_enabled); | ||
200 | gk20a_idle(g); | ||
201 | |||
202 | nvgpu_info(g, "SLCG is %s.", g->slcg_enabled ? "enabled" : | ||
203 | "disabled"); | ||
204 | |||
205 | return count; | ||
206 | } | ||
207 | |||
208 | static ssize_t slcg_enable_read(struct device *dev, | ||
209 | struct device_attribute *attr, char *buf) | ||
210 | { | ||
211 | struct gk20a *g = get_gk20a(dev); | ||
212 | |||
213 | return snprintf(buf, PAGE_SIZE, "%d\n", g->slcg_enabled ? 1 : 0); | ||
214 | } | ||
215 | |||
216 | static DEVICE_ATTR(slcg_enable, ROOTRW, slcg_enable_read, slcg_enable_store); | ||
217 | |||
218 | static ssize_t ptimer_scale_factor_show(struct device *dev, | ||
219 | struct device_attribute *attr, | ||
220 | char *buf) | ||
221 | { | ||
222 | struct gk20a *g = get_gk20a(dev); | ||
223 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
224 | u32 src_freq_hz = platform->ptimer_src_freq; | ||
225 | u32 scaling_factor_fp; | ||
226 | ssize_t res; | ||
227 | |||
228 | if (!src_freq_hz) { | ||
229 | nvgpu_err(g, "reference clk_m rate is not set correctly"); | ||
230 | return -EINVAL; | ||
231 | } | ||
232 | |||
233 | scaling_factor_fp = (u32)(PTIMER_REF_FREQ_HZ) / | ||
234 | ((u32)(src_freq_hz) / | ||
235 | (u32)(PTIMER_FP_FACTOR)); | ||
236 | res = snprintf(buf, | ||
237 | PAGE_SIZE, | ||
238 | "%u.%u\n", | ||
239 | scaling_factor_fp / PTIMER_FP_FACTOR, | ||
240 | scaling_factor_fp % PTIMER_FP_FACTOR); | ||
241 | |||
242 | return res; | ||
243 | |||
244 | } | ||
245 | |||
246 | static DEVICE_ATTR(ptimer_scale_factor, | ||
247 | S_IRUGO, | ||
248 | ptimer_scale_factor_show, | ||
249 | NULL); | ||
250 | |||
251 | static ssize_t ptimer_ref_freq_show(struct device *dev, | ||
252 | struct device_attribute *attr, | ||
253 | char *buf) | ||
254 | { | ||
255 | struct gk20a *g = get_gk20a(dev); | ||
256 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
257 | u32 src_freq_hz = platform->ptimer_src_freq; | ||
258 | ssize_t res; | ||
259 | |||
260 | if (!src_freq_hz) { | ||
261 | nvgpu_err(g, "reference clk_m rate is not set correctly"); | ||
262 | return -EINVAL; | ||
263 | } | ||
264 | |||
265 | res = snprintf(buf, PAGE_SIZE, "%u\n", PTIMER_REF_FREQ_HZ); | ||
266 | |||
267 | return res; | ||
268 | |||
269 | } | ||
270 | |||
271 | static DEVICE_ATTR(ptimer_ref_freq, | ||
272 | S_IRUGO, | ||
273 | ptimer_ref_freq_show, | ||
274 | NULL); | ||
275 | |||
276 | static ssize_t ptimer_src_freq_show(struct device *dev, | ||
277 | struct device_attribute *attr, | ||
278 | char *buf) | ||
279 | { | ||
280 | struct gk20a *g = get_gk20a(dev); | ||
281 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
282 | u32 src_freq_hz = platform->ptimer_src_freq; | ||
283 | ssize_t res; | ||
284 | |||
285 | if (!src_freq_hz) { | ||
286 | nvgpu_err(g, "reference clk_m rate is not set correctly"); | ||
287 | return -EINVAL; | ||
288 | } | ||
289 | |||
290 | res = snprintf(buf, PAGE_SIZE, "%u\n", src_freq_hz); | ||
291 | |||
292 | return res; | ||
293 | |||
294 | } | ||
295 | |||
296 | static DEVICE_ATTR(ptimer_src_freq, | ||
297 | S_IRUGO, | ||
298 | ptimer_src_freq_show, | ||
299 | NULL); | ||
300 | |||
301 | |||
302 | #if defined(CONFIG_PM) | ||
303 | static ssize_t railgate_enable_store(struct device *dev, | ||
304 | struct device_attribute *attr, const char *buf, size_t count) | ||
305 | { | ||
306 | unsigned long railgate_enable = 0; | ||
307 | /* dev is guaranteed to be valid here. Ok to de-reference */ | ||
308 | struct gk20a *g = get_gk20a(dev); | ||
309 | int err = 0; | ||
310 | |||
311 | if (kstrtoul(buf, 10, &railgate_enable) < 0) | ||
312 | return -EINVAL; | ||
313 | |||
314 | if (railgate_enable && !g->can_railgate) { | ||
315 | /* release extra ref count */ | ||
316 | gk20a_idle(g); | ||
317 | g->can_railgate = true; | ||
318 | g->user_railgate_disabled = false; | ||
319 | } else if (railgate_enable == 0 && g->can_railgate) { | ||
320 | /* take extra ref count */ | ||
321 | err = gk20a_busy(g); | ||
322 | if (err) | ||
323 | return err; | ||
324 | g->can_railgate = false; | ||
325 | g->user_railgate_disabled = true; | ||
326 | } | ||
327 | |||
328 | nvgpu_info(g, "railgate is %s.", g->can_railgate ? | ||
329 | "enabled" : "disabled"); | ||
330 | |||
331 | return count; | ||
332 | } | ||
333 | |||
334 | static ssize_t railgate_enable_read(struct device *dev, | ||
335 | struct device_attribute *attr, char *buf) | ||
336 | { | ||
337 | struct gk20a *g = get_gk20a(dev); | ||
338 | |||
339 | return snprintf(buf, PAGE_SIZE, "%d\n", g->can_railgate ? 1 : 0); | ||
340 | } | ||
341 | |||
342 | static DEVICE_ATTR(railgate_enable, ROOTRW, railgate_enable_read, | ||
343 | railgate_enable_store); | ||
344 | #endif | ||
345 | |||
346 | static ssize_t railgate_delay_store(struct device *dev, | ||
347 | struct device_attribute *attr, | ||
348 | const char *buf, size_t count) | ||
349 | { | ||
350 | int railgate_delay = 0, ret = 0; | ||
351 | struct gk20a *g = get_gk20a(dev); | ||
352 | int err; | ||
353 | |||
354 | if (!g->can_railgate) { | ||
355 | nvgpu_info(g, "does not support power-gating"); | ||
356 | return count; | ||
357 | } | ||
358 | |||
359 | ret = sscanf(buf, "%d", &railgate_delay); | ||
360 | if (ret == 1 && railgate_delay >= 0) { | ||
361 | g->railgate_delay = railgate_delay; | ||
362 | pm_runtime_set_autosuspend_delay(dev, g->railgate_delay); | ||
363 | } else | ||
364 | nvgpu_err(g, "Invalid powergate delay"); | ||
365 | |||
366 | /* wake-up system to make rail-gating delay effective immediately */ | ||
367 | err = gk20a_busy(g); | ||
368 | if (err) | ||
369 | return err; | ||
370 | gk20a_idle(g); | ||
371 | |||
372 | return count; | ||
373 | } | ||
374 | static ssize_t railgate_delay_show(struct device *dev, | ||
375 | struct device_attribute *attr, char *buf) | ||
376 | { | ||
377 | struct gk20a *g = get_gk20a(dev); | ||
378 | |||
379 | return snprintf(buf, PAGE_SIZE, "%d\n", g->railgate_delay); | ||
380 | } | ||
381 | static DEVICE_ATTR(railgate_delay, ROOTRW, railgate_delay_show, | ||
382 | railgate_delay_store); | ||
383 | |||
384 | static ssize_t is_railgated_show(struct device *dev, | ||
385 | struct device_attribute *attr, char *buf) | ||
386 | { | ||
387 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
388 | bool is_railgated = 0; | ||
389 | |||
390 | if (platform->is_railgated) | ||
391 | is_railgated = platform->is_railgated(platform->g->dev); | ||
392 | |||
393 | return snprintf(buf, PAGE_SIZE, "%s\n", is_railgated ? "yes" : "no"); | ||
394 | } | ||
395 | static DEVICE_ATTR(is_railgated, S_IRUGO, is_railgated_show, NULL); | ||
396 | |||
397 | static ssize_t counters_show(struct device *dev, | ||
398 | struct device_attribute *attr, char *buf) | ||
399 | { | ||
400 | struct gk20a *g = get_gk20a(dev); | ||
401 | u32 busy_cycles, total_cycles; | ||
402 | ssize_t res; | ||
403 | |||
404 | nvgpu_pmu_get_load_counters(g, &busy_cycles, &total_cycles); | ||
405 | |||
406 | res = snprintf(buf, PAGE_SIZE, "%u %u\n", busy_cycles, total_cycles); | ||
407 | |||
408 | return res; | ||
409 | } | ||
410 | static DEVICE_ATTR(counters, S_IRUGO, counters_show, NULL); | ||
411 | |||
412 | static ssize_t counters_show_reset(struct device *dev, | ||
413 | struct device_attribute *attr, char *buf) | ||
414 | { | ||
415 | ssize_t res = counters_show(dev, attr, buf); | ||
416 | struct gk20a *g = get_gk20a(dev); | ||
417 | |||
418 | nvgpu_pmu_reset_load_counters(g); | ||
419 | |||
420 | return res; | ||
421 | } | ||
422 | static DEVICE_ATTR(counters_reset, S_IRUGO, counters_show_reset, NULL); | ||
423 | |||
424 | static ssize_t gk20a_load_show(struct device *dev, | ||
425 | struct device_attribute *attr, | ||
426 | char *buf) | ||
427 | { | ||
428 | struct gk20a *g = get_gk20a(dev); | ||
429 | u32 busy_time; | ||
430 | ssize_t res; | ||
431 | int err; | ||
432 | |||
433 | if (!g->power_on) { | ||
434 | busy_time = 0; | ||
435 | } else { | ||
436 | err = gk20a_busy(g); | ||
437 | if (err) | ||
438 | return err; | ||
439 | |||
440 | nvgpu_pmu_load_update(g); | ||
441 | nvgpu_pmu_load_norm(g, &busy_time); | ||
442 | gk20a_idle(g); | ||
443 | } | ||
444 | |||
445 | res = snprintf(buf, PAGE_SIZE, "%u\n", busy_time); | ||
446 | |||
447 | return res; | ||
448 | } | ||
449 | static DEVICE_ATTR(load, S_IRUGO, gk20a_load_show, NULL); | ||
450 | |||
451 | static ssize_t elpg_enable_store(struct device *dev, | ||
452 | struct device_attribute *attr, const char *buf, size_t count) | ||
453 | { | ||
454 | struct gk20a *g = get_gk20a(dev); | ||
455 | unsigned long val = 0; | ||
456 | int err; | ||
457 | |||
458 | if (kstrtoul(buf, 10, &val) < 0) | ||
459 | return -EINVAL; | ||
460 | |||
461 | if (!g->power_on) { | ||
462 | g->elpg_enabled = val ? true : false; | ||
463 | } else { | ||
464 | err = gk20a_busy(g); | ||
465 | if (err) | ||
466 | return -EAGAIN; | ||
467 | /* | ||
468 | * Since elpg is refcounted, we should not unnecessarily call | ||
469 | * enable/disable if it is already so. | ||
470 | */ | ||
471 | if (val && !g->elpg_enabled) { | ||
472 | g->elpg_enabled = true; | ||
473 | nvgpu_pmu_pg_global_enable(g, true); | ||
474 | |||
475 | } else if (!val && g->elpg_enabled) { | ||
476 | if (g->ops.pmu.pmu_pg_engines_feature_list && | ||
477 | g->ops.pmu.pmu_pg_engines_feature_list(g, | ||
478 | PMU_PG_ELPG_ENGINE_ID_GRAPHICS) != | ||
479 | PMU_PG_FEATURE_GR_POWER_GATING_ENABLED) { | ||
480 | nvgpu_pmu_pg_global_enable(g, false); | ||
481 | g->elpg_enabled = false; | ||
482 | } else { | ||
483 | g->elpg_enabled = false; | ||
484 | nvgpu_pmu_pg_global_enable(g, false); | ||
485 | } | ||
486 | } | ||
487 | gk20a_idle(g); | ||
488 | } | ||
489 | nvgpu_info(g, "ELPG is %s.", g->elpg_enabled ? "enabled" : | ||
490 | "disabled"); | ||
491 | |||
492 | return count; | ||
493 | } | ||
494 | |||
495 | static ssize_t elpg_enable_read(struct device *dev, | ||
496 | struct device_attribute *attr, char *buf) | ||
497 | { | ||
498 | struct gk20a *g = get_gk20a(dev); | ||
499 | |||
500 | return snprintf(buf, PAGE_SIZE, "%d\n", g->elpg_enabled ? 1 : 0); | ||
501 | } | ||
502 | |||
503 | static DEVICE_ATTR(elpg_enable, ROOTRW, elpg_enable_read, elpg_enable_store); | ||
504 | |||
505 | static ssize_t mscg_enable_store(struct device *dev, | ||
506 | struct device_attribute *attr, const char *buf, size_t count) | ||
507 | { | ||
508 | struct gk20a *g = get_gk20a(dev); | ||
509 | struct nvgpu_pmu *pmu = &g->pmu; | ||
510 | unsigned long val = 0; | ||
511 | int err; | ||
512 | |||
513 | if (kstrtoul(buf, 10, &val) < 0) | ||
514 | return -EINVAL; | ||
515 | |||
516 | if (!g->power_on) { | ||
517 | g->mscg_enabled = val ? true : false; | ||
518 | } else { | ||
519 | err = gk20a_busy(g); | ||
520 | if (err) | ||
521 | return -EAGAIN; | ||
522 | /* | ||
523 | * Since elpg is refcounted, we should not unnecessarily call | ||
524 | * enable/disable if it is already so. | ||
525 | */ | ||
526 | if (val && !g->mscg_enabled) { | ||
527 | g->mscg_enabled = true; | ||
528 | if (g->ops.pmu.pmu_is_lpwr_feature_supported(g, | ||
529 | PMU_PG_LPWR_FEATURE_MSCG)) { | ||
530 | if (!ACCESS_ONCE(pmu->mscg_stat)) { | ||
531 | WRITE_ONCE(pmu->mscg_stat, | ||
532 | PMU_MSCG_ENABLED); | ||
533 | /* make status visible */ | ||
534 | smp_mb(); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | } else if (!val && g->mscg_enabled) { | ||
539 | if (g->ops.pmu.pmu_is_lpwr_feature_supported(g, | ||
540 | PMU_PG_LPWR_FEATURE_MSCG)) { | ||
541 | nvgpu_pmu_pg_global_enable(g, false); | ||
542 | WRITE_ONCE(pmu->mscg_stat, PMU_MSCG_DISABLED); | ||
543 | /* make status visible */ | ||
544 | smp_mb(); | ||
545 | g->mscg_enabled = false; | ||
546 | if (g->elpg_enabled) | ||
547 | nvgpu_pmu_pg_global_enable(g, true); | ||
548 | } | ||
549 | g->mscg_enabled = false; | ||
550 | } | ||
551 | gk20a_idle(g); | ||
552 | } | ||
553 | nvgpu_info(g, "MSCG is %s.", g->mscg_enabled ? "enabled" : | ||
554 | "disabled"); | ||
555 | |||
556 | return count; | ||
557 | } | ||
558 | |||
559 | static ssize_t mscg_enable_read(struct device *dev, | ||
560 | struct device_attribute *attr, char *buf) | ||
561 | { | ||
562 | struct gk20a *g = get_gk20a(dev); | ||
563 | |||
564 | return snprintf(buf, PAGE_SIZE, "%d\n", g->mscg_enabled ? 1 : 0); | ||
565 | } | ||
566 | |||
567 | static DEVICE_ATTR(mscg_enable, ROOTRW, mscg_enable_read, mscg_enable_store); | ||
568 | |||
569 | static ssize_t aelpg_param_store(struct device *dev, | ||
570 | struct device_attribute *attr, const char *buf, size_t count) | ||
571 | { | ||
572 | struct gk20a *g = get_gk20a(dev); | ||
573 | int status = 0; | ||
574 | union pmu_ap_cmd ap_cmd; | ||
575 | int *paramlist = (int *)g->pmu.aelpg_param; | ||
576 | u32 defaultparam[5] = { | ||
577 | APCTRL_SAMPLING_PERIOD_PG_DEFAULT_US, | ||
578 | APCTRL_MINIMUM_IDLE_FILTER_DEFAULT_US, | ||
579 | APCTRL_MINIMUM_TARGET_SAVING_DEFAULT_US, | ||
580 | APCTRL_POWER_BREAKEVEN_DEFAULT_US, | ||
581 | APCTRL_CYCLES_PER_SAMPLE_MAX_DEFAULT | ||
582 | }; | ||
583 | |||
584 | /* Get each parameter value from input string*/ | ||
585 | sscanf(buf, "%d %d %d %d %d", ¶mlist[0], ¶mlist[1], | ||
586 | ¶mlist[2], ¶mlist[3], ¶mlist[4]); | ||
587 | |||
588 | /* If parameter value is 0 then reset to SW default values*/ | ||
589 | if ((paramlist[0] | paramlist[1] | paramlist[2] | ||
590 | | paramlist[3] | paramlist[4]) == 0x00) { | ||
591 | memcpy(paramlist, defaultparam, sizeof(defaultparam)); | ||
592 | } | ||
593 | |||
594 | /* If aelpg is enabled & pmu is ready then post values to | ||
595 | * PMU else store then post later | ||
596 | */ | ||
597 | if (g->aelpg_enabled && g->pmu.pmu_ready) { | ||
598 | /* Disable AELPG */ | ||
599 | ap_cmd.disable_ctrl.cmd_id = PMU_AP_CMD_ID_DISABLE_CTRL; | ||
600 | ap_cmd.disable_ctrl.ctrl_id = PMU_AP_CTRL_ID_GRAPHICS; | ||
601 | status = nvgpu_pmu_ap_send_command(g, &ap_cmd, false); | ||
602 | |||
603 | /* Enable AELPG */ | ||
604 | nvgpu_aelpg_init(g); | ||
605 | nvgpu_aelpg_init_and_enable(g, PMU_AP_CTRL_ID_GRAPHICS); | ||
606 | } | ||
607 | |||
608 | return count; | ||
609 | } | ||
610 | |||
611 | static ssize_t aelpg_param_read(struct device *dev, | ||
612 | struct device_attribute *attr, char *buf) | ||
613 | { | ||
614 | struct gk20a *g = get_gk20a(dev); | ||
615 | |||
616 | return snprintf(buf, PAGE_SIZE, | ||
617 | "%d %d %d %d %d\n", g->pmu.aelpg_param[0], | ||
618 | g->pmu.aelpg_param[1], g->pmu.aelpg_param[2], | ||
619 | g->pmu.aelpg_param[3], g->pmu.aelpg_param[4]); | ||
620 | } | ||
621 | |||
622 | static DEVICE_ATTR(aelpg_param, ROOTRW, | ||
623 | aelpg_param_read, aelpg_param_store); | ||
624 | |||
625 | static ssize_t aelpg_enable_store(struct device *dev, | ||
626 | struct device_attribute *attr, const char *buf, size_t count) | ||
627 | { | ||
628 | struct gk20a *g = get_gk20a(dev); | ||
629 | unsigned long val = 0; | ||
630 | int status = 0; | ||
631 | union pmu_ap_cmd ap_cmd; | ||
632 | int err; | ||
633 | |||
634 | if (kstrtoul(buf, 10, &val) < 0) | ||
635 | return -EINVAL; | ||
636 | |||
637 | err = gk20a_busy(g); | ||
638 | if (err) | ||
639 | return err; | ||
640 | |||
641 | if (g->pmu.pmu_ready) { | ||
642 | if (val && !g->aelpg_enabled) { | ||
643 | g->aelpg_enabled = true; | ||
644 | /* Enable AELPG */ | ||
645 | ap_cmd.enable_ctrl.cmd_id = PMU_AP_CMD_ID_ENABLE_CTRL; | ||
646 | ap_cmd.enable_ctrl.ctrl_id = PMU_AP_CTRL_ID_GRAPHICS; | ||
647 | status = nvgpu_pmu_ap_send_command(g, &ap_cmd, false); | ||
648 | } else if (!val && g->aelpg_enabled) { | ||
649 | g->aelpg_enabled = false; | ||
650 | /* Disable AELPG */ | ||
651 | ap_cmd.disable_ctrl.cmd_id = PMU_AP_CMD_ID_DISABLE_CTRL; | ||
652 | ap_cmd.disable_ctrl.ctrl_id = PMU_AP_CTRL_ID_GRAPHICS; | ||
653 | status = nvgpu_pmu_ap_send_command(g, &ap_cmd, false); | ||
654 | } | ||
655 | } else { | ||
656 | nvgpu_info(g, "PMU is not ready, AELPG request failed"); | ||
657 | } | ||
658 | gk20a_idle(g); | ||
659 | |||
660 | nvgpu_info(g, "AELPG is %s.", g->aelpg_enabled ? "enabled" : | ||
661 | "disabled"); | ||
662 | |||
663 | return count; | ||
664 | } | ||
665 | |||
666 | static ssize_t aelpg_enable_read(struct device *dev, | ||
667 | struct device_attribute *attr, char *buf) | ||
668 | { | ||
669 | struct gk20a *g = get_gk20a(dev); | ||
670 | |||
671 | return snprintf(buf, PAGE_SIZE, "%d\n", g->aelpg_enabled ? 1 : 0); | ||
672 | } | ||
673 | |||
674 | static DEVICE_ATTR(aelpg_enable, ROOTRW, | ||
675 | aelpg_enable_read, aelpg_enable_store); | ||
676 | |||
677 | |||
678 | static ssize_t allow_all_enable_read(struct device *dev, | ||
679 | struct device_attribute *attr, char *buf) | ||
680 | { | ||
681 | struct gk20a *g = get_gk20a(dev); | ||
682 | |||
683 | return snprintf(buf, PAGE_SIZE, "%d\n", g->allow_all ? 1 : 0); | ||
684 | } | ||
685 | |||
686 | static ssize_t allow_all_enable_store(struct device *dev, | ||
687 | struct device_attribute *attr, const char *buf, size_t count) | ||
688 | { | ||
689 | struct gk20a *g = get_gk20a(dev); | ||
690 | unsigned long val = 0; | ||
691 | int err; | ||
692 | |||
693 | if (kstrtoul(buf, 10, &val) < 0) | ||
694 | return -EINVAL; | ||
695 | |||
696 | err = gk20a_busy(g); | ||
697 | g->allow_all = (val ? true : false); | ||
698 | gk20a_idle(g); | ||
699 | |||
700 | return count; | ||
701 | } | ||
702 | |||
703 | static DEVICE_ATTR(allow_all, ROOTRW, | ||
704 | allow_all_enable_read, allow_all_enable_store); | ||
705 | |||
706 | static ssize_t emc3d_ratio_store(struct device *dev, | ||
707 | struct device_attribute *attr, const char *buf, size_t count) | ||
708 | { | ||
709 | struct gk20a *g = get_gk20a(dev); | ||
710 | unsigned long val = 0; | ||
711 | |||
712 | if (kstrtoul(buf, 10, &val) < 0) | ||
713 | return -EINVAL; | ||
714 | |||
715 | g->emc3d_ratio = val; | ||
716 | |||
717 | return count; | ||
718 | } | ||
719 | |||
720 | static ssize_t emc3d_ratio_read(struct device *dev, | ||
721 | struct device_attribute *attr, char *buf) | ||
722 | { | ||
723 | struct gk20a *g = get_gk20a(dev); | ||
724 | |||
725 | return snprintf(buf, PAGE_SIZE, "%d\n", g->emc3d_ratio); | ||
726 | } | ||
727 | |||
728 | static DEVICE_ATTR(emc3d_ratio, ROOTRW, emc3d_ratio_read, emc3d_ratio_store); | ||
729 | |||
730 | static ssize_t fmax_at_vmin_safe_read(struct device *dev, | ||
731 | struct device_attribute *attr, char *buf) | ||
732 | { | ||
733 | struct gk20a *g = get_gk20a(dev); | ||
734 | unsigned long gpu_fmax_at_vmin_hz = 0; | ||
735 | struct clk *clk = g->clk.tegra_clk; | ||
736 | |||
737 | gpu_fmax_at_vmin_hz = tegra_dvfs_get_fmax_at_vmin_safe_t(clk); | ||
738 | |||
739 | return snprintf(buf, PAGE_SIZE, "%d\n", (int)(gpu_fmax_at_vmin_hz)); | ||
740 | } | ||
741 | |||
742 | static DEVICE_ATTR(fmax_at_vmin_safe, S_IRUGO, fmax_at_vmin_safe_read, NULL); | ||
743 | |||
744 | #ifdef CONFIG_PM | ||
745 | static ssize_t force_idle_store(struct device *dev, | ||
746 | struct device_attribute *attr, const char *buf, size_t count) | ||
747 | { | ||
748 | struct gk20a *g = get_gk20a(dev); | ||
749 | unsigned long val = 0; | ||
750 | int err = 0; | ||
751 | |||
752 | if (kstrtoul(buf, 10, &val) < 0) | ||
753 | return -EINVAL; | ||
754 | |||
755 | if (val) { | ||
756 | if (g->forced_idle) | ||
757 | return count; /* do nothing */ | ||
758 | else { | ||
759 | err = __gk20a_do_idle(g, false); | ||
760 | if (!err) { | ||
761 | g->forced_idle = 1; | ||
762 | nvgpu_info(g, "gpu is idle : %d", | ||
763 | g->forced_idle); | ||
764 | } | ||
765 | } | ||
766 | } else { | ||
767 | if (!g->forced_idle) | ||
768 | return count; /* do nothing */ | ||
769 | else { | ||
770 | err = __gk20a_do_unidle(g); | ||
771 | if (!err) { | ||
772 | g->forced_idle = 0; | ||
773 | nvgpu_info(g, "gpu is idle : %d", | ||
774 | g->forced_idle); | ||
775 | } | ||
776 | } | ||
777 | } | ||
778 | |||
779 | return count; | ||
780 | } | ||
781 | |||
782 | static ssize_t force_idle_read(struct device *dev, | ||
783 | struct device_attribute *attr, char *buf) | ||
784 | { | ||
785 | struct gk20a *g = get_gk20a(dev); | ||
786 | |||
787 | return snprintf(buf, PAGE_SIZE, "%d\n", g->forced_idle ? 1 : 0); | ||
788 | } | ||
789 | |||
790 | static DEVICE_ATTR(force_idle, ROOTRW, force_idle_read, force_idle_store); | ||
791 | #endif | ||
792 | |||
793 | static ssize_t tpc_fs_mask_store(struct device *dev, | ||
794 | struct device_attribute *attr, const char *buf, size_t count) | ||
795 | { | ||
796 | struct gk20a *g = get_gk20a(dev); | ||
797 | unsigned long val = 0; | ||
798 | |||
799 | if (kstrtoul(buf, 10, &val) < 0) | ||
800 | return -EINVAL; | ||
801 | |||
802 | if (!g->gr.gpc_tpc_mask) | ||
803 | return -ENODEV; | ||
804 | |||
805 | if (val && val != g->gr.gpc_tpc_mask[0] && g->ops.gr.set_gpc_tpc_mask) { | ||
806 | g->gr.gpc_tpc_mask[0] = val; | ||
807 | g->tpc_fs_mask_user = val; | ||
808 | |||
809 | g->ops.gr.set_gpc_tpc_mask(g, 0); | ||
810 | |||
811 | nvgpu_vfree(g, g->gr.ctx_vars.local_golden_image); | ||
812 | g->gr.ctx_vars.local_golden_image = NULL; | ||
813 | g->gr.ctx_vars.golden_image_initialized = false; | ||
814 | g->gr.ctx_vars.golden_image_size = 0; | ||
815 | g->gr.sw_ready = false; | ||
816 | } | ||
817 | |||
818 | return count; | ||
819 | } | ||
820 | |||
821 | static ssize_t tpc_fs_mask_read(struct device *dev, | ||
822 | struct device_attribute *attr, char *buf) | ||
823 | { | ||
824 | struct gk20a *g = get_gk20a(dev); | ||
825 | struct gr_gk20a *gr = &g->gr; | ||
826 | u32 gpc_index; | ||
827 | u32 tpc_fs_mask = 0; | ||
828 | int err = 0; | ||
829 | |||
830 | err = gk20a_busy(g); | ||
831 | if (err) | ||
832 | return err; | ||
833 | |||
834 | for (gpc_index = 0; gpc_index < gr->gpc_count; gpc_index++) { | ||
835 | if (g->ops.gr.get_gpc_tpc_mask) | ||
836 | tpc_fs_mask |= | ||
837 | g->ops.gr.get_gpc_tpc_mask(g, gpc_index) << | ||
838 | (gr->max_tpc_per_gpc_count * gpc_index); | ||
839 | } | ||
840 | |||
841 | gk20a_idle(g); | ||
842 | |||
843 | return snprintf(buf, PAGE_SIZE, "0x%x\n", tpc_fs_mask); | ||
844 | } | ||
845 | |||
846 | static DEVICE_ATTR(tpc_fs_mask, ROOTRW, tpc_fs_mask_read, tpc_fs_mask_store); | ||
847 | |||
848 | static ssize_t min_timeslice_us_read(struct device *dev, | ||
849 | struct device_attribute *attr, char *buf) | ||
850 | { | ||
851 | struct gk20a *g = get_gk20a(dev); | ||
852 | |||
853 | return snprintf(buf, PAGE_SIZE, "%u\n", g->min_timeslice_us); | ||
854 | } | ||
855 | |||
856 | static ssize_t min_timeslice_us_store(struct device *dev, | ||
857 | struct device_attribute *attr, const char *buf, size_t count) | ||
858 | { | ||
859 | struct gk20a *g = get_gk20a(dev); | ||
860 | unsigned long val; | ||
861 | |||
862 | if (kstrtoul(buf, 10, &val) < 0) | ||
863 | return -EINVAL; | ||
864 | |||
865 | if (val > g->max_timeslice_us) | ||
866 | return -EINVAL; | ||
867 | |||
868 | g->min_timeslice_us = val; | ||
869 | |||
870 | return count; | ||
871 | } | ||
872 | |||
873 | static DEVICE_ATTR(min_timeslice_us, ROOTRW, min_timeslice_us_read, | ||
874 | min_timeslice_us_store); | ||
875 | |||
876 | static ssize_t max_timeslice_us_read(struct device *dev, | ||
877 | struct device_attribute *attr, char *buf) | ||
878 | { | ||
879 | struct gk20a *g = get_gk20a(dev); | ||
880 | |||
881 | return snprintf(buf, PAGE_SIZE, "%u\n", g->max_timeslice_us); | ||
882 | } | ||
883 | |||
884 | static ssize_t max_timeslice_us_store(struct device *dev, | ||
885 | struct device_attribute *attr, const char *buf, size_t count) | ||
886 | { | ||
887 | struct gk20a *g = get_gk20a(dev); | ||
888 | unsigned long val; | ||
889 | |||
890 | if (kstrtoul(buf, 10, &val) < 0) | ||
891 | return -EINVAL; | ||
892 | |||
893 | if (val < g->min_timeslice_us) | ||
894 | return -EINVAL; | ||
895 | |||
896 | g->max_timeslice_us = val; | ||
897 | |||
898 | return count; | ||
899 | } | ||
900 | |||
901 | static DEVICE_ATTR(max_timeslice_us, ROOTRW, max_timeslice_us_read, | ||
902 | max_timeslice_us_store); | ||
903 | |||
904 | static ssize_t czf_bypass_store(struct device *dev, | ||
905 | struct device_attribute *attr, const char *buf, size_t count) | ||
906 | { | ||
907 | struct gk20a *g = get_gk20a(dev); | ||
908 | unsigned long val; | ||
909 | |||
910 | if (kstrtoul(buf, 10, &val) < 0) | ||
911 | return -EINVAL; | ||
912 | |||
913 | if (val >= 4) | ||
914 | return -EINVAL; | ||
915 | |||
916 | g->gr.czf_bypass = val; | ||
917 | |||
918 | return count; | ||
919 | } | ||
920 | |||
921 | static ssize_t czf_bypass_read(struct device *dev, | ||
922 | struct device_attribute *attr, char *buf) | ||
923 | { | ||
924 | struct gk20a *g = get_gk20a(dev); | ||
925 | |||
926 | return sprintf(buf, "%d\n", g->gr.czf_bypass); | ||
927 | } | ||
928 | |||
929 | static DEVICE_ATTR(czf_bypass, ROOTRW, czf_bypass_read, czf_bypass_store); | ||
930 | |||
931 | |||
932 | void nvgpu_remove_sysfs(struct device *dev) | ||
933 | { | ||
934 | device_remove_file(dev, &dev_attr_elcg_enable); | ||
935 | device_remove_file(dev, &dev_attr_blcg_enable); | ||
936 | device_remove_file(dev, &dev_attr_slcg_enable); | ||
937 | device_remove_file(dev, &dev_attr_ptimer_scale_factor); | ||
938 | device_remove_file(dev, &dev_attr_ptimer_ref_freq); | ||
939 | device_remove_file(dev, &dev_attr_ptimer_src_freq); | ||
940 | device_remove_file(dev, &dev_attr_elpg_enable); | ||
941 | device_remove_file(dev, &dev_attr_mscg_enable); | ||
942 | device_remove_file(dev, &dev_attr_emc3d_ratio); | ||
943 | device_remove_file(dev, &dev_attr_fmax_at_vmin_safe); | ||
944 | device_remove_file(dev, &dev_attr_counters); | ||
945 | device_remove_file(dev, &dev_attr_counters_reset); | ||
946 | device_remove_file(dev, &dev_attr_load); | ||
947 | device_remove_file(dev, &dev_attr_railgate_delay); | ||
948 | device_remove_file(dev, &dev_attr_is_railgated); | ||
949 | #ifdef CONFIG_PM | ||
950 | device_remove_file(dev, &dev_attr_force_idle); | ||
951 | device_remove_file(dev, &dev_attr_railgate_enable); | ||
952 | #endif | ||
953 | device_remove_file(dev, &dev_attr_aelpg_param); | ||
954 | device_remove_file(dev, &dev_attr_aelpg_enable); | ||
955 | device_remove_file(dev, &dev_attr_allow_all); | ||
956 | device_remove_file(dev, &dev_attr_tpc_fs_mask); | ||
957 | device_remove_file(dev, &dev_attr_min_timeslice_us); | ||
958 | device_remove_file(dev, &dev_attr_max_timeslice_us); | ||
959 | |||
960 | #ifdef CONFIG_TEGRA_GK20A_NVHOST | ||
961 | nvgpu_nvhost_remove_symlink(get_gk20a(dev)); | ||
962 | #endif | ||
963 | |||
964 | device_remove_file(dev, &dev_attr_czf_bypass); | ||
965 | |||
966 | if (strcmp(dev_name(dev), "gpu.0")) { | ||
967 | struct kobject *kobj = &dev->kobj; | ||
968 | struct device *parent = container_of((kobj->parent), | ||
969 | struct device, kobj); | ||
970 | sysfs_remove_link(&parent->kobj, "gpu.0"); | ||
971 | } | ||
972 | } | ||
973 | |||
974 | int nvgpu_create_sysfs(struct device *dev) | ||
975 | { | ||
976 | struct gk20a *g = get_gk20a(dev); | ||
977 | int error = 0; | ||
978 | |||
979 | error |= device_create_file(dev, &dev_attr_elcg_enable); | ||
980 | error |= device_create_file(dev, &dev_attr_blcg_enable); | ||
981 | error |= device_create_file(dev, &dev_attr_slcg_enable); | ||
982 | error |= device_create_file(dev, &dev_attr_ptimer_scale_factor); | ||
983 | error |= device_create_file(dev, &dev_attr_ptimer_ref_freq); | ||
984 | error |= device_create_file(dev, &dev_attr_ptimer_src_freq); | ||
985 | error |= device_create_file(dev, &dev_attr_elpg_enable); | ||
986 | error |= device_create_file(dev, &dev_attr_mscg_enable); | ||
987 | error |= device_create_file(dev, &dev_attr_emc3d_ratio); | ||
988 | error |= device_create_file(dev, &dev_attr_fmax_at_vmin_safe); | ||
989 | error |= device_create_file(dev, &dev_attr_counters); | ||
990 | error |= device_create_file(dev, &dev_attr_counters_reset); | ||
991 | error |= device_create_file(dev, &dev_attr_load); | ||
992 | error |= device_create_file(dev, &dev_attr_railgate_delay); | ||
993 | error |= device_create_file(dev, &dev_attr_is_railgated); | ||
994 | #ifdef CONFIG_PM | ||
995 | error |= device_create_file(dev, &dev_attr_force_idle); | ||
996 | error |= device_create_file(dev, &dev_attr_railgate_enable); | ||
997 | #endif | ||
998 | error |= device_create_file(dev, &dev_attr_aelpg_param); | ||
999 | error |= device_create_file(dev, &dev_attr_aelpg_enable); | ||
1000 | error |= device_create_file(dev, &dev_attr_allow_all); | ||
1001 | error |= device_create_file(dev, &dev_attr_tpc_fs_mask); | ||
1002 | error |= device_create_file(dev, &dev_attr_min_timeslice_us); | ||
1003 | error |= device_create_file(dev, &dev_attr_max_timeslice_us); | ||
1004 | |||
1005 | #ifdef CONFIG_TEGRA_GK20A_NVHOST | ||
1006 | error |= nvgpu_nvhost_create_symlink(g); | ||
1007 | #endif | ||
1008 | |||
1009 | error |= device_create_file(dev, &dev_attr_czf_bypass); | ||
1010 | |||
1011 | if (strcmp(dev_name(dev), "gpu.0")) { | ||
1012 | struct kobject *kobj = &dev->kobj; | ||
1013 | struct device *parent = container_of((kobj->parent), | ||
1014 | struct device, kobj); | ||
1015 | error |= sysfs_create_link(&parent->kobj, | ||
1016 | &dev->kobj, "gpu.0"); | ||
1017 | } | ||
1018 | |||
1019 | if (error) | ||
1020 | nvgpu_err(g, "Failed to create sysfs attributes!\n"); | ||
1021 | |||
1022 | return error; | ||
1023 | } | ||
diff --git a/drivers/gpu/nvgpu/common/linux/sysfs.h b/drivers/gpu/nvgpu/common/linux/sysfs.h new file mode 100644 index 00000000..80925844 --- /dev/null +++ b/drivers/gpu/nvgpu/common/linux/sysfs.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | #ifndef NVGPU_SYSFS_H | ||
17 | #define NVGPU_SYSFS_H | ||
18 | |||
19 | struct device; | ||
20 | |||
21 | int nvgpu_create_sysfs(struct device *dev); | ||
22 | void nvgpu_remove_sysfs(struct device *dev); | ||
23 | |||
24 | #endif | ||