xref: /OK3568_Linux_fs/kernel/drivers/gpu/arm/midgard/mali_kbase_vinstr.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  *
3*4882a593Smuzhiyun  * (C) COPYRIGHT 2011-2017 ARM Limited. All rights reserved.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This program is free software and is provided to you under the terms of the
6*4882a593Smuzhiyun  * GNU General Public License version 2 as published by the Free Software
7*4882a593Smuzhiyun  * Foundation, and any use by you of this program is subject to the terms
8*4882a593Smuzhiyun  * of such GNU licence.
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * A copy of the licence is included with the program, and can also be obtained
11*4882a593Smuzhiyun  * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
12*4882a593Smuzhiyun  * Boston, MA  02110-1301, USA.
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <linux/anon_inodes.h>
19*4882a593Smuzhiyun #include <linux/atomic.h>
20*4882a593Smuzhiyun #include <linux/hrtimer.h>
21*4882a593Smuzhiyun #include <linux/jiffies.h>
22*4882a593Smuzhiyun #include <linux/kthread.h>
23*4882a593Smuzhiyun #include <linux/list.h>
24*4882a593Smuzhiyun #include <linux/mm.h>
25*4882a593Smuzhiyun #include <linux/poll.h>
26*4882a593Smuzhiyun #include <linux/preempt.h>
27*4882a593Smuzhiyun #include <linux/slab.h>
28*4882a593Smuzhiyun #include <linux/wait.h>
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #include <mali_kbase.h>
31*4882a593Smuzhiyun #include <mali_kbase_hwaccess_instr.h>
32*4882a593Smuzhiyun #include <mali_kbase_hwaccess_jm.h>
33*4882a593Smuzhiyun #include <mali_kbase_hwcnt_reader.h>
34*4882a593Smuzhiyun #include <mali_kbase_mem_linux.h>
35*4882a593Smuzhiyun #include <mali_kbase_tlstream.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /*****************************************************************************/
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun /* Hwcnt reader API version */
40*4882a593Smuzhiyun #define HWCNT_READER_API        1
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun /* The number of nanoseconds in a second. */
43*4882a593Smuzhiyun #define NSECS_IN_SEC            1000000000ull /* ns */
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun /* The time resolution of dumping service. */
46*4882a593Smuzhiyun #define DUMPING_RESOLUTION      500000ull /* ns */
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun /* The maximal supported number of dumping buffers. */
49*4882a593Smuzhiyun #define MAX_BUFFER_COUNT        32
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /* Size and number of hw counters blocks. */
52*4882a593Smuzhiyun #define NR_CNT_BLOCKS_PER_GROUP 8
53*4882a593Smuzhiyun #define NR_CNT_PER_BLOCK        64
54*4882a593Smuzhiyun #define NR_BYTES_PER_CNT        4
55*4882a593Smuzhiyun #define NR_BYTES_PER_HDR        16
56*4882a593Smuzhiyun #define PRFCNT_EN_MASK_OFFSET   0x8
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun /*****************************************************************************/
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun enum {
61*4882a593Smuzhiyun 	SHADER_HWCNT_BM,
62*4882a593Smuzhiyun 	TILER_HWCNT_BM,
63*4882a593Smuzhiyun 	MMU_L2_HWCNT_BM,
64*4882a593Smuzhiyun 	JM_HWCNT_BM
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun enum vinstr_state {
68*4882a593Smuzhiyun 	VINSTR_IDLE,
69*4882a593Smuzhiyun 	VINSTR_DUMPING,
70*4882a593Smuzhiyun 	VINSTR_SUSPENDING,
71*4882a593Smuzhiyun 	VINSTR_SUSPENDED,
72*4882a593Smuzhiyun 	VINSTR_RESUMING
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun /**
76*4882a593Smuzhiyun  * struct kbase_vinstr_context - vinstr context per device
77*4882a593Smuzhiyun  * @lock:              protects the entire vinstr context
78*4882a593Smuzhiyun  * @kbdev:             pointer to kbase device
79*4882a593Smuzhiyun  * @kctx:              pointer to kbase context
80*4882a593Smuzhiyun  * @vmap:              vinstr vmap for mapping hwcnt dump buffer
81*4882a593Smuzhiyun  * @gpu_va:            GPU hwcnt dump buffer address
82*4882a593Smuzhiyun  * @cpu_va:            the CPU side mapping of the hwcnt dump buffer
83*4882a593Smuzhiyun  * @dump_size:         size of the dump buffer in bytes
84*4882a593Smuzhiyun  * @bitmap:            current set of counters monitored, not always in sync
85*4882a593Smuzhiyun  *                     with hardware
86*4882a593Smuzhiyun  * @reprogram:         when true, reprogram hwcnt block with the new set of
87*4882a593Smuzhiyun  *                     counters
88*4882a593Smuzhiyun  * @state:             vinstr state
89*4882a593Smuzhiyun  * @state_lock:        protects information about vinstr state
90*4882a593Smuzhiyun  * @suspend_waitq:     notification queue to trigger state re-validation
91*4882a593Smuzhiyun  * @suspend_cnt:       reference counter of vinstr's suspend state
92*4882a593Smuzhiyun  * @suspend_work:      worker to execute on entering suspended state
93*4882a593Smuzhiyun  * @resume_work:       worker to execute on leaving suspended state
94*4882a593Smuzhiyun  * @nclients:          number of attached clients, pending or otherwise
95*4882a593Smuzhiyun  * @waiting_clients:   head of list of clients being periodically sampled
96*4882a593Smuzhiyun  * @idle_clients:      head of list of clients being idle
97*4882a593Smuzhiyun  * @suspended_clients: head of list of clients being suspended
98*4882a593Smuzhiyun  * @thread:            periodic sampling thread
99*4882a593Smuzhiyun  * @waitq:             notification queue of sampling thread
100*4882a593Smuzhiyun  * @request_pending:   request for action for sampling thread
101*4882a593Smuzhiyun  */
102*4882a593Smuzhiyun struct kbase_vinstr_context {
103*4882a593Smuzhiyun 	struct mutex             lock;
104*4882a593Smuzhiyun 	struct kbase_device      *kbdev;
105*4882a593Smuzhiyun 	struct kbase_context     *kctx;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	struct kbase_vmap_struct vmap;
108*4882a593Smuzhiyun 	u64                      gpu_va;
109*4882a593Smuzhiyun 	void                     *cpu_va;
110*4882a593Smuzhiyun 	size_t                   dump_size;
111*4882a593Smuzhiyun 	u32                      bitmap[4];
112*4882a593Smuzhiyun 	bool                     reprogram;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	enum vinstr_state        state;
115*4882a593Smuzhiyun 	struct spinlock          state_lock;
116*4882a593Smuzhiyun 	wait_queue_head_t        suspend_waitq;
117*4882a593Smuzhiyun 	unsigned int             suspend_cnt;
118*4882a593Smuzhiyun 	struct work_struct       suspend_work;
119*4882a593Smuzhiyun 	struct work_struct       resume_work;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	u32                      nclients;
122*4882a593Smuzhiyun 	struct list_head         waiting_clients;
123*4882a593Smuzhiyun 	struct list_head         idle_clients;
124*4882a593Smuzhiyun 	struct list_head         suspended_clients;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	struct task_struct       *thread;
127*4882a593Smuzhiyun 	wait_queue_head_t        waitq;
128*4882a593Smuzhiyun 	atomic_t                 request_pending;
129*4882a593Smuzhiyun };
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun /**
132*4882a593Smuzhiyun  * struct kbase_vinstr_client - a vinstr client attached to a vinstr context
133*4882a593Smuzhiyun  * @vinstr_ctx:    vinstr context client is attached to
134*4882a593Smuzhiyun  * @list:          node used to attach this client to list in vinstr context
135*4882a593Smuzhiyun  * @buffer_count:  number of buffers this client is using
136*4882a593Smuzhiyun  * @event_mask:    events this client reacts to
137*4882a593Smuzhiyun  * @dump_size:     size of one dump buffer in bytes
138*4882a593Smuzhiyun  * @bitmap:        bitmap request for JM, TILER, SHADER and MMU counters
139*4882a593Smuzhiyun  * @legacy_buffer: userspace hwcnt dump buffer (legacy interface)
140*4882a593Smuzhiyun  * @kernel_buffer: kernel hwcnt dump buffer (kernel client interface)
141*4882a593Smuzhiyun  * @accum_buffer:  temporary accumulation buffer for preserving counters
142*4882a593Smuzhiyun  * @dump_time:     next time this clients shall request hwcnt dump
143*4882a593Smuzhiyun  * @dump_interval: interval between periodic hwcnt dumps
144*4882a593Smuzhiyun  * @dump_buffers:  kernel hwcnt dump buffers allocated by this client
145*4882a593Smuzhiyun  * @dump_buffers_meta: metadata of dump buffers
146*4882a593Smuzhiyun  * @meta_idx:      index of metadata being accessed by userspace
147*4882a593Smuzhiyun  * @read_idx:      index of buffer read by userspace
148*4882a593Smuzhiyun  * @write_idx:     index of buffer being written by dumping service
149*4882a593Smuzhiyun  * @waitq:         client's notification queue
150*4882a593Smuzhiyun  * @pending:       when true, client has attached but hwcnt not yet updated
151*4882a593Smuzhiyun  */
152*4882a593Smuzhiyun struct kbase_vinstr_client {
153*4882a593Smuzhiyun 	struct kbase_vinstr_context        *vinstr_ctx;
154*4882a593Smuzhiyun 	struct list_head                   list;
155*4882a593Smuzhiyun 	unsigned int                       buffer_count;
156*4882a593Smuzhiyun 	u32                                event_mask;
157*4882a593Smuzhiyun 	size_t                             dump_size;
158*4882a593Smuzhiyun 	u32                                bitmap[4];
159*4882a593Smuzhiyun 	void __user                        *legacy_buffer;
160*4882a593Smuzhiyun 	void                               *kernel_buffer;
161*4882a593Smuzhiyun 	void                               *accum_buffer;
162*4882a593Smuzhiyun 	u64                                dump_time;
163*4882a593Smuzhiyun 	u32                                dump_interval;
164*4882a593Smuzhiyun 	char                               *dump_buffers;
165*4882a593Smuzhiyun 	struct kbase_hwcnt_reader_metadata *dump_buffers_meta;
166*4882a593Smuzhiyun 	atomic_t                           meta_idx;
167*4882a593Smuzhiyun 	atomic_t                           read_idx;
168*4882a593Smuzhiyun 	atomic_t                           write_idx;
169*4882a593Smuzhiyun 	wait_queue_head_t                  waitq;
170*4882a593Smuzhiyun 	bool                               pending;
171*4882a593Smuzhiyun };
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun /**
174*4882a593Smuzhiyun  * struct kbasep_vinstr_wake_up_timer - vinstr service thread wake up timer
175*4882a593Smuzhiyun  * @hrtimer:    high resolution timer
176*4882a593Smuzhiyun  * @vinstr_ctx: vinstr context
177*4882a593Smuzhiyun  */
178*4882a593Smuzhiyun struct kbasep_vinstr_wake_up_timer {
179*4882a593Smuzhiyun 	struct hrtimer              hrtimer;
180*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx;
181*4882a593Smuzhiyun };
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun /*****************************************************************************/
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun static int kbasep_vinstr_service_task(void *data);
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun static unsigned int kbasep_vinstr_hwcnt_reader_poll(
188*4882a593Smuzhiyun 		struct file *filp,
189*4882a593Smuzhiyun 		poll_table  *wait);
190*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl(
191*4882a593Smuzhiyun 		struct file   *filp,
192*4882a593Smuzhiyun 		unsigned int  cmd,
193*4882a593Smuzhiyun 		unsigned long arg);
194*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_mmap(
195*4882a593Smuzhiyun 		struct file           *filp,
196*4882a593Smuzhiyun 		struct vm_area_struct *vma);
197*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_release(
198*4882a593Smuzhiyun 		struct inode *inode,
199*4882a593Smuzhiyun 		struct file  *filp);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun /* The timeline stream file operations structure. */
202*4882a593Smuzhiyun static const struct file_operations vinstr_client_fops = {
203*4882a593Smuzhiyun 	.poll           = kbasep_vinstr_hwcnt_reader_poll,
204*4882a593Smuzhiyun 	.unlocked_ioctl = kbasep_vinstr_hwcnt_reader_ioctl,
205*4882a593Smuzhiyun 	.compat_ioctl   = kbasep_vinstr_hwcnt_reader_ioctl,
206*4882a593Smuzhiyun 	.mmap           = kbasep_vinstr_hwcnt_reader_mmap,
207*4882a593Smuzhiyun 	.release        = kbasep_vinstr_hwcnt_reader_release,
208*4882a593Smuzhiyun };
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun /*****************************************************************************/
211*4882a593Smuzhiyun 
enable_hwcnt(struct kbase_vinstr_context * vinstr_ctx)212*4882a593Smuzhiyun static int enable_hwcnt(struct kbase_vinstr_context *vinstr_ctx)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun 	struct kbase_context *kctx = vinstr_ctx->kctx;
215*4882a593Smuzhiyun 	struct kbase_device *kbdev = kctx->kbdev;
216*4882a593Smuzhiyun 	struct kbase_uk_hwcnt_setup setup;
217*4882a593Smuzhiyun 	int err;
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	setup.dump_buffer = vinstr_ctx->gpu_va;
220*4882a593Smuzhiyun 	setup.jm_bm       = vinstr_ctx->bitmap[JM_HWCNT_BM];
221*4882a593Smuzhiyun 	setup.tiler_bm    = vinstr_ctx->bitmap[TILER_HWCNT_BM];
222*4882a593Smuzhiyun 	setup.shader_bm   = vinstr_ctx->bitmap[SHADER_HWCNT_BM];
223*4882a593Smuzhiyun 	setup.mmu_l2_bm   = vinstr_ctx->bitmap[MMU_L2_HWCNT_BM];
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	/* Mark the context as active so the GPU is kept turned on */
226*4882a593Smuzhiyun 	/* A suspend won't happen here, because we're in a syscall from a
227*4882a593Smuzhiyun 	 * userspace thread. */
228*4882a593Smuzhiyun 	kbase_pm_context_active(kbdev);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	/* Schedule the context in */
231*4882a593Smuzhiyun 	kbasep_js_schedule_privileged_ctx(kbdev, kctx);
232*4882a593Smuzhiyun 	err = kbase_instr_hwcnt_enable_internal(kbdev, kctx, &setup);
233*4882a593Smuzhiyun 	if (err) {
234*4882a593Smuzhiyun 		/* Release the context. This had its own Power Manager Active
235*4882a593Smuzhiyun 		 * reference */
236*4882a593Smuzhiyun 		kbasep_js_release_privileged_ctx(kbdev, kctx);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 		/* Also release our Power Manager Active reference */
239*4882a593Smuzhiyun 		kbase_pm_context_idle(kbdev);
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	return err;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun 
disable_hwcnt(struct kbase_vinstr_context * vinstr_ctx)245*4882a593Smuzhiyun static void disable_hwcnt(struct kbase_vinstr_context *vinstr_ctx)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun 	struct kbase_context *kctx = vinstr_ctx->kctx;
248*4882a593Smuzhiyun 	struct kbase_device *kbdev = kctx->kbdev;
249*4882a593Smuzhiyun 	int err;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	err = kbase_instr_hwcnt_disable_internal(kctx);
252*4882a593Smuzhiyun 	if (err) {
253*4882a593Smuzhiyun 		dev_warn(kbdev->dev, "Failed to disable HW counters (ctx:%p)",
254*4882a593Smuzhiyun 				kctx);
255*4882a593Smuzhiyun 		return;
256*4882a593Smuzhiyun 	}
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	/* Release the context. This had its own Power Manager Active reference. */
259*4882a593Smuzhiyun 	kbasep_js_release_privileged_ctx(kbdev, kctx);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	/* Also release our Power Manager Active reference. */
262*4882a593Smuzhiyun 	kbase_pm_context_idle(kbdev);
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	dev_dbg(kbdev->dev, "HW counters dumping disabled for context %p", kctx);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun 
reprogram_hwcnt(struct kbase_vinstr_context * vinstr_ctx)267*4882a593Smuzhiyun static int reprogram_hwcnt(struct kbase_vinstr_context *vinstr_ctx)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun 	disable_hwcnt(vinstr_ctx);
270*4882a593Smuzhiyun 	return enable_hwcnt(vinstr_ctx);
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
hwcnt_bitmap_set(u32 dst[4],u32 src[4])273*4882a593Smuzhiyun static void hwcnt_bitmap_set(u32 dst[4], u32 src[4])
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	dst[JM_HWCNT_BM]     = src[JM_HWCNT_BM];
276*4882a593Smuzhiyun 	dst[TILER_HWCNT_BM]  = src[TILER_HWCNT_BM];
277*4882a593Smuzhiyun 	dst[SHADER_HWCNT_BM] = src[SHADER_HWCNT_BM];
278*4882a593Smuzhiyun 	dst[MMU_L2_HWCNT_BM] = src[MMU_L2_HWCNT_BM];
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun 
hwcnt_bitmap_union(u32 dst[4],u32 src[4])281*4882a593Smuzhiyun static void hwcnt_bitmap_union(u32 dst[4], u32 src[4])
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun 	dst[JM_HWCNT_BM]     |= src[JM_HWCNT_BM];
284*4882a593Smuzhiyun 	dst[TILER_HWCNT_BM]  |= src[TILER_HWCNT_BM];
285*4882a593Smuzhiyun 	dst[SHADER_HWCNT_BM] |= src[SHADER_HWCNT_BM];
286*4882a593Smuzhiyun 	dst[MMU_L2_HWCNT_BM] |= src[MMU_L2_HWCNT_BM];
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun 
kbase_vinstr_dump_size(struct kbase_device * kbdev)289*4882a593Smuzhiyun size_t kbase_vinstr_dump_size(struct kbase_device *kbdev)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun 	size_t dump_size;
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
294*4882a593Smuzhiyun 	if (kbase_hw_has_feature(kbdev, BASE_HW_FEATURE_V4)) {
295*4882a593Smuzhiyun 		u32 nr_cg;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 		nr_cg = kbdev->gpu_props.num_core_groups;
298*4882a593Smuzhiyun 		dump_size = nr_cg * NR_CNT_BLOCKS_PER_GROUP *
299*4882a593Smuzhiyun 				NR_CNT_PER_BLOCK *
300*4882a593Smuzhiyun 				NR_BYTES_PER_CNT;
301*4882a593Smuzhiyun 	} else
302*4882a593Smuzhiyun #endif /* CONFIG_MALI_NO_MALI */
303*4882a593Smuzhiyun 	{
304*4882a593Smuzhiyun 		/* assume v5 for now */
305*4882a593Smuzhiyun 		base_gpu_props *props = &kbdev->gpu_props.props;
306*4882a593Smuzhiyun 		u32 nr_l2 = props->l2_props.num_l2_slices;
307*4882a593Smuzhiyun 		u64 core_mask = props->coherency_info.group[0].core_mask;
308*4882a593Smuzhiyun 		u32 nr_blocks = fls64(core_mask);
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 		/* JM and tiler counter blocks are always present */
311*4882a593Smuzhiyun 		dump_size = (2 + nr_l2 + nr_blocks) *
312*4882a593Smuzhiyun 				NR_CNT_PER_BLOCK *
313*4882a593Smuzhiyun 				NR_BYTES_PER_CNT;
314*4882a593Smuzhiyun 	}
315*4882a593Smuzhiyun 	return dump_size;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_dump_size);
318*4882a593Smuzhiyun 
kbasep_vinstr_dump_size_ctx(struct kbase_vinstr_context * vinstr_ctx)319*4882a593Smuzhiyun static size_t kbasep_vinstr_dump_size_ctx(
320*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	return kbase_vinstr_dump_size(vinstr_ctx->kctx->kbdev);
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
kbasep_vinstr_map_kernel_dump_buffer(struct kbase_vinstr_context * vinstr_ctx)325*4882a593Smuzhiyun static int kbasep_vinstr_map_kernel_dump_buffer(
326*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun 	struct kbase_va_region *reg;
329*4882a593Smuzhiyun 	struct kbase_context *kctx = vinstr_ctx->kctx;
330*4882a593Smuzhiyun 	u64 flags, nr_pages;
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 	flags = BASE_MEM_PROT_CPU_RD | BASE_MEM_PROT_GPU_WR;
333*4882a593Smuzhiyun 	vinstr_ctx->dump_size = kbasep_vinstr_dump_size_ctx(vinstr_ctx);
334*4882a593Smuzhiyun 	nr_pages = PFN_UP(vinstr_ctx->dump_size);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	reg = kbase_mem_alloc(kctx, nr_pages, nr_pages, 0, &flags,
337*4882a593Smuzhiyun 			&vinstr_ctx->gpu_va);
338*4882a593Smuzhiyun 	if (!reg)
339*4882a593Smuzhiyun 		return -ENOMEM;
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	vinstr_ctx->cpu_va = kbase_vmap(
342*4882a593Smuzhiyun 			kctx,
343*4882a593Smuzhiyun 			vinstr_ctx->gpu_va,
344*4882a593Smuzhiyun 			vinstr_ctx->dump_size,
345*4882a593Smuzhiyun 			&vinstr_ctx->vmap);
346*4882a593Smuzhiyun 	if (!vinstr_ctx->cpu_va) {
347*4882a593Smuzhiyun 		kbase_mem_free(kctx, vinstr_ctx->gpu_va);
348*4882a593Smuzhiyun 		return -ENOMEM;
349*4882a593Smuzhiyun 	}
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	return 0;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
kbasep_vinstr_unmap_kernel_dump_buffer(struct kbase_vinstr_context * vinstr_ctx)354*4882a593Smuzhiyun static void kbasep_vinstr_unmap_kernel_dump_buffer(
355*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun 	struct kbase_context *kctx = vinstr_ctx->kctx;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	kbase_vunmap(kctx, &vinstr_ctx->vmap);
360*4882a593Smuzhiyun 	kbase_mem_free(kctx, vinstr_ctx->gpu_va);
361*4882a593Smuzhiyun }
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun /**
364*4882a593Smuzhiyun  * kbasep_vinstr_create_kctx - create kernel context for vinstr
365*4882a593Smuzhiyun  * @vinstr_ctx: vinstr context
366*4882a593Smuzhiyun  * Return: zero on success
367*4882a593Smuzhiyun  */
kbasep_vinstr_create_kctx(struct kbase_vinstr_context * vinstr_ctx)368*4882a593Smuzhiyun static int kbasep_vinstr_create_kctx(struct kbase_vinstr_context *vinstr_ctx)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun 	struct kbase_device *kbdev = vinstr_ctx->kbdev;
371*4882a593Smuzhiyun 	struct kbasep_kctx_list_element *element;
372*4882a593Smuzhiyun 	unsigned long flags;
373*4882a593Smuzhiyun 	bool enable_backend = false;
374*4882a593Smuzhiyun 	int err;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	vinstr_ctx->kctx = kbase_create_context(vinstr_ctx->kbdev, true);
377*4882a593Smuzhiyun 	if (!vinstr_ctx->kctx)
378*4882a593Smuzhiyun 		return -ENOMEM;
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	/* Map the master kernel dump buffer.  The HW dumps the counters
381*4882a593Smuzhiyun 	 * into this memory region. */
382*4882a593Smuzhiyun 	err = kbasep_vinstr_map_kernel_dump_buffer(vinstr_ctx);
383*4882a593Smuzhiyun 	if (err) {
384*4882a593Smuzhiyun 		kbase_destroy_context(vinstr_ctx->kctx);
385*4882a593Smuzhiyun 		vinstr_ctx->kctx = NULL;
386*4882a593Smuzhiyun 		return err;
387*4882a593Smuzhiyun 	}
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	/* Add kernel context to list of contexts associated with device. */
390*4882a593Smuzhiyun 	element = kzalloc(sizeof(*element), GFP_KERNEL);
391*4882a593Smuzhiyun 	if (element) {
392*4882a593Smuzhiyun 		element->kctx = vinstr_ctx->kctx;
393*4882a593Smuzhiyun 		mutex_lock(&kbdev->kctx_list_lock);
394*4882a593Smuzhiyun 		list_add(&element->link, &kbdev->kctx_list);
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 		/* Inform timeline client about new context.
397*4882a593Smuzhiyun 		 * Do this while holding the lock to avoid tracepoint
398*4882a593Smuzhiyun 		 * being created in both body and summary stream. */
399*4882a593Smuzhiyun 		KBASE_TLSTREAM_TL_NEW_CTX(
400*4882a593Smuzhiyun 				vinstr_ctx->kctx,
401*4882a593Smuzhiyun 				(u32)(vinstr_ctx->kctx->id),
402*4882a593Smuzhiyun 				(u32)(vinstr_ctx->kctx->tgid));
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 		mutex_unlock(&kbdev->kctx_list_lock);
405*4882a593Smuzhiyun 	} else {
406*4882a593Smuzhiyun 		/* Don't treat this as a fail - just warn about it. */
407*4882a593Smuzhiyun 		dev_warn(kbdev->dev,
408*4882a593Smuzhiyun 				"couldn't add kctx to kctx_list\n");
409*4882a593Smuzhiyun 	}
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	/* Don't enable hardware counters if vinstr is suspended.
412*4882a593Smuzhiyun 	 * Note that vinstr resume code is run under vinstr context lock,
413*4882a593Smuzhiyun 	 * lower layer will be enabled as needed on resume. */
414*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
415*4882a593Smuzhiyun 	if (VINSTR_IDLE == vinstr_ctx->state)
416*4882a593Smuzhiyun 		enable_backend = true;
417*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
418*4882a593Smuzhiyun 	if (enable_backend)
419*4882a593Smuzhiyun 		err = enable_hwcnt(vinstr_ctx);
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	if (err) {
422*4882a593Smuzhiyun 		kbasep_vinstr_unmap_kernel_dump_buffer(vinstr_ctx);
423*4882a593Smuzhiyun 		kbase_destroy_context(vinstr_ctx->kctx);
424*4882a593Smuzhiyun 		if (element) {
425*4882a593Smuzhiyun 			mutex_lock(&kbdev->kctx_list_lock);
426*4882a593Smuzhiyun 			list_del(&element->link);
427*4882a593Smuzhiyun 			kfree(element);
428*4882a593Smuzhiyun 			mutex_unlock(&kbdev->kctx_list_lock);
429*4882a593Smuzhiyun 		}
430*4882a593Smuzhiyun 		KBASE_TLSTREAM_TL_DEL_CTX(vinstr_ctx->kctx);
431*4882a593Smuzhiyun 		vinstr_ctx->kctx = NULL;
432*4882a593Smuzhiyun 		return err;
433*4882a593Smuzhiyun 	}
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	vinstr_ctx->thread = kthread_run(
436*4882a593Smuzhiyun 			kbasep_vinstr_service_task,
437*4882a593Smuzhiyun 			vinstr_ctx,
438*4882a593Smuzhiyun 			"mali_vinstr_service");
439*4882a593Smuzhiyun 	if (!vinstr_ctx->thread) {
440*4882a593Smuzhiyun 		disable_hwcnt(vinstr_ctx);
441*4882a593Smuzhiyun 		kbasep_vinstr_unmap_kernel_dump_buffer(vinstr_ctx);
442*4882a593Smuzhiyun 		kbase_destroy_context(vinstr_ctx->kctx);
443*4882a593Smuzhiyun 		if (element) {
444*4882a593Smuzhiyun 			mutex_lock(&kbdev->kctx_list_lock);
445*4882a593Smuzhiyun 			list_del(&element->link);
446*4882a593Smuzhiyun 			kfree(element);
447*4882a593Smuzhiyun 			mutex_unlock(&kbdev->kctx_list_lock);
448*4882a593Smuzhiyun 		}
449*4882a593Smuzhiyun 		KBASE_TLSTREAM_TL_DEL_CTX(vinstr_ctx->kctx);
450*4882a593Smuzhiyun 		vinstr_ctx->kctx = NULL;
451*4882a593Smuzhiyun 		return -EFAULT;
452*4882a593Smuzhiyun 	}
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	return 0;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun /**
458*4882a593Smuzhiyun  * kbasep_vinstr_destroy_kctx - destroy vinstr's kernel context
459*4882a593Smuzhiyun  * @vinstr_ctx: vinstr context
460*4882a593Smuzhiyun  */
kbasep_vinstr_destroy_kctx(struct kbase_vinstr_context * vinstr_ctx)461*4882a593Smuzhiyun static void kbasep_vinstr_destroy_kctx(struct kbase_vinstr_context *vinstr_ctx)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun 	struct kbase_device             *kbdev = vinstr_ctx->kbdev;
464*4882a593Smuzhiyun 	struct kbasep_kctx_list_element *element;
465*4882a593Smuzhiyun 	struct kbasep_kctx_list_element *tmp;
466*4882a593Smuzhiyun 	bool                            found = false;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	/* Release hw counters dumping resources. */
469*4882a593Smuzhiyun 	vinstr_ctx->thread = NULL;
470*4882a593Smuzhiyun 	disable_hwcnt(vinstr_ctx);
471*4882a593Smuzhiyun 	kbasep_vinstr_unmap_kernel_dump_buffer(vinstr_ctx);
472*4882a593Smuzhiyun 	kbase_destroy_context(vinstr_ctx->kctx);
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	/* Remove kernel context from the device's contexts list. */
475*4882a593Smuzhiyun 	mutex_lock(&kbdev->kctx_list_lock);
476*4882a593Smuzhiyun 	list_for_each_entry_safe(element, tmp, &kbdev->kctx_list, link) {
477*4882a593Smuzhiyun 		if (element->kctx == vinstr_ctx->kctx) {
478*4882a593Smuzhiyun 			list_del(&element->link);
479*4882a593Smuzhiyun 			kfree(element);
480*4882a593Smuzhiyun 			found = true;
481*4882a593Smuzhiyun 		}
482*4882a593Smuzhiyun 	}
483*4882a593Smuzhiyun 	mutex_unlock(&kbdev->kctx_list_lock);
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun 	if (!found)
486*4882a593Smuzhiyun 		dev_warn(kbdev->dev, "kctx not in kctx_list\n");
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	/* Inform timeline client about context destruction. */
489*4882a593Smuzhiyun 	KBASE_TLSTREAM_TL_DEL_CTX(vinstr_ctx->kctx);
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	vinstr_ctx->kctx = NULL;
492*4882a593Smuzhiyun }
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun /**
495*4882a593Smuzhiyun  * kbasep_vinstr_attach_client - Attach a client to the vinstr core
496*4882a593Smuzhiyun  * @vinstr_ctx:    vinstr context
497*4882a593Smuzhiyun  * @buffer_count:  requested number of dump buffers
498*4882a593Smuzhiyun  * @bitmap:        bitmaps describing which counters should be enabled
499*4882a593Smuzhiyun  * @argp:          pointer where notification descriptor shall be stored
500*4882a593Smuzhiyun  * @kernel_buffer: pointer to kernel side buffer
501*4882a593Smuzhiyun  *
502*4882a593Smuzhiyun  * Return: vinstr opaque client handle or NULL on failure
503*4882a593Smuzhiyun  */
kbasep_vinstr_attach_client(struct kbase_vinstr_context * vinstr_ctx,u32 buffer_count,u32 bitmap[4],void * argp,void * kernel_buffer)504*4882a593Smuzhiyun static struct kbase_vinstr_client *kbasep_vinstr_attach_client(
505*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx, u32 buffer_count,
506*4882a593Smuzhiyun 		u32 bitmap[4], void *argp, void *kernel_buffer)
507*4882a593Smuzhiyun {
508*4882a593Smuzhiyun 	struct task_struct         *thread = NULL;
509*4882a593Smuzhiyun 	struct kbase_vinstr_client *cli;
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	if (buffer_count > MAX_BUFFER_COUNT
514*4882a593Smuzhiyun 	    || (buffer_count & (buffer_count - 1)))
515*4882a593Smuzhiyun 		return NULL;
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	cli = kzalloc(sizeof(*cli), GFP_KERNEL);
518*4882a593Smuzhiyun 	if (!cli)
519*4882a593Smuzhiyun 		return NULL;
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	cli->vinstr_ctx   = vinstr_ctx;
522*4882a593Smuzhiyun 	cli->buffer_count = buffer_count;
523*4882a593Smuzhiyun 	cli->event_mask   =
524*4882a593Smuzhiyun 		(1 << BASE_HWCNT_READER_EVENT_MANUAL) |
525*4882a593Smuzhiyun 		(1 << BASE_HWCNT_READER_EVENT_PERIODIC);
526*4882a593Smuzhiyun 	cli->pending      = true;
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	hwcnt_bitmap_set(cli->bitmap, bitmap);
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	hwcnt_bitmap_union(vinstr_ctx->bitmap, cli->bitmap);
533*4882a593Smuzhiyun 	vinstr_ctx->reprogram = true;
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun 	/* If this is the first client, create the vinstr kbase
536*4882a593Smuzhiyun 	 * context. This context is permanently resident until the
537*4882a593Smuzhiyun 	 * last client exits. */
538*4882a593Smuzhiyun 	if (!vinstr_ctx->nclients) {
539*4882a593Smuzhiyun 		hwcnt_bitmap_set(vinstr_ctx->bitmap, cli->bitmap);
540*4882a593Smuzhiyun 		if (kbasep_vinstr_create_kctx(vinstr_ctx) < 0)
541*4882a593Smuzhiyun 			goto error;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 		vinstr_ctx->reprogram = false;
544*4882a593Smuzhiyun 		cli->pending = false;
545*4882a593Smuzhiyun 	}
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun 	/* The GPU resets the counter block every time there is a request
548*4882a593Smuzhiyun 	 * to dump it. We need a per client kernel buffer for accumulating
549*4882a593Smuzhiyun 	 * the counters. */
550*4882a593Smuzhiyun 	cli->dump_size    = kbasep_vinstr_dump_size_ctx(vinstr_ctx);
551*4882a593Smuzhiyun 	cli->accum_buffer = kzalloc(cli->dump_size, GFP_KERNEL);
552*4882a593Smuzhiyun 	if (!cli->accum_buffer)
553*4882a593Smuzhiyun 		goto error;
554*4882a593Smuzhiyun 
555*4882a593Smuzhiyun 	/* Prepare buffers. */
556*4882a593Smuzhiyun 	if (cli->buffer_count) {
557*4882a593Smuzhiyun 		int *fd = (int *)argp;
558*4882a593Smuzhiyun 		size_t tmp;
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 		/* Allocate area for buffers metadata storage. */
561*4882a593Smuzhiyun 		tmp = sizeof(struct kbase_hwcnt_reader_metadata) *
562*4882a593Smuzhiyun 			cli->buffer_count;
563*4882a593Smuzhiyun 		cli->dump_buffers_meta = kmalloc(tmp, GFP_KERNEL);
564*4882a593Smuzhiyun 		if (!cli->dump_buffers_meta)
565*4882a593Smuzhiyun 			goto error;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 		/* Allocate required number of dumping buffers. */
568*4882a593Smuzhiyun 		cli->dump_buffers = (char *)__get_free_pages(
569*4882a593Smuzhiyun 				GFP_KERNEL | __GFP_ZERO,
570*4882a593Smuzhiyun 				get_order(cli->dump_size * cli->buffer_count));
571*4882a593Smuzhiyun 		if (!cli->dump_buffers)
572*4882a593Smuzhiyun 			goto error;
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun 		/* Create descriptor for user-kernel data exchange. */
575*4882a593Smuzhiyun 		*fd = anon_inode_getfd(
576*4882a593Smuzhiyun 				"[mali_vinstr_desc]",
577*4882a593Smuzhiyun 				&vinstr_client_fops,
578*4882a593Smuzhiyun 				cli,
579*4882a593Smuzhiyun 				O_RDONLY | O_CLOEXEC);
580*4882a593Smuzhiyun 		if (0 > *fd)
581*4882a593Smuzhiyun 			goto error;
582*4882a593Smuzhiyun 	} else if (kernel_buffer) {
583*4882a593Smuzhiyun 		cli->kernel_buffer = kernel_buffer;
584*4882a593Smuzhiyun 	} else {
585*4882a593Smuzhiyun 		cli->legacy_buffer = (void __user *)argp;
586*4882a593Smuzhiyun 	}
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	atomic_set(&cli->read_idx, 0);
589*4882a593Smuzhiyun 	atomic_set(&cli->meta_idx, 0);
590*4882a593Smuzhiyun 	atomic_set(&cli->write_idx, 0);
591*4882a593Smuzhiyun 	init_waitqueue_head(&cli->waitq);
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	vinstr_ctx->nclients++;
594*4882a593Smuzhiyun 	list_add(&cli->list, &vinstr_ctx->idle_clients);
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	return cli;
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun error:
601*4882a593Smuzhiyun 	kfree(cli->dump_buffers_meta);
602*4882a593Smuzhiyun 	if (cli->dump_buffers)
603*4882a593Smuzhiyun 		free_pages(
604*4882a593Smuzhiyun 				(unsigned long)cli->dump_buffers,
605*4882a593Smuzhiyun 				get_order(cli->dump_size * cli->buffer_count));
606*4882a593Smuzhiyun 	kfree(cli->accum_buffer);
607*4882a593Smuzhiyun 	if (!vinstr_ctx->nclients && vinstr_ctx->kctx) {
608*4882a593Smuzhiyun 		thread = vinstr_ctx->thread;
609*4882a593Smuzhiyun 		kbasep_vinstr_destroy_kctx(vinstr_ctx);
610*4882a593Smuzhiyun 	}
611*4882a593Smuzhiyun 	kfree(cli);
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	/* Thread must be stopped after lock is released. */
616*4882a593Smuzhiyun 	if (thread)
617*4882a593Smuzhiyun 		kthread_stop(thread);
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	return NULL;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun 
kbase_vinstr_detach_client(struct kbase_vinstr_client * cli)622*4882a593Smuzhiyun void kbase_vinstr_detach_client(struct kbase_vinstr_client *cli)
623*4882a593Smuzhiyun {
624*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx;
625*4882a593Smuzhiyun 	struct kbase_vinstr_client  *iter, *tmp;
626*4882a593Smuzhiyun 	struct task_struct          *thread = NULL;
627*4882a593Smuzhiyun 	u32 zerobitmap[4] = { 0 };
628*4882a593Smuzhiyun 	int cli_found = 0;
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli);
631*4882a593Smuzhiyun 	vinstr_ctx = cli->vinstr_ctx;
632*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	list_for_each_entry_safe(iter, tmp, &vinstr_ctx->idle_clients, list) {
637*4882a593Smuzhiyun 		if (iter == cli) {
638*4882a593Smuzhiyun 			vinstr_ctx->reprogram = true;
639*4882a593Smuzhiyun 			cli_found = 1;
640*4882a593Smuzhiyun 			list_del(&iter->list);
641*4882a593Smuzhiyun 			break;
642*4882a593Smuzhiyun 		}
643*4882a593Smuzhiyun 	}
644*4882a593Smuzhiyun 	if (!cli_found) {
645*4882a593Smuzhiyun 		list_for_each_entry_safe(
646*4882a593Smuzhiyun 				iter, tmp, &vinstr_ctx->waiting_clients, list) {
647*4882a593Smuzhiyun 			if (iter == cli) {
648*4882a593Smuzhiyun 				vinstr_ctx->reprogram = true;
649*4882a593Smuzhiyun 				cli_found = 1;
650*4882a593Smuzhiyun 				list_del(&iter->list);
651*4882a593Smuzhiyun 				break;
652*4882a593Smuzhiyun 			}
653*4882a593Smuzhiyun 		}
654*4882a593Smuzhiyun 	}
655*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli_found);
656*4882a593Smuzhiyun 
657*4882a593Smuzhiyun 	kfree(cli->dump_buffers_meta);
658*4882a593Smuzhiyun 	free_pages(
659*4882a593Smuzhiyun 			(unsigned long)cli->dump_buffers,
660*4882a593Smuzhiyun 			get_order(cli->dump_size * cli->buffer_count));
661*4882a593Smuzhiyun 	kfree(cli->accum_buffer);
662*4882a593Smuzhiyun 	kfree(cli);
663*4882a593Smuzhiyun 
664*4882a593Smuzhiyun 	vinstr_ctx->nclients--;
665*4882a593Smuzhiyun 	if (!vinstr_ctx->nclients) {
666*4882a593Smuzhiyun 		thread = vinstr_ctx->thread;
667*4882a593Smuzhiyun 		kbasep_vinstr_destroy_kctx(vinstr_ctx);
668*4882a593Smuzhiyun 	}
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun 	/* Rebuild context bitmap now that the client has detached */
671*4882a593Smuzhiyun 	hwcnt_bitmap_set(vinstr_ctx->bitmap, zerobitmap);
672*4882a593Smuzhiyun 	list_for_each_entry(iter, &vinstr_ctx->idle_clients, list)
673*4882a593Smuzhiyun 		hwcnt_bitmap_union(vinstr_ctx->bitmap, iter->bitmap);
674*4882a593Smuzhiyun 	list_for_each_entry(iter, &vinstr_ctx->waiting_clients, list)
675*4882a593Smuzhiyun 		hwcnt_bitmap_union(vinstr_ctx->bitmap, iter->bitmap);
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
678*4882a593Smuzhiyun 
679*4882a593Smuzhiyun 	/* Thread must be stopped after lock is released. */
680*4882a593Smuzhiyun 	if (thread)
681*4882a593Smuzhiyun 		kthread_stop(thread);
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_detach_client);
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun /* Accumulate counters in the dump buffer */
accum_dump_buffer(void * dst,void * src,size_t dump_size)686*4882a593Smuzhiyun static void accum_dump_buffer(void *dst, void *src, size_t dump_size)
687*4882a593Smuzhiyun {
688*4882a593Smuzhiyun 	size_t block_size = NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT;
689*4882a593Smuzhiyun 	u32 *d = dst;
690*4882a593Smuzhiyun 	u32 *s = src;
691*4882a593Smuzhiyun 	size_t i, j;
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	for (i = 0; i < dump_size; i += block_size) {
694*4882a593Smuzhiyun 		/* skip over the header block */
695*4882a593Smuzhiyun 		d += NR_BYTES_PER_HDR / sizeof(u32);
696*4882a593Smuzhiyun 		s += NR_BYTES_PER_HDR / sizeof(u32);
697*4882a593Smuzhiyun 		for (j = 0; j < (block_size - NR_BYTES_PER_HDR) / sizeof(u32); j++) {
698*4882a593Smuzhiyun 			/* saturate result if addition would result in wraparound */
699*4882a593Smuzhiyun 			if (U32_MAX - *d < *s)
700*4882a593Smuzhiyun 				*d = U32_MAX;
701*4882a593Smuzhiyun 			else
702*4882a593Smuzhiyun 				*d += *s;
703*4882a593Smuzhiyun 			d++;
704*4882a593Smuzhiyun 			s++;
705*4882a593Smuzhiyun 		}
706*4882a593Smuzhiyun 	}
707*4882a593Smuzhiyun }
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun /* This is the Midgard v4 patch function.  It copies the headers for each
710*4882a593Smuzhiyun  * of the defined blocks from the master kernel buffer and then patches up
711*4882a593Smuzhiyun  * the performance counter enable mask for each of the blocks to exclude
712*4882a593Smuzhiyun  * counters that were not requested by the client. */
patch_dump_buffer_hdr_v4(struct kbase_vinstr_context * vinstr_ctx,struct kbase_vinstr_client * cli)713*4882a593Smuzhiyun static void patch_dump_buffer_hdr_v4(
714*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx,
715*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun 	u32 *mask;
718*4882a593Smuzhiyun 	u8 *dst = cli->accum_buffer;
719*4882a593Smuzhiyun 	u8 *src = vinstr_ctx->cpu_va;
720*4882a593Smuzhiyun 	u32 nr_cg = vinstr_ctx->kctx->kbdev->gpu_props.num_core_groups;
721*4882a593Smuzhiyun 	size_t i, group_size, group;
722*4882a593Smuzhiyun 	enum {
723*4882a593Smuzhiyun 		SC0_BASE    = 0 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
724*4882a593Smuzhiyun 		SC1_BASE    = 1 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
725*4882a593Smuzhiyun 		SC2_BASE    = 2 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
726*4882a593Smuzhiyun 		SC3_BASE    = 3 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
727*4882a593Smuzhiyun 		TILER_BASE  = 4 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
728*4882a593Smuzhiyun 		MMU_L2_BASE = 5 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT,
729*4882a593Smuzhiyun 		JM_BASE     = 7 * NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT
730*4882a593Smuzhiyun 	};
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 	group_size = NR_CNT_BLOCKS_PER_GROUP *
733*4882a593Smuzhiyun 			NR_CNT_PER_BLOCK *
734*4882a593Smuzhiyun 			NR_BYTES_PER_CNT;
735*4882a593Smuzhiyun 	for (i = 0; i < nr_cg; i++) {
736*4882a593Smuzhiyun 		group = i * group_size;
737*4882a593Smuzhiyun 		/* copy shader core headers */
738*4882a593Smuzhiyun 		memcpy(&dst[group + SC0_BASE], &src[group + SC0_BASE],
739*4882a593Smuzhiyun 		       NR_BYTES_PER_HDR);
740*4882a593Smuzhiyun 		memcpy(&dst[group + SC1_BASE], &src[group + SC1_BASE],
741*4882a593Smuzhiyun 		       NR_BYTES_PER_HDR);
742*4882a593Smuzhiyun 		memcpy(&dst[group + SC2_BASE], &src[group + SC2_BASE],
743*4882a593Smuzhiyun 		      NR_BYTES_PER_HDR);
744*4882a593Smuzhiyun 		memcpy(&dst[group + SC3_BASE], &src[group + SC3_BASE],
745*4882a593Smuzhiyun 		      NR_BYTES_PER_HDR);
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 		/* copy tiler header */
748*4882a593Smuzhiyun 		memcpy(&dst[group + TILER_BASE], &src[group + TILER_BASE],
749*4882a593Smuzhiyun 		      NR_BYTES_PER_HDR);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 		/* copy mmu header */
752*4882a593Smuzhiyun 		memcpy(&dst[group + MMU_L2_BASE], &src[group + MMU_L2_BASE],
753*4882a593Smuzhiyun 		      NR_BYTES_PER_HDR);
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 		/* copy job manager header */
756*4882a593Smuzhiyun 		memcpy(&dst[group + JM_BASE], &src[group + JM_BASE],
757*4882a593Smuzhiyun 		      NR_BYTES_PER_HDR);
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 		/* patch the shader core enable mask */
760*4882a593Smuzhiyun 		mask = (u32 *)&dst[group + SC0_BASE + PRFCNT_EN_MASK_OFFSET];
761*4882a593Smuzhiyun 		*mask &= cli->bitmap[SHADER_HWCNT_BM];
762*4882a593Smuzhiyun 		mask = (u32 *)&dst[group + SC1_BASE + PRFCNT_EN_MASK_OFFSET];
763*4882a593Smuzhiyun 		*mask &= cli->bitmap[SHADER_HWCNT_BM];
764*4882a593Smuzhiyun 		mask = (u32 *)&dst[group + SC2_BASE + PRFCNT_EN_MASK_OFFSET];
765*4882a593Smuzhiyun 		*mask &= cli->bitmap[SHADER_HWCNT_BM];
766*4882a593Smuzhiyun 		mask = (u32 *)&dst[group + SC3_BASE + PRFCNT_EN_MASK_OFFSET];
767*4882a593Smuzhiyun 		*mask &= cli->bitmap[SHADER_HWCNT_BM];
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 		/* patch the tiler core enable mask */
770*4882a593Smuzhiyun 		mask = (u32 *)&dst[group + TILER_BASE + PRFCNT_EN_MASK_OFFSET];
771*4882a593Smuzhiyun 		*mask &= cli->bitmap[TILER_HWCNT_BM];
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 		/* patch the mmu core enable mask */
774*4882a593Smuzhiyun 		mask = (u32 *)&dst[group + MMU_L2_BASE + PRFCNT_EN_MASK_OFFSET];
775*4882a593Smuzhiyun 		*mask &= cli->bitmap[MMU_L2_HWCNT_BM];
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 		/* patch the job manager enable mask */
778*4882a593Smuzhiyun 		mask = (u32 *)&dst[group + JM_BASE + PRFCNT_EN_MASK_OFFSET];
779*4882a593Smuzhiyun 		*mask &= cli->bitmap[JM_HWCNT_BM];
780*4882a593Smuzhiyun 	}
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun /* This is the Midgard v5 patch function.  It copies the headers for each
784*4882a593Smuzhiyun  * of the defined blocks from the master kernel buffer and then patches up
785*4882a593Smuzhiyun  * the performance counter enable mask for each of the blocks to exclude
786*4882a593Smuzhiyun  * counters that were not requested by the client. */
patch_dump_buffer_hdr_v5(struct kbase_vinstr_context * vinstr_ctx,struct kbase_vinstr_client * cli)787*4882a593Smuzhiyun static void patch_dump_buffer_hdr_v5(
788*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx,
789*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli)
790*4882a593Smuzhiyun {
791*4882a593Smuzhiyun 	struct kbase_device *kbdev = vinstr_ctx->kctx->kbdev;
792*4882a593Smuzhiyun 	u32 i, nr_l2;
793*4882a593Smuzhiyun 	u64 core_mask;
794*4882a593Smuzhiyun 	u32 *mask;
795*4882a593Smuzhiyun 	u8 *dst = cli->accum_buffer;
796*4882a593Smuzhiyun 	u8 *src = vinstr_ctx->cpu_va;
797*4882a593Smuzhiyun 	size_t block_size = NR_CNT_PER_BLOCK * NR_BYTES_PER_CNT;
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	/* copy and patch job manager header */
800*4882a593Smuzhiyun 	memcpy(dst, src, NR_BYTES_PER_HDR);
801*4882a593Smuzhiyun 	mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
802*4882a593Smuzhiyun 	*mask &= cli->bitmap[JM_HWCNT_BM];
803*4882a593Smuzhiyun 	dst += block_size;
804*4882a593Smuzhiyun 	src += block_size;
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	/* copy and patch tiler header */
807*4882a593Smuzhiyun 	memcpy(dst, src, NR_BYTES_PER_HDR);
808*4882a593Smuzhiyun 	mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
809*4882a593Smuzhiyun 	*mask &= cli->bitmap[TILER_HWCNT_BM];
810*4882a593Smuzhiyun 	dst += block_size;
811*4882a593Smuzhiyun 	src += block_size;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	/* copy and patch MMU/L2C headers */
814*4882a593Smuzhiyun 	nr_l2 = kbdev->gpu_props.props.l2_props.num_l2_slices;
815*4882a593Smuzhiyun 	for (i = 0; i < nr_l2; i++) {
816*4882a593Smuzhiyun 		memcpy(dst, src, NR_BYTES_PER_HDR);
817*4882a593Smuzhiyun 		mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
818*4882a593Smuzhiyun 		*mask &= cli->bitmap[MMU_L2_HWCNT_BM];
819*4882a593Smuzhiyun 		dst += block_size;
820*4882a593Smuzhiyun 		src += block_size;
821*4882a593Smuzhiyun 	}
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	/* copy and patch shader core headers */
824*4882a593Smuzhiyun 	core_mask = kbdev->gpu_props.props.coherency_info.group[0].core_mask;
825*4882a593Smuzhiyun 	while (0ull != core_mask) {
826*4882a593Smuzhiyun 		memcpy(dst, src, NR_BYTES_PER_HDR);
827*4882a593Smuzhiyun 		if (0ull != (core_mask & 1ull)) {
828*4882a593Smuzhiyun 			/* if block is not reserved update header */
829*4882a593Smuzhiyun 			mask = (u32 *)&dst[PRFCNT_EN_MASK_OFFSET];
830*4882a593Smuzhiyun 			*mask &= cli->bitmap[SHADER_HWCNT_BM];
831*4882a593Smuzhiyun 		}
832*4882a593Smuzhiyun 		dst += block_size;
833*4882a593Smuzhiyun 		src += block_size;
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 		core_mask >>= 1;
836*4882a593Smuzhiyun 	}
837*4882a593Smuzhiyun }
838*4882a593Smuzhiyun 
839*4882a593Smuzhiyun /**
840*4882a593Smuzhiyun  * accum_clients - accumulate dumped hw counters for all known clients
841*4882a593Smuzhiyun  * @vinstr_ctx: vinstr context
842*4882a593Smuzhiyun  */
accum_clients(struct kbase_vinstr_context * vinstr_ctx)843*4882a593Smuzhiyun static void accum_clients(struct kbase_vinstr_context *vinstr_ctx)
844*4882a593Smuzhiyun {
845*4882a593Smuzhiyun 	struct kbase_vinstr_client *iter;
846*4882a593Smuzhiyun 	int v4 = 0;
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
849*4882a593Smuzhiyun 	v4 = kbase_hw_has_feature(vinstr_ctx->kbdev, BASE_HW_FEATURE_V4);
850*4882a593Smuzhiyun #endif
851*4882a593Smuzhiyun 
852*4882a593Smuzhiyun 	list_for_each_entry(iter, &vinstr_ctx->idle_clients, list) {
853*4882a593Smuzhiyun 		/* Don't bother accumulating clients whose hwcnt requests
854*4882a593Smuzhiyun 		 * have not yet been honoured. */
855*4882a593Smuzhiyun 		if (iter->pending)
856*4882a593Smuzhiyun 			continue;
857*4882a593Smuzhiyun 		if (v4)
858*4882a593Smuzhiyun 			patch_dump_buffer_hdr_v4(vinstr_ctx, iter);
859*4882a593Smuzhiyun 		else
860*4882a593Smuzhiyun 			patch_dump_buffer_hdr_v5(vinstr_ctx, iter);
861*4882a593Smuzhiyun 		accum_dump_buffer(
862*4882a593Smuzhiyun 				iter->accum_buffer,
863*4882a593Smuzhiyun 				vinstr_ctx->cpu_va,
864*4882a593Smuzhiyun 				iter->dump_size);
865*4882a593Smuzhiyun 	}
866*4882a593Smuzhiyun 	list_for_each_entry(iter, &vinstr_ctx->waiting_clients, list) {
867*4882a593Smuzhiyun 		/* Don't bother accumulating clients whose hwcnt requests
868*4882a593Smuzhiyun 		 * have not yet been honoured. */
869*4882a593Smuzhiyun 		if (iter->pending)
870*4882a593Smuzhiyun 			continue;
871*4882a593Smuzhiyun 		if (v4)
872*4882a593Smuzhiyun 			patch_dump_buffer_hdr_v4(vinstr_ctx, iter);
873*4882a593Smuzhiyun 		else
874*4882a593Smuzhiyun 			patch_dump_buffer_hdr_v5(vinstr_ctx, iter);
875*4882a593Smuzhiyun 		accum_dump_buffer(
876*4882a593Smuzhiyun 				iter->accum_buffer,
877*4882a593Smuzhiyun 				vinstr_ctx->cpu_va,
878*4882a593Smuzhiyun 				iter->dump_size);
879*4882a593Smuzhiyun 	}
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun /*****************************************************************************/
883*4882a593Smuzhiyun 
884*4882a593Smuzhiyun /**
885*4882a593Smuzhiyun  * kbasep_vinstr_get_timestamp - return timestamp
886*4882a593Smuzhiyun  *
887*4882a593Smuzhiyun  * Function returns timestamp value based on raw monotonic timer. Value will
888*4882a593Smuzhiyun  * wrap around zero in case of overflow.
889*4882a593Smuzhiyun  *
890*4882a593Smuzhiyun  * Return: timestamp value
891*4882a593Smuzhiyun  */
kbasep_vinstr_get_timestamp(void)892*4882a593Smuzhiyun static u64 kbasep_vinstr_get_timestamp(void)
893*4882a593Smuzhiyun {
894*4882a593Smuzhiyun 	struct timespec64 ts;
895*4882a593Smuzhiyun 
896*4882a593Smuzhiyun 	ktime_get_raw_ts64(&ts);
897*4882a593Smuzhiyun 	return (u64)ts.tv_sec * NSECS_IN_SEC + ts.tv_nsec;
898*4882a593Smuzhiyun }
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun /**
901*4882a593Smuzhiyun  * kbasep_vinstr_add_dump_request - register client's dumping request
902*4882a593Smuzhiyun  * @cli:             requesting client
903*4882a593Smuzhiyun  * @waiting_clients: list of pending dumping requests
904*4882a593Smuzhiyun  */
kbasep_vinstr_add_dump_request(struct kbase_vinstr_client * cli,struct list_head * waiting_clients)905*4882a593Smuzhiyun static void kbasep_vinstr_add_dump_request(
906*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli,
907*4882a593Smuzhiyun 		struct list_head *waiting_clients)
908*4882a593Smuzhiyun {
909*4882a593Smuzhiyun 	struct kbase_vinstr_client *tmp;
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun 	if (list_empty(waiting_clients)) {
912*4882a593Smuzhiyun 		list_add(&cli->list, waiting_clients);
913*4882a593Smuzhiyun 		return;
914*4882a593Smuzhiyun 	}
915*4882a593Smuzhiyun 	list_for_each_entry(tmp, waiting_clients, list) {
916*4882a593Smuzhiyun 		if (tmp->dump_time > cli->dump_time) {
917*4882a593Smuzhiyun 			list_add_tail(&cli->list, &tmp->list);
918*4882a593Smuzhiyun 			return;
919*4882a593Smuzhiyun 		}
920*4882a593Smuzhiyun 	}
921*4882a593Smuzhiyun 	list_add_tail(&cli->list, waiting_clients);
922*4882a593Smuzhiyun }
923*4882a593Smuzhiyun 
924*4882a593Smuzhiyun /**
925*4882a593Smuzhiyun  * kbasep_vinstr_collect_and_accumulate - collect hw counters via low level
926*4882a593Smuzhiyun  *                                        dump and accumulate them for known
927*4882a593Smuzhiyun  *                                        clients
928*4882a593Smuzhiyun  * @vinstr_ctx: vinstr context
929*4882a593Smuzhiyun  * @timestamp: pointer where collection timestamp will be recorded
930*4882a593Smuzhiyun  *
931*4882a593Smuzhiyun  * Return: zero on success
932*4882a593Smuzhiyun  */
kbasep_vinstr_collect_and_accumulate(struct kbase_vinstr_context * vinstr_ctx,u64 * timestamp)933*4882a593Smuzhiyun static int kbasep_vinstr_collect_and_accumulate(
934*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx, u64 *timestamp)
935*4882a593Smuzhiyun {
936*4882a593Smuzhiyun 	unsigned long flags;
937*4882a593Smuzhiyun 	int rcode;
938*4882a593Smuzhiyun 
939*4882a593Smuzhiyun #ifdef CONFIG_MALI_NO_MALI
940*4882a593Smuzhiyun 	/* The dummy model needs the CPU mapping. */
941*4882a593Smuzhiyun 	gpu_model_set_dummy_prfcnt_base_cpu(vinstr_ctx->cpu_va);
942*4882a593Smuzhiyun #endif
943*4882a593Smuzhiyun 
944*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
945*4882a593Smuzhiyun 	if (VINSTR_IDLE != vinstr_ctx->state) {
946*4882a593Smuzhiyun 		spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
947*4882a593Smuzhiyun 		return -EAGAIN;
948*4882a593Smuzhiyun 	} else {
949*4882a593Smuzhiyun 		vinstr_ctx->state = VINSTR_DUMPING;
950*4882a593Smuzhiyun 	}
951*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
952*4882a593Smuzhiyun 
953*4882a593Smuzhiyun 	/* Request HW counters dump.
954*4882a593Smuzhiyun 	 * Disable preemption to make dump timestamp more accurate. */
955*4882a593Smuzhiyun 	preempt_disable();
956*4882a593Smuzhiyun 	*timestamp = kbasep_vinstr_get_timestamp();
957*4882a593Smuzhiyun 	rcode = kbase_instr_hwcnt_request_dump(vinstr_ctx->kctx);
958*4882a593Smuzhiyun 	preempt_enable();
959*4882a593Smuzhiyun 
960*4882a593Smuzhiyun 	if (!rcode)
961*4882a593Smuzhiyun 		rcode = kbase_instr_hwcnt_wait_for_dump(vinstr_ctx->kctx);
962*4882a593Smuzhiyun 	WARN_ON(rcode);
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
965*4882a593Smuzhiyun 	switch (vinstr_ctx->state)
966*4882a593Smuzhiyun 	{
967*4882a593Smuzhiyun 	case VINSTR_SUSPENDING:
968*4882a593Smuzhiyun 		schedule_work(&vinstr_ctx->suspend_work);
969*4882a593Smuzhiyun 		break;
970*4882a593Smuzhiyun 	case VINSTR_DUMPING:
971*4882a593Smuzhiyun 		vinstr_ctx->state = VINSTR_IDLE;
972*4882a593Smuzhiyun 		wake_up_all(&vinstr_ctx->suspend_waitq);
973*4882a593Smuzhiyun 		break;
974*4882a593Smuzhiyun 	default:
975*4882a593Smuzhiyun 		break;
976*4882a593Smuzhiyun 	}
977*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
978*4882a593Smuzhiyun 
979*4882a593Smuzhiyun 	/* Accumulate values of collected counters. */
980*4882a593Smuzhiyun 	if (!rcode)
981*4882a593Smuzhiyun 		accum_clients(vinstr_ctx);
982*4882a593Smuzhiyun 
983*4882a593Smuzhiyun 	return rcode;
984*4882a593Smuzhiyun }
985*4882a593Smuzhiyun 
986*4882a593Smuzhiyun /**
987*4882a593Smuzhiyun  * kbasep_vinstr_fill_dump_buffer - copy accumulated counters to empty kernel
988*4882a593Smuzhiyun  *                                  buffer
989*4882a593Smuzhiyun  * @cli:       requesting client
990*4882a593Smuzhiyun  * @timestamp: timestamp when counters were collected
991*4882a593Smuzhiyun  * @event_id:  id of event that caused triggered counters collection
992*4882a593Smuzhiyun  *
993*4882a593Smuzhiyun  * Return: zero on success
994*4882a593Smuzhiyun  */
kbasep_vinstr_fill_dump_buffer(struct kbase_vinstr_client * cli,u64 timestamp,enum base_hwcnt_reader_event event_id)995*4882a593Smuzhiyun static int kbasep_vinstr_fill_dump_buffer(
996*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli, u64 timestamp,
997*4882a593Smuzhiyun 		enum base_hwcnt_reader_event event_id)
998*4882a593Smuzhiyun {
999*4882a593Smuzhiyun 	unsigned int write_idx = atomic_read(&cli->write_idx);
1000*4882a593Smuzhiyun 	unsigned int read_idx  = atomic_read(&cli->read_idx);
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	struct kbase_hwcnt_reader_metadata *meta;
1003*4882a593Smuzhiyun 	void                               *buffer;
1004*4882a593Smuzhiyun 
1005*4882a593Smuzhiyun 	/* Check if there is a place to copy HWC block into. */
1006*4882a593Smuzhiyun 	if (write_idx - read_idx == cli->buffer_count)
1007*4882a593Smuzhiyun 		return -1;
1008*4882a593Smuzhiyun 	write_idx %= cli->buffer_count;
1009*4882a593Smuzhiyun 
1010*4882a593Smuzhiyun 	/* Fill in dump buffer and its metadata. */
1011*4882a593Smuzhiyun 	buffer = &cli->dump_buffers[write_idx * cli->dump_size];
1012*4882a593Smuzhiyun 	meta   = &cli->dump_buffers_meta[write_idx];
1013*4882a593Smuzhiyun 	meta->timestamp  = timestamp;
1014*4882a593Smuzhiyun 	meta->event_id   = event_id;
1015*4882a593Smuzhiyun 	meta->buffer_idx = write_idx;
1016*4882a593Smuzhiyun 	memcpy(buffer, cli->accum_buffer, cli->dump_size);
1017*4882a593Smuzhiyun 	return 0;
1018*4882a593Smuzhiyun }
1019*4882a593Smuzhiyun 
1020*4882a593Smuzhiyun /**
1021*4882a593Smuzhiyun  * kbasep_vinstr_fill_dump_buffer_legacy - copy accumulated counters to buffer
1022*4882a593Smuzhiyun  *                                         allocated in userspace
1023*4882a593Smuzhiyun  * @cli: requesting client
1024*4882a593Smuzhiyun  *
1025*4882a593Smuzhiyun  * Return: zero on success
1026*4882a593Smuzhiyun  *
1027*4882a593Smuzhiyun  * This is part of legacy ioctl interface.
1028*4882a593Smuzhiyun  */
kbasep_vinstr_fill_dump_buffer_legacy(struct kbase_vinstr_client * cli)1029*4882a593Smuzhiyun static int kbasep_vinstr_fill_dump_buffer_legacy(
1030*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli)
1031*4882a593Smuzhiyun {
1032*4882a593Smuzhiyun 	void __user  *buffer = cli->legacy_buffer;
1033*4882a593Smuzhiyun 	int          rcode;
1034*4882a593Smuzhiyun 
1035*4882a593Smuzhiyun 	/* Copy data to user buffer. */
1036*4882a593Smuzhiyun 	rcode = copy_to_user(buffer, cli->accum_buffer, cli->dump_size);
1037*4882a593Smuzhiyun 	if (rcode)
1038*4882a593Smuzhiyun 		pr_warn("error while copying buffer to user\n");
1039*4882a593Smuzhiyun 	return rcode;
1040*4882a593Smuzhiyun }
1041*4882a593Smuzhiyun 
1042*4882a593Smuzhiyun /**
1043*4882a593Smuzhiyun  * kbasep_vinstr_fill_dump_buffer_kernel - copy accumulated counters to buffer
1044*4882a593Smuzhiyun  *                                         allocated in kernel space
1045*4882a593Smuzhiyun  * @cli: requesting client
1046*4882a593Smuzhiyun  *
1047*4882a593Smuzhiyun  * Return: zero on success
1048*4882a593Smuzhiyun  *
1049*4882a593Smuzhiyun  * This is part of the kernel client interface.
1050*4882a593Smuzhiyun  */
kbasep_vinstr_fill_dump_buffer_kernel(struct kbase_vinstr_client * cli)1051*4882a593Smuzhiyun static int kbasep_vinstr_fill_dump_buffer_kernel(
1052*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli)
1053*4882a593Smuzhiyun {
1054*4882a593Smuzhiyun 	memcpy(cli->kernel_buffer, cli->accum_buffer, cli->dump_size);
1055*4882a593Smuzhiyun 
1056*4882a593Smuzhiyun 	return 0;
1057*4882a593Smuzhiyun }
1058*4882a593Smuzhiyun 
1059*4882a593Smuzhiyun /**
1060*4882a593Smuzhiyun  * kbasep_vinstr_reprogram - reprogram hwcnt set collected by inst
1061*4882a593Smuzhiyun  * @vinstr_ctx: vinstr context
1062*4882a593Smuzhiyun  */
kbasep_vinstr_reprogram(struct kbase_vinstr_context * vinstr_ctx)1063*4882a593Smuzhiyun static void kbasep_vinstr_reprogram(
1064*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx)
1065*4882a593Smuzhiyun {
1066*4882a593Smuzhiyun 	unsigned long flags;
1067*4882a593Smuzhiyun 	bool suspended = false;
1068*4882a593Smuzhiyun 
1069*4882a593Smuzhiyun 	/* Don't enable hardware counters if vinstr is suspended. */
1070*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
1071*4882a593Smuzhiyun 	if (VINSTR_IDLE != vinstr_ctx->state)
1072*4882a593Smuzhiyun 		suspended = true;
1073*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
1074*4882a593Smuzhiyun 	if (suspended)
1075*4882a593Smuzhiyun 		return;
1076*4882a593Smuzhiyun 
1077*4882a593Smuzhiyun 	/* Change to suspended state is done while holding vinstr context
1078*4882a593Smuzhiyun 	 * lock. Below code will then no re-enable the instrumentation. */
1079*4882a593Smuzhiyun 
1080*4882a593Smuzhiyun 	if (vinstr_ctx->reprogram) {
1081*4882a593Smuzhiyun 		struct kbase_vinstr_client *iter;
1082*4882a593Smuzhiyun 
1083*4882a593Smuzhiyun 		if (!reprogram_hwcnt(vinstr_ctx)) {
1084*4882a593Smuzhiyun 			vinstr_ctx->reprogram = false;
1085*4882a593Smuzhiyun 			list_for_each_entry(
1086*4882a593Smuzhiyun 					iter,
1087*4882a593Smuzhiyun 					&vinstr_ctx->idle_clients,
1088*4882a593Smuzhiyun 					list)
1089*4882a593Smuzhiyun 				iter->pending = false;
1090*4882a593Smuzhiyun 			list_for_each_entry(
1091*4882a593Smuzhiyun 					iter,
1092*4882a593Smuzhiyun 					&vinstr_ctx->waiting_clients,
1093*4882a593Smuzhiyun 					list)
1094*4882a593Smuzhiyun 				iter->pending = false;
1095*4882a593Smuzhiyun 		}
1096*4882a593Smuzhiyun 	}
1097*4882a593Smuzhiyun }
1098*4882a593Smuzhiyun 
1099*4882a593Smuzhiyun /**
1100*4882a593Smuzhiyun  * kbasep_vinstr_update_client - copy accumulated counters to user readable
1101*4882a593Smuzhiyun  *                               buffer and notify the user
1102*4882a593Smuzhiyun  * @cli:       requesting client
1103*4882a593Smuzhiyun  * @timestamp: timestamp when counters were collected
1104*4882a593Smuzhiyun  * @event_id:  id of event that caused triggered counters collection
1105*4882a593Smuzhiyun  *
1106*4882a593Smuzhiyun  * Return: zero on success
1107*4882a593Smuzhiyun  */
kbasep_vinstr_update_client(struct kbase_vinstr_client * cli,u64 timestamp,enum base_hwcnt_reader_event event_id)1108*4882a593Smuzhiyun static int kbasep_vinstr_update_client(
1109*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli, u64 timestamp,
1110*4882a593Smuzhiyun 		enum base_hwcnt_reader_event event_id)
1111*4882a593Smuzhiyun {
1112*4882a593Smuzhiyun 	int rcode = 0;
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun 	/* Copy collected counters to user readable buffer. */
1115*4882a593Smuzhiyun 	if (cli->buffer_count)
1116*4882a593Smuzhiyun 		rcode = kbasep_vinstr_fill_dump_buffer(
1117*4882a593Smuzhiyun 				cli, timestamp, event_id);
1118*4882a593Smuzhiyun 	else if (cli->kernel_buffer)
1119*4882a593Smuzhiyun 		rcode = kbasep_vinstr_fill_dump_buffer_kernel(cli);
1120*4882a593Smuzhiyun 	else
1121*4882a593Smuzhiyun 		rcode = kbasep_vinstr_fill_dump_buffer_legacy(cli);
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun 	if (rcode)
1124*4882a593Smuzhiyun 		goto exit;
1125*4882a593Smuzhiyun 
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun 	/* Notify client. Make sure all changes to memory are visible. */
1128*4882a593Smuzhiyun 	wmb();
1129*4882a593Smuzhiyun 	atomic_inc(&cli->write_idx);
1130*4882a593Smuzhiyun 	wake_up_interruptible(&cli->waitq);
1131*4882a593Smuzhiyun 
1132*4882a593Smuzhiyun 	/* Prepare for next request. */
1133*4882a593Smuzhiyun 	memset(cli->accum_buffer, 0, cli->dump_size);
1134*4882a593Smuzhiyun 
1135*4882a593Smuzhiyun exit:
1136*4882a593Smuzhiyun 	return rcode;
1137*4882a593Smuzhiyun }
1138*4882a593Smuzhiyun 
1139*4882a593Smuzhiyun /**
1140*4882a593Smuzhiyun  * kbasep_vinstr_wake_up_callback - vinstr wake up timer wake up function
1141*4882a593Smuzhiyun  *
1142*4882a593Smuzhiyun  * @hrtimer: high resolution timer
1143*4882a593Smuzhiyun  *
1144*4882a593Smuzhiyun  * Return: High resolution timer restart enum.
1145*4882a593Smuzhiyun  */
kbasep_vinstr_wake_up_callback(struct hrtimer * hrtimer)1146*4882a593Smuzhiyun static enum hrtimer_restart kbasep_vinstr_wake_up_callback(
1147*4882a593Smuzhiyun 		struct hrtimer *hrtimer)
1148*4882a593Smuzhiyun {
1149*4882a593Smuzhiyun 	struct kbasep_vinstr_wake_up_timer *timer =
1150*4882a593Smuzhiyun 		container_of(
1151*4882a593Smuzhiyun 			hrtimer,
1152*4882a593Smuzhiyun 			struct kbasep_vinstr_wake_up_timer,
1153*4882a593Smuzhiyun 			hrtimer);
1154*4882a593Smuzhiyun 
1155*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(timer);
1156*4882a593Smuzhiyun 
1157*4882a593Smuzhiyun 	atomic_set(&timer->vinstr_ctx->request_pending, 1);
1158*4882a593Smuzhiyun 	wake_up_all(&timer->vinstr_ctx->waitq);
1159*4882a593Smuzhiyun 
1160*4882a593Smuzhiyun 	return HRTIMER_NORESTART;
1161*4882a593Smuzhiyun }
1162*4882a593Smuzhiyun 
1163*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_OBJECT_TIMERS
1164*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0))
1165*4882a593Smuzhiyun /**
1166*4882a593Smuzhiyun  * kbase_destroy_hrtimer_on_stack - kernel's destroy_hrtimer_on_stack(),
1167*4882a593Smuzhiyun  *                                  rewritten
1168*4882a593Smuzhiyun  *
1169*4882a593Smuzhiyun  * @timer: high resolution timer
1170*4882a593Smuzhiyun  *
1171*4882a593Smuzhiyun  * destroy_hrtimer_on_stack() was exported only for 4.7.0 kernel so for
1172*4882a593Smuzhiyun  * earlier kernel versions it is not possible to call it explicitly.
1173*4882a593Smuzhiyun  * Since this function must accompany hrtimer_init_on_stack(), which
1174*4882a593Smuzhiyun  * has to be used for hrtimer initialization if CONFIG_DEBUG_OBJECT_TIMERS
1175*4882a593Smuzhiyun  * is defined in order to avoid the warning about object on stack not being
1176*4882a593Smuzhiyun  * annotated, we rewrite it here to be used for earlier kernel versions.
1177*4882a593Smuzhiyun  */
kbase_destroy_hrtimer_on_stack(struct hrtimer * timer)1178*4882a593Smuzhiyun static void kbase_destroy_hrtimer_on_stack(struct hrtimer *timer)
1179*4882a593Smuzhiyun {
1180*4882a593Smuzhiyun 	debug_object_free(timer, &hrtimer_debug_descr);
1181*4882a593Smuzhiyun }
1182*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) */
1183*4882a593Smuzhiyun #endif /* CONFIG_DEBUG_OBJECT_TIMERS */
1184*4882a593Smuzhiyun 
1185*4882a593Smuzhiyun /**
1186*4882a593Smuzhiyun  * kbasep_vinstr_service_task - HWC dumping service thread
1187*4882a593Smuzhiyun  *
1188*4882a593Smuzhiyun  * @data: Pointer to vinstr context structure.
1189*4882a593Smuzhiyun  *
1190*4882a593Smuzhiyun  * Return: Always returns zero.
1191*4882a593Smuzhiyun  */
kbasep_vinstr_service_task(void * data)1192*4882a593Smuzhiyun static int kbasep_vinstr_service_task(void *data)
1193*4882a593Smuzhiyun {
1194*4882a593Smuzhiyun 	struct kbase_vinstr_context        *vinstr_ctx = data;
1195*4882a593Smuzhiyun 	struct kbasep_vinstr_wake_up_timer timer;
1196*4882a593Smuzhiyun 
1197*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun 	hrtimer_init_on_stack(&timer.hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
1200*4882a593Smuzhiyun 
1201*4882a593Smuzhiyun 	timer.hrtimer.function = kbasep_vinstr_wake_up_callback;
1202*4882a593Smuzhiyun 	timer.vinstr_ctx       = vinstr_ctx;
1203*4882a593Smuzhiyun 
1204*4882a593Smuzhiyun 	while (!kthread_should_stop()) {
1205*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli = NULL;
1206*4882a593Smuzhiyun 		struct kbase_vinstr_client *tmp;
1207*4882a593Smuzhiyun 		int                        rcode;
1208*4882a593Smuzhiyun 
1209*4882a593Smuzhiyun 		u64              timestamp = kbasep_vinstr_get_timestamp();
1210*4882a593Smuzhiyun 		u64              dump_time = 0;
1211*4882a593Smuzhiyun 		struct list_head expired_requests;
1212*4882a593Smuzhiyun 
1213*4882a593Smuzhiyun 		/* Hold lock while performing operations on lists of clients. */
1214*4882a593Smuzhiyun 		mutex_lock(&vinstr_ctx->lock);
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun 		/* Closing thread must not interact with client requests. */
1217*4882a593Smuzhiyun 		if (current == vinstr_ctx->thread) {
1218*4882a593Smuzhiyun 			atomic_set(&vinstr_ctx->request_pending, 0);
1219*4882a593Smuzhiyun 
1220*4882a593Smuzhiyun 			if (!list_empty(&vinstr_ctx->waiting_clients)) {
1221*4882a593Smuzhiyun 				cli = list_first_entry(
1222*4882a593Smuzhiyun 						&vinstr_ctx->waiting_clients,
1223*4882a593Smuzhiyun 						struct kbase_vinstr_client,
1224*4882a593Smuzhiyun 						list);
1225*4882a593Smuzhiyun 				dump_time = cli->dump_time;
1226*4882a593Smuzhiyun 			}
1227*4882a593Smuzhiyun 		}
1228*4882a593Smuzhiyun 
1229*4882a593Smuzhiyun 		if (!cli || ((s64)timestamp - (s64)dump_time < 0ll)) {
1230*4882a593Smuzhiyun 			mutex_unlock(&vinstr_ctx->lock);
1231*4882a593Smuzhiyun 
1232*4882a593Smuzhiyun 			/* Sleep until next dumping event or service request. */
1233*4882a593Smuzhiyun 			if (cli) {
1234*4882a593Smuzhiyun 				u64 diff = dump_time - timestamp;
1235*4882a593Smuzhiyun 
1236*4882a593Smuzhiyun 				hrtimer_start(
1237*4882a593Smuzhiyun 						&timer.hrtimer,
1238*4882a593Smuzhiyun 						ns_to_ktime(diff),
1239*4882a593Smuzhiyun 						HRTIMER_MODE_REL);
1240*4882a593Smuzhiyun 			}
1241*4882a593Smuzhiyun 			wait_event(
1242*4882a593Smuzhiyun 					vinstr_ctx->waitq,
1243*4882a593Smuzhiyun 					atomic_read(
1244*4882a593Smuzhiyun 						&vinstr_ctx->request_pending) ||
1245*4882a593Smuzhiyun 					kthread_should_stop());
1246*4882a593Smuzhiyun 			hrtimer_cancel(&timer.hrtimer);
1247*4882a593Smuzhiyun 			continue;
1248*4882a593Smuzhiyun 		}
1249*4882a593Smuzhiyun 
1250*4882a593Smuzhiyun 		rcode = kbasep_vinstr_collect_and_accumulate(vinstr_ctx,
1251*4882a593Smuzhiyun 				&timestamp);
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 		INIT_LIST_HEAD(&expired_requests);
1254*4882a593Smuzhiyun 
1255*4882a593Smuzhiyun 		/* Find all expired requests. */
1256*4882a593Smuzhiyun 		list_for_each_entry_safe(
1257*4882a593Smuzhiyun 				cli,
1258*4882a593Smuzhiyun 				tmp,
1259*4882a593Smuzhiyun 				&vinstr_ctx->waiting_clients,
1260*4882a593Smuzhiyun 				list) {
1261*4882a593Smuzhiyun 			s64 tdiff =
1262*4882a593Smuzhiyun 				(s64)(timestamp + DUMPING_RESOLUTION) -
1263*4882a593Smuzhiyun 				(s64)cli->dump_time;
1264*4882a593Smuzhiyun 			if (tdiff >= 0ll) {
1265*4882a593Smuzhiyun 				list_del(&cli->list);
1266*4882a593Smuzhiyun 				list_add(&cli->list, &expired_requests);
1267*4882a593Smuzhiyun 			} else {
1268*4882a593Smuzhiyun 				break;
1269*4882a593Smuzhiyun 			}
1270*4882a593Smuzhiyun 		}
1271*4882a593Smuzhiyun 
1272*4882a593Smuzhiyun 		/* Fill data for each request found. */
1273*4882a593Smuzhiyun 		list_for_each_entry_safe(cli, tmp, &expired_requests, list) {
1274*4882a593Smuzhiyun 			/* Ensure that legacy buffer will not be used from
1275*4882a593Smuzhiyun 			 * this kthread context. */
1276*4882a593Smuzhiyun 			BUG_ON(0 == cli->buffer_count);
1277*4882a593Smuzhiyun 			/* Expect only periodically sampled clients. */
1278*4882a593Smuzhiyun 			BUG_ON(0 == cli->dump_interval);
1279*4882a593Smuzhiyun 
1280*4882a593Smuzhiyun 			if (!rcode)
1281*4882a593Smuzhiyun 				kbasep_vinstr_update_client(
1282*4882a593Smuzhiyun 						cli,
1283*4882a593Smuzhiyun 						timestamp,
1284*4882a593Smuzhiyun 						BASE_HWCNT_READER_EVENT_PERIODIC);
1285*4882a593Smuzhiyun 
1286*4882a593Smuzhiyun 			/* Set new dumping time. Drop missed probing times. */
1287*4882a593Smuzhiyun 			do {
1288*4882a593Smuzhiyun 				cli->dump_time += cli->dump_interval;
1289*4882a593Smuzhiyun 			} while (cli->dump_time < timestamp);
1290*4882a593Smuzhiyun 
1291*4882a593Smuzhiyun 			list_del(&cli->list);
1292*4882a593Smuzhiyun 			kbasep_vinstr_add_dump_request(
1293*4882a593Smuzhiyun 					cli,
1294*4882a593Smuzhiyun 					&vinstr_ctx->waiting_clients);
1295*4882a593Smuzhiyun 		}
1296*4882a593Smuzhiyun 
1297*4882a593Smuzhiyun 		/* Reprogram counters set if required. */
1298*4882a593Smuzhiyun 		kbasep_vinstr_reprogram(vinstr_ctx);
1299*4882a593Smuzhiyun 
1300*4882a593Smuzhiyun 		mutex_unlock(&vinstr_ctx->lock);
1301*4882a593Smuzhiyun 	}
1302*4882a593Smuzhiyun 
1303*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_OBJECTS_TIMERS
1304*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0))
1305*4882a593Smuzhiyun 	kbase_destroy_hrtimer_on_stack(&timer.hrtimer);
1306*4882a593Smuzhiyun #else
1307*4882a593Smuzhiyun 	destroy_hrtimer_on_stack(&timer.hrtimer);
1308*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)) */
1309*4882a593Smuzhiyun #endif /* CONFIG_DEBUG_OBJECTS_TIMERS */
1310*4882a593Smuzhiyun 
1311*4882a593Smuzhiyun 	return 0;
1312*4882a593Smuzhiyun }
1313*4882a593Smuzhiyun 
1314*4882a593Smuzhiyun /*****************************************************************************/
1315*4882a593Smuzhiyun 
1316*4882a593Smuzhiyun /**
1317*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_buffer_ready - check if client has ready buffers
1318*4882a593Smuzhiyun  * @cli: pointer to vinstr client structure
1319*4882a593Smuzhiyun  *
1320*4882a593Smuzhiyun  * Return: non-zero if client has at least one dumping buffer filled that was
1321*4882a593Smuzhiyun  *         not notified to user yet
1322*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_buffer_ready(struct kbase_vinstr_client * cli)1323*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_buffer_ready(
1324*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli)
1325*4882a593Smuzhiyun {
1326*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli);
1327*4882a593Smuzhiyun 	return atomic_read(&cli->write_idx) != atomic_read(&cli->meta_idx);
1328*4882a593Smuzhiyun }
1329*4882a593Smuzhiyun 
1330*4882a593Smuzhiyun /**
1331*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_ioctl_get_buffer - hwcnt reader's ioctl command
1332*4882a593Smuzhiyun  * @cli:    pointer to vinstr client structure
1333*4882a593Smuzhiyun  * @buffer: pointer to userspace buffer
1334*4882a593Smuzhiyun  * @size:   size of buffer
1335*4882a593Smuzhiyun  *
1336*4882a593Smuzhiyun  * Return: zero on success
1337*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_ioctl_get_buffer(struct kbase_vinstr_client * cli,void __user * buffer,size_t size)1338*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_get_buffer(
1339*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli, void __user *buffer,
1340*4882a593Smuzhiyun 		size_t size)
1341*4882a593Smuzhiyun {
1342*4882a593Smuzhiyun 	unsigned int meta_idx = atomic_read(&cli->meta_idx);
1343*4882a593Smuzhiyun 	unsigned int idx = meta_idx % cli->buffer_count;
1344*4882a593Smuzhiyun 
1345*4882a593Smuzhiyun 	struct kbase_hwcnt_reader_metadata *meta = &cli->dump_buffers_meta[idx];
1346*4882a593Smuzhiyun 
1347*4882a593Smuzhiyun 	/* Metadata sanity check. */
1348*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(idx == meta->buffer_idx);
1349*4882a593Smuzhiyun 
1350*4882a593Smuzhiyun 	if (sizeof(struct kbase_hwcnt_reader_metadata) != size)
1351*4882a593Smuzhiyun 		return -EINVAL;
1352*4882a593Smuzhiyun 
1353*4882a593Smuzhiyun 	/* Check if there is any buffer available. */
1354*4882a593Smuzhiyun 	if (atomic_read(&cli->write_idx) == meta_idx)
1355*4882a593Smuzhiyun 		return -EAGAIN;
1356*4882a593Smuzhiyun 
1357*4882a593Smuzhiyun 	/* Check if previously taken buffer was put back. */
1358*4882a593Smuzhiyun 	if (atomic_read(&cli->read_idx) != meta_idx)
1359*4882a593Smuzhiyun 		return -EBUSY;
1360*4882a593Smuzhiyun 
1361*4882a593Smuzhiyun 	/* Copy next available buffer's metadata to user. */
1362*4882a593Smuzhiyun 	if (copy_to_user(buffer, meta, size))
1363*4882a593Smuzhiyun 		return -EFAULT;
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 	atomic_inc(&cli->meta_idx);
1366*4882a593Smuzhiyun 
1367*4882a593Smuzhiyun 	return 0;
1368*4882a593Smuzhiyun }
1369*4882a593Smuzhiyun 
1370*4882a593Smuzhiyun /**
1371*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_ioctl_put_buffer - hwcnt reader's ioctl command
1372*4882a593Smuzhiyun  * @cli:    pointer to vinstr client structure
1373*4882a593Smuzhiyun  * @buffer: pointer to userspace buffer
1374*4882a593Smuzhiyun  * @size:   size of buffer
1375*4882a593Smuzhiyun  *
1376*4882a593Smuzhiyun  * Return: zero on success
1377*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_ioctl_put_buffer(struct kbase_vinstr_client * cli,void __user * buffer,size_t size)1378*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_put_buffer(
1379*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli, void __user *buffer,
1380*4882a593Smuzhiyun 		size_t size)
1381*4882a593Smuzhiyun {
1382*4882a593Smuzhiyun 	unsigned int read_idx = atomic_read(&cli->read_idx);
1383*4882a593Smuzhiyun 	unsigned int idx = read_idx % cli->buffer_count;
1384*4882a593Smuzhiyun 
1385*4882a593Smuzhiyun 	struct kbase_hwcnt_reader_metadata meta;
1386*4882a593Smuzhiyun 
1387*4882a593Smuzhiyun 	if (sizeof(struct kbase_hwcnt_reader_metadata) != size)
1388*4882a593Smuzhiyun 		return -EINVAL;
1389*4882a593Smuzhiyun 
1390*4882a593Smuzhiyun 	/* Check if any buffer was taken. */
1391*4882a593Smuzhiyun 	if (atomic_read(&cli->meta_idx) == read_idx)
1392*4882a593Smuzhiyun 		return -EPERM;
1393*4882a593Smuzhiyun 
1394*4882a593Smuzhiyun 	/* Check if correct buffer is put back. */
1395*4882a593Smuzhiyun 	if (copy_from_user(&meta, buffer, size))
1396*4882a593Smuzhiyun 		return -EFAULT;
1397*4882a593Smuzhiyun 	if (idx != meta.buffer_idx)
1398*4882a593Smuzhiyun 		return -EINVAL;
1399*4882a593Smuzhiyun 
1400*4882a593Smuzhiyun 	atomic_inc(&cli->read_idx);
1401*4882a593Smuzhiyun 
1402*4882a593Smuzhiyun 	return 0;
1403*4882a593Smuzhiyun }
1404*4882a593Smuzhiyun 
1405*4882a593Smuzhiyun /**
1406*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_ioctl_set_interval - hwcnt reader's ioctl command
1407*4882a593Smuzhiyun  * @cli:      pointer to vinstr client structure
1408*4882a593Smuzhiyun  * @interval: periodic dumping interval (disable periodic dumping if zero)
1409*4882a593Smuzhiyun  *
1410*4882a593Smuzhiyun  * Return: zero on success
1411*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_ioctl_set_interval(struct kbase_vinstr_client * cli,u32 interval)1412*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_set_interval(
1413*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli, u32 interval)
1414*4882a593Smuzhiyun {
1415*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1416*4882a593Smuzhiyun 
1417*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1418*4882a593Smuzhiyun 
1419*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun 	list_del(&cli->list);
1422*4882a593Smuzhiyun 
1423*4882a593Smuzhiyun 	cli->dump_interval = interval;
1424*4882a593Smuzhiyun 
1425*4882a593Smuzhiyun 	/* If interval is non-zero, enable periodic dumping for this client. */
1426*4882a593Smuzhiyun 	if (cli->dump_interval) {
1427*4882a593Smuzhiyun 		if (DUMPING_RESOLUTION > cli->dump_interval)
1428*4882a593Smuzhiyun 			cli->dump_interval = DUMPING_RESOLUTION;
1429*4882a593Smuzhiyun 		cli->dump_time =
1430*4882a593Smuzhiyun 			kbasep_vinstr_get_timestamp() + cli->dump_interval;
1431*4882a593Smuzhiyun 
1432*4882a593Smuzhiyun 		kbasep_vinstr_add_dump_request(
1433*4882a593Smuzhiyun 				cli, &vinstr_ctx->waiting_clients);
1434*4882a593Smuzhiyun 
1435*4882a593Smuzhiyun 		atomic_set(&vinstr_ctx->request_pending, 1);
1436*4882a593Smuzhiyun 		wake_up_all(&vinstr_ctx->waitq);
1437*4882a593Smuzhiyun 	} else {
1438*4882a593Smuzhiyun 		list_add(&cli->list, &vinstr_ctx->idle_clients);
1439*4882a593Smuzhiyun 	}
1440*4882a593Smuzhiyun 
1441*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
1442*4882a593Smuzhiyun 
1443*4882a593Smuzhiyun 	return 0;
1444*4882a593Smuzhiyun }
1445*4882a593Smuzhiyun 
1446*4882a593Smuzhiyun /**
1447*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_event_mask - return event mask for event id
1448*4882a593Smuzhiyun  * @event_id: id of event
1449*4882a593Smuzhiyun  * Return: event_mask or zero if event is not supported or maskable
1450*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_event_mask(enum base_hwcnt_reader_event event_id)1451*4882a593Smuzhiyun static u32 kbasep_vinstr_hwcnt_reader_event_mask(
1452*4882a593Smuzhiyun 		enum base_hwcnt_reader_event event_id)
1453*4882a593Smuzhiyun {
1454*4882a593Smuzhiyun 	u32 event_mask = 0;
1455*4882a593Smuzhiyun 
1456*4882a593Smuzhiyun 	switch (event_id) {
1457*4882a593Smuzhiyun 	case BASE_HWCNT_READER_EVENT_PREJOB:
1458*4882a593Smuzhiyun 	case BASE_HWCNT_READER_EVENT_POSTJOB:
1459*4882a593Smuzhiyun 		/* These event are maskable. */
1460*4882a593Smuzhiyun 		event_mask = (1 << event_id);
1461*4882a593Smuzhiyun 		break;
1462*4882a593Smuzhiyun 
1463*4882a593Smuzhiyun 	case BASE_HWCNT_READER_EVENT_MANUAL:
1464*4882a593Smuzhiyun 	case BASE_HWCNT_READER_EVENT_PERIODIC:
1465*4882a593Smuzhiyun 		/* These event are non-maskable. */
1466*4882a593Smuzhiyun 	default:
1467*4882a593Smuzhiyun 		/* These event are not supported. */
1468*4882a593Smuzhiyun 		break;
1469*4882a593Smuzhiyun 	}
1470*4882a593Smuzhiyun 
1471*4882a593Smuzhiyun 	return event_mask;
1472*4882a593Smuzhiyun }
1473*4882a593Smuzhiyun 
1474*4882a593Smuzhiyun /**
1475*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_ioctl_enable_event - hwcnt reader's ioctl command
1476*4882a593Smuzhiyun  * @cli:      pointer to vinstr client structure
1477*4882a593Smuzhiyun  * @event_id: id of event to enable
1478*4882a593Smuzhiyun  *
1479*4882a593Smuzhiyun  * Return: zero on success
1480*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_ioctl_enable_event(struct kbase_vinstr_client * cli,enum base_hwcnt_reader_event event_id)1481*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_enable_event(
1482*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli,
1483*4882a593Smuzhiyun 		enum base_hwcnt_reader_event event_id)
1484*4882a593Smuzhiyun {
1485*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1486*4882a593Smuzhiyun 	u32                         event_mask;
1487*4882a593Smuzhiyun 
1488*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1489*4882a593Smuzhiyun 
1490*4882a593Smuzhiyun 	event_mask = kbasep_vinstr_hwcnt_reader_event_mask(event_id);
1491*4882a593Smuzhiyun 	if (!event_mask)
1492*4882a593Smuzhiyun 		return -EINVAL;
1493*4882a593Smuzhiyun 
1494*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
1495*4882a593Smuzhiyun 	cli->event_mask |= event_mask;
1496*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
1497*4882a593Smuzhiyun 
1498*4882a593Smuzhiyun 	return 0;
1499*4882a593Smuzhiyun }
1500*4882a593Smuzhiyun 
1501*4882a593Smuzhiyun /**
1502*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_ioctl_disable_event - hwcnt reader's ioctl command
1503*4882a593Smuzhiyun  * @cli:      pointer to vinstr client structure
1504*4882a593Smuzhiyun  * @event_id: id of event to disable
1505*4882a593Smuzhiyun  *
1506*4882a593Smuzhiyun  * Return: zero on success
1507*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_ioctl_disable_event(struct kbase_vinstr_client * cli,enum base_hwcnt_reader_event event_id)1508*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_disable_event(
1509*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli,
1510*4882a593Smuzhiyun 		enum base_hwcnt_reader_event event_id)
1511*4882a593Smuzhiyun {
1512*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1513*4882a593Smuzhiyun 	u32                         event_mask;
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1516*4882a593Smuzhiyun 
1517*4882a593Smuzhiyun 	event_mask = kbasep_vinstr_hwcnt_reader_event_mask(event_id);
1518*4882a593Smuzhiyun 	if (!event_mask)
1519*4882a593Smuzhiyun 		return -EINVAL;
1520*4882a593Smuzhiyun 
1521*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
1522*4882a593Smuzhiyun 	cli->event_mask &= ~event_mask;
1523*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
1524*4882a593Smuzhiyun 
1525*4882a593Smuzhiyun 	return 0;
1526*4882a593Smuzhiyun }
1527*4882a593Smuzhiyun 
1528*4882a593Smuzhiyun /**
1529*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_ioctl_get_hwver - hwcnt reader's ioctl command
1530*4882a593Smuzhiyun  * @cli:   pointer to vinstr client structure
1531*4882a593Smuzhiyun  * @hwver: pointer to user buffer where hw version will be stored
1532*4882a593Smuzhiyun  *
1533*4882a593Smuzhiyun  * Return: zero on success
1534*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(struct kbase_vinstr_client * cli,u32 __user * hwver)1535*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(
1536*4882a593Smuzhiyun 		struct kbase_vinstr_client *cli, u32 __user *hwver)
1537*4882a593Smuzhiyun {
1538*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
1539*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx = cli->vinstr_ctx;
1540*4882a593Smuzhiyun #endif
1541*4882a593Smuzhiyun 
1542*4882a593Smuzhiyun 	u32                         ver = 5;
1543*4882a593Smuzhiyun 
1544*4882a593Smuzhiyun #ifndef CONFIG_MALI_NO_MALI
1545*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1546*4882a593Smuzhiyun 	if (kbase_hw_has_feature(vinstr_ctx->kbdev, BASE_HW_FEATURE_V4))
1547*4882a593Smuzhiyun 		ver = 4;
1548*4882a593Smuzhiyun #endif
1549*4882a593Smuzhiyun 
1550*4882a593Smuzhiyun 	return put_user(ver, hwver);
1551*4882a593Smuzhiyun }
1552*4882a593Smuzhiyun 
1553*4882a593Smuzhiyun /**
1554*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_ioctl - hwcnt reader's ioctl
1555*4882a593Smuzhiyun  * @filp:   pointer to file structure
1556*4882a593Smuzhiyun  * @cmd:    user command
1557*4882a593Smuzhiyun  * @arg:    command's argument
1558*4882a593Smuzhiyun  *
1559*4882a593Smuzhiyun  * Return: zero on success
1560*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)1561*4882a593Smuzhiyun static long kbasep_vinstr_hwcnt_reader_ioctl(struct file *filp,
1562*4882a593Smuzhiyun 		unsigned int cmd, unsigned long arg)
1563*4882a593Smuzhiyun {
1564*4882a593Smuzhiyun 	long                       rcode = 0;
1565*4882a593Smuzhiyun 	struct kbase_vinstr_client *cli;
1566*4882a593Smuzhiyun 
1567*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(filp);
1568*4882a593Smuzhiyun 
1569*4882a593Smuzhiyun 	cli = filp->private_data;
1570*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli);
1571*4882a593Smuzhiyun 
1572*4882a593Smuzhiyun 	if (unlikely(KBASE_HWCNT_READER != _IOC_TYPE(cmd)))
1573*4882a593Smuzhiyun 		return -EINVAL;
1574*4882a593Smuzhiyun 
1575*4882a593Smuzhiyun 	switch (cmd) {
1576*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_GET_API_VERSION:
1577*4882a593Smuzhiyun 		rcode = put_user(HWCNT_READER_API, (u32 __user *)arg);
1578*4882a593Smuzhiyun 		break;
1579*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_GET_HWVER:
1580*4882a593Smuzhiyun 		rcode = kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(
1581*4882a593Smuzhiyun 				cli, (u32 __user *)arg);
1582*4882a593Smuzhiyun 		break;
1583*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_GET_BUFFER_SIZE:
1584*4882a593Smuzhiyun 		KBASE_DEBUG_ASSERT(cli->vinstr_ctx);
1585*4882a593Smuzhiyun 		rcode = put_user(
1586*4882a593Smuzhiyun 				(u32)cli->vinstr_ctx->dump_size,
1587*4882a593Smuzhiyun 				(u32 __user *)arg);
1588*4882a593Smuzhiyun 		break;
1589*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_DUMP:
1590*4882a593Smuzhiyun 		rcode = kbase_vinstr_hwc_dump(
1591*4882a593Smuzhiyun 				cli, BASE_HWCNT_READER_EVENT_MANUAL);
1592*4882a593Smuzhiyun 		break;
1593*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_CLEAR:
1594*4882a593Smuzhiyun 		rcode = kbase_vinstr_hwc_clear(cli);
1595*4882a593Smuzhiyun 		break;
1596*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_GET_BUFFER:
1597*4882a593Smuzhiyun 		rcode = kbasep_vinstr_hwcnt_reader_ioctl_get_buffer(
1598*4882a593Smuzhiyun 				cli, (void __user *)arg, _IOC_SIZE(cmd));
1599*4882a593Smuzhiyun 		break;
1600*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_PUT_BUFFER:
1601*4882a593Smuzhiyun 		rcode = kbasep_vinstr_hwcnt_reader_ioctl_put_buffer(
1602*4882a593Smuzhiyun 				cli, (void __user *)arg, _IOC_SIZE(cmd));
1603*4882a593Smuzhiyun 		break;
1604*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_SET_INTERVAL:
1605*4882a593Smuzhiyun 		rcode = kbasep_vinstr_hwcnt_reader_ioctl_set_interval(
1606*4882a593Smuzhiyun 				cli, (u32)arg);
1607*4882a593Smuzhiyun 		break;
1608*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_ENABLE_EVENT:
1609*4882a593Smuzhiyun 		rcode = kbasep_vinstr_hwcnt_reader_ioctl_enable_event(
1610*4882a593Smuzhiyun 				cli, (enum base_hwcnt_reader_event)arg);
1611*4882a593Smuzhiyun 		break;
1612*4882a593Smuzhiyun 	case KBASE_HWCNT_READER_DISABLE_EVENT:
1613*4882a593Smuzhiyun 		rcode = kbasep_vinstr_hwcnt_reader_ioctl_disable_event(
1614*4882a593Smuzhiyun 				cli, (enum base_hwcnt_reader_event)arg);
1615*4882a593Smuzhiyun 		break;
1616*4882a593Smuzhiyun 	default:
1617*4882a593Smuzhiyun 		rcode = -EINVAL;
1618*4882a593Smuzhiyun 		break;
1619*4882a593Smuzhiyun 	}
1620*4882a593Smuzhiyun 
1621*4882a593Smuzhiyun 	return rcode;
1622*4882a593Smuzhiyun }
1623*4882a593Smuzhiyun 
1624*4882a593Smuzhiyun /**
1625*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_poll - hwcnt reader's poll
1626*4882a593Smuzhiyun  * @filp: pointer to file structure
1627*4882a593Smuzhiyun  * @wait: pointer to poll table
1628*4882a593Smuzhiyun  * Return: POLLIN if data can be read without blocking, otherwise zero
1629*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_poll(struct file * filp,poll_table * wait)1630*4882a593Smuzhiyun static unsigned int kbasep_vinstr_hwcnt_reader_poll(struct file *filp,
1631*4882a593Smuzhiyun 		poll_table *wait)
1632*4882a593Smuzhiyun {
1633*4882a593Smuzhiyun 	struct kbase_vinstr_client *cli;
1634*4882a593Smuzhiyun 
1635*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(filp);
1636*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(wait);
1637*4882a593Smuzhiyun 
1638*4882a593Smuzhiyun 	cli = filp->private_data;
1639*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli);
1640*4882a593Smuzhiyun 
1641*4882a593Smuzhiyun 	poll_wait(filp, &cli->waitq, wait);
1642*4882a593Smuzhiyun 	if (kbasep_vinstr_hwcnt_reader_buffer_ready(cli))
1643*4882a593Smuzhiyun 		return POLLIN;
1644*4882a593Smuzhiyun 	return 0;
1645*4882a593Smuzhiyun }
1646*4882a593Smuzhiyun 
1647*4882a593Smuzhiyun /**
1648*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_mmap - hwcnt reader's mmap
1649*4882a593Smuzhiyun  * @filp: pointer to file structure
1650*4882a593Smuzhiyun  * @vma:  pointer to vma structure
1651*4882a593Smuzhiyun  * Return: zero on success
1652*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_mmap(struct file * filp,struct vm_area_struct * vma)1653*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_mmap(struct file *filp,
1654*4882a593Smuzhiyun 		struct vm_area_struct *vma)
1655*4882a593Smuzhiyun {
1656*4882a593Smuzhiyun 	struct kbase_vinstr_client *cli;
1657*4882a593Smuzhiyun 	unsigned long size, addr, pfn, offset;
1658*4882a593Smuzhiyun 	unsigned long vm_size = vma->vm_end - vma->vm_start;
1659*4882a593Smuzhiyun 
1660*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(filp);
1661*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vma);
1662*4882a593Smuzhiyun 
1663*4882a593Smuzhiyun 	cli = filp->private_data;
1664*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli);
1665*4882a593Smuzhiyun 
1666*4882a593Smuzhiyun 	size = cli->buffer_count * cli->dump_size;
1667*4882a593Smuzhiyun 
1668*4882a593Smuzhiyun 	if (vma->vm_pgoff > (size >> PAGE_SHIFT))
1669*4882a593Smuzhiyun 		return -EINVAL;
1670*4882a593Smuzhiyun 
1671*4882a593Smuzhiyun 	offset = vma->vm_pgoff << PAGE_SHIFT;
1672*4882a593Smuzhiyun 	if (vm_size > size - offset)
1673*4882a593Smuzhiyun 		return -EINVAL;
1674*4882a593Smuzhiyun 
1675*4882a593Smuzhiyun 	addr = __pa((unsigned long)cli->dump_buffers + offset);
1676*4882a593Smuzhiyun 	pfn = addr >> PAGE_SHIFT;
1677*4882a593Smuzhiyun 
1678*4882a593Smuzhiyun 	return remap_pfn_range(
1679*4882a593Smuzhiyun 			vma,
1680*4882a593Smuzhiyun 			vma->vm_start,
1681*4882a593Smuzhiyun 			pfn,
1682*4882a593Smuzhiyun 			vm_size,
1683*4882a593Smuzhiyun 			vma->vm_page_prot);
1684*4882a593Smuzhiyun }
1685*4882a593Smuzhiyun 
1686*4882a593Smuzhiyun /**
1687*4882a593Smuzhiyun  * kbasep_vinstr_hwcnt_reader_release - hwcnt reader's release
1688*4882a593Smuzhiyun  * @inode: pointer to inode structure
1689*4882a593Smuzhiyun  * @filp:  pointer to file structure
1690*4882a593Smuzhiyun  * Return always return zero
1691*4882a593Smuzhiyun  */
kbasep_vinstr_hwcnt_reader_release(struct inode * inode,struct file * filp)1692*4882a593Smuzhiyun static int kbasep_vinstr_hwcnt_reader_release(struct inode *inode,
1693*4882a593Smuzhiyun 		struct file *filp)
1694*4882a593Smuzhiyun {
1695*4882a593Smuzhiyun 	struct kbase_vinstr_client *cli;
1696*4882a593Smuzhiyun 
1697*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(inode);
1698*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(filp);
1699*4882a593Smuzhiyun 
1700*4882a593Smuzhiyun 	cli = filp->private_data;
1701*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli);
1702*4882a593Smuzhiyun 
1703*4882a593Smuzhiyun 	kbase_vinstr_detach_client(cli);
1704*4882a593Smuzhiyun 	return 0;
1705*4882a593Smuzhiyun }
1706*4882a593Smuzhiyun 
1707*4882a593Smuzhiyun /*****************************************************************************/
1708*4882a593Smuzhiyun 
1709*4882a593Smuzhiyun /**
1710*4882a593Smuzhiyun  * kbasep_vinstr_kick_scheduler - trigger scheduler cycle
1711*4882a593Smuzhiyun  * @kbdev: pointer to kbase device structure
1712*4882a593Smuzhiyun  */
kbasep_vinstr_kick_scheduler(struct kbase_device * kbdev)1713*4882a593Smuzhiyun static void kbasep_vinstr_kick_scheduler(struct kbase_device *kbdev)
1714*4882a593Smuzhiyun {
1715*4882a593Smuzhiyun 	struct kbasep_js_device_data *js_devdata = &kbdev->js_data;
1716*4882a593Smuzhiyun 	unsigned long flags;
1717*4882a593Smuzhiyun 
1718*4882a593Smuzhiyun 	down(&js_devdata->schedule_sem);
1719*4882a593Smuzhiyun 	spin_lock_irqsave(&kbdev->hwaccess_lock, flags);
1720*4882a593Smuzhiyun 	kbase_backend_slot_update(kbdev);
1721*4882a593Smuzhiyun 	spin_unlock_irqrestore(&kbdev->hwaccess_lock, flags);
1722*4882a593Smuzhiyun 	up(&js_devdata->schedule_sem);
1723*4882a593Smuzhiyun }
1724*4882a593Smuzhiyun 
1725*4882a593Smuzhiyun /**
1726*4882a593Smuzhiyun  * kbasep_vinstr_suspend_worker - worker suspending vinstr module
1727*4882a593Smuzhiyun  * @data: pointer to work structure
1728*4882a593Smuzhiyun  */
kbasep_vinstr_suspend_worker(struct work_struct * data)1729*4882a593Smuzhiyun static void kbasep_vinstr_suspend_worker(struct work_struct *data)
1730*4882a593Smuzhiyun {
1731*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx;
1732*4882a593Smuzhiyun 	unsigned long flags;
1733*4882a593Smuzhiyun 
1734*4882a593Smuzhiyun 	vinstr_ctx = container_of(data, struct kbase_vinstr_context,
1735*4882a593Smuzhiyun 			suspend_work);
1736*4882a593Smuzhiyun 
1737*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
1738*4882a593Smuzhiyun 
1739*4882a593Smuzhiyun 	if (vinstr_ctx->kctx)
1740*4882a593Smuzhiyun 		disable_hwcnt(vinstr_ctx);
1741*4882a593Smuzhiyun 
1742*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
1743*4882a593Smuzhiyun 	vinstr_ctx->state = VINSTR_SUSPENDED;
1744*4882a593Smuzhiyun 	wake_up_all(&vinstr_ctx->suspend_waitq);
1745*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
1746*4882a593Smuzhiyun 
1747*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
1748*4882a593Smuzhiyun 
1749*4882a593Smuzhiyun 	/* Kick GPU scheduler to allow entering protected mode.
1750*4882a593Smuzhiyun 	 * This must happen after vinstr was suspended. */
1751*4882a593Smuzhiyun 	kbasep_vinstr_kick_scheduler(vinstr_ctx->kbdev);
1752*4882a593Smuzhiyun }
1753*4882a593Smuzhiyun 
1754*4882a593Smuzhiyun /**
1755*4882a593Smuzhiyun  * kbasep_vinstr_suspend_worker - worker resuming vinstr module
1756*4882a593Smuzhiyun  * @data: pointer to work structure
1757*4882a593Smuzhiyun  */
kbasep_vinstr_resume_worker(struct work_struct * data)1758*4882a593Smuzhiyun static void kbasep_vinstr_resume_worker(struct work_struct *data)
1759*4882a593Smuzhiyun {
1760*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx;
1761*4882a593Smuzhiyun 	unsigned long flags;
1762*4882a593Smuzhiyun 
1763*4882a593Smuzhiyun 	vinstr_ctx = container_of(data, struct kbase_vinstr_context,
1764*4882a593Smuzhiyun 			resume_work);
1765*4882a593Smuzhiyun 
1766*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
1767*4882a593Smuzhiyun 
1768*4882a593Smuzhiyun 	if (vinstr_ctx->kctx)
1769*4882a593Smuzhiyun 		enable_hwcnt(vinstr_ctx);
1770*4882a593Smuzhiyun 
1771*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
1772*4882a593Smuzhiyun 	vinstr_ctx->state = VINSTR_IDLE;
1773*4882a593Smuzhiyun 	wake_up_all(&vinstr_ctx->suspend_waitq);
1774*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
1775*4882a593Smuzhiyun 
1776*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
1777*4882a593Smuzhiyun 
1778*4882a593Smuzhiyun 	/* Kick GPU scheduler to allow entering protected mode.
1779*4882a593Smuzhiyun 	 * Note that scheduler state machine might requested re-entry to
1780*4882a593Smuzhiyun 	 * protected mode before vinstr was resumed.
1781*4882a593Smuzhiyun 	 * This must happen after vinstr was release. */
1782*4882a593Smuzhiyun 	kbasep_vinstr_kick_scheduler(vinstr_ctx->kbdev);
1783*4882a593Smuzhiyun }
1784*4882a593Smuzhiyun 
1785*4882a593Smuzhiyun /*****************************************************************************/
1786*4882a593Smuzhiyun 
kbase_vinstr_init(struct kbase_device * kbdev)1787*4882a593Smuzhiyun struct kbase_vinstr_context *kbase_vinstr_init(struct kbase_device *kbdev)
1788*4882a593Smuzhiyun {
1789*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx;
1790*4882a593Smuzhiyun 
1791*4882a593Smuzhiyun 	vinstr_ctx = kzalloc(sizeof(*vinstr_ctx), GFP_KERNEL);
1792*4882a593Smuzhiyun 	if (!vinstr_ctx)
1793*4882a593Smuzhiyun 		return NULL;
1794*4882a593Smuzhiyun 
1795*4882a593Smuzhiyun 	INIT_LIST_HEAD(&vinstr_ctx->idle_clients);
1796*4882a593Smuzhiyun 	INIT_LIST_HEAD(&vinstr_ctx->waiting_clients);
1797*4882a593Smuzhiyun 	mutex_init(&vinstr_ctx->lock);
1798*4882a593Smuzhiyun 	spin_lock_init(&vinstr_ctx->state_lock);
1799*4882a593Smuzhiyun 	vinstr_ctx->kbdev = kbdev;
1800*4882a593Smuzhiyun 	vinstr_ctx->thread = NULL;
1801*4882a593Smuzhiyun 	vinstr_ctx->state = VINSTR_IDLE;
1802*4882a593Smuzhiyun 	vinstr_ctx->suspend_cnt = 0;
1803*4882a593Smuzhiyun 	INIT_WORK(&vinstr_ctx->suspend_work, kbasep_vinstr_suspend_worker);
1804*4882a593Smuzhiyun 	INIT_WORK(&vinstr_ctx->resume_work, kbasep_vinstr_resume_worker);
1805*4882a593Smuzhiyun 	init_waitqueue_head(&vinstr_ctx->suspend_waitq);
1806*4882a593Smuzhiyun 
1807*4882a593Smuzhiyun 	atomic_set(&vinstr_ctx->request_pending, 0);
1808*4882a593Smuzhiyun 	init_waitqueue_head(&vinstr_ctx->waitq);
1809*4882a593Smuzhiyun 
1810*4882a593Smuzhiyun 	return vinstr_ctx;
1811*4882a593Smuzhiyun }
1812*4882a593Smuzhiyun 
kbase_vinstr_term(struct kbase_vinstr_context * vinstr_ctx)1813*4882a593Smuzhiyun void kbase_vinstr_term(struct kbase_vinstr_context *vinstr_ctx)
1814*4882a593Smuzhiyun {
1815*4882a593Smuzhiyun 	struct kbase_vinstr_client *cli;
1816*4882a593Smuzhiyun 
1817*4882a593Smuzhiyun 	/* Stop service thread first. */
1818*4882a593Smuzhiyun 	if (vinstr_ctx->thread)
1819*4882a593Smuzhiyun 		kthread_stop(vinstr_ctx->thread);
1820*4882a593Smuzhiyun 
1821*4882a593Smuzhiyun 	/* Wait for workers. */
1822*4882a593Smuzhiyun 	flush_work(&vinstr_ctx->suspend_work);
1823*4882a593Smuzhiyun 	flush_work(&vinstr_ctx->resume_work);
1824*4882a593Smuzhiyun 
1825*4882a593Smuzhiyun 	while (1) {
1826*4882a593Smuzhiyun 		struct list_head *list = &vinstr_ctx->idle_clients;
1827*4882a593Smuzhiyun 
1828*4882a593Smuzhiyun 		if (list_empty(list)) {
1829*4882a593Smuzhiyun 			list = &vinstr_ctx->waiting_clients;
1830*4882a593Smuzhiyun 			if (list_empty(list))
1831*4882a593Smuzhiyun 				break;
1832*4882a593Smuzhiyun 		}
1833*4882a593Smuzhiyun 
1834*4882a593Smuzhiyun 		cli = list_first_entry(list, struct kbase_vinstr_client, list);
1835*4882a593Smuzhiyun 		list_del(&cli->list);
1836*4882a593Smuzhiyun 		kfree(cli->accum_buffer);
1837*4882a593Smuzhiyun 		kfree(cli);
1838*4882a593Smuzhiyun 		vinstr_ctx->nclients--;
1839*4882a593Smuzhiyun 	}
1840*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(!vinstr_ctx->nclients);
1841*4882a593Smuzhiyun 	if (vinstr_ctx->kctx)
1842*4882a593Smuzhiyun 		kbasep_vinstr_destroy_kctx(vinstr_ctx);
1843*4882a593Smuzhiyun 	kfree(vinstr_ctx);
1844*4882a593Smuzhiyun }
1845*4882a593Smuzhiyun 
kbase_vinstr_hwcnt_reader_setup(struct kbase_vinstr_context * vinstr_ctx,struct kbase_uk_hwcnt_reader_setup * setup)1846*4882a593Smuzhiyun int kbase_vinstr_hwcnt_reader_setup(struct kbase_vinstr_context *vinstr_ctx,
1847*4882a593Smuzhiyun 		struct kbase_uk_hwcnt_reader_setup *setup)
1848*4882a593Smuzhiyun {
1849*4882a593Smuzhiyun 	struct kbase_vinstr_client  *cli;
1850*4882a593Smuzhiyun 	u32                         bitmap[4];
1851*4882a593Smuzhiyun 
1852*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1853*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(setup);
1854*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(setup->buffer_count);
1855*4882a593Smuzhiyun 
1856*4882a593Smuzhiyun 	bitmap[SHADER_HWCNT_BM] = setup->shader_bm;
1857*4882a593Smuzhiyun 	bitmap[TILER_HWCNT_BM]  = setup->tiler_bm;
1858*4882a593Smuzhiyun 	bitmap[MMU_L2_HWCNT_BM] = setup->mmu_l2_bm;
1859*4882a593Smuzhiyun 	bitmap[JM_HWCNT_BM]     = setup->jm_bm;
1860*4882a593Smuzhiyun 
1861*4882a593Smuzhiyun 	cli = kbasep_vinstr_attach_client(
1862*4882a593Smuzhiyun 			vinstr_ctx,
1863*4882a593Smuzhiyun 			setup->buffer_count,
1864*4882a593Smuzhiyun 			bitmap,
1865*4882a593Smuzhiyun 			&setup->fd,
1866*4882a593Smuzhiyun 			NULL);
1867*4882a593Smuzhiyun 
1868*4882a593Smuzhiyun 	if (!cli)
1869*4882a593Smuzhiyun 		return -ENOMEM;
1870*4882a593Smuzhiyun 
1871*4882a593Smuzhiyun 	return 0;
1872*4882a593Smuzhiyun }
1873*4882a593Smuzhiyun 
kbase_vinstr_legacy_hwc_setup(struct kbase_vinstr_context * vinstr_ctx,struct kbase_vinstr_client ** cli,struct kbase_uk_hwcnt_setup * setup)1874*4882a593Smuzhiyun int kbase_vinstr_legacy_hwc_setup(
1875*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx,
1876*4882a593Smuzhiyun 		struct kbase_vinstr_client  **cli,
1877*4882a593Smuzhiyun 		struct kbase_uk_hwcnt_setup *setup)
1878*4882a593Smuzhiyun {
1879*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1880*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(setup);
1881*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(cli);
1882*4882a593Smuzhiyun 
1883*4882a593Smuzhiyun 	if (setup->dump_buffer) {
1884*4882a593Smuzhiyun 		u32 bitmap[4];
1885*4882a593Smuzhiyun 
1886*4882a593Smuzhiyun 		bitmap[SHADER_HWCNT_BM] = setup->shader_bm;
1887*4882a593Smuzhiyun 		bitmap[TILER_HWCNT_BM]  = setup->tiler_bm;
1888*4882a593Smuzhiyun 		bitmap[MMU_L2_HWCNT_BM] = setup->mmu_l2_bm;
1889*4882a593Smuzhiyun 		bitmap[JM_HWCNT_BM]     = setup->jm_bm;
1890*4882a593Smuzhiyun 
1891*4882a593Smuzhiyun 		if (*cli)
1892*4882a593Smuzhiyun 			return -EBUSY;
1893*4882a593Smuzhiyun 
1894*4882a593Smuzhiyun 		*cli = kbasep_vinstr_attach_client(
1895*4882a593Smuzhiyun 				vinstr_ctx,
1896*4882a593Smuzhiyun 				0,
1897*4882a593Smuzhiyun 				bitmap,
1898*4882a593Smuzhiyun 				(void *)(long)setup->dump_buffer,
1899*4882a593Smuzhiyun 				NULL);
1900*4882a593Smuzhiyun 
1901*4882a593Smuzhiyun 		if (!(*cli))
1902*4882a593Smuzhiyun 			return -ENOMEM;
1903*4882a593Smuzhiyun 	} else {
1904*4882a593Smuzhiyun 		if (!*cli)
1905*4882a593Smuzhiyun 			return -EINVAL;
1906*4882a593Smuzhiyun 
1907*4882a593Smuzhiyun 		kbase_vinstr_detach_client(*cli);
1908*4882a593Smuzhiyun 		*cli = NULL;
1909*4882a593Smuzhiyun 	}
1910*4882a593Smuzhiyun 
1911*4882a593Smuzhiyun 	return 0;
1912*4882a593Smuzhiyun }
1913*4882a593Smuzhiyun 
kbase_vinstr_hwcnt_kernel_setup(struct kbase_vinstr_context * vinstr_ctx,struct kbase_uk_hwcnt_reader_setup * setup,void * kernel_buffer)1914*4882a593Smuzhiyun struct kbase_vinstr_client *kbase_vinstr_hwcnt_kernel_setup(
1915*4882a593Smuzhiyun 		struct kbase_vinstr_context *vinstr_ctx,
1916*4882a593Smuzhiyun 		struct kbase_uk_hwcnt_reader_setup *setup,
1917*4882a593Smuzhiyun 		void *kernel_buffer)
1918*4882a593Smuzhiyun {
1919*4882a593Smuzhiyun 	u32 bitmap[4];
1920*4882a593Smuzhiyun 
1921*4882a593Smuzhiyun 	if (!vinstr_ctx || !setup || !kernel_buffer)
1922*4882a593Smuzhiyun 		return NULL;
1923*4882a593Smuzhiyun 
1924*4882a593Smuzhiyun 	bitmap[SHADER_HWCNT_BM] = setup->shader_bm;
1925*4882a593Smuzhiyun 	bitmap[TILER_HWCNT_BM]  = setup->tiler_bm;
1926*4882a593Smuzhiyun 	bitmap[MMU_L2_HWCNT_BM] = setup->mmu_l2_bm;
1927*4882a593Smuzhiyun 	bitmap[JM_HWCNT_BM]     = setup->jm_bm;
1928*4882a593Smuzhiyun 
1929*4882a593Smuzhiyun 	return kbasep_vinstr_attach_client(
1930*4882a593Smuzhiyun 			vinstr_ctx,
1931*4882a593Smuzhiyun 			0,
1932*4882a593Smuzhiyun 			bitmap,
1933*4882a593Smuzhiyun 			NULL,
1934*4882a593Smuzhiyun 			kernel_buffer);
1935*4882a593Smuzhiyun }
1936*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_hwcnt_kernel_setup);
1937*4882a593Smuzhiyun 
kbase_vinstr_hwc_dump(struct kbase_vinstr_client * cli,enum base_hwcnt_reader_event event_id)1938*4882a593Smuzhiyun int kbase_vinstr_hwc_dump(struct kbase_vinstr_client *cli,
1939*4882a593Smuzhiyun 		enum base_hwcnt_reader_event event_id)
1940*4882a593Smuzhiyun {
1941*4882a593Smuzhiyun 	int                         rcode = 0;
1942*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx;
1943*4882a593Smuzhiyun 	u64                         timestamp;
1944*4882a593Smuzhiyun 	u32                         event_mask;
1945*4882a593Smuzhiyun 
1946*4882a593Smuzhiyun 	if (!cli)
1947*4882a593Smuzhiyun 		return -EINVAL;
1948*4882a593Smuzhiyun 
1949*4882a593Smuzhiyun 	vinstr_ctx = cli->vinstr_ctx;
1950*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1951*4882a593Smuzhiyun 
1952*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(event_id < BASE_HWCNT_READER_EVENT_COUNT);
1953*4882a593Smuzhiyun 	event_mask = 1 << event_id;
1954*4882a593Smuzhiyun 
1955*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
1956*4882a593Smuzhiyun 
1957*4882a593Smuzhiyun 	if (event_mask & cli->event_mask) {
1958*4882a593Smuzhiyun 		rcode = kbasep_vinstr_collect_and_accumulate(
1959*4882a593Smuzhiyun 				vinstr_ctx,
1960*4882a593Smuzhiyun 				&timestamp);
1961*4882a593Smuzhiyun 		if (rcode)
1962*4882a593Smuzhiyun 			goto exit;
1963*4882a593Smuzhiyun 
1964*4882a593Smuzhiyun 		rcode = kbasep_vinstr_update_client(cli, timestamp, event_id);
1965*4882a593Smuzhiyun 		if (rcode)
1966*4882a593Smuzhiyun 			goto exit;
1967*4882a593Smuzhiyun 
1968*4882a593Smuzhiyun 		kbasep_vinstr_reprogram(vinstr_ctx);
1969*4882a593Smuzhiyun 	}
1970*4882a593Smuzhiyun 
1971*4882a593Smuzhiyun exit:
1972*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
1973*4882a593Smuzhiyun 
1974*4882a593Smuzhiyun 	return rcode;
1975*4882a593Smuzhiyun }
1976*4882a593Smuzhiyun KBASE_EXPORT_TEST_API(kbase_vinstr_hwc_dump);
1977*4882a593Smuzhiyun 
kbase_vinstr_hwc_clear(struct kbase_vinstr_client * cli)1978*4882a593Smuzhiyun int kbase_vinstr_hwc_clear(struct kbase_vinstr_client *cli)
1979*4882a593Smuzhiyun {
1980*4882a593Smuzhiyun 	struct kbase_vinstr_context *vinstr_ctx;
1981*4882a593Smuzhiyun 	int                         rcode;
1982*4882a593Smuzhiyun 	u64                         unused;
1983*4882a593Smuzhiyun 
1984*4882a593Smuzhiyun 	if (!cli)
1985*4882a593Smuzhiyun 		return -EINVAL;
1986*4882a593Smuzhiyun 
1987*4882a593Smuzhiyun 	vinstr_ctx = cli->vinstr_ctx;
1988*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
1989*4882a593Smuzhiyun 
1990*4882a593Smuzhiyun 	mutex_lock(&vinstr_ctx->lock);
1991*4882a593Smuzhiyun 
1992*4882a593Smuzhiyun 	rcode = kbasep_vinstr_collect_and_accumulate(vinstr_ctx, &unused);
1993*4882a593Smuzhiyun 	if (rcode)
1994*4882a593Smuzhiyun 		goto exit;
1995*4882a593Smuzhiyun 	rcode = kbase_instr_hwcnt_clear(vinstr_ctx->kctx);
1996*4882a593Smuzhiyun 	if (rcode)
1997*4882a593Smuzhiyun 		goto exit;
1998*4882a593Smuzhiyun 	memset(cli->accum_buffer, 0, cli->dump_size);
1999*4882a593Smuzhiyun 
2000*4882a593Smuzhiyun 	kbasep_vinstr_reprogram(vinstr_ctx);
2001*4882a593Smuzhiyun 
2002*4882a593Smuzhiyun exit:
2003*4882a593Smuzhiyun 	mutex_unlock(&vinstr_ctx->lock);
2004*4882a593Smuzhiyun 
2005*4882a593Smuzhiyun 	return rcode;
2006*4882a593Smuzhiyun }
2007*4882a593Smuzhiyun 
kbase_vinstr_try_suspend(struct kbase_vinstr_context * vinstr_ctx)2008*4882a593Smuzhiyun int kbase_vinstr_try_suspend(struct kbase_vinstr_context *vinstr_ctx)
2009*4882a593Smuzhiyun {
2010*4882a593Smuzhiyun 	unsigned long flags;
2011*4882a593Smuzhiyun 	int ret = -EAGAIN;
2012*4882a593Smuzhiyun 
2013*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
2014*4882a593Smuzhiyun 
2015*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
2016*4882a593Smuzhiyun 	switch (vinstr_ctx->state) {
2017*4882a593Smuzhiyun 	case VINSTR_SUSPENDED:
2018*4882a593Smuzhiyun 		vinstr_ctx->suspend_cnt++;
2019*4882a593Smuzhiyun 		/* overflow shall not happen */
2020*4882a593Smuzhiyun 		BUG_ON(0 == vinstr_ctx->suspend_cnt);
2021*4882a593Smuzhiyun 		ret = 0;
2022*4882a593Smuzhiyun 		break;
2023*4882a593Smuzhiyun 
2024*4882a593Smuzhiyun 	case VINSTR_IDLE:
2025*4882a593Smuzhiyun 		vinstr_ctx->state = VINSTR_SUSPENDING;
2026*4882a593Smuzhiyun 		schedule_work(&vinstr_ctx->suspend_work);
2027*4882a593Smuzhiyun 		break;
2028*4882a593Smuzhiyun 
2029*4882a593Smuzhiyun 	case VINSTR_DUMPING:
2030*4882a593Smuzhiyun 		vinstr_ctx->state = VINSTR_SUSPENDING;
2031*4882a593Smuzhiyun 		break;
2032*4882a593Smuzhiyun 
2033*4882a593Smuzhiyun 	case VINSTR_SUSPENDING:
2034*4882a593Smuzhiyun 		/* fall through */
2035*4882a593Smuzhiyun 	case VINSTR_RESUMING:
2036*4882a593Smuzhiyun 		break;
2037*4882a593Smuzhiyun 
2038*4882a593Smuzhiyun 	default:
2039*4882a593Smuzhiyun 		BUG();
2040*4882a593Smuzhiyun 		break;
2041*4882a593Smuzhiyun 	}
2042*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
2043*4882a593Smuzhiyun 
2044*4882a593Smuzhiyun 	return ret;
2045*4882a593Smuzhiyun }
2046*4882a593Smuzhiyun 
kbase_vinstr_suspend(struct kbase_vinstr_context * vinstr_ctx)2047*4882a593Smuzhiyun void kbase_vinstr_suspend(struct kbase_vinstr_context *vinstr_ctx)
2048*4882a593Smuzhiyun {
2049*4882a593Smuzhiyun 	wait_event(vinstr_ctx->suspend_waitq,
2050*4882a593Smuzhiyun 			(0 == kbase_vinstr_try_suspend(vinstr_ctx)));
2051*4882a593Smuzhiyun }
2052*4882a593Smuzhiyun 
kbase_vinstr_resume(struct kbase_vinstr_context * vinstr_ctx)2053*4882a593Smuzhiyun void kbase_vinstr_resume(struct kbase_vinstr_context *vinstr_ctx)
2054*4882a593Smuzhiyun {
2055*4882a593Smuzhiyun 	unsigned long flags;
2056*4882a593Smuzhiyun 
2057*4882a593Smuzhiyun 	KBASE_DEBUG_ASSERT(vinstr_ctx);
2058*4882a593Smuzhiyun 
2059*4882a593Smuzhiyun 	spin_lock_irqsave(&vinstr_ctx->state_lock, flags);
2060*4882a593Smuzhiyun 	BUG_ON(VINSTR_SUSPENDING == vinstr_ctx->state);
2061*4882a593Smuzhiyun 	if (VINSTR_SUSPENDED == vinstr_ctx->state) {
2062*4882a593Smuzhiyun 		BUG_ON(0 == vinstr_ctx->suspend_cnt);
2063*4882a593Smuzhiyun 		vinstr_ctx->suspend_cnt--;
2064*4882a593Smuzhiyun 		if (0 == vinstr_ctx->suspend_cnt) {
2065*4882a593Smuzhiyun 			vinstr_ctx->state = VINSTR_RESUMING;
2066*4882a593Smuzhiyun 			schedule_work(&vinstr_ctx->resume_work);
2067*4882a593Smuzhiyun 		}
2068*4882a593Smuzhiyun 	}
2069*4882a593Smuzhiyun 	spin_unlock_irqrestore(&vinstr_ctx->state_lock, flags);
2070*4882a593Smuzhiyun }
2071