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