xref: /linux/drivers/gpu/drm/msm/adreno/a6xx_preempt.c (revision 60675d4ca1ef0857e44eba5849b74a3a998d0c0f)
1e7ae83daSAntonino Maniscalco // SPDX-License-Identifier: GPL-2.0
2e7ae83daSAntonino Maniscalco /* Copyright (c) 2018, The Linux Foundation. All rights reserved. */
3e7ae83daSAntonino Maniscalco /* Copyright (c) 2023 Collabora, Ltd. */
4e7ae83daSAntonino Maniscalco /* Copyright (c) 2024 Valve Corporation */
5e7ae83daSAntonino Maniscalco 
6e7ae83daSAntonino Maniscalco #include "msm_gem.h"
7e7ae83daSAntonino Maniscalco #include "a6xx_gpu.h"
8e7ae83daSAntonino Maniscalco #include "a6xx_gmu.xml.h"
9e7ae83daSAntonino Maniscalco #include "msm_mmu.h"
1035d36dc1SAntonino Maniscalco #include "msm_gpu_trace.h"
11e7ae83daSAntonino Maniscalco 
12e7ae83daSAntonino Maniscalco /*
13e7ae83daSAntonino Maniscalco  * Try to transition the preemption state from old to new. Return
14e7ae83daSAntonino Maniscalco  * true on success or false if the original state wasn't 'old'
15e7ae83daSAntonino Maniscalco  */
16e7ae83daSAntonino Maniscalco static inline bool try_preempt_state(struct a6xx_gpu *a6xx_gpu,
17e7ae83daSAntonino Maniscalco 		enum a6xx_preempt_state old, enum a6xx_preempt_state new)
18e7ae83daSAntonino Maniscalco {
19e7ae83daSAntonino Maniscalco 	enum a6xx_preempt_state cur = atomic_cmpxchg(&a6xx_gpu->preempt_state,
20e7ae83daSAntonino Maniscalco 		old, new);
21e7ae83daSAntonino Maniscalco 
22e7ae83daSAntonino Maniscalco 	return (cur == old);
23e7ae83daSAntonino Maniscalco }
24e7ae83daSAntonino Maniscalco 
25e7ae83daSAntonino Maniscalco /*
26e7ae83daSAntonino Maniscalco  * Force the preemption state to the specified state.  This is used in cases
27e7ae83daSAntonino Maniscalco  * where the current state is known and won't change
28e7ae83daSAntonino Maniscalco  */
29e7ae83daSAntonino Maniscalco static inline void set_preempt_state(struct a6xx_gpu *gpu,
30e7ae83daSAntonino Maniscalco 		enum a6xx_preempt_state new)
31e7ae83daSAntonino Maniscalco {
32e7ae83daSAntonino Maniscalco 	/*
33e7ae83daSAntonino Maniscalco 	 * preempt_state may be read by other cores trying to trigger a
34e7ae83daSAntonino Maniscalco 	 * preemption or in the interrupt handler so barriers are needed
35e7ae83daSAntonino Maniscalco 	 * before...
36e7ae83daSAntonino Maniscalco 	 */
37e7ae83daSAntonino Maniscalco 	smp_mb__before_atomic();
38e7ae83daSAntonino Maniscalco 	atomic_set(&gpu->preempt_state, new);
39e7ae83daSAntonino Maniscalco 	/* ... and after*/
40e7ae83daSAntonino Maniscalco 	smp_mb__after_atomic();
41e7ae83daSAntonino Maniscalco }
42e7ae83daSAntonino Maniscalco 
43e7ae83daSAntonino Maniscalco /* Write the most recent wptr for the given ring into the hardware */
44e7ae83daSAntonino Maniscalco static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
45e7ae83daSAntonino Maniscalco {
46e7ae83daSAntonino Maniscalco 	unsigned long flags;
47e7ae83daSAntonino Maniscalco 	uint32_t wptr;
48e7ae83daSAntonino Maniscalco 
49e7ae83daSAntonino Maniscalco 	spin_lock_irqsave(&ring->preempt_lock, flags);
50e7ae83daSAntonino Maniscalco 
51e7ae83daSAntonino Maniscalco 	if (ring->restore_wptr) {
52e7ae83daSAntonino Maniscalco 		wptr = get_wptr(ring);
53e7ae83daSAntonino Maniscalco 
54e7ae83daSAntonino Maniscalco 		gpu_write(gpu, REG_A6XX_CP_RB_WPTR, wptr);
55e7ae83daSAntonino Maniscalco 
56e7ae83daSAntonino Maniscalco 		ring->restore_wptr = false;
57e7ae83daSAntonino Maniscalco 	}
58e7ae83daSAntonino Maniscalco 
59e7ae83daSAntonino Maniscalco 	spin_unlock_irqrestore(&ring->preempt_lock, flags);
60e7ae83daSAntonino Maniscalco }
61e7ae83daSAntonino Maniscalco 
62e7ae83daSAntonino Maniscalco /* Return the highest priority ringbuffer with something in it */
63e7ae83daSAntonino Maniscalco static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
64e7ae83daSAntonino Maniscalco {
65e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
66e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
67e7ae83daSAntonino Maniscalco 
68e7ae83daSAntonino Maniscalco 	unsigned long flags;
69e7ae83daSAntonino Maniscalco 	int i;
70e7ae83daSAntonino Maniscalco 
71e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++) {
72e7ae83daSAntonino Maniscalco 		bool empty;
73e7ae83daSAntonino Maniscalco 		struct msm_ringbuffer *ring = gpu->rb[i];
74e7ae83daSAntonino Maniscalco 
75e7ae83daSAntonino Maniscalco 		spin_lock_irqsave(&ring->preempt_lock, flags);
76e7ae83daSAntonino Maniscalco 		empty = (get_wptr(ring) == gpu->funcs->get_rptr(gpu, ring));
77e7ae83daSAntonino Maniscalco 		if (!empty && ring == a6xx_gpu->cur_ring)
78e7ae83daSAntonino Maniscalco 			empty = ring->memptrs->fence == a6xx_gpu->last_seqno[i];
79e7ae83daSAntonino Maniscalco 		spin_unlock_irqrestore(&ring->preempt_lock, flags);
80e7ae83daSAntonino Maniscalco 
81e7ae83daSAntonino Maniscalco 		if (!empty)
82e7ae83daSAntonino Maniscalco 			return ring;
83e7ae83daSAntonino Maniscalco 	}
84e7ae83daSAntonino Maniscalco 
85e7ae83daSAntonino Maniscalco 	return NULL;
86e7ae83daSAntonino Maniscalco }
87e7ae83daSAntonino Maniscalco 
88e7ae83daSAntonino Maniscalco static void a6xx_preempt_timer(struct timer_list *t)
89e7ae83daSAntonino Maniscalco {
90e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = from_timer(a6xx_gpu, t, preempt_timer);
91e7ae83daSAntonino Maniscalco 	struct msm_gpu *gpu = &a6xx_gpu->base.base;
92e7ae83daSAntonino Maniscalco 	struct drm_device *dev = gpu->dev;
93e7ae83daSAntonino Maniscalco 
94e7ae83daSAntonino Maniscalco 	if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
95e7ae83daSAntonino Maniscalco 		return;
96e7ae83daSAntonino Maniscalco 
97e7ae83daSAntonino Maniscalco 	dev_err(dev->dev, "%s: preemption timed out\n", gpu->name);
98e7ae83daSAntonino Maniscalco 	kthread_queue_work(gpu->worker, &gpu->recover_work);
99e7ae83daSAntonino Maniscalco }
100e7ae83daSAntonino Maniscalco 
10150117cadSAntonino Maniscalco static void preempt_prepare_postamble(struct a6xx_gpu *a6xx_gpu)
10250117cadSAntonino Maniscalco {
10350117cadSAntonino Maniscalco 	u32 *postamble = a6xx_gpu->preempt_postamble_ptr;
10450117cadSAntonino Maniscalco 	u32 count = 0;
10550117cadSAntonino Maniscalco 
10650117cadSAntonino Maniscalco 	postamble[count++] = PKT7(CP_REG_RMW, 3);
10750117cadSAntonino Maniscalco 	postamble[count++] = REG_A6XX_RBBM_PERFCTR_SRAM_INIT_CMD;
10850117cadSAntonino Maniscalco 	postamble[count++] = 0;
10950117cadSAntonino Maniscalco 	postamble[count++] = 1;
11050117cadSAntonino Maniscalco 
11150117cadSAntonino Maniscalco 	postamble[count++] = PKT7(CP_WAIT_REG_MEM, 6);
11250117cadSAntonino Maniscalco 	postamble[count++] = CP_WAIT_REG_MEM_0_FUNCTION(WRITE_EQ);
11350117cadSAntonino Maniscalco 	postamble[count++] = CP_WAIT_REG_MEM_1_POLL_ADDR_LO(
11450117cadSAntonino Maniscalco 				REG_A6XX_RBBM_PERFCTR_SRAM_INIT_STATUS);
11550117cadSAntonino Maniscalco 	postamble[count++] = CP_WAIT_REG_MEM_2_POLL_ADDR_HI(0);
11650117cadSAntonino Maniscalco 	postamble[count++] = CP_WAIT_REG_MEM_3_REF(0x1);
11750117cadSAntonino Maniscalco 	postamble[count++] = CP_WAIT_REG_MEM_4_MASK(0x1);
11850117cadSAntonino Maniscalco 	postamble[count++] = CP_WAIT_REG_MEM_5_DELAY_LOOP_CYCLES(0);
11950117cadSAntonino Maniscalco 
12050117cadSAntonino Maniscalco 	a6xx_gpu->preempt_postamble_len = count;
12150117cadSAntonino Maniscalco 
12250117cadSAntonino Maniscalco 	a6xx_gpu->postamble_enabled = true;
12350117cadSAntonino Maniscalco }
12450117cadSAntonino Maniscalco 
12550117cadSAntonino Maniscalco static void preempt_disable_postamble(struct a6xx_gpu *a6xx_gpu)
12650117cadSAntonino Maniscalco {
12750117cadSAntonino Maniscalco 	u32 *postamble = a6xx_gpu->preempt_postamble_ptr;
12850117cadSAntonino Maniscalco 
12950117cadSAntonino Maniscalco 	/*
13050117cadSAntonino Maniscalco 	 * Disable the postamble by replacing the first packet header with a NOP
13150117cadSAntonino Maniscalco 	 * that covers the whole buffer.
13250117cadSAntonino Maniscalco 	 */
13350117cadSAntonino Maniscalco 	*postamble = PKT7(CP_NOP, (a6xx_gpu->preempt_postamble_len - 1));
13450117cadSAntonino Maniscalco 
13550117cadSAntonino Maniscalco 	a6xx_gpu->postamble_enabled = false;
13650117cadSAntonino Maniscalco }
13750117cadSAntonino Maniscalco 
138e7ae83daSAntonino Maniscalco void a6xx_preempt_irq(struct msm_gpu *gpu)
139e7ae83daSAntonino Maniscalco {
140e7ae83daSAntonino Maniscalco 	uint32_t status;
141e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
142e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
143e7ae83daSAntonino Maniscalco 	struct drm_device *dev = gpu->dev;
144e7ae83daSAntonino Maniscalco 
145e7ae83daSAntonino Maniscalco 	if (!try_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
146e7ae83daSAntonino Maniscalco 		return;
147e7ae83daSAntonino Maniscalco 
148e7ae83daSAntonino Maniscalco 	/* Delete the preemption watchdog timer */
149e7ae83daSAntonino Maniscalco 	del_timer(&a6xx_gpu->preempt_timer);
150e7ae83daSAntonino Maniscalco 
151e7ae83daSAntonino Maniscalco 	/*
152e7ae83daSAntonino Maniscalco 	 * The hardware should be setting the stop bit of CP_CONTEXT_SWITCH_CNTL
153e7ae83daSAntonino Maniscalco 	 * to zero before firing the interrupt, but there is a non zero chance
154e7ae83daSAntonino Maniscalco 	 * of a hardware condition or a software race that could set it again
155e7ae83daSAntonino Maniscalco 	 * before we have a chance to finish. If that happens, log and go for
156e7ae83daSAntonino Maniscalco 	 * recovery
157e7ae83daSAntonino Maniscalco 	 */
158e7ae83daSAntonino Maniscalco 	status = gpu_read(gpu, REG_A6XX_CP_CONTEXT_SWITCH_CNTL);
159e7ae83daSAntonino Maniscalco 	if (unlikely(status & A6XX_CP_CONTEXT_SWITCH_CNTL_STOP)) {
160e7ae83daSAntonino Maniscalco 		DRM_DEV_ERROR(&gpu->pdev->dev,
161e7ae83daSAntonino Maniscalco 					  "!!!!!!!!!!!!!!!! preemption faulted !!!!!!!!!!!!!! irq\n");
162e7ae83daSAntonino Maniscalco 		set_preempt_state(a6xx_gpu, PREEMPT_FAULTED);
163e7ae83daSAntonino Maniscalco 		dev_err(dev->dev, "%s: Preemption failed to complete\n",
164e7ae83daSAntonino Maniscalco 			gpu->name);
165e7ae83daSAntonino Maniscalco 		kthread_queue_work(gpu->worker, &gpu->recover_work);
166e7ae83daSAntonino Maniscalco 		return;
167e7ae83daSAntonino Maniscalco 	}
168e7ae83daSAntonino Maniscalco 
169e7ae83daSAntonino Maniscalco 	a6xx_gpu->cur_ring = a6xx_gpu->next_ring;
170e7ae83daSAntonino Maniscalco 	a6xx_gpu->next_ring = NULL;
171e7ae83daSAntonino Maniscalco 
172e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_FINISH);
173e7ae83daSAntonino Maniscalco 
174e7ae83daSAntonino Maniscalco 	update_wptr(gpu, a6xx_gpu->cur_ring);
175e7ae83daSAntonino Maniscalco 
176e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_NONE);
177e7ae83daSAntonino Maniscalco 
17835d36dc1SAntonino Maniscalco 	trace_msm_gpu_preemption_irq(a6xx_gpu->cur_ring->id);
17935d36dc1SAntonino Maniscalco 
180e7ae83daSAntonino Maniscalco 	/*
181e7ae83daSAntonino Maniscalco 	 * Retrigger preemption to avoid a deadlock that might occur when preemption
182e7ae83daSAntonino Maniscalco 	 * is skipped due to it being already in flight when requested.
183e7ae83daSAntonino Maniscalco 	 */
184e7ae83daSAntonino Maniscalco 	a6xx_preempt_trigger(gpu);
185e7ae83daSAntonino Maniscalco }
186e7ae83daSAntonino Maniscalco 
187e7ae83daSAntonino Maniscalco void a6xx_preempt_hw_init(struct msm_gpu *gpu)
188e7ae83daSAntonino Maniscalco {
189e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
190e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
191e7ae83daSAntonino Maniscalco 	int i;
192e7ae83daSAntonino Maniscalco 
193e7ae83daSAntonino Maniscalco 	/* No preemption if we only have one ring */
194e7ae83daSAntonino Maniscalco 	if (gpu->nr_rings == 1)
195e7ae83daSAntonino Maniscalco 		return;
196e7ae83daSAntonino Maniscalco 
197e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++) {
198e7ae83daSAntonino Maniscalco 		struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[i];
199e7ae83daSAntonino Maniscalco 
200e7ae83daSAntonino Maniscalco 		record_ptr->wptr = 0;
201e7ae83daSAntonino Maniscalco 		record_ptr->rptr = 0;
202e7ae83daSAntonino Maniscalco 		record_ptr->rptr_addr = shadowptr(a6xx_gpu, gpu->rb[i]);
203e7ae83daSAntonino Maniscalco 		record_ptr->info = 0;
204e7ae83daSAntonino Maniscalco 		record_ptr->data = 0;
205e7ae83daSAntonino Maniscalco 		record_ptr->rbase = gpu->rb[i]->iova;
206e7ae83daSAntonino Maniscalco 	}
207e7ae83daSAntonino Maniscalco 
208e7ae83daSAntonino Maniscalco 	/* Write a 0 to signal that we aren't switching pagetables */
209e7ae83daSAntonino Maniscalco 	gpu_write64(gpu, REG_A6XX_CP_CONTEXT_SWITCH_SMMU_INFO, 0);
210e7ae83daSAntonino Maniscalco 
211e7ae83daSAntonino Maniscalco 	/* Enable the GMEM save/restore feature for preemption */
212e7ae83daSAntonino Maniscalco 	gpu_write(gpu, REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE, 0x1);
213e7ae83daSAntonino Maniscalco 
214e7ae83daSAntonino Maniscalco 	/* Reset the preemption state */
215e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_NONE);
216e7ae83daSAntonino Maniscalco 
217e7ae83daSAntonino Maniscalco 	spin_lock_init(&a6xx_gpu->eval_lock);
218e7ae83daSAntonino Maniscalco 
219e7ae83daSAntonino Maniscalco 	/* Always come up on rb 0 */
220e7ae83daSAntonino Maniscalco 	a6xx_gpu->cur_ring = gpu->rb[0];
221e7ae83daSAntonino Maniscalco }
222e7ae83daSAntonino Maniscalco 
223e7ae83daSAntonino Maniscalco void a6xx_preempt_trigger(struct msm_gpu *gpu)
224e7ae83daSAntonino Maniscalco {
225e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
226e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
227e7ae83daSAntonino Maniscalco 	unsigned long flags;
228e7ae83daSAntonino Maniscalco 	struct msm_ringbuffer *ring;
229e7ae83daSAntonino Maniscalco 	unsigned int cntl;
23050117cadSAntonino Maniscalco 	bool sysprof;
231e7ae83daSAntonino Maniscalco 
232e7ae83daSAntonino Maniscalco 	if (gpu->nr_rings == 1)
233e7ae83daSAntonino Maniscalco 		return;
234e7ae83daSAntonino Maniscalco 
235e7ae83daSAntonino Maniscalco 	/*
236e7ae83daSAntonino Maniscalco 	 * Lock to make sure another thread attempting preemption doesn't skip it
237e7ae83daSAntonino Maniscalco 	 * while we are still evaluating the next ring. This makes sure the other
238e7ae83daSAntonino Maniscalco 	 * thread does start preemption if we abort it and avoids a soft lock.
239e7ae83daSAntonino Maniscalco 	 */
240e7ae83daSAntonino Maniscalco 	spin_lock_irqsave(&a6xx_gpu->eval_lock, flags);
241e7ae83daSAntonino Maniscalco 
242e7ae83daSAntonino Maniscalco 	/*
243e7ae83daSAntonino Maniscalco 	 * Try to start preemption by moving from NONE to START. If
244e7ae83daSAntonino Maniscalco 	 * unsuccessful, a preemption is already in flight
245e7ae83daSAntonino Maniscalco 	 */
246e7ae83daSAntonino Maniscalco 	if (!try_preempt_state(a6xx_gpu, PREEMPT_NONE, PREEMPT_START)) {
247e7ae83daSAntonino Maniscalco 		spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
248e7ae83daSAntonino Maniscalco 		return;
249e7ae83daSAntonino Maniscalco 	}
250e7ae83daSAntonino Maniscalco 
251e7ae83daSAntonino Maniscalco 	cntl = A6XX_CP_CONTEXT_SWITCH_CNTL_LEVEL(a6xx_gpu->preempt_level);
252e7ae83daSAntonino Maniscalco 
253e7ae83daSAntonino Maniscalco 	if (a6xx_gpu->skip_save_restore)
254e7ae83daSAntonino Maniscalco 		cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_SKIP_SAVE_RESTORE;
255e7ae83daSAntonino Maniscalco 
256e7ae83daSAntonino Maniscalco 	if (a6xx_gpu->uses_gmem)
257e7ae83daSAntonino Maniscalco 		cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_USES_GMEM;
258e7ae83daSAntonino Maniscalco 
259e7ae83daSAntonino Maniscalco 	cntl |= A6XX_CP_CONTEXT_SWITCH_CNTL_STOP;
260e7ae83daSAntonino Maniscalco 
261e7ae83daSAntonino Maniscalco 	/* Get the next ring to preempt to */
262e7ae83daSAntonino Maniscalco 	ring = get_next_ring(gpu);
263e7ae83daSAntonino Maniscalco 
264e7ae83daSAntonino Maniscalco 	/*
265e7ae83daSAntonino Maniscalco 	 * If no ring is populated or the highest priority ring is the current
266e7ae83daSAntonino Maniscalco 	 * one do nothing except to update the wptr to the latest and greatest
267e7ae83daSAntonino Maniscalco 	 */
268e7ae83daSAntonino Maniscalco 	if (!ring || (a6xx_gpu->cur_ring == ring)) {
269e7ae83daSAntonino Maniscalco 		set_preempt_state(a6xx_gpu, PREEMPT_FINISH);
270e7ae83daSAntonino Maniscalco 		update_wptr(gpu, a6xx_gpu->cur_ring);
271e7ae83daSAntonino Maniscalco 		set_preempt_state(a6xx_gpu, PREEMPT_NONE);
272e7ae83daSAntonino Maniscalco 		spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
273e7ae83daSAntonino Maniscalco 		return;
274e7ae83daSAntonino Maniscalco 	}
275e7ae83daSAntonino Maniscalco 
276e7ae83daSAntonino Maniscalco 	spin_unlock_irqrestore(&a6xx_gpu->eval_lock, flags);
277e7ae83daSAntonino Maniscalco 
278e7ae83daSAntonino Maniscalco 	spin_lock_irqsave(&ring->preempt_lock, flags);
279e7ae83daSAntonino Maniscalco 
280e7ae83daSAntonino Maniscalco 	struct a7xx_cp_smmu_info *smmu_info_ptr =
281e7ae83daSAntonino Maniscalco 		a6xx_gpu->preempt_smmu[ring->id];
282e7ae83daSAntonino Maniscalco 	struct a6xx_preempt_record *record_ptr = a6xx_gpu->preempt[ring->id];
283e7ae83daSAntonino Maniscalco 	u64 ttbr0 = ring->memptrs->ttbr0;
284e7ae83daSAntonino Maniscalco 	u32 context_idr = ring->memptrs->context_idr;
285e7ae83daSAntonino Maniscalco 
286e7ae83daSAntonino Maniscalco 	smmu_info_ptr->ttbr0 = ttbr0;
287e7ae83daSAntonino Maniscalco 	smmu_info_ptr->context_idr = context_idr;
288e7ae83daSAntonino Maniscalco 	record_ptr->wptr = get_wptr(ring);
289e7ae83daSAntonino Maniscalco 
290e7ae83daSAntonino Maniscalco 	/*
291e7ae83daSAntonino Maniscalco 	 * The GPU will write the wptr we set above when we preempt. Reset
292e7ae83daSAntonino Maniscalco 	 * restore_wptr to make sure that we don't write WPTR to the same
293e7ae83daSAntonino Maniscalco 	 * thing twice. It's still possible subsequent submissions will update
294e7ae83daSAntonino Maniscalco 	 * wptr again, in which case they will set the flag to true. This has
295e7ae83daSAntonino Maniscalco 	 * to be protected by the lock for setting the flag and updating wptr
296e7ae83daSAntonino Maniscalco 	 * to be atomic.
297e7ae83daSAntonino Maniscalco 	 */
298e7ae83daSAntonino Maniscalco 	ring->restore_wptr = false;
299e7ae83daSAntonino Maniscalco 
300*45a4f888SEverest K.C. 	trace_msm_gpu_preemption_trigger(a6xx_gpu->cur_ring->id, ring->id);
30135d36dc1SAntonino Maniscalco 
302e7ae83daSAntonino Maniscalco 	spin_unlock_irqrestore(&ring->preempt_lock, flags);
303e7ae83daSAntonino Maniscalco 
304e7ae83daSAntonino Maniscalco 	gpu_write64(gpu,
305e7ae83daSAntonino Maniscalco 		REG_A6XX_CP_CONTEXT_SWITCH_SMMU_INFO,
306e7ae83daSAntonino Maniscalco 		a6xx_gpu->preempt_smmu_iova[ring->id]);
307e7ae83daSAntonino Maniscalco 
308e7ae83daSAntonino Maniscalco 	gpu_write64(gpu,
309e7ae83daSAntonino Maniscalco 		REG_A6XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR,
310e7ae83daSAntonino Maniscalco 		a6xx_gpu->preempt_iova[ring->id]);
311e7ae83daSAntonino Maniscalco 
312e7ae83daSAntonino Maniscalco 	a6xx_gpu->next_ring = ring;
313e7ae83daSAntonino Maniscalco 
314e7ae83daSAntonino Maniscalco 	/* Start a timer to catch a stuck preemption */
315e7ae83daSAntonino Maniscalco 	mod_timer(&a6xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000));
316e7ae83daSAntonino Maniscalco 
31750117cadSAntonino Maniscalco 	/* Enable or disable postamble as needed */
31850117cadSAntonino Maniscalco 	sysprof = refcount_read(&a6xx_gpu->base.base.sysprof_active) > 1;
31950117cadSAntonino Maniscalco 
32050117cadSAntonino Maniscalco 	if (!sysprof && !a6xx_gpu->postamble_enabled)
32150117cadSAntonino Maniscalco 		preempt_prepare_postamble(a6xx_gpu);
32250117cadSAntonino Maniscalco 
32350117cadSAntonino Maniscalco 	if (sysprof && a6xx_gpu->postamble_enabled)
32450117cadSAntonino Maniscalco 		preempt_disable_postamble(a6xx_gpu);
32550117cadSAntonino Maniscalco 
326e7ae83daSAntonino Maniscalco 	/* Set the preemption state to triggered */
327e7ae83daSAntonino Maniscalco 	set_preempt_state(a6xx_gpu, PREEMPT_TRIGGERED);
328e7ae83daSAntonino Maniscalco 
329e7ae83daSAntonino Maniscalco 	/* Trigger the preemption */
330e7ae83daSAntonino Maniscalco 	gpu_write(gpu, REG_A6XX_CP_CONTEXT_SWITCH_CNTL, cntl);
331e7ae83daSAntonino Maniscalco }
332e7ae83daSAntonino Maniscalco 
333e7ae83daSAntonino Maniscalco static int preempt_init_ring(struct a6xx_gpu *a6xx_gpu,
334e7ae83daSAntonino Maniscalco 		struct msm_ringbuffer *ring)
335e7ae83daSAntonino Maniscalco {
336e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
337e7ae83daSAntonino Maniscalco 	struct msm_gpu *gpu = &adreno_gpu->base;
338e7ae83daSAntonino Maniscalco 	struct drm_gem_object *bo = NULL;
339e7ae83daSAntonino Maniscalco 	phys_addr_t ttbr;
340e7ae83daSAntonino Maniscalco 	u64 iova = 0;
341e7ae83daSAntonino Maniscalco 	void *ptr;
342e7ae83daSAntonino Maniscalco 	int asid;
343e7ae83daSAntonino Maniscalco 
344e7ae83daSAntonino Maniscalco 	ptr = msm_gem_kernel_new(gpu->dev,
345e7ae83daSAntonino Maniscalco 		PREEMPT_RECORD_SIZE(adreno_gpu),
346e7ae83daSAntonino Maniscalco 		MSM_BO_WC | MSM_BO_MAP_PRIV, gpu->aspace, &bo, &iova);
347e7ae83daSAntonino Maniscalco 
348e7ae83daSAntonino Maniscalco 	if (IS_ERR(ptr))
349e7ae83daSAntonino Maniscalco 		return PTR_ERR(ptr);
350e7ae83daSAntonino Maniscalco 
351e7ae83daSAntonino Maniscalco 	memset(ptr, 0, PREEMPT_RECORD_SIZE(adreno_gpu));
352e7ae83daSAntonino Maniscalco 
353e7ae83daSAntonino Maniscalco 	msm_gem_object_set_name(bo, "preempt_record ring%d", ring->id);
354e7ae83daSAntonino Maniscalco 
355e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_bo[ring->id] = bo;
356e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_iova[ring->id] = iova;
357e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt[ring->id] = ptr;
358e7ae83daSAntonino Maniscalco 
359e7ae83daSAntonino Maniscalco 	struct a6xx_preempt_record *record_ptr = ptr;
360e7ae83daSAntonino Maniscalco 
361e7ae83daSAntonino Maniscalco 	ptr = msm_gem_kernel_new(gpu->dev,
362e7ae83daSAntonino Maniscalco 		PREEMPT_SMMU_INFO_SIZE,
363e7ae83daSAntonino Maniscalco 		MSM_BO_WC | MSM_BO_MAP_PRIV | MSM_BO_GPU_READONLY,
364e7ae83daSAntonino Maniscalco 		gpu->aspace, &bo, &iova);
365e7ae83daSAntonino Maniscalco 
366e7ae83daSAntonino Maniscalco 	if (IS_ERR(ptr))
367e7ae83daSAntonino Maniscalco 		return PTR_ERR(ptr);
368e7ae83daSAntonino Maniscalco 
369e7ae83daSAntonino Maniscalco 	memset(ptr, 0, PREEMPT_SMMU_INFO_SIZE);
370e7ae83daSAntonino Maniscalco 
371e7ae83daSAntonino Maniscalco 	msm_gem_object_set_name(bo, "preempt_smmu_info ring%d", ring->id);
372e7ae83daSAntonino Maniscalco 
373e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_smmu_bo[ring->id] = bo;
374e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_smmu_iova[ring->id] = iova;
375e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_smmu[ring->id] = ptr;
376e7ae83daSAntonino Maniscalco 
377e7ae83daSAntonino Maniscalco 	struct a7xx_cp_smmu_info *smmu_info_ptr = ptr;
378e7ae83daSAntonino Maniscalco 
379e7ae83daSAntonino Maniscalco 	msm_iommu_pagetable_params(gpu->aspace->mmu, &ttbr, &asid);
380e7ae83daSAntonino Maniscalco 
381e7ae83daSAntonino Maniscalco 	smmu_info_ptr->magic = GEN7_CP_SMMU_INFO_MAGIC;
382e7ae83daSAntonino Maniscalco 	smmu_info_ptr->ttbr0 = ttbr;
383e7ae83daSAntonino Maniscalco 	smmu_info_ptr->asid = 0xdecafbad;
384e7ae83daSAntonino Maniscalco 	smmu_info_ptr->context_idr = 0;
385e7ae83daSAntonino Maniscalco 
386e7ae83daSAntonino Maniscalco 	/* Set up the defaults on the preemption record */
387e7ae83daSAntonino Maniscalco 	record_ptr->magic = A6XX_PREEMPT_RECORD_MAGIC;
388e7ae83daSAntonino Maniscalco 	record_ptr->info = 0;
389e7ae83daSAntonino Maniscalco 	record_ptr->data = 0;
390e7ae83daSAntonino Maniscalco 	record_ptr->rptr = 0;
391e7ae83daSAntonino Maniscalco 	record_ptr->wptr = 0;
392e7ae83daSAntonino Maniscalco 	record_ptr->cntl = MSM_GPU_RB_CNTL_DEFAULT;
393e7ae83daSAntonino Maniscalco 	record_ptr->rbase = ring->iova;
394e7ae83daSAntonino Maniscalco 	record_ptr->counter = 0;
395e7ae83daSAntonino Maniscalco 	record_ptr->bv_rptr_addr = rbmemptr(ring, bv_rptr);
396e7ae83daSAntonino Maniscalco 
397e7ae83daSAntonino Maniscalco 	return 0;
398e7ae83daSAntonino Maniscalco }
399e7ae83daSAntonino Maniscalco 
400e7ae83daSAntonino Maniscalco void a6xx_preempt_fini(struct msm_gpu *gpu)
401e7ae83daSAntonino Maniscalco {
402e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
403e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
404e7ae83daSAntonino Maniscalco 	int i;
405e7ae83daSAntonino Maniscalco 
406e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++)
407e7ae83daSAntonino Maniscalco 		msm_gem_kernel_put(a6xx_gpu->preempt_bo[i], gpu->aspace);
408e7ae83daSAntonino Maniscalco }
409e7ae83daSAntonino Maniscalco 
410e7ae83daSAntonino Maniscalco void a6xx_preempt_init(struct msm_gpu *gpu)
411e7ae83daSAntonino Maniscalco {
412e7ae83daSAntonino Maniscalco 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
413e7ae83daSAntonino Maniscalco 	struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
414e7ae83daSAntonino Maniscalco 	int i;
415e7ae83daSAntonino Maniscalco 
416e7ae83daSAntonino Maniscalco 	/* No preemption if we only have one ring */
417e7ae83daSAntonino Maniscalco 	if (gpu->nr_rings <= 1)
418e7ae83daSAntonino Maniscalco 		return;
419e7ae83daSAntonino Maniscalco 
420e7ae83daSAntonino Maniscalco 	for (i = 0; i < gpu->nr_rings; i++) {
421e7ae83daSAntonino Maniscalco 		if (preempt_init_ring(a6xx_gpu, gpu->rb[i]))
422e7ae83daSAntonino Maniscalco 			goto fail;
423e7ae83daSAntonino Maniscalco 	}
424e7ae83daSAntonino Maniscalco 
425e7ae83daSAntonino Maniscalco 	/* TODO: make this configurable? */
426e7ae83daSAntonino Maniscalco 	a6xx_gpu->preempt_level = 1;
427e7ae83daSAntonino Maniscalco 	a6xx_gpu->uses_gmem = 1;
428e7ae83daSAntonino Maniscalco 	a6xx_gpu->skip_save_restore = 1;
429e7ae83daSAntonino Maniscalco 
43050117cadSAntonino Maniscalco 	a6xx_gpu->preempt_postamble_ptr  = msm_gem_kernel_new(gpu->dev,
43150117cadSAntonino Maniscalco 			PAGE_SIZE,
43250117cadSAntonino Maniscalco 			MSM_BO_WC | MSM_BO_MAP_PRIV | MSM_BO_GPU_READONLY,
43350117cadSAntonino Maniscalco 			gpu->aspace, &a6xx_gpu->preempt_postamble_bo,
43450117cadSAntonino Maniscalco 			&a6xx_gpu->preempt_postamble_iova);
43550117cadSAntonino Maniscalco 
43650117cadSAntonino Maniscalco 	preempt_prepare_postamble(a6xx_gpu);
43750117cadSAntonino Maniscalco 
43850117cadSAntonino Maniscalco 	if (IS_ERR(a6xx_gpu->preempt_postamble_ptr))
43950117cadSAntonino Maniscalco 		goto fail;
44050117cadSAntonino Maniscalco 
441e7ae83daSAntonino Maniscalco 	timer_setup(&a6xx_gpu->preempt_timer, a6xx_preempt_timer, 0);
442e7ae83daSAntonino Maniscalco 
443e7ae83daSAntonino Maniscalco 	return;
444e7ae83daSAntonino Maniscalco fail:
445e7ae83daSAntonino Maniscalco 	/*
446e7ae83daSAntonino Maniscalco 	 * On any failure our adventure is over. Clean up and
447e7ae83daSAntonino Maniscalco 	 * set nr_rings to 1 to force preemption off
448e7ae83daSAntonino Maniscalco 	 */
449e7ae83daSAntonino Maniscalco 	a6xx_preempt_fini(gpu);
450e7ae83daSAntonino Maniscalco 	gpu->nr_rings = 1;
451e7ae83daSAntonino Maniscalco 
452e7ae83daSAntonino Maniscalco 	DRM_DEV_ERROR(&gpu->pdev->dev,
453e7ae83daSAntonino Maniscalco 				  "preemption init failed, disabling preemption\n");
454e7ae83daSAntonino Maniscalco 
455e7ae83daSAntonino Maniscalco 	return;
456e7ae83daSAntonino Maniscalco }
457