diff options
Diffstat (limited to 'drivers/gpu/nvgpu/gp10b')
-rw-r--r-- | drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c | 793 |
1 files changed, 0 insertions, 793 deletions
diff --git a/drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c b/drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c deleted file mode 100644 index 3a232402..00000000 --- a/drivers/gpu/nvgpu/gp10b/platform_gp10b_tegra.c +++ /dev/null | |||
@@ -1,793 +0,0 @@ | |||
1 | /* | ||
2 | * GP10B Tegra Platform Interface | ||
3 | * | ||
4 | * Copyright (c) 2014-2017, 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 | |||
16 | #include <linux/of_platform.h> | ||
17 | #include <linux/nvhost.h> | ||
18 | #include <linux/debugfs.h> | ||
19 | #include <soc/tegra/tegra_powergate.h> | ||
20 | #include <linux/platform_data/tegra_edp.h> | ||
21 | #include <linux/dma-buf.h> | ||
22 | #include <linux/nvmap.h> | ||
23 | #include <linux/reset.h> | ||
24 | #include <linux/hashtable.h> | ||
25 | |||
26 | #include <linux/platform/tegra/emc_bwmgr.h> | ||
27 | |||
28 | #include <uapi/linux/nvgpu.h> | ||
29 | |||
30 | #include <soc/tegra/tegra_bpmp.h> | ||
31 | |||
32 | #include "gk20a/platform_gk20a.h" | ||
33 | #include "gk20a/gk20a.h" | ||
34 | #include "gk20a/gk20a_scale.h" | ||
35 | |||
36 | #include "platform_tegra.h" | ||
37 | #include "gr_gp10b.h" | ||
38 | #include "ltc_gp10b.h" | ||
39 | #include "gp10b_sysfs.h" | ||
40 | |||
41 | #include <nvgpu/hw/gp10b/hw_gr_gp10b.h> | ||
42 | #include <nvgpu/hw/gp10b/hw_ltc_gp10b.h> | ||
43 | |||
44 | /* Select every GP10B_FREQ_SELECT_STEP'th frequency from h/w table */ | ||
45 | #define GP10B_FREQ_SELECT_STEP 8 | ||
46 | /* Max number of freq supported in h/w */ | ||
47 | #define GP10B_MAX_SUPPORTED_FREQS 120 | ||
48 | static unsigned long | ||
49 | gp10b_freq_table[GP10B_MAX_SUPPORTED_FREQS / GP10B_FREQ_SELECT_STEP]; | ||
50 | |||
51 | #define TEGRA_GP10B_BW_PER_FREQ 64 | ||
52 | #define TEGRA_DDR4_BW_PER_FREQ 16 | ||
53 | |||
54 | #define EMC_BW_RATIO (TEGRA_GP10B_BW_PER_FREQ / TEGRA_DDR4_BW_PER_FREQ) | ||
55 | |||
56 | static struct { | ||
57 | char *name; | ||
58 | unsigned long default_rate; | ||
59 | } tegra_gp10b_clocks[] = { | ||
60 | {"gpu", 1000000000}, | ||
61 | {"gpu_sys", 204000000} }; | ||
62 | |||
63 | static void gr_gp10b_remove_sysfs(struct device *dev); | ||
64 | |||
65 | /* | ||
66 | * gp10b_tegra_get_clocks() | ||
67 | * | ||
68 | * This function finds clocks in tegra platform and populates | ||
69 | * the clock information to gp10b platform data. | ||
70 | */ | ||
71 | |||
72 | int gp10b_tegra_get_clocks(struct device *dev) | ||
73 | { | ||
74 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
75 | unsigned int i; | ||
76 | |||
77 | if (platform->is_fmodel) | ||
78 | return 0; | ||
79 | |||
80 | platform->num_clks = 0; | ||
81 | for (i = 0; i < ARRAY_SIZE(tegra_gp10b_clocks); i++) { | ||
82 | long rate = tegra_gp10b_clocks[i].default_rate; | ||
83 | struct clk *c; | ||
84 | |||
85 | c = clk_get(dev, tegra_gp10b_clocks[i].name); | ||
86 | if (IS_ERR(c)) { | ||
87 | gk20a_err(dev, "cannot get clock %s", | ||
88 | tegra_gp10b_clocks[i].name); | ||
89 | } else { | ||
90 | clk_set_rate(c, rate); | ||
91 | platform->clk[i] = c; | ||
92 | } | ||
93 | } | ||
94 | platform->num_clks = i; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static void gp10b_tegra_scale_init(struct device *dev) | ||
100 | { | ||
101 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
102 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
103 | struct tegra_bwmgr_client *bwmgr_handle; | ||
104 | |||
105 | if (!profile) | ||
106 | return; | ||
107 | |||
108 | bwmgr_handle = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_GPU); | ||
109 | if (!bwmgr_handle) | ||
110 | return; | ||
111 | |||
112 | profile->private_data = (void *)bwmgr_handle; | ||
113 | } | ||
114 | |||
115 | static void gp10b_tegra_scale_exit(struct device *dev) | ||
116 | { | ||
117 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
118 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
119 | |||
120 | if (profile) | ||
121 | tegra_bwmgr_unregister( | ||
122 | (struct tegra_bwmgr_client *)profile->private_data); | ||
123 | } | ||
124 | |||
125 | static int gp10b_tegra_probe(struct device *dev) | ||
126 | { | ||
127 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
128 | struct device_node *np = dev->of_node; | ||
129 | struct device_node *host1x_node; | ||
130 | struct platform_device *host1x_pdev; | ||
131 | const __be32 *host1x_ptr; | ||
132 | |||
133 | host1x_ptr = of_get_property(np, "nvidia,host1x", NULL); | ||
134 | if (!host1x_ptr) { | ||
135 | gk20a_err(dev, "host1x device not available"); | ||
136 | return -ENOSYS; | ||
137 | } | ||
138 | |||
139 | host1x_node = of_find_node_by_phandle(be32_to_cpup(host1x_ptr)); | ||
140 | host1x_pdev = of_find_device_by_node(host1x_node); | ||
141 | if (!host1x_pdev) { | ||
142 | gk20a_err(dev, "host1x device not available"); | ||
143 | return -ENOSYS; | ||
144 | } | ||
145 | |||
146 | platform->g->host1x_dev = host1x_pdev; | ||
147 | platform->bypass_smmu = !device_is_iommuable(dev); | ||
148 | platform->disable_bigpage = platform->bypass_smmu; | ||
149 | |||
150 | platform->g->gr.t18x.ctx_vars.dump_ctxsw_stats_on_channel_close | ||
151 | = false; | ||
152 | platform->g->gr.t18x.ctx_vars.dump_ctxsw_stats_on_channel_close | ||
153 | = false; | ||
154 | |||
155 | platform->g->gr.t18x.ctx_vars.force_preemption_gfxp = false; | ||
156 | platform->g->gr.t18x.ctx_vars.force_preemption_cilp = false; | ||
157 | |||
158 | platform->g->gr.t18x.ctx_vars.debugfs_force_preemption_gfxp = | ||
159 | debugfs_create_bool("force_preemption_gfxp", S_IRUGO|S_IWUSR, | ||
160 | platform->debugfs, | ||
161 | &platform->g->gr.t18x.ctx_vars.force_preemption_gfxp); | ||
162 | |||
163 | platform->g->gr.t18x.ctx_vars.debugfs_force_preemption_cilp = | ||
164 | debugfs_create_bool("force_preemption_cilp", S_IRUGO|S_IWUSR, | ||
165 | platform->debugfs, | ||
166 | &platform->g->gr.t18x.ctx_vars.force_preemption_cilp); | ||
167 | |||
168 | platform->g->gr.t18x.ctx_vars.debugfs_dump_ctxsw_stats = | ||
169 | debugfs_create_bool("dump_ctxsw_stats_on_channel_close", | ||
170 | S_IRUGO|S_IWUSR, | ||
171 | platform->debugfs, | ||
172 | &platform->g->gr.t18x. | ||
173 | ctx_vars.dump_ctxsw_stats_on_channel_close); | ||
174 | |||
175 | platform->g->mm.vidmem_is_vidmem = platform->vidmem_is_vidmem; | ||
176 | |||
177 | gp10b_tegra_get_clocks(dev); | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int gp10b_tegra_late_probe(struct device *dev) | ||
183 | { | ||
184 | /*Create GP10B specific sysfs*/ | ||
185 | gp10b_create_sysfs(dev); | ||
186 | |||
187 | /* Initialise tegra specific scaling quirks */ | ||
188 | gp10b_tegra_scale_init(dev); | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static int gp10b_tegra_remove(struct device *dev) | ||
193 | { | ||
194 | gr_gp10b_remove_sysfs(dev); | ||
195 | /*Remove GP10B specific sysfs*/ | ||
196 | gp10b_remove_sysfs(dev); | ||
197 | |||
198 | /* deinitialise tegra specific scaling quirks */ | ||
199 | gp10b_tegra_scale_exit(dev); | ||
200 | |||
201 | return 0; | ||
202 | |||
203 | } | ||
204 | |||
205 | static bool gp10b_tegra_is_railgated(struct device *dev) | ||
206 | { | ||
207 | bool ret = false; | ||
208 | |||
209 | if (tegra_bpmp_running()) | ||
210 | ret = !tegra_powergate_is_powered(TEGRA186_POWER_DOMAIN_GPU); | ||
211 | |||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static int gp10b_tegra_railgate(struct device *dev) | ||
216 | { | ||
217 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
218 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
219 | |||
220 | /* remove emc frequency floor */ | ||
221 | if (profile) | ||
222 | tegra_bwmgr_set_emc( | ||
223 | (struct tegra_bwmgr_client *)profile->private_data, | ||
224 | 0, TEGRA_BWMGR_SET_EMC_FLOOR); | ||
225 | |||
226 | if (tegra_bpmp_running() && | ||
227 | tegra_powergate_is_powered(TEGRA186_POWER_DOMAIN_GPU)) { | ||
228 | int i; | ||
229 | for (i = 0; i < platform->num_clks; i++) { | ||
230 | if (platform->clk[i]) | ||
231 | clk_disable_unprepare(platform->clk[i]); | ||
232 | } | ||
233 | tegra_powergate_partition(TEGRA186_POWER_DOMAIN_GPU); | ||
234 | } | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int gp10b_tegra_unrailgate(struct device *dev) | ||
239 | { | ||
240 | int ret = 0; | ||
241 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
242 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
243 | |||
244 | if (tegra_bpmp_running()) { | ||
245 | int i; | ||
246 | ret = tegra_unpowergate_partition(TEGRA186_POWER_DOMAIN_GPU); | ||
247 | for (i = 0; i < platform->num_clks; i++) { | ||
248 | if (platform->clk[i]) | ||
249 | clk_prepare_enable(platform->clk[i]); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | /* to start with set emc frequency floor to max rate*/ | ||
254 | if (profile) | ||
255 | tegra_bwmgr_set_emc( | ||
256 | (struct tegra_bwmgr_client *)profile->private_data, | ||
257 | tegra_bwmgr_get_max_emc_rate(), | ||
258 | TEGRA_BWMGR_SET_EMC_FLOOR); | ||
259 | return ret; | ||
260 | } | ||
261 | |||
262 | static int gp10b_tegra_suspend(struct device *dev) | ||
263 | { | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | int gp10b_tegra_reset_assert(struct device *dev) | ||
268 | { | ||
269 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
270 | int ret = 0; | ||
271 | |||
272 | if (!platform->reset_control) | ||
273 | return -EINVAL; | ||
274 | |||
275 | ret = reset_control_assert(platform->reset_control); | ||
276 | |||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | int gp10b_tegra_reset_deassert(struct device *dev) | ||
281 | { | ||
282 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
283 | int ret = 0; | ||
284 | |||
285 | if (!platform->reset_control) | ||
286 | return -EINVAL; | ||
287 | |||
288 | ret = reset_control_deassert(platform->reset_control); | ||
289 | |||
290 | return ret; | ||
291 | } | ||
292 | |||
293 | static void gp10b_tegra_prescale(struct device *dev) | ||
294 | { | ||
295 | struct gk20a *g = get_gk20a(dev); | ||
296 | u32 avg = 0; | ||
297 | |||
298 | gk20a_dbg_fn(""); | ||
299 | |||
300 | gk20a_pmu_load_norm(g, &avg); | ||
301 | |||
302 | gk20a_dbg_fn("done"); | ||
303 | } | ||
304 | |||
305 | static void gp10b_tegra_postscale(struct device *pdev, | ||
306 | unsigned long freq) | ||
307 | { | ||
308 | struct gk20a_platform *platform = gk20a_get_platform(pdev); | ||
309 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
310 | struct gk20a *g = get_gk20a(pdev); | ||
311 | unsigned long emc_rate; | ||
312 | |||
313 | gk20a_dbg_fn(""); | ||
314 | if (profile && !gp10b_tegra_is_railgated(pdev)) { | ||
315 | unsigned long emc_scale; | ||
316 | |||
317 | if (freq <= gp10b_freq_table[0]) | ||
318 | emc_scale = 0; | ||
319 | else | ||
320 | emc_scale = g->emc3d_ratio; | ||
321 | |||
322 | emc_rate = (freq * EMC_BW_RATIO * emc_scale) / 1000; | ||
323 | |||
324 | if (emc_rate > tegra_bwmgr_get_max_emc_rate()) | ||
325 | emc_rate = tegra_bwmgr_get_max_emc_rate(); | ||
326 | |||
327 | tegra_bwmgr_set_emc( | ||
328 | (struct tegra_bwmgr_client *)profile->private_data, | ||
329 | emc_rate, TEGRA_BWMGR_SET_EMC_FLOOR); | ||
330 | } | ||
331 | gk20a_dbg_fn("done"); | ||
332 | } | ||
333 | |||
334 | static unsigned long gp10b_get_clk_rate(struct device *dev) | ||
335 | { | ||
336 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
337 | |||
338 | return clk_get_rate(platform->clk[0]); | ||
339 | |||
340 | } | ||
341 | |||
342 | static long gp10b_round_clk_rate(struct device *dev, unsigned long rate) | ||
343 | { | ||
344 | struct gk20a *g = get_gk20a(dev); | ||
345 | struct gk20a_scale_profile *profile = g->scale_profile; | ||
346 | unsigned long *freq_table = profile->devfreq_profile.freq_table; | ||
347 | int max_states = profile->devfreq_profile.max_state; | ||
348 | int i; | ||
349 | |||
350 | for (i = 0; i < max_states; ++i) | ||
351 | if (freq_table[i] >= rate) | ||
352 | return freq_table[i]; | ||
353 | |||
354 | return freq_table[max_states - 1]; | ||
355 | } | ||
356 | |||
357 | static int gp10b_set_clk_rate(struct device *dev, unsigned long rate) | ||
358 | { | ||
359 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
360 | |||
361 | return clk_set_rate(platform->clk[0], rate); | ||
362 | } | ||
363 | |||
364 | static int gp10b_clk_get_freqs(struct device *dev, | ||
365 | unsigned long **freqs, int *num_freqs) | ||
366 | { | ||
367 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
368 | unsigned long max_rate; | ||
369 | unsigned long new_rate = 0, prev_rate = 0; | ||
370 | int i = 0, freq_counter = 0; | ||
371 | |||
372 | max_rate = clk_round_rate(platform->clk[0], (UINT_MAX - 1)); | ||
373 | |||
374 | /* | ||
375 | * Walk the h/w frequency table and only select | ||
376 | * GP10B_FREQ_SELECT_STEP'th frequencies and | ||
377 | * add MAX freq to last | ||
378 | */ | ||
379 | for (; i < GP10B_MAX_SUPPORTED_FREQS; ++i) { | ||
380 | prev_rate = new_rate; | ||
381 | new_rate = clk_round_rate(platform->clk[0], prev_rate + 1); | ||
382 | |||
383 | if (i % GP10B_FREQ_SELECT_STEP == 0 || | ||
384 | new_rate == max_rate) { | ||
385 | gp10b_freq_table[freq_counter++] = new_rate; | ||
386 | |||
387 | if (new_rate == max_rate) | ||
388 | break; | ||
389 | } | ||
390 | } | ||
391 | |||
392 | WARN_ON(i == GP10B_MAX_SUPPORTED_FREQS); | ||
393 | |||
394 | /* Fill freq table */ | ||
395 | *freqs = gp10b_freq_table; | ||
396 | *num_freqs = freq_counter; | ||
397 | |||
398 | gk20a_dbg_info("min rate: %ld max rate: %ld num_of_freq %d\n", | ||
399 | gp10b_freq_table[0], max_rate, *num_freqs); | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | struct gk20a_platform gp10b_tegra_platform = { | ||
405 | .has_syncpoints = true, | ||
406 | |||
407 | /* power management configuration */ | ||
408 | .railgate_delay = 500, | ||
409 | |||
410 | /* power management configuration */ | ||
411 | .can_railgate = true, | ||
412 | .enable_elpg = true, | ||
413 | .can_elpg = true, | ||
414 | .enable_blcg = true, | ||
415 | .enable_slcg = true, | ||
416 | .enable_elcg = true, | ||
417 | .enable_aelpg = true, | ||
418 | |||
419 | /* ptimer src frequency in hz*/ | ||
420 | .ptimer_src_freq = 31250000, | ||
421 | |||
422 | .ch_wdt_timeout_ms = 5000, | ||
423 | |||
424 | .probe = gp10b_tegra_probe, | ||
425 | .late_probe = gp10b_tegra_late_probe, | ||
426 | .remove = gp10b_tegra_remove, | ||
427 | |||
428 | /* power management callbacks */ | ||
429 | .suspend = gp10b_tegra_suspend, | ||
430 | .railgate = gp10b_tegra_railgate, | ||
431 | .unrailgate = gp10b_tegra_unrailgate, | ||
432 | .is_railgated = gp10b_tegra_is_railgated, | ||
433 | |||
434 | .busy = gk20a_tegra_busy, | ||
435 | .idle = gk20a_tegra_idle, | ||
436 | |||
437 | .dump_platform_dependencies = gk20a_tegra_debug_dump, | ||
438 | |||
439 | .default_big_page_size = SZ_64K, | ||
440 | |||
441 | .has_cde = true, | ||
442 | |||
443 | .has_ce = true, | ||
444 | |||
445 | .clk_get_rate = gp10b_get_clk_rate, | ||
446 | .clk_round_rate = gp10b_round_clk_rate, | ||
447 | .clk_set_rate = gp10b_set_clk_rate, | ||
448 | .get_clk_freqs = gp10b_clk_get_freqs, | ||
449 | |||
450 | /* frequency scaling configuration */ | ||
451 | .prescale = gp10b_tegra_prescale, | ||
452 | .postscale = gp10b_tegra_postscale, | ||
453 | .devfreq_governor = "nvhost_podgov", | ||
454 | |||
455 | .qos_notify = gk20a_scale_qos_notify, | ||
456 | |||
457 | .secure_alloc = gk20a_tegra_secure_alloc, | ||
458 | .secure_page_alloc = gk20a_tegra_secure_page_alloc, | ||
459 | |||
460 | .reset_assert = gp10b_tegra_reset_assert, | ||
461 | .reset_deassert = gp10b_tegra_reset_deassert, | ||
462 | |||
463 | .force_reset_in_do_idle = false, | ||
464 | |||
465 | .soc_name = "tegra18x", | ||
466 | |||
467 | .vidmem_is_vidmem = false, | ||
468 | }; | ||
469 | |||
470 | |||
471 | #define ECC_STAT_NAME_MAX_SIZE 100 | ||
472 | |||
473 | |||
474 | static DEFINE_HASHTABLE(ecc_hash_table, 5); | ||
475 | |||
476 | static struct device_attribute *dev_attr_sm_lrf_ecc_single_err_count_array; | ||
477 | static struct device_attribute *dev_attr_sm_lrf_ecc_double_err_count_array; | ||
478 | |||
479 | static struct device_attribute *dev_attr_sm_shm_ecc_sec_count_array; | ||
480 | static struct device_attribute *dev_attr_sm_shm_ecc_sed_count_array; | ||
481 | static struct device_attribute *dev_attr_sm_shm_ecc_ded_count_array; | ||
482 | |||
483 | static struct device_attribute *dev_attr_tex_ecc_total_sec_pipe0_count_array; | ||
484 | static struct device_attribute *dev_attr_tex_ecc_total_ded_pipe0_count_array; | ||
485 | static struct device_attribute *dev_attr_tex_ecc_unique_sec_pipe0_count_array; | ||
486 | static struct device_attribute *dev_attr_tex_ecc_unique_ded_pipe0_count_array; | ||
487 | static struct device_attribute *dev_attr_tex_ecc_total_sec_pipe1_count_array; | ||
488 | static struct device_attribute *dev_attr_tex_ecc_total_ded_pipe1_count_array; | ||
489 | static struct device_attribute *dev_attr_tex_ecc_unique_sec_pipe1_count_array; | ||
490 | static struct device_attribute *dev_attr_tex_ecc_unique_ded_pipe1_count_array; | ||
491 | |||
492 | static struct device_attribute *dev_attr_l2_ecc_sec_count_array; | ||
493 | static struct device_attribute *dev_attr_l2_ecc_ded_count_array; | ||
494 | |||
495 | |||
496 | static u32 gen_ecc_hash_key(char *str) | ||
497 | { | ||
498 | int i = 0; | ||
499 | u32 hash_key = 0; | ||
500 | |||
501 | while (str[i]) { | ||
502 | hash_key += (u32)(str[i]); | ||
503 | i++; | ||
504 | }; | ||
505 | |||
506 | return hash_key; | ||
507 | } | ||
508 | |||
509 | static ssize_t ecc_stat_show(struct device *dev, | ||
510 | struct device_attribute *attr, | ||
511 | char *buf) | ||
512 | { | ||
513 | const char *ecc_stat_full_name = attr->attr.name; | ||
514 | const char *ecc_stat_base_name; | ||
515 | unsigned int hw_unit; | ||
516 | struct ecc_stat *ecc_stat; | ||
517 | u32 hash_key; | ||
518 | |||
519 | if (sscanf(ecc_stat_full_name, "ltc%u", &hw_unit) == 1) { | ||
520 | ecc_stat_base_name = &(ecc_stat_full_name[strlen("ltc0_")]); | ||
521 | } else if (sscanf(ecc_stat_full_name, "gpc0_tpc%u", &hw_unit) == 1) { | ||
522 | ecc_stat_base_name = &(ecc_stat_full_name[strlen("gpc0_tpc0_")]); | ||
523 | } else { | ||
524 | return snprintf(buf, | ||
525 | PAGE_SIZE, | ||
526 | "Error: Invalid ECC stat name!\n"); | ||
527 | } | ||
528 | |||
529 | hash_key = gen_ecc_hash_key((char *)ecc_stat_base_name); | ||
530 | hash_for_each_possible(ecc_hash_table, | ||
531 | ecc_stat, | ||
532 | hash_node, | ||
533 | hash_key) { | ||
534 | if (!strcmp(ecc_stat_full_name, ecc_stat->names[hw_unit])) | ||
535 | return snprintf(buf, PAGE_SIZE, "%u\n", ecc_stat->counters[hw_unit]); | ||
536 | } | ||
537 | |||
538 | return snprintf(buf, PAGE_SIZE, "Error: No ECC stat found!\n"); | ||
539 | } | ||
540 | |||
541 | static int ecc_stat_create(struct device *dev, | ||
542 | int is_l2, | ||
543 | char *ecc_stat_name, | ||
544 | struct ecc_stat *ecc_stat, | ||
545 | struct device_attribute *dev_attr_array) | ||
546 | { | ||
547 | int error = 0; | ||
548 | struct gk20a *g = get_gk20a(dev); | ||
549 | int num_hw_units = 0; | ||
550 | int hw_unit = 0; | ||
551 | u32 hash_key = 0; | ||
552 | |||
553 | if (is_l2) | ||
554 | num_hw_units = g->ltc_count; | ||
555 | else | ||
556 | num_hw_units = g->gr.tpc_count; | ||
557 | |||
558 | /* Allocate arrays */ | ||
559 | dev_attr_array = kzalloc(sizeof(struct device_attribute) * num_hw_units, GFP_KERNEL); | ||
560 | ecc_stat->counters = kzalloc(sizeof(u32) * num_hw_units, GFP_KERNEL); | ||
561 | ecc_stat->names = kzalloc(sizeof(char *) * num_hw_units, GFP_KERNEL); | ||
562 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
563 | ecc_stat->names[hw_unit] = kzalloc(sizeof(char) * ECC_STAT_NAME_MAX_SIZE, GFP_KERNEL); | ||
564 | } | ||
565 | |||
566 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
567 | /* Fill in struct device_attribute members */ | ||
568 | if (is_l2) | ||
569 | snprintf(ecc_stat->names[hw_unit], | ||
570 | ECC_STAT_NAME_MAX_SIZE, | ||
571 | "ltc%d_%s", | ||
572 | hw_unit, | ||
573 | ecc_stat_name); | ||
574 | else | ||
575 | snprintf(ecc_stat->names[hw_unit], | ||
576 | ECC_STAT_NAME_MAX_SIZE, | ||
577 | "gpc0_tpc%d_%s", | ||
578 | hw_unit, | ||
579 | ecc_stat_name); | ||
580 | |||
581 | sysfs_attr_init(&dev_attr_array[hw_unit].attr); | ||
582 | dev_attr_array[hw_unit].attr.name = ecc_stat->names[hw_unit]; | ||
583 | dev_attr_array[hw_unit].attr.mode = VERIFY_OCTAL_PERMISSIONS(S_IRUGO); | ||
584 | dev_attr_array[hw_unit].show = ecc_stat_show; | ||
585 | dev_attr_array[hw_unit].store = NULL; | ||
586 | |||
587 | /* Create sysfs file */ | ||
588 | error |= device_create_file(dev, &dev_attr_array[hw_unit]); | ||
589 | } | ||
590 | |||
591 | /* Add hash table entry */ | ||
592 | hash_key = gen_ecc_hash_key(ecc_stat_name); | ||
593 | hash_add(ecc_hash_table, | ||
594 | &ecc_stat->hash_node, | ||
595 | hash_key); | ||
596 | |||
597 | return error; | ||
598 | } | ||
599 | |||
600 | static void ecc_stat_remove(struct device *dev, | ||
601 | int is_l2, | ||
602 | struct ecc_stat *ecc_stat, | ||
603 | struct device_attribute *dev_attr_array) | ||
604 | { | ||
605 | struct gk20a *g = get_gk20a(dev); | ||
606 | int num_hw_units = 0; | ||
607 | int hw_unit = 0; | ||
608 | |||
609 | if (is_l2) | ||
610 | num_hw_units = g->ltc_count; | ||
611 | else | ||
612 | num_hw_units = g->gr.tpc_count; | ||
613 | |||
614 | /* Remove sysfs files */ | ||
615 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
616 | device_remove_file(dev, &dev_attr_array[hw_unit]); | ||
617 | } | ||
618 | |||
619 | /* Remove hash table entry */ | ||
620 | hash_del(&ecc_stat->hash_node); | ||
621 | |||
622 | /* Free arrays */ | ||
623 | kfree(ecc_stat->counters); | ||
624 | for (hw_unit = 0; hw_unit < num_hw_units; hw_unit++) { | ||
625 | kfree(ecc_stat->names[hw_unit]); | ||
626 | } | ||
627 | kfree(ecc_stat->names); | ||
628 | kfree(dev_attr_array); | ||
629 | } | ||
630 | |||
631 | void gr_gp10b_create_sysfs(struct device *dev) | ||
632 | { | ||
633 | int error = 0; | ||
634 | struct gk20a *g = get_gk20a(dev); | ||
635 | |||
636 | /* This stat creation function is called on GR init. GR can get | ||
637 | initialized multiple times but we only need to create the ECC | ||
638 | stats once. Therefore, add the following check to avoid | ||
639 | creating duplicate stat sysfs nodes. */ | ||
640 | if (g->gr.t18x.ecc_stats.sm_lrf_single_err_count.counters != NULL) | ||
641 | return; | ||
642 | |||
643 | error |= ecc_stat_create(dev, | ||
644 | 0, | ||
645 | "sm_lrf_ecc_single_err_count", | ||
646 | &g->gr.t18x.ecc_stats.sm_lrf_single_err_count, | ||
647 | dev_attr_sm_lrf_ecc_single_err_count_array); | ||
648 | error |= ecc_stat_create(dev, | ||
649 | 0, | ||
650 | "sm_lrf_ecc_double_err_count", | ||
651 | &g->gr.t18x.ecc_stats.sm_lrf_double_err_count, | ||
652 | dev_attr_sm_lrf_ecc_double_err_count_array); | ||
653 | |||
654 | error |= ecc_stat_create(dev, | ||
655 | 0, | ||
656 | "sm_shm_ecc_sec_count", | ||
657 | &g->gr.t18x.ecc_stats.sm_shm_sec_count, | ||
658 | dev_attr_sm_shm_ecc_sec_count_array); | ||
659 | error |= ecc_stat_create(dev, | ||
660 | 0, | ||
661 | "sm_shm_ecc_sed_count", | ||
662 | &g->gr.t18x.ecc_stats.sm_shm_sed_count, | ||
663 | dev_attr_sm_shm_ecc_sed_count_array); | ||
664 | error |= ecc_stat_create(dev, | ||
665 | 0, | ||
666 | "sm_shm_ecc_ded_count", | ||
667 | &g->gr.t18x.ecc_stats.sm_shm_ded_count, | ||
668 | dev_attr_sm_shm_ecc_ded_count_array); | ||
669 | |||
670 | error |= ecc_stat_create(dev, | ||
671 | 0, | ||
672 | "tex_ecc_total_sec_pipe0_count", | ||
673 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe0_count, | ||
674 | dev_attr_tex_ecc_total_sec_pipe0_count_array); | ||
675 | error |= ecc_stat_create(dev, | ||
676 | 0, | ||
677 | "tex_ecc_total_ded_pipe0_count", | ||
678 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe0_count, | ||
679 | dev_attr_tex_ecc_total_ded_pipe0_count_array); | ||
680 | error |= ecc_stat_create(dev, | ||
681 | 0, | ||
682 | "tex_ecc_unique_sec_pipe0_count", | ||
683 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe0_count, | ||
684 | dev_attr_tex_ecc_unique_sec_pipe0_count_array); | ||
685 | error |= ecc_stat_create(dev, | ||
686 | 0, | ||
687 | "tex_ecc_unique_ded_pipe0_count", | ||
688 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe0_count, | ||
689 | dev_attr_tex_ecc_unique_ded_pipe0_count_array); | ||
690 | error |= ecc_stat_create(dev, | ||
691 | 0, | ||
692 | "tex_ecc_total_sec_pipe1_count", | ||
693 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe1_count, | ||
694 | dev_attr_tex_ecc_total_sec_pipe1_count_array); | ||
695 | error |= ecc_stat_create(dev, | ||
696 | 0, | ||
697 | "tex_ecc_total_ded_pipe1_count", | ||
698 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe1_count, | ||
699 | dev_attr_tex_ecc_total_ded_pipe1_count_array); | ||
700 | error |= ecc_stat_create(dev, | ||
701 | 0, | ||
702 | "tex_ecc_unique_sec_pipe1_count", | ||
703 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe1_count, | ||
704 | dev_attr_tex_ecc_unique_sec_pipe1_count_array); | ||
705 | error |= ecc_stat_create(dev, | ||
706 | 0, | ||
707 | "tex_ecc_unique_ded_pipe1_count", | ||
708 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe1_count, | ||
709 | dev_attr_tex_ecc_unique_ded_pipe1_count_array); | ||
710 | |||
711 | error |= ecc_stat_create(dev, | ||
712 | 1, | ||
713 | "lts0_ecc_sec_count", | ||
714 | &g->gr.t18x.ecc_stats.l2_sec_count, | ||
715 | dev_attr_l2_ecc_sec_count_array); | ||
716 | error |= ecc_stat_create(dev, | ||
717 | 1, | ||
718 | "lts0_ecc_ded_count", | ||
719 | &g->gr.t18x.ecc_stats.l2_ded_count, | ||
720 | dev_attr_l2_ecc_ded_count_array); | ||
721 | |||
722 | if (error) | ||
723 | dev_err(dev, "Failed to create sysfs attributes!\n"); | ||
724 | } | ||
725 | |||
726 | static void gr_gp10b_remove_sysfs(struct device *dev) | ||
727 | { | ||
728 | struct gk20a *g = get_gk20a(dev); | ||
729 | |||
730 | ecc_stat_remove(dev, | ||
731 | 0, | ||
732 | &g->gr.t18x.ecc_stats.sm_lrf_single_err_count, | ||
733 | dev_attr_sm_lrf_ecc_single_err_count_array); | ||
734 | ecc_stat_remove(dev, | ||
735 | 0, | ||
736 | &g->gr.t18x.ecc_stats.sm_lrf_double_err_count, | ||
737 | dev_attr_sm_lrf_ecc_double_err_count_array); | ||
738 | |||
739 | ecc_stat_remove(dev, | ||
740 | 0, | ||
741 | &g->gr.t18x.ecc_stats.sm_shm_sec_count, | ||
742 | dev_attr_sm_shm_ecc_sec_count_array); | ||
743 | ecc_stat_remove(dev, | ||
744 | 0, | ||
745 | &g->gr.t18x.ecc_stats.sm_shm_sed_count, | ||
746 | dev_attr_sm_shm_ecc_sed_count_array); | ||
747 | ecc_stat_remove(dev, | ||
748 | 0, | ||
749 | &g->gr.t18x.ecc_stats.sm_shm_ded_count, | ||
750 | dev_attr_sm_shm_ecc_ded_count_array); | ||
751 | |||
752 | ecc_stat_remove(dev, | ||
753 | 0, | ||
754 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe0_count, | ||
755 | dev_attr_tex_ecc_total_sec_pipe0_count_array); | ||
756 | ecc_stat_remove(dev, | ||
757 | 0, | ||
758 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe0_count, | ||
759 | dev_attr_tex_ecc_total_ded_pipe0_count_array); | ||
760 | ecc_stat_remove(dev, | ||
761 | 0, | ||
762 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe0_count, | ||
763 | dev_attr_tex_ecc_unique_sec_pipe0_count_array); | ||
764 | ecc_stat_remove(dev, | ||
765 | 0, | ||
766 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe0_count, | ||
767 | dev_attr_tex_ecc_unique_ded_pipe0_count_array); | ||
768 | ecc_stat_remove(dev, | ||
769 | 0, | ||
770 | &g->gr.t18x.ecc_stats.tex_total_sec_pipe1_count, | ||
771 | dev_attr_tex_ecc_total_sec_pipe1_count_array); | ||
772 | ecc_stat_remove(dev, | ||
773 | 0, | ||
774 | &g->gr.t18x.ecc_stats.tex_total_ded_pipe1_count, | ||
775 | dev_attr_tex_ecc_total_ded_pipe1_count_array); | ||
776 | ecc_stat_remove(dev, | ||
777 | 0, | ||
778 | &g->gr.t18x.ecc_stats.tex_unique_sec_pipe1_count, | ||
779 | dev_attr_tex_ecc_unique_sec_pipe1_count_array); | ||
780 | ecc_stat_remove(dev, | ||
781 | 0, | ||
782 | &g->gr.t18x.ecc_stats.tex_unique_ded_pipe1_count, | ||
783 | dev_attr_tex_ecc_unique_ded_pipe1_count_array); | ||
784 | |||
785 | ecc_stat_remove(dev, | ||
786 | 1, | ||
787 | &g->gr.t18x.ecc_stats.l2_sec_count, | ||
788 | dev_attr_l2_ecc_sec_count_array); | ||
789 | ecc_stat_remove(dev, | ||
790 | 1, | ||
791 | &g->gr.t18x.ecc_stats.l2_ded_count, | ||
792 | dev_attr_l2_ecc_ded_count_array); | ||
793 | } | ||