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