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