diff options
Diffstat (limited to 'drivers/gpu/nvgpu/tegra/linux')
-rw-r--r-- | drivers/gpu/nvgpu/tegra/linux/platform_gk20a_tegra.c | 1158 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/tegra/linux/platform_gp10b_tegra.c | 791 |
2 files changed, 1949 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/tegra/linux/platform_gk20a_tegra.c b/drivers/gpu/nvgpu/tegra/linux/platform_gk20a_tegra.c new file mode 100644 index 00000000..56ba1ecd --- /dev/null +++ b/drivers/gpu/nvgpu/tegra/linux/platform_gk20a_tegra.c | |||
@@ -0,0 +1,1158 @@ | |||
1 | /* | ||
2 | * GK20A 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/version.h> | ||
17 | #include <linux/of_platform.h> | ||
18 | #include <linux/nvhost.h> | ||
19 | #include <linux/debugfs.h> | ||
20 | #include <linux/platform_data/tegra_edp.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <uapi/linux/nvgpu.h> | ||
23 | #include <linux/dma-buf.h> | ||
24 | #include <linux/nvmap.h> | ||
25 | #include <linux/reset.h> | ||
26 | #include <linux/tegra_soctherm.h> | ||
27 | #include <linux/platform/tegra/clock.h> | ||
28 | #if defined(CONFIG_TEGRA_CLK_FRAMEWORK) | ||
29 | #include <linux/platform/tegra/dvfs.h> | ||
30 | #endif | ||
31 | #include <linux/platform/tegra/common.h> | ||
32 | #include <linux/platform/tegra/mc.h> | ||
33 | #include <linux/clk/tegra.h> | ||
34 | #if defined(CONFIG_COMMON_CLK) | ||
35 | #include <soc/tegra/tegra-dvfs.h> | ||
36 | #endif | ||
37 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) | ||
38 | #include <soc/tegra/fuse.h> | ||
39 | #endif | ||
40 | #ifdef CONFIG_TEGRA_BWMGR | ||
41 | #include <linux/platform/tegra/emc_bwmgr.h> | ||
42 | #endif | ||
43 | |||
44 | #include <linux/platform/tegra/tegra_emc.h> | ||
45 | |||
46 | #include "gk20a/gk20a.h" | ||
47 | #include "gk20a/hal_gk20a.h" | ||
48 | #include "gk20a/platform_gk20a.h" | ||
49 | #include "gk20a/gk20a_scale.h" | ||
50 | #include "gm20b/clk_gm20b.h" | ||
51 | |||
52 | #define TEGRA_GK20A_BW_PER_FREQ 32 | ||
53 | #define TEGRA_GM20B_BW_PER_FREQ 64 | ||
54 | #define TEGRA_DDR3_BW_PER_FREQ 16 | ||
55 | #define TEGRA_DDR4_BW_PER_FREQ 16 | ||
56 | #define MC_CLIENT_GPU 34 | ||
57 | #define PMC_GPU_RG_CNTRL_0 0x2d4 | ||
58 | |||
59 | #ifdef CONFIG_COMMON_CLK | ||
60 | #define GPU_RAIL_NAME "vdd-gpu" | ||
61 | #else | ||
62 | #define GPU_RAIL_NAME "vdd_gpu" | ||
63 | #endif | ||
64 | |||
65 | extern struct device tegra_vpr_dev; | ||
66 | |||
67 | #ifdef CONFIG_TEGRA_BWMGR | ||
68 | struct gk20a_emc_params { | ||
69 | unsigned long bw_ratio; | ||
70 | unsigned long freq_last_set; | ||
71 | struct tegra_bwmgr_client *bwmgr_cl; | ||
72 | }; | ||
73 | #else | ||
74 | struct gk20a_emc_params { | ||
75 | unsigned long bw_ratio; | ||
76 | unsigned long freq_last_set; | ||
77 | }; | ||
78 | #endif | ||
79 | |||
80 | static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); | ||
81 | static inline u32 __maybe_unused pmc_read(unsigned long reg) | ||
82 | { | ||
83 | return readl(pmc + reg); | ||
84 | } | ||
85 | |||
86 | static inline void __maybe_unused pmc_write(u32 val, unsigned long reg) | ||
87 | { | ||
88 | writel_relaxed(val, pmc + reg); | ||
89 | } | ||
90 | #define MHZ_TO_HZ(x) ((x) * 1000000) | ||
91 | #define HZ_TO_MHZ(x) ((x) / 1000000) | ||
92 | |||
93 | static void gk20a_tegra_secure_page_destroy(struct device *dev, | ||
94 | struct secure_page_buffer *secure_buffer) | ||
95 | { | ||
96 | dma_free_attrs(&tegra_vpr_dev, secure_buffer->size, | ||
97 | (void *)(uintptr_t)secure_buffer->iova, | ||
98 | secure_buffer->iova, &secure_buffer->attrs); | ||
99 | } | ||
100 | |||
101 | int gk20a_tegra_secure_page_alloc(struct device *dev) | ||
102 | { | ||
103 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
104 | struct secure_page_buffer *secure_buffer = &platform->secure_buffer; | ||
105 | DEFINE_DMA_ATTRS(attrs); | ||
106 | dma_addr_t iova; | ||
107 | size_t size = PAGE_SIZE; | ||
108 | |||
109 | if (platform->is_fmodel) | ||
110 | return -EINVAL; | ||
111 | |||
112 | (void)dma_alloc_attrs(&tegra_vpr_dev, size, &iova, | ||
113 | DMA_MEMORY_NOMAP, &attrs); | ||
114 | if (dma_mapping_error(&tegra_vpr_dev, iova)) | ||
115 | return -ENOMEM; | ||
116 | |||
117 | secure_buffer->size = size; | ||
118 | secure_buffer->iova = iova; | ||
119 | secure_buffer->attrs = attrs; | ||
120 | secure_buffer->destroy = gk20a_tegra_secure_page_destroy; | ||
121 | |||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static void gk20a_tegra_secure_destroy(struct gk20a *g, | ||
126 | struct gr_ctx_buffer_desc *desc) | ||
127 | { | ||
128 | DEFINE_DMA_ATTRS(attrs); | ||
129 | |||
130 | if (desc->mem.sgt) { | ||
131 | phys_addr_t pa = sg_phys(desc->mem.sgt->sgl); | ||
132 | dma_free_attrs(&tegra_vpr_dev, desc->mem.size, | ||
133 | (void *)(uintptr_t)pa, | ||
134 | pa, &attrs); | ||
135 | gk20a_free_sgtable(&desc->mem.sgt); | ||
136 | desc->mem.sgt = NULL; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | int gk20a_tegra_secure_alloc(struct device *dev, | ||
141 | struct gr_ctx_buffer_desc *desc, | ||
142 | size_t size) | ||
143 | { | ||
144 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
145 | DEFINE_DMA_ATTRS(attrs); | ||
146 | dma_addr_t iova; | ||
147 | struct sg_table *sgt; | ||
148 | struct page *page; | ||
149 | int err = 0; | ||
150 | |||
151 | if (!platform->secure_alloc_ready) | ||
152 | return -EINVAL; | ||
153 | |||
154 | (void)dma_alloc_attrs(&tegra_vpr_dev, size, &iova, | ||
155 | DMA_MEMORY_NOMAP, &attrs); | ||
156 | if (dma_mapping_error(&tegra_vpr_dev, iova)) | ||
157 | return -ENOMEM; | ||
158 | |||
159 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | ||
160 | if (!sgt) { | ||
161 | gk20a_err(dev, "failed to allocate memory\n"); | ||
162 | goto fail; | ||
163 | } | ||
164 | err = sg_alloc_table(sgt, 1, GFP_KERNEL); | ||
165 | if (err) { | ||
166 | gk20a_err(dev, "failed to allocate sg_table\n"); | ||
167 | goto fail_sgt; | ||
168 | } | ||
169 | page = phys_to_page(iova); | ||
170 | sg_set_page(sgt->sgl, page, size, 0); | ||
171 | /* This bypasses SMMU for VPR during gmmu_map. */ | ||
172 | sg_dma_address(sgt->sgl) = 0; | ||
173 | |||
174 | desc->destroy = gk20a_tegra_secure_destroy; | ||
175 | |||
176 | desc->mem.sgt = sgt; | ||
177 | desc->mem.size = size; | ||
178 | desc->mem.aperture = APERTURE_SYSMEM; | ||
179 | |||
180 | return err; | ||
181 | |||
182 | fail_sgt: | ||
183 | kfree(sgt); | ||
184 | fail: | ||
185 | dma_free_attrs(&tegra_vpr_dev, desc->mem.size, | ||
186 | (void *)(uintptr_t)iova, iova, &attrs); | ||
187 | return err; | ||
188 | } | ||
189 | |||
190 | /* | ||
191 | * gk20a_tegra_get_emc_rate() | ||
192 | * | ||
193 | * This function returns the minimum emc clock based on gpu frequency | ||
194 | */ | ||
195 | |||
196 | static unsigned long gk20a_tegra_get_emc_rate(struct gk20a *g, | ||
197 | struct gk20a_emc_params *emc_params) | ||
198 | { | ||
199 | unsigned long gpu_freq, gpu_fmax_at_vmin; | ||
200 | unsigned long emc_rate, emc_scale; | ||
201 | |||
202 | gpu_freq = clk_get_rate(g->clk.tegra_clk); | ||
203 | gpu_fmax_at_vmin = tegra_dvfs_get_fmax_at_vmin_safe_t( | ||
204 | clk_get_parent(g->clk.tegra_clk)); | ||
205 | |||
206 | /* When scaling emc, account for the gpu load when the | ||
207 | * gpu frequency is less than or equal to fmax@vmin. */ | ||
208 | if (gpu_freq <= gpu_fmax_at_vmin) | ||
209 | emc_scale = min(g->pmu.load_avg, g->emc3d_ratio); | ||
210 | else | ||
211 | emc_scale = g->emc3d_ratio; | ||
212 | |||
213 | emc_rate = | ||
214 | (HZ_TO_MHZ(gpu_freq) * emc_params->bw_ratio * emc_scale) / 1000; | ||
215 | |||
216 | return MHZ_TO_HZ(emc_rate); | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * gk20a_tegra_postscale(profile, freq) | ||
221 | * | ||
222 | * This function sets emc frequency based on current gpu frequency | ||
223 | */ | ||
224 | |||
225 | static void gk20a_tegra_postscale(struct device *dev, | ||
226 | unsigned long freq) | ||
227 | { | ||
228 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
229 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
230 | struct gk20a_emc_params *emc_params = profile->private_data; | ||
231 | struct gk20a *g = get_gk20a(dev); | ||
232 | struct clk *emc_clk = platform->clk[2]; | ||
233 | enum tegra_chipid chip_id = tegra_get_chip_id(); | ||
234 | unsigned long emc_target; | ||
235 | unsigned long emc_freq_lower, emc_freq_upper, emc_freq_rounded; | ||
236 | |||
237 | emc_target = gk20a_tegra_get_emc_rate(g, emc_params); | ||
238 | |||
239 | switch (chip_id) { | ||
240 | case TEGRA124: | ||
241 | case TEGRA132: | ||
242 | /* T124 and T132 don't apply any rounding. The resulting | ||
243 | * emc frequency gets implicitly rounded up after issuing | ||
244 | * the clock_set_request. | ||
245 | * So explicitly round up the emc target here to achieve | ||
246 | * the same outcome. */ | ||
247 | emc_freq_rounded = | ||
248 | tegra_emc_round_rate_updown(emc_target, true); | ||
249 | break; | ||
250 | |||
251 | case TEGRA210: | ||
252 | emc_freq_lower = (unsigned long) | ||
253 | tegra_emc_round_rate_updown(emc_target, false); | ||
254 | emc_freq_upper = (unsigned long) | ||
255 | tegra_emc_round_rate_updown(emc_target, true); | ||
256 | |||
257 | /* round to the nearest frequency step */ | ||
258 | if (emc_target < (emc_freq_lower + emc_freq_upper) / 2) | ||
259 | emc_freq_rounded = emc_freq_lower; | ||
260 | else | ||
261 | emc_freq_rounded = emc_freq_upper; | ||
262 | break; | ||
263 | |||
264 | default: | ||
265 | /* a proper rounding function needs to be implemented | ||
266 | * for emc in t18x */ | ||
267 | emc_freq_rounded = clk_round_rate(emc_clk, emc_target); | ||
268 | break; | ||
269 | } | ||
270 | |||
271 | /* only change the emc clock if new rounded frequency is different | ||
272 | * from previously set emc rate */ | ||
273 | if (emc_freq_rounded != emc_params->freq_last_set) { | ||
274 | clk_set_rate(emc_clk, emc_freq_rounded); | ||
275 | emc_params->freq_last_set = emc_freq_rounded; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * gk20a_tegra_prescale(profile, freq) | ||
281 | * | ||
282 | * This function informs EDP about changed constraints. | ||
283 | */ | ||
284 | |||
285 | static void gk20a_tegra_prescale(struct device *dev) | ||
286 | { | ||
287 | struct gk20a *g = get_gk20a(dev); | ||
288 | u32 avg = 0; | ||
289 | |||
290 | gk20a_pmu_load_norm(g, &avg); | ||
291 | tegra_edp_notify_gpu_load(avg, clk_get_rate(g->clk.tegra_clk)); | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * gk20a_tegra_calibrate_emc() | ||
296 | * | ||
297 | */ | ||
298 | |||
299 | static void gk20a_tegra_calibrate_emc(struct device *dev, | ||
300 | struct gk20a_emc_params *emc_params) | ||
301 | { | ||
302 | enum tegra_chipid cid = tegra_get_chip_id(); | ||
303 | long gpu_bw, emc_bw; | ||
304 | |||
305 | /* store gpu bw based on soc */ | ||
306 | switch (cid) { | ||
307 | case TEGRA210: | ||
308 | gpu_bw = TEGRA_GM20B_BW_PER_FREQ; | ||
309 | break; | ||
310 | case TEGRA124: | ||
311 | case TEGRA132: | ||
312 | gpu_bw = TEGRA_GK20A_BW_PER_FREQ; | ||
313 | break; | ||
314 | default: | ||
315 | gpu_bw = 0; | ||
316 | break; | ||
317 | } | ||
318 | |||
319 | /* TODO detect DDR type. | ||
320 | * Okay for now since DDR3 and DDR4 have the same BW ratio */ | ||
321 | emc_bw = TEGRA_DDR3_BW_PER_FREQ; | ||
322 | |||
323 | /* Calculate the bandwidth ratio of gpu_freq <-> emc_freq | ||
324 | * NOTE the ratio must come out as an integer */ | ||
325 | emc_params->bw_ratio = (gpu_bw / emc_bw); | ||
326 | } | ||
327 | |||
328 | #ifdef CONFIG_TEGRA_BWMGR | ||
329 | void gm20b_bwmgr_set_rate(struct gk20a_platform *platform, bool enb) | ||
330 | { | ||
331 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
332 | struct gk20a_emc_params *params; | ||
333 | unsigned long rate; | ||
334 | |||
335 | if (!profile || !profile->private_data) | ||
336 | return; | ||
337 | |||
338 | params = (struct gk20a_emc_params *)profile->private_data; | ||
339 | rate = (enb) ? params->freq_last_set : 0; | ||
340 | tegra_bwmgr_set_emc(params->bwmgr_cl, rate, TEGRA_BWMGR_SET_EMC_FLOOR); | ||
341 | } | ||
342 | |||
343 | static void gm20b_tegra_postscale(struct device *dev, unsigned long freq) | ||
344 | { | ||
345 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
346 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
347 | struct gk20a_emc_params *emc_params; | ||
348 | unsigned long emc_rate; | ||
349 | |||
350 | if (!profile) | ||
351 | return; | ||
352 | |||
353 | emc_params = profile->private_data; | ||
354 | emc_rate = gk20a_tegra_get_emc_rate(get_gk20a(dev), emc_params); | ||
355 | |||
356 | if (emc_rate > tegra_bwmgr_get_max_emc_rate()) | ||
357 | emc_rate = tegra_bwmgr_get_max_emc_rate(); | ||
358 | |||
359 | emc_params->freq_last_set = emc_rate; | ||
360 | nvgpu_mutex_acquire(&platform->railgate_lock); | ||
361 | if (platform->is_railgated && !platform->is_railgated(dev)) | ||
362 | goto done; | ||
363 | |||
364 | tegra_bwmgr_set_emc(emc_params->bwmgr_cl, emc_rate, | ||
365 | TEGRA_BWMGR_SET_EMC_FLOOR); | ||
366 | |||
367 | done: | ||
368 | nvgpu_mutex_release(&platform->railgate_lock); | ||
369 | } | ||
370 | |||
371 | #endif | ||
372 | |||
373 | #ifdef CONFIG_TEGRA_CLK_FRAMEWORK | ||
374 | /* | ||
375 | * gk20a_tegra_railgate() | ||
376 | * | ||
377 | * Gate (disable) gk20a power rail | ||
378 | */ | ||
379 | |||
380 | static int gk20a_tegra_railgate(struct device *dev) | ||
381 | { | ||
382 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
383 | int ret = 0; | ||
384 | |||
385 | if (platform->is_fmodel || | ||
386 | !tegra_dvfs_is_rail_up(platform->gpu_rail)) | ||
387 | return 0; | ||
388 | |||
389 | tegra_mc_flush(MC_CLIENT_GPU); | ||
390 | |||
391 | udelay(10); | ||
392 | |||
393 | /* enable clamp */ | ||
394 | pmc_write(0x1, PMC_GPU_RG_CNTRL_0); | ||
395 | pmc_read(PMC_GPU_RG_CNTRL_0); | ||
396 | |||
397 | udelay(10); | ||
398 | |||
399 | platform->reset_assert(dev); | ||
400 | |||
401 | udelay(10); | ||
402 | |||
403 | /* | ||
404 | * GPCPLL is already disabled before entering this function; reference | ||
405 | * clocks are enabled until now - disable them just before rail gating | ||
406 | */ | ||
407 | clk_disable(platform->clk[0]); | ||
408 | clk_disable(platform->clk[1]); | ||
409 | |||
410 | udelay(10); | ||
411 | |||
412 | if (tegra_dvfs_is_rail_up(platform->gpu_rail)) { | ||
413 | ret = tegra_dvfs_rail_power_down(platform->gpu_rail); | ||
414 | if (ret) | ||
415 | goto err_power_off; | ||
416 | } else | ||
417 | pr_info("No GPU regulator?\n"); | ||
418 | |||
419 | return 0; | ||
420 | |||
421 | err_power_off: | ||
422 | gk20a_err(dev, "Could not railgate GPU"); | ||
423 | return ret; | ||
424 | } | ||
425 | |||
426 | |||
427 | /* | ||
428 | * gk20a_tegra_unrailgate() | ||
429 | * | ||
430 | * Ungate (enable) gk20a power rail | ||
431 | */ | ||
432 | |||
433 | static int gk20a_tegra_unrailgate(struct device *dev) | ||
434 | { | ||
435 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
436 | int ret = 0; | ||
437 | bool first = false; | ||
438 | |||
439 | if (platform->is_fmodel) | ||
440 | return 0; | ||
441 | |||
442 | if (!platform->gpu_rail) { | ||
443 | platform->gpu_rail = tegra_dvfs_get_rail_by_name("vdd_gpu"); | ||
444 | if (IS_ERR_OR_NULL(platform->gpu_rail)) { | ||
445 | WARN(1, "No GPU regulator?\n"); | ||
446 | return -EINVAL; | ||
447 | } | ||
448 | first = true; | ||
449 | } | ||
450 | |||
451 | ret = tegra_dvfs_rail_power_up(platform->gpu_rail); | ||
452 | if (ret) | ||
453 | return ret; | ||
454 | |||
455 | if (!first) { | ||
456 | ret = clk_enable(platform->clk[0]); | ||
457 | if (ret) { | ||
458 | gk20a_err(dev, "could not turn on gpu pll"); | ||
459 | goto err_clk_on; | ||
460 | } | ||
461 | ret = clk_enable(platform->clk[1]); | ||
462 | if (ret) { | ||
463 | gk20a_err(dev, "could not turn on pwr clock"); | ||
464 | goto err_clk_on; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | udelay(10); | ||
469 | |||
470 | platform->reset_assert(dev); | ||
471 | |||
472 | udelay(10); | ||
473 | |||
474 | pmc_write(0, PMC_GPU_RG_CNTRL_0); | ||
475 | pmc_read(PMC_GPU_RG_CNTRL_0); | ||
476 | |||
477 | udelay(10); | ||
478 | |||
479 | platform->reset_deassert(dev); | ||
480 | |||
481 | /* Flush MC after boot/railgate/SC7 */ | ||
482 | tegra_mc_flush(MC_CLIENT_GPU); | ||
483 | |||
484 | udelay(10); | ||
485 | |||
486 | tegra_mc_flush_done(MC_CLIENT_GPU); | ||
487 | |||
488 | udelay(10); | ||
489 | |||
490 | return 0; | ||
491 | |||
492 | err_clk_on: | ||
493 | tegra_dvfs_rail_power_down(platform->gpu_rail); | ||
494 | |||
495 | return ret; | ||
496 | } | ||
497 | |||
498 | #endif | ||
499 | |||
500 | |||
501 | #if defined(CONFIG_TEGRA_CLK_FRAMEWORK) || defined(CONFIG_TEGRA_DVFS) | ||
502 | /* | ||
503 | * gk20a_tegra_is_railgated() | ||
504 | * | ||
505 | * Check status of gk20a power rail | ||
506 | */ | ||
507 | |||
508 | static bool gk20a_tegra_is_railgated(struct device *dev) | ||
509 | { | ||
510 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
511 | bool ret = false; | ||
512 | |||
513 | if (!platform->is_fmodel) | ||
514 | ret = !tegra_dvfs_is_rail_up(platform->gpu_rail); | ||
515 | |||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | /* | ||
520 | * gm20b_tegra_railgate() | ||
521 | * | ||
522 | * Gate (disable) gm20b power rail | ||
523 | */ | ||
524 | |||
525 | static int gm20b_tegra_railgate(struct device *dev) | ||
526 | { | ||
527 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
528 | int ret = 0; | ||
529 | |||
530 | if (platform->is_fmodel || | ||
531 | !tegra_dvfs_is_rail_up(platform->gpu_rail)) | ||
532 | return 0; | ||
533 | |||
534 | tegra_mc_flush(MC_CLIENT_GPU); | ||
535 | |||
536 | udelay(10); | ||
537 | |||
538 | /* enable clamp */ | ||
539 | pmc_write(0x1, PMC_GPU_RG_CNTRL_0); | ||
540 | pmc_read(PMC_GPU_RG_CNTRL_0); | ||
541 | |||
542 | udelay(10); | ||
543 | |||
544 | platform->reset_assert(dev); | ||
545 | |||
546 | udelay(10); | ||
547 | |||
548 | /* | ||
549 | * GPCPLL is already disabled before entering this function; reference | ||
550 | * clocks are enabled until now - disable them just before rail gating | ||
551 | */ | ||
552 | clk_disable_unprepare(platform->clk_reset); | ||
553 | clk_disable_unprepare(platform->clk[0]); | ||
554 | clk_disable_unprepare(platform->clk[1]); | ||
555 | if (platform->clk[3]) | ||
556 | clk_disable_unprepare(platform->clk[3]); | ||
557 | |||
558 | udelay(10); | ||
559 | |||
560 | tegra_soctherm_gpu_tsens_invalidate(1); | ||
561 | |||
562 | if (tegra_dvfs_is_rail_up(platform->gpu_rail)) { | ||
563 | ret = tegra_dvfs_rail_power_down(platform->gpu_rail); | ||
564 | if (ret) | ||
565 | goto err_power_off; | ||
566 | } else | ||
567 | pr_info("No GPU regulator?\n"); | ||
568 | |||
569 | #ifdef CONFIG_TEGRA_BWMGR | ||
570 | gm20b_bwmgr_set_rate(platform, false); | ||
571 | #endif | ||
572 | |||
573 | return 0; | ||
574 | |||
575 | err_power_off: | ||
576 | gk20a_err(dev, "Could not railgate GPU"); | ||
577 | return ret; | ||
578 | } | ||
579 | |||
580 | |||
581 | /* | ||
582 | * gm20b_tegra_unrailgate() | ||
583 | * | ||
584 | * Ungate (enable) gm20b power rail | ||
585 | */ | ||
586 | |||
587 | static int gm20b_tegra_unrailgate(struct device *dev) | ||
588 | { | ||
589 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
590 | int ret = 0; | ||
591 | bool first = false; | ||
592 | |||
593 | if (platform->is_fmodel) | ||
594 | return 0; | ||
595 | |||
596 | #ifdef CONFIG_TEGRA_CLK_FRAMEWORK | ||
597 | if (!platform->gpu_rail) { | ||
598 | platform->gpu_rail = tegra_dvfs_get_rail_by_name(GPU_RAIL_NAME); | ||
599 | if (IS_ERR_OR_NULL(platform->gpu_rail)) { | ||
600 | WARN(1, "No GPU regulator?\n"); | ||
601 | return -EINVAL; | ||
602 | } | ||
603 | first = true; | ||
604 | } | ||
605 | #endif | ||
606 | |||
607 | ret = tegra_dvfs_rail_power_up(platform->gpu_rail); | ||
608 | if (ret) | ||
609 | return ret; | ||
610 | |||
611 | #ifdef CONFIG_TEGRA_BWMGR | ||
612 | gm20b_bwmgr_set_rate(platform, true); | ||
613 | #endif | ||
614 | |||
615 | tegra_soctherm_gpu_tsens_invalidate(0); | ||
616 | |||
617 | if (!platform->clk_reset) { | ||
618 | platform->clk_reset = clk_get(dev, "gpu_gate"); | ||
619 | if (IS_ERR(platform->clk_reset)) { | ||
620 | gk20a_err(dev, "fail to get gpu reset clk\n"); | ||
621 | goto err_clk_on; | ||
622 | } | ||
623 | } | ||
624 | |||
625 | if (!first) { | ||
626 | ret = clk_prepare_enable(platform->clk_reset); | ||
627 | if (ret) { | ||
628 | gk20a_err(dev, "could not turn on gpu_gate"); | ||
629 | goto err_clk_on; | ||
630 | } | ||
631 | |||
632 | ret = clk_prepare_enable(platform->clk[0]); | ||
633 | if (ret) { | ||
634 | gk20a_err(dev, "could not turn on gpu pll"); | ||
635 | goto err_clk_on; | ||
636 | } | ||
637 | ret = clk_prepare_enable(platform->clk[1]); | ||
638 | if (ret) { | ||
639 | gk20a_err(dev, "could not turn on pwr clock"); | ||
640 | goto err_clk_on; | ||
641 | } | ||
642 | |||
643 | if (platform->clk[3]) { | ||
644 | ret = clk_prepare_enable(platform->clk[3]); | ||
645 | if (ret) { | ||
646 | gk20a_err(dev, "could not turn on fuse clock"); | ||
647 | goto err_clk_on; | ||
648 | } | ||
649 | } | ||
650 | } | ||
651 | |||
652 | udelay(10); | ||
653 | |||
654 | platform->reset_assert(dev); | ||
655 | |||
656 | udelay(10); | ||
657 | |||
658 | pmc_write(0, PMC_GPU_RG_CNTRL_0); | ||
659 | pmc_read(PMC_GPU_RG_CNTRL_0); | ||
660 | |||
661 | udelay(10); | ||
662 | |||
663 | clk_disable(platform->clk_reset); | ||
664 | platform->reset_deassert(dev); | ||
665 | clk_enable(platform->clk_reset); | ||
666 | |||
667 | /* Flush MC after boot/railgate/SC7 */ | ||
668 | tegra_mc_flush(MC_CLIENT_GPU); | ||
669 | |||
670 | udelay(10); | ||
671 | |||
672 | tegra_mc_flush_done(MC_CLIENT_GPU); | ||
673 | |||
674 | udelay(10); | ||
675 | |||
676 | return 0; | ||
677 | |||
678 | err_clk_on: | ||
679 | tegra_dvfs_rail_power_down(platform->gpu_rail); | ||
680 | |||
681 | return ret; | ||
682 | } | ||
683 | #endif | ||
684 | |||
685 | |||
686 | static struct { | ||
687 | char *name; | ||
688 | unsigned long default_rate; | ||
689 | } tegra_gk20a_clocks[] = { | ||
690 | #ifdef CONFIG_TEGRA_CLK_FRAMEWORK | ||
691 | {"PLLG_ref", UINT_MAX}, | ||
692 | {"pwr", 204000000}, | ||
693 | {"emc", UINT_MAX}, | ||
694 | #elif defined(CONFIG_COMMON_CLK) | ||
695 | {"gpu_ref", UINT_MAX}, | ||
696 | {"pll_p_out5", 204000000}, | ||
697 | {"emc", UINT_MAX}, | ||
698 | {"fuse", UINT_MAX}, | ||
699 | #endif | ||
700 | }; | ||
701 | |||
702 | |||
703 | |||
704 | /* | ||
705 | * gk20a_tegra_get_clocks() | ||
706 | * | ||
707 | * This function finds clocks in tegra platform and populates | ||
708 | * the clock information to gk20a platform data. | ||
709 | */ | ||
710 | |||
711 | static int gk20a_tegra_get_clocks(struct device *dev) | ||
712 | { | ||
713 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
714 | char devname[16]; | ||
715 | unsigned int i; | ||
716 | int ret = 0; | ||
717 | |||
718 | BUG_ON(GK20A_CLKS_MAX < ARRAY_SIZE(tegra_gk20a_clocks)); | ||
719 | |||
720 | snprintf(devname, sizeof(devname), "tegra_%s", dev_name(dev)); | ||
721 | |||
722 | platform->num_clks = 0; | ||
723 | for (i = 0; i < ARRAY_SIZE(tegra_gk20a_clocks); i++) { | ||
724 | long rate = tegra_gk20a_clocks[i].default_rate; | ||
725 | struct clk *c; | ||
726 | |||
727 | c = clk_get_sys(devname, tegra_gk20a_clocks[i].name); | ||
728 | if (IS_ERR(c)) { | ||
729 | ret = PTR_ERR(c); | ||
730 | goto err_get_clock; | ||
731 | } | ||
732 | rate = clk_round_rate(c, rate); | ||
733 | clk_set_rate(c, rate); | ||
734 | platform->clk[i] = c; | ||
735 | } | ||
736 | platform->num_clks = i; | ||
737 | |||
738 | return 0; | ||
739 | |||
740 | err_get_clock: | ||
741 | |||
742 | while (i--) | ||
743 | clk_put(platform->clk[i]); | ||
744 | return ret; | ||
745 | } | ||
746 | |||
747 | static int gk20a_tegra_reset_assert(struct device *dev) | ||
748 | { | ||
749 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
750 | |||
751 | if (!platform->clk_reset) | ||
752 | platform->clk_reset = platform->clk[0]; | ||
753 | |||
754 | tegra_periph_reset_assert(platform->clk_reset); | ||
755 | |||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static int gk20a_tegra_reset_deassert(struct device *dev) | ||
760 | { | ||
761 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
762 | |||
763 | if (!platform->clk_reset) | ||
764 | return -EINVAL; | ||
765 | |||
766 | tegra_periph_reset_deassert(platform->clk_reset); | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | #if defined(CONFIG_RESET_CONTROLLER) && defined(CONFIG_COMMON_CLK) | ||
772 | static int gm20b_tegra_reset_assert(struct device *dev) | ||
773 | { | ||
774 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
775 | |||
776 | if (!platform->reset_control) { | ||
777 | WARN(1, "Reset control not initialized\n"); | ||
778 | return -ENOSYS; | ||
779 | } | ||
780 | |||
781 | return reset_control_assert(platform->reset_control); | ||
782 | } | ||
783 | |||
784 | static int gm20b_tegra_reset_deassert(struct device *dev) | ||
785 | { | ||
786 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
787 | |||
788 | if (!platform->reset_control) { | ||
789 | WARN(1, "Reset control not initialized\n"); | ||
790 | return -ENOSYS; | ||
791 | } | ||
792 | |||
793 | return reset_control_deassert(platform->reset_control); | ||
794 | } | ||
795 | #endif | ||
796 | |||
797 | static void gk20a_tegra_scale_init(struct device *dev) | ||
798 | { | ||
799 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
800 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
801 | struct gk20a_emc_params *emc_params; | ||
802 | |||
803 | if (!profile) | ||
804 | return; | ||
805 | |||
806 | emc_params = kzalloc(sizeof(*emc_params), GFP_KERNEL); | ||
807 | if (!emc_params) | ||
808 | return; | ||
809 | |||
810 | emc_params->freq_last_set = -1; | ||
811 | gk20a_tegra_calibrate_emc(dev, emc_params); | ||
812 | |||
813 | #ifdef CONFIG_TEGRA_BWMGR | ||
814 | emc_params->bwmgr_cl = tegra_bwmgr_register(TEGRA_BWMGR_CLIENT_GPU); | ||
815 | if (!emc_params->bwmgr_cl) { | ||
816 | gk20a_dbg_info("%s Missing GPU BWMGR client\n", __func__); | ||
817 | return; | ||
818 | } | ||
819 | #endif | ||
820 | |||
821 | profile->private_data = emc_params; | ||
822 | } | ||
823 | |||
824 | static void gk20a_tegra_scale_exit(struct device *dev) | ||
825 | { | ||
826 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
827 | struct gk20a_scale_profile *profile = platform->g->scale_profile; | ||
828 | struct gk20a_emc_params *emc_params; | ||
829 | |||
830 | if (!profile) | ||
831 | return; | ||
832 | |||
833 | emc_params = profile->private_data; | ||
834 | #ifdef CONFIG_TEGRA_BWMGR | ||
835 | tegra_bwmgr_unregister(emc_params->bwmgr_cl); | ||
836 | #endif | ||
837 | |||
838 | kfree(profile->private_data); | ||
839 | } | ||
840 | |||
841 | void gk20a_tegra_debug_dump(struct device *dev) | ||
842 | { | ||
843 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
844 | struct gk20a *g = platform->g; | ||
845 | |||
846 | if (g->host1x_dev) | ||
847 | nvhost_debug_dump_device(g->host1x_dev); | ||
848 | } | ||
849 | |||
850 | int gk20a_tegra_busy(struct device *dev) | ||
851 | { | ||
852 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
853 | struct gk20a *g = platform->g; | ||
854 | |||
855 | if (g->host1x_dev) | ||
856 | return nvhost_module_busy_ext(g->host1x_dev); | ||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | void gk20a_tegra_idle(struct device *dev) | ||
861 | { | ||
862 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
863 | struct gk20a *g = platform->g; | ||
864 | |||
865 | if (g->host1x_dev) | ||
866 | nvhost_module_idle_ext(g->host1x_dev); | ||
867 | } | ||
868 | |||
869 | static int gk20a_tegra_probe(struct device *dev) | ||
870 | { | ||
871 | struct gk20a_platform *platform = dev_get_drvdata(dev); | ||
872 | struct device_node *np = dev->of_node; | ||
873 | const __be32 *host1x_ptr; | ||
874 | struct platform_device *host1x_pdev = NULL; | ||
875 | bool joint_xpu_rail = false; | ||
876 | int ret; | ||
877 | |||
878 | #ifdef CONFIG_COMMON_CLK | ||
879 | /* DVFS is not guaranteed to be initialized at the time of probe on | ||
880 | * kernels with Common Clock Framework enabled. | ||
881 | */ | ||
882 | if (!platform->gpu_rail) { | ||
883 | platform->gpu_rail = tegra_dvfs_get_rail_by_name(GPU_RAIL_NAME); | ||
884 | if (!platform->gpu_rail) { | ||
885 | gk20a_dbg_info("deferring probe no gpu_rail\n"); | ||
886 | return -EPROBE_DEFER; | ||
887 | } | ||
888 | } | ||
889 | |||
890 | if (!tegra_dvfs_is_rail_ready(platform->gpu_rail)) { | ||
891 | gk20a_dbg_info("deferring probe gpu_rail not ready\n"); | ||
892 | return -EPROBE_DEFER; | ||
893 | } | ||
894 | #endif | ||
895 | |||
896 | host1x_ptr = of_get_property(np, "nvidia,host1x", NULL); | ||
897 | if (host1x_ptr) { | ||
898 | struct device_node *host1x_node = | ||
899 | of_find_node_by_phandle(be32_to_cpup(host1x_ptr)); | ||
900 | |||
901 | host1x_pdev = of_find_device_by_node(host1x_node); | ||
902 | if (!host1x_pdev) { | ||
903 | dev_warn(dev, "host1x device not available"); | ||
904 | return -EPROBE_DEFER; | ||
905 | } | ||
906 | |||
907 | } else { | ||
908 | host1x_pdev = to_platform_device(dev->parent); | ||
909 | dev_warn(dev, "host1x reference not found. assuming host1x to be parent"); | ||
910 | } | ||
911 | |||
912 | platform->g->host1x_dev = host1x_pdev; | ||
913 | |||
914 | #ifdef CONFIG_OF | ||
915 | joint_xpu_rail = of_property_read_bool(of_chosen, | ||
916 | "nvidia,tegra-joint_xpu_rail"); | ||
917 | #endif | ||
918 | |||
919 | if (joint_xpu_rail) { | ||
920 | gk20a_dbg_info("XPU rails are joint\n"); | ||
921 | platform->can_railgate = false; | ||
922 | } | ||
923 | |||
924 | /* WAR for bug 1547668: Disable railgating and scaling irrespective of | ||
925 | * platform data if the rework has not been made. */ | ||
926 | |||
927 | if (tegra_get_chip_id() == TEGRA210) { | ||
928 | np = of_find_node_by_path("/gpu-dvfs-rework"); | ||
929 | if (!(np && of_device_is_available(np))) { | ||
930 | platform->devfreq_governor = ""; | ||
931 | dev_warn(dev, "board does not support scaling"); | ||
932 | } | ||
933 | } | ||
934 | |||
935 | if (tegra_get_chip_id() == TEGRA132) | ||
936 | platform->soc_name = "tegra13x"; | ||
937 | |||
938 | platform->g->mm.vidmem_is_vidmem = platform->vidmem_is_vidmem; | ||
939 | |||
940 | gk20a_tegra_get_clocks(dev); | ||
941 | |||
942 | if (platform->clk_register) { | ||
943 | ret = platform->clk_register(platform->g); | ||
944 | if (ret) | ||
945 | return ret; | ||
946 | } | ||
947 | |||
948 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) | ||
949 | pmc = ioremap(TEGRA_PMC_BASE, 4096); | ||
950 | #endif | ||
951 | |||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | static int gk20a_tegra_late_probe(struct device *dev) | ||
956 | { | ||
957 | /* Initialise tegra specific scaling quirks */ | ||
958 | gk20a_tegra_scale_init(dev); | ||
959 | |||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | static int gk20a_tegra_remove(struct device *dev) | ||
964 | { | ||
965 | /* deinitialise tegra specific scaling quirks */ | ||
966 | gk20a_tegra_scale_exit(dev); | ||
967 | |||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | static int gk20a_tegra_suspend(struct device *dev) | ||
972 | { | ||
973 | tegra_edp_notify_gpu_load(0, 0); | ||
974 | return 0; | ||
975 | } | ||
976 | |||
977 | #if defined(CONFIG_TEGRA_CLK_FRAMEWORK) || defined(CONFIG_COMMON_CLK) | ||
978 | static unsigned long gk20a_get_clk_rate(struct device *dev) | ||
979 | { | ||
980 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
981 | struct gk20a *g = platform->g; | ||
982 | |||
983 | return gk20a_clk_get_rate(g); | ||
984 | |||
985 | } | ||
986 | |||
987 | static long gk20a_round_clk_rate(struct device *dev, unsigned long rate) | ||
988 | { | ||
989 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
990 | struct gk20a *g = platform->g; | ||
991 | |||
992 | return gk20a_clk_round_rate(g, rate); | ||
993 | } | ||
994 | |||
995 | static int gk20a_set_clk_rate(struct device *dev, unsigned long rate) | ||
996 | { | ||
997 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
998 | struct gk20a *g = platform->g; | ||
999 | |||
1000 | return gk20a_clk_set_rate(g, rate); | ||
1001 | } | ||
1002 | |||
1003 | static int gk20a_clk_get_freqs(struct device *dev, | ||
1004 | unsigned long **freqs, int *num_freqs) | ||
1005 | { | ||
1006 | struct gk20a_platform *platform = gk20a_get_platform(dev); | ||
1007 | struct gk20a *g = platform->g; | ||
1008 | |||
1009 | /* make sure the clock is available */ | ||
1010 | if (!gk20a_clk_get(g)) | ||
1011 | return -ENOSYS; | ||
1012 | |||
1013 | return tegra_dvfs_get_freqs(clk_get_parent(g->clk.tegra_clk), | ||
1014 | freqs, num_freqs); | ||
1015 | } | ||
1016 | #endif | ||
1017 | |||
1018 | |||
1019 | struct gk20a_platform gk20a_tegra_platform = { | ||
1020 | .has_syncpoints = true, | ||
1021 | .aggressive_sync_destroy_thresh = 64, | ||
1022 | |||
1023 | /* power management configuration */ | ||
1024 | .railgate_delay = 500, | ||
1025 | .can_railgate = true, | ||
1026 | .can_elpg = true, | ||
1027 | .enable_slcg = true, | ||
1028 | .enable_blcg = true, | ||
1029 | .enable_elcg = true, | ||
1030 | .enable_elpg = true, | ||
1031 | .enable_aelpg = true, | ||
1032 | .ptimer_src_freq = 12000000, | ||
1033 | |||
1034 | .force_reset_in_do_idle = false, | ||
1035 | |||
1036 | .default_big_page_size = SZ_128K, | ||
1037 | |||
1038 | .ch_wdt_timeout_ms = 7000, | ||
1039 | |||
1040 | .probe = gk20a_tegra_probe, | ||
1041 | .late_probe = gk20a_tegra_late_probe, | ||
1042 | .remove = gk20a_tegra_remove, | ||
1043 | |||
1044 | /* power management callbacks */ | ||
1045 | .suspend = gk20a_tegra_suspend, | ||
1046 | #ifdef CONFIG_TEGRA_CLK_FRAMEWORK | ||
1047 | .railgate = gk20a_tegra_railgate, | ||
1048 | .unrailgate = gk20a_tegra_unrailgate, | ||
1049 | .is_railgated = gk20a_tegra_is_railgated, | ||
1050 | #endif | ||
1051 | |||
1052 | .busy = gk20a_tegra_busy, | ||
1053 | .idle = gk20a_tegra_idle, | ||
1054 | |||
1055 | .reset_assert = gk20a_tegra_reset_assert, | ||
1056 | .reset_deassert = gk20a_tegra_reset_deassert, | ||
1057 | |||
1058 | #ifdef CONFIG_TEGRA_CLK_FRAMEWORK | ||
1059 | .clk_get_rate = gk20a_get_clk_rate, | ||
1060 | .clk_round_rate = gk20a_round_clk_rate, | ||
1061 | .clk_set_rate = gk20a_set_clk_rate, | ||
1062 | .get_clk_freqs = gk20a_clk_get_freqs, | ||
1063 | #endif | ||
1064 | |||
1065 | /* frequency scaling configuration */ | ||
1066 | .prescale = gk20a_tegra_prescale, | ||
1067 | .postscale = gk20a_tegra_postscale, | ||
1068 | .devfreq_governor = "nvhost_podgov", | ||
1069 | .qos_notify = gk20a_scale_qos_notify, | ||
1070 | |||
1071 | .secure_alloc = gk20a_tegra_secure_alloc, | ||
1072 | .secure_page_alloc = gk20a_tegra_secure_page_alloc, | ||
1073 | .dump_platform_dependencies = gk20a_tegra_debug_dump, | ||
1074 | |||
1075 | .has_ce = true, | ||
1076 | |||
1077 | .soc_name = "tegra12x", | ||
1078 | |||
1079 | .vidmem_is_vidmem = false, | ||
1080 | }; | ||
1081 | |||
1082 | struct gk20a_platform gm20b_tegra_platform = { | ||
1083 | .has_syncpoints = true, | ||
1084 | .aggressive_sync_destroy_thresh = 64, | ||
1085 | |||
1086 | /* power management configuration */ | ||
1087 | .railgate_delay = 500, | ||
1088 | .can_railgate = true, | ||
1089 | .can_elpg = true, | ||
1090 | .enable_slcg = true, | ||
1091 | .enable_blcg = true, | ||
1092 | .enable_elcg = true, | ||
1093 | .enable_elpg = true, | ||
1094 | .enable_aelpg = true, | ||
1095 | .ptimer_src_freq = 19200000, | ||
1096 | |||
1097 | .force_reset_in_do_idle = false, | ||
1098 | |||
1099 | .default_big_page_size = SZ_128K, | ||
1100 | |||
1101 | .ch_wdt_timeout_ms = 5000, | ||
1102 | |||
1103 | .probe = gk20a_tegra_probe, | ||
1104 | .late_probe = gk20a_tegra_late_probe, | ||
1105 | .remove = gk20a_tegra_remove, | ||
1106 | /* power management callbacks */ | ||
1107 | .suspend = gk20a_tegra_suspend, | ||
1108 | |||
1109 | #if defined(CONFIG_TEGRA_CLK_FRAMEWORK) || defined(CONFIG_TEGRA_DVFS) | ||
1110 | .railgate = gm20b_tegra_railgate, | ||
1111 | .unrailgate = gm20b_tegra_unrailgate, | ||
1112 | .is_railgated = gk20a_tegra_is_railgated, | ||
1113 | #endif | ||
1114 | |||
1115 | .busy = gk20a_tegra_busy, | ||
1116 | .idle = gk20a_tegra_idle, | ||
1117 | |||
1118 | #if defined(CONFIG_RESET_CONTROLLER) && defined(CONFIG_COMMON_CLK) | ||
1119 | .reset_assert = gm20b_tegra_reset_assert, | ||
1120 | .reset_deassert = gm20b_tegra_reset_deassert, | ||
1121 | #else | ||
1122 | .reset_assert = gk20a_tegra_reset_assert, | ||
1123 | .reset_deassert = gk20a_tegra_reset_deassert, | ||
1124 | #endif | ||
1125 | |||
1126 | #if defined(CONFIG_TEGRA_CLK_FRAMEWORK) || defined(CONFIG_COMMON_CLK) | ||
1127 | .clk_get_rate = gk20a_get_clk_rate, | ||
1128 | .clk_round_rate = gk20a_round_clk_rate, | ||
1129 | .clk_set_rate = gk20a_set_clk_rate, | ||
1130 | .get_clk_freqs = gk20a_clk_get_freqs, | ||
1131 | #endif | ||
1132 | |||
1133 | #ifdef CONFIG_COMMON_CLK | ||
1134 | .clk_register = gm20b_register_gpcclk, | ||
1135 | #endif | ||
1136 | |||
1137 | /* frequency scaling configuration */ | ||
1138 | .prescale = gk20a_tegra_prescale, | ||
1139 | #ifdef CONFIG_TEGRA_BWMGR | ||
1140 | .postscale = gm20b_tegra_postscale, | ||
1141 | #else | ||
1142 | .postscale = gk20a_tegra_postscale, | ||
1143 | #endif | ||
1144 | .devfreq_governor = "nvhost_podgov", | ||
1145 | .qos_notify = gk20a_scale_qos_notify, | ||
1146 | |||
1147 | .secure_alloc = gk20a_tegra_secure_alloc, | ||
1148 | .secure_page_alloc = gk20a_tegra_secure_page_alloc, | ||
1149 | .dump_platform_dependencies = gk20a_tegra_debug_dump, | ||
1150 | |||
1151 | .has_cde = true, | ||
1152 | |||
1153 | .has_ce = true, | ||
1154 | |||
1155 | .soc_name = "tegra21x", | ||
1156 | |||
1157 | .vidmem_is_vidmem = false, | ||
1158 | }; | ||
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 | } | ||