1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2019 Collabora Ltd */
4 #include <drm/drm_file.h>
5 #include <drm/drm_gem_shmem_helper.h>
6 #include <drm/panfrost_drm.h>
7 #include <linux/completion.h>
8 #include <linux/iopoll.h>
9 #include <linux/pm_runtime.h>
10 #include <linux/slab.h>
11 #include <linux/uaccess.h>
13 #include "panfrost_device.h"
14 #include "panfrost_features.h"
15 #include "panfrost_gem.h"
16 #include "panfrost_issues.h"
17 #include "panfrost_job.h"
18 #include "panfrost_mmu.h"
19 #include "panfrost_perfcnt.h"
20 #include "panfrost_regs.h"
22 #define COUNTERS_PER_BLOCK 64
23 #define BYTES_PER_COUNTER 4
24 #define BLOCKS_PER_COREGROUP 8
25 #define V4_SHADERS_PER_COREGROUP 4
27 struct panfrost_perfcnt
{
28 struct panfrost_gem_mapping
*mapping
;
31 struct panfrost_file_priv
*user
;
33 struct completion dump_comp
;
36 void panfrost_perfcnt_clean_cache_done(struct panfrost_device
*pfdev
)
38 complete(&pfdev
->perfcnt
->dump_comp
);
41 void panfrost_perfcnt_sample_done(struct panfrost_device
*pfdev
)
43 gpu_write(pfdev
, GPU_CMD
, GPU_CMD_CLEAN_CACHES
);
46 static int panfrost_perfcnt_dump_locked(struct panfrost_device
*pfdev
)
51 reinit_completion(&pfdev
->perfcnt
->dump_comp
);
52 gpuva
= pfdev
->perfcnt
->mapping
->mmnode
.start
<< PAGE_SHIFT
;
53 gpu_write(pfdev
, GPU_PERFCNT_BASE_LO
, gpuva
);
54 gpu_write(pfdev
, GPU_PERFCNT_BASE_HI
, gpuva
>> 32);
55 gpu_write(pfdev
, GPU_INT_CLEAR
,
56 GPU_IRQ_CLEAN_CACHES_COMPLETED
|
57 GPU_IRQ_PERFCNT_SAMPLE_COMPLETED
);
58 gpu_write(pfdev
, GPU_CMD
, GPU_CMD_PERFCNT_SAMPLE
);
59 ret
= wait_for_completion_interruptible_timeout(&pfdev
->perfcnt
->dump_comp
,
60 msecs_to_jiffies(1000));
69 static int panfrost_perfcnt_enable_locked(struct panfrost_device
*pfdev
,
70 struct drm_file
*file_priv
,
71 unsigned int counterset
)
73 struct panfrost_file_priv
*user
= file_priv
->driver_priv
;
74 struct panfrost_perfcnt
*perfcnt
= pfdev
->perfcnt
;
75 struct drm_gem_shmem_object
*bo
;
79 if (user
== perfcnt
->user
)
81 else if (perfcnt
->user
)
84 ret
= pm_runtime_get_sync(pfdev
->dev
);
88 bo
= drm_gem_shmem_create(pfdev
->ddev
, perfcnt
->bosize
);
92 /* Map the perfcnt buf in the address space attached to file_priv. */
93 ret
= panfrost_gem_open(&bo
->base
, file_priv
);
97 perfcnt
->mapping
= panfrost_gem_mapping_get(to_panfrost_bo(&bo
->base
),
99 if (!perfcnt
->mapping
) {
104 perfcnt
->buf
= drm_gem_shmem_vmap(&bo
->base
);
105 if (IS_ERR(perfcnt
->buf
)) {
106 ret
= PTR_ERR(perfcnt
->buf
);
107 goto err_put_mapping
;
111 * Invalidate the cache and clear the counters to start from a fresh
114 reinit_completion(&pfdev
->perfcnt
->dump_comp
);
115 gpu_write(pfdev
, GPU_INT_CLEAR
,
116 GPU_IRQ_CLEAN_CACHES_COMPLETED
|
117 GPU_IRQ_PERFCNT_SAMPLE_COMPLETED
);
118 gpu_write(pfdev
, GPU_CMD
, GPU_CMD_PERFCNT_CLEAR
);
119 gpu_write(pfdev
, GPU_CMD
, GPU_CMD_CLEAN_INV_CACHES
);
120 ret
= wait_for_completion_timeout(&pfdev
->perfcnt
->dump_comp
,
121 msecs_to_jiffies(1000));
127 perfcnt
->user
= user
;
130 * Always use address space 0 for now.
131 * FIXME: this needs to be updated when we start using different
134 cfg
= GPU_PERFCNT_CFG_AS(0) |
135 GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL
);
138 * Bifrost GPUs have 2 set of counters, but we're only interested by
139 * the first one for now.
141 if (panfrost_model_is_bifrost(pfdev
))
142 cfg
|= GPU_PERFCNT_CFG_SETSEL(counterset
);
144 gpu_write(pfdev
, GPU_PRFCNT_JM_EN
, 0xffffffff);
145 gpu_write(pfdev
, GPU_PRFCNT_SHADER_EN
, 0xffffffff);
146 gpu_write(pfdev
, GPU_PRFCNT_MMU_L2_EN
, 0xffffffff);
149 * Due to PRLAM-8186 we need to disable the Tiler before we enable HW
152 if (panfrost_has_hw_issue(pfdev
, HW_ISSUE_8186
))
153 gpu_write(pfdev
, GPU_PRFCNT_TILER_EN
, 0);
155 gpu_write(pfdev
, GPU_PRFCNT_TILER_EN
, 0xffffffff);
157 gpu_write(pfdev
, GPU_PERFCNT_CFG
, cfg
);
159 if (panfrost_has_hw_issue(pfdev
, HW_ISSUE_8186
))
160 gpu_write(pfdev
, GPU_PRFCNT_TILER_EN
, 0xffffffff);
162 /* The BO ref is retained by the mapping. */
163 drm_gem_object_put_unlocked(&bo
->base
);
168 drm_gem_shmem_vunmap(&bo
->base
, perfcnt
->buf
);
170 panfrost_gem_mapping_put(perfcnt
->mapping
);
172 panfrost_gem_close(&bo
->base
, file_priv
);
174 drm_gem_object_put_unlocked(&bo
->base
);
178 static int panfrost_perfcnt_disable_locked(struct panfrost_device
*pfdev
,
179 struct drm_file
*file_priv
)
181 struct panfrost_file_priv
*user
= file_priv
->driver_priv
;
182 struct panfrost_perfcnt
*perfcnt
= pfdev
->perfcnt
;
184 if (user
!= perfcnt
->user
)
187 gpu_write(pfdev
, GPU_PRFCNT_JM_EN
, 0x0);
188 gpu_write(pfdev
, GPU_PRFCNT_SHADER_EN
, 0x0);
189 gpu_write(pfdev
, GPU_PRFCNT_MMU_L2_EN
, 0x0);
190 gpu_write(pfdev
, GPU_PRFCNT_TILER_EN
, 0);
191 gpu_write(pfdev
, GPU_PERFCNT_CFG
,
192 GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF
));
194 perfcnt
->user
= NULL
;
195 drm_gem_shmem_vunmap(&perfcnt
->mapping
->obj
->base
.base
, perfcnt
->buf
);
197 panfrost_gem_close(&perfcnt
->mapping
->obj
->base
.base
, file_priv
);
198 panfrost_gem_mapping_put(perfcnt
->mapping
);
199 perfcnt
->mapping
= NULL
;
200 pm_runtime_mark_last_busy(pfdev
->dev
);
201 pm_runtime_put_autosuspend(pfdev
->dev
);
206 int panfrost_ioctl_perfcnt_enable(struct drm_device
*dev
, void *data
,
207 struct drm_file
*file_priv
)
209 struct panfrost_device
*pfdev
= dev
->dev_private
;
210 struct panfrost_perfcnt
*perfcnt
= pfdev
->perfcnt
;
211 struct drm_panfrost_perfcnt_enable
*req
= data
;
214 ret
= panfrost_unstable_ioctl_check();
218 /* Only Bifrost GPUs have 2 set of counters. */
219 if (req
->counterset
> (panfrost_model_is_bifrost(pfdev
) ? 1 : 0))
222 mutex_lock(&perfcnt
->lock
);
224 ret
= panfrost_perfcnt_enable_locked(pfdev
, file_priv
,
227 ret
= panfrost_perfcnt_disable_locked(pfdev
, file_priv
);
228 mutex_unlock(&perfcnt
->lock
);
233 int panfrost_ioctl_perfcnt_dump(struct drm_device
*dev
, void *data
,
234 struct drm_file
*file_priv
)
236 struct panfrost_device
*pfdev
= dev
->dev_private
;
237 struct panfrost_perfcnt
*perfcnt
= pfdev
->perfcnt
;
238 struct drm_panfrost_perfcnt_dump
*req
= data
;
239 void __user
*user_ptr
= (void __user
*)(uintptr_t)req
->buf_ptr
;
242 ret
= panfrost_unstable_ioctl_check();
246 mutex_lock(&perfcnt
->lock
);
247 if (perfcnt
->user
!= file_priv
->driver_priv
) {
252 ret
= panfrost_perfcnt_dump_locked(pfdev
);
256 if (copy_to_user(user_ptr
, perfcnt
->buf
, perfcnt
->bosize
))
260 mutex_unlock(&perfcnt
->lock
);
265 void panfrost_perfcnt_close(struct drm_file
*file_priv
)
267 struct panfrost_file_priv
*pfile
= file_priv
->driver_priv
;
268 struct panfrost_device
*pfdev
= pfile
->pfdev
;
269 struct panfrost_perfcnt
*perfcnt
= pfdev
->perfcnt
;
271 pm_runtime_get_sync(pfdev
->dev
);
272 mutex_lock(&perfcnt
->lock
);
273 if (perfcnt
->user
== pfile
)
274 panfrost_perfcnt_disable_locked(pfdev
, file_priv
);
275 mutex_unlock(&perfcnt
->lock
);
276 pm_runtime_mark_last_busy(pfdev
->dev
);
277 pm_runtime_put_autosuspend(pfdev
->dev
);
280 int panfrost_perfcnt_init(struct panfrost_device
*pfdev
)
282 struct panfrost_perfcnt
*perfcnt
;
285 if (panfrost_has_hw_feature(pfdev
, HW_FEATURE_V4
)) {
286 unsigned int ncoregroups
;
288 ncoregroups
= hweight64(pfdev
->features
.l2_present
);
289 size
= ncoregroups
* BLOCKS_PER_COREGROUP
*
290 COUNTERS_PER_BLOCK
* BYTES_PER_COUNTER
;
292 unsigned int nl2c
, ncores
;
295 * TODO: define a macro to extract the number of l2 caches from
298 nl2c
= ((pfdev
->features
.mem_features
>> 8) & GENMASK(3, 0)) + 1;
301 * shader_present might be sparse, but the counters layout
302 * forces to dump unused regions too, hence the fls64() call
303 * instead of hweight64().
305 ncores
= fls64(pfdev
->features
.shader_present
);
308 * There's always one JM and one Tiler block, hence the '+ 2'
311 size
= (nl2c
+ ncores
+ 2) *
312 COUNTERS_PER_BLOCK
* BYTES_PER_COUNTER
;
315 perfcnt
= devm_kzalloc(pfdev
->dev
, sizeof(*perfcnt
), GFP_KERNEL
);
319 perfcnt
->bosize
= size
;
321 /* Start with everything disabled. */
322 gpu_write(pfdev
, GPU_PERFCNT_CFG
,
323 GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF
));
324 gpu_write(pfdev
, GPU_PRFCNT_JM_EN
, 0);
325 gpu_write(pfdev
, GPU_PRFCNT_SHADER_EN
, 0);
326 gpu_write(pfdev
, GPU_PRFCNT_MMU_L2_EN
, 0);
327 gpu_write(pfdev
, GPU_PRFCNT_TILER_EN
, 0);
329 init_completion(&perfcnt
->dump_comp
);
330 mutex_init(&perfcnt
->lock
);
331 pfdev
->perfcnt
= perfcnt
;
336 void panfrost_perfcnt_fini(struct panfrost_device
*pfdev
)
338 /* Disable everything before leaving. */
339 gpu_write(pfdev
, GPU_PERFCNT_CFG
,
340 GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF
));
341 gpu_write(pfdev
, GPU_PRFCNT_JM_EN
, 0);
342 gpu_write(pfdev
, GPU_PRFCNT_SHADER_EN
, 0);
343 gpu_write(pfdev
, GPU_PRFCNT_MMU_L2_EN
, 0);
344 gpu_write(pfdev
, GPU_PRFCNT_TILER_EN
, 0);