197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b1fc2839SJordan Crouse /* Copyright (c) 2017 The Linux Foundation. All rights reserved.
3b1fc2839SJordan Crouse */
4b1fc2839SJordan Crouse
5b1fc2839SJordan Crouse #include "msm_gem.h"
6b1fc2839SJordan Crouse #include "a5xx_gpu.h"
7b1fc2839SJordan Crouse
8b1fc2839SJordan Crouse /*
9b1fc2839SJordan Crouse * Try to transition the preemption state from old to new. Return
10b1fc2839SJordan Crouse * true on success or false if the original state wasn't 'old'
11b1fc2839SJordan Crouse */
try_preempt_state(struct a5xx_gpu * a5xx_gpu,enum preempt_state old,enum preempt_state new)12b1fc2839SJordan Crouse static inline bool try_preempt_state(struct a5xx_gpu *a5xx_gpu,
13b1fc2839SJordan Crouse enum preempt_state old, enum preempt_state new)
14b1fc2839SJordan Crouse {
15b1fc2839SJordan Crouse enum preempt_state cur = atomic_cmpxchg(&a5xx_gpu->preempt_state,
16b1fc2839SJordan Crouse old, new);
17b1fc2839SJordan Crouse
18b1fc2839SJordan Crouse return (cur == old);
19b1fc2839SJordan Crouse }
20b1fc2839SJordan Crouse
21b1fc2839SJordan Crouse /*
22b1fc2839SJordan Crouse * Force the preemption state to the specified state. This is used in cases
23b1fc2839SJordan Crouse * where the current state is known and won't change
24b1fc2839SJordan Crouse */
set_preempt_state(struct a5xx_gpu * gpu,enum preempt_state new)25b1fc2839SJordan Crouse static inline void set_preempt_state(struct a5xx_gpu *gpu,
26b1fc2839SJordan Crouse enum preempt_state new)
27b1fc2839SJordan Crouse {
28b1fc2839SJordan Crouse /*
29b1fc2839SJordan Crouse * preempt_state may be read by other cores trying to trigger a
30b1fc2839SJordan Crouse * preemption or in the interrupt handler so barriers are needed
31b1fc2839SJordan Crouse * before...
32b1fc2839SJordan Crouse */
33b1fc2839SJordan Crouse smp_mb__before_atomic();
34b1fc2839SJordan Crouse atomic_set(&gpu->preempt_state, new);
35b1fc2839SJordan Crouse /* ... and after*/
36b1fc2839SJordan Crouse smp_mb__after_atomic();
37b1fc2839SJordan Crouse }
38b1fc2839SJordan Crouse
39b1fc2839SJordan Crouse /* Write the most recent wptr for the given ring into the hardware */
update_wptr(struct msm_gpu * gpu,struct msm_ringbuffer * ring)40b1fc2839SJordan Crouse static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
41b1fc2839SJordan Crouse {
42b1fc2839SJordan Crouse unsigned long flags;
43b1fc2839SJordan Crouse uint32_t wptr;
44b1fc2839SJordan Crouse
45b1fc2839SJordan Crouse if (!ring)
46b1fc2839SJordan Crouse return;
47b1fc2839SJordan Crouse
4877c40603SRob Clark spin_lock_irqsave(&ring->preempt_lock, flags);
49b1fc2839SJordan Crouse wptr = get_wptr(ring);
5077c40603SRob Clark spin_unlock_irqrestore(&ring->preempt_lock, flags);
51b1fc2839SJordan Crouse
52b1fc2839SJordan Crouse gpu_write(gpu, REG_A5XX_CP_RB_WPTR, wptr);
53b1fc2839SJordan Crouse }
54b1fc2839SJordan Crouse
55b1fc2839SJordan Crouse /* Return the highest priority ringbuffer with something in it */
get_next_ring(struct msm_gpu * gpu)56b1fc2839SJordan Crouse static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
57b1fc2839SJordan Crouse {
58b1fc2839SJordan Crouse struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
59b1fc2839SJordan Crouse struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
60b1fc2839SJordan Crouse unsigned long flags;
61b1fc2839SJordan Crouse int i;
62b1fc2839SJordan Crouse
63b1fc2839SJordan Crouse for (i = 0; i < gpu->nr_rings; i++) {
64b1fc2839SJordan Crouse bool empty;
6577c40603SRob Clark struct msm_ringbuffer *ring = gpu->rb[i];
66b1fc2839SJordan Crouse
6777c40603SRob Clark spin_lock_irqsave(&ring->preempt_lock, flags);
68b1fc2839SJordan Crouse empty = (get_wptr(ring) == gpu->funcs->get_rptr(gpu, ring));
69b1fc2839SJordan Crouse if (!empty && ring == a5xx_gpu->cur_ring)
70b1fc2839SJordan Crouse empty = ring->memptrs->fence == a5xx_gpu->last_seqno[i];
71b1fc2839SJordan Crouse spin_unlock_irqrestore(&ring->preempt_lock, flags);
72b1fc2839SJordan Crouse
73b1fc2839SJordan Crouse if (!empty)
74b1fc2839SJordan Crouse return ring;
75b1fc2839SJordan Crouse }
76e99e88a9SKees Cook
77b1fc2839SJordan Crouse return NULL;
78e99e88a9SKees Cook }
79b1fc2839SJordan Crouse
a5xx_preempt_timer(struct timer_list * t)80b1fc2839SJordan Crouse static void a5xx_preempt_timer(struct timer_list *t)
81b1fc2839SJordan Crouse {
82b1fc2839SJordan Crouse struct a5xx_gpu *a5xx_gpu = from_timer(a5xx_gpu, t, preempt_timer);
83b1fc2839SJordan Crouse struct msm_gpu *gpu = &a5xx_gpu->base.base;
84b1fc2839SJordan Crouse struct drm_device *dev = gpu->dev;
856a41da17SMamta Shukla
867e688294SRob Clark if (!try_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
87b1fc2839SJordan Crouse return;
88b1fc2839SJordan Crouse
89b1fc2839SJordan Crouse DRM_DEV_ERROR(dev->dev, "%s: preemption timed out\n", gpu->name);
90b1fc2839SJordan Crouse kthread_queue_work(gpu->worker, &gpu->recover_work);
91b1fc2839SJordan Crouse }
92b1fc2839SJordan Crouse
93b1fc2839SJordan Crouse /* Try to trigger a preemption switch */
a5xx_preempt_trigger(struct msm_gpu * gpu)94b1fc2839SJordan Crouse void a5xx_preempt_trigger(struct msm_gpu *gpu)
95b1fc2839SJordan Crouse {
96b1fc2839SJordan Crouse struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
97b1fc2839SJordan Crouse struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
98b1fc2839SJordan Crouse unsigned long flags;
99b1fc2839SJordan Crouse struct msm_ringbuffer *ring;
100b1fc2839SJordan Crouse
101b1fc2839SJordan Crouse if (gpu->nr_rings == 1)
102b1fc2839SJordan Crouse return;
103b1fc2839SJordan Crouse
104b1fc2839SJordan Crouse /*
105b1fc2839SJordan Crouse * Serialize preemption start to ensure that we always make
106b1fc2839SJordan Crouse * decision on latest state. Otherwise we can get stuck in
107b1fc2839SJordan Crouse * lower priority or empty ring.
108b1fc2839SJordan Crouse */
109b1fc2839SJordan Crouse spin_lock_irqsave(&a5xx_gpu->preempt_start_lock, flags);
110b1fc2839SJordan Crouse
111b1fc2839SJordan Crouse /*
112b1fc2839SJordan Crouse * Try to start preemption by moving from NONE to START. If
113b1fc2839SJordan Crouse * unsuccessful, a preemption is already in flight
114b1fc2839SJordan Crouse */
115b1fc2839SJordan Crouse if (!try_preempt_state(a5xx_gpu, PREEMPT_NONE, PREEMPT_START))
116b1fc2839SJordan Crouse goto out;
117b1fc2839SJordan Crouse
118b1fc2839SJordan Crouse /* Get the next ring to preempt to */
119b1fc2839SJordan Crouse ring = get_next_ring(gpu);
120b1fc2839SJordan Crouse
121b1fc2839SJordan Crouse /*
122b1fc2839SJordan Crouse * If no ring is populated or the highest priority ring is the current
123b1fc2839SJordan Crouse * one do nothing except to update the wptr to the latest and greatest
124b1fc2839SJordan Crouse */
125b1fc2839SJordan Crouse if (!ring || (a5xx_gpu->cur_ring == ring)) {
126b1fc2839SJordan Crouse /*
127b1fc2839SJordan Crouse * Its possible that while a preemption request is in progress
128b1fc2839SJordan Crouse * from an irq context, a user context trying to submit might
129b1fc2839SJordan Crouse * fail to update the write pointer, because it determines
130b1fc2839SJordan Crouse * that the preempt state is not PREEMPT_NONE.
131b1fc2839SJordan Crouse *
132b1fc2839SJordan Crouse * Close the race by introducing an intermediate
133b1fc2839SJordan Crouse * state PREEMPT_ABORT to let the submit path
13477c40603SRob Clark * know that the ringbuffer is not going to change
135b1fc2839SJordan Crouse * and can safely update the write pointer.
13677c40603SRob Clark */
137b1fc2839SJordan Crouse
138b1fc2839SJordan Crouse set_preempt_state(a5xx_gpu, PREEMPT_ABORT);
139b1fc2839SJordan Crouse update_wptr(gpu, a5xx_gpu->cur_ring);
140b1fc2839SJordan Crouse set_preempt_state(a5xx_gpu, PREEMPT_NONE);
141b1fc2839SJordan Crouse goto out;
142b1fc2839SJordan Crouse }
143b1fc2839SJordan Crouse
144b1fc2839SJordan Crouse spin_unlock_irqrestore(&a5xx_gpu->preempt_start_lock, flags);
145b1fc2839SJordan Crouse
146b1fc2839SJordan Crouse /* Make sure the wptr doesn't update while we're in motion */
147b1fc2839SJordan Crouse spin_lock_irqsave(&ring->preempt_lock, flags);
148b1fc2839SJordan Crouse a5xx_gpu->preempt[ring->id]->wptr = get_wptr(ring);
149b1fc2839SJordan Crouse spin_unlock_irqrestore(&ring->preempt_lock, flags);
150b1fc2839SJordan Crouse
151b1fc2839SJordan Crouse /* Set the address of the incoming preemption record */
152b1fc2839SJordan Crouse gpu_write64(gpu, REG_A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_LO,
153b1fc2839SJordan Crouse a5xx_gpu->preempt_iova[ring->id]);
154b1fc2839SJordan Crouse
155b1fc2839SJordan Crouse a5xx_gpu->next_ring = ring;
156b1fc2839SJordan Crouse
157b1fc2839SJordan Crouse /* Start a timer to catch a stuck preemption */
158b1fc2839SJordan Crouse mod_timer(&a5xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000));
159b1fc2839SJordan Crouse
160b1fc2839SJordan Crouse /* Set the preemption state to triggered */
161b1fc2839SJordan Crouse set_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED);
162b1fc2839SJordan Crouse
163b1fc2839SJordan Crouse /* Make sure everything is written before hitting the button */
164b1fc2839SJordan Crouse wmb();
165b1fc2839SJordan Crouse
166b1fc2839SJordan Crouse /* And actually start the preemption */
167b1fc2839SJordan Crouse gpu_write(gpu, REG_A5XX_CP_CONTEXT_SWITCH_CNTL, 1);
168b1fc2839SJordan Crouse return;
169b1fc2839SJordan Crouse
170b1fc2839SJordan Crouse out:
171b1fc2839SJordan Crouse spin_unlock_irqrestore(&a5xx_gpu->preempt_start_lock, flags);
172b1fc2839SJordan Crouse }
173b1fc2839SJordan Crouse
a5xx_preempt_irq(struct msm_gpu * gpu)174b1fc2839SJordan Crouse void a5xx_preempt_irq(struct msm_gpu *gpu)
175b1fc2839SJordan Crouse {
176b1fc2839SJordan Crouse uint32_t status;
177b1fc2839SJordan Crouse struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
178b1fc2839SJordan Crouse struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
1796a41da17SMamta Shukla struct drm_device *dev = gpu->dev;
180b1fc2839SJordan Crouse
1817e688294SRob Clark if (!try_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
182b1fc2839SJordan Crouse return;
183b1fc2839SJordan Crouse
184b1fc2839SJordan Crouse /* Delete the preemption watchdog timer */
185b1fc2839SJordan Crouse del_timer(&a5xx_gpu->preempt_timer);
186b1fc2839SJordan Crouse
187b1fc2839SJordan Crouse /*
188b1fc2839SJordan Crouse * The hardware should be setting CP_CONTEXT_SWITCH_CNTL to zero before
189b1fc2839SJordan Crouse * firing the interrupt, but there is a non zero chance of a hardware
190b1fc2839SJordan Crouse * condition or a software race that could set it again before we have a
191b1fc2839SJordan Crouse * chance to finish. If that happens, log and go for recovery
192b1fc2839SJordan Crouse */
193b1fc2839SJordan Crouse status = gpu_read(gpu, REG_A5XX_CP_CONTEXT_SWITCH_CNTL);
194b1fc2839SJordan Crouse if (unlikely(status)) {
195b1fc2839SJordan Crouse set_preempt_state(a5xx_gpu, PREEMPT_FAULTED);
196b1fc2839SJordan Crouse DRM_DEV_ERROR(dev->dev, "%s: Preemption failed to complete\n",
197b1fc2839SJordan Crouse gpu->name);
198b1fc2839SJordan Crouse kthread_queue_work(gpu->worker, &gpu->recover_work);
199fc6510acSSharat Masetty return;
200fc6510acSSharat Masetty }
201fc6510acSSharat Masetty
202fc6510acSSharat Masetty a5xx_gpu->cur_ring = a5xx_gpu->next_ring;
203fc6510acSSharat Masetty a5xx_gpu->next_ring = NULL;
204fc6510acSSharat Masetty
205fc6510acSSharat Masetty update_wptr(gpu, a5xx_gpu->cur_ring);
206b1fc2839SJordan Crouse
207b1fc2839SJordan Crouse set_preempt_state(a5xx_gpu, PREEMPT_NONE);
208b1fc2839SJordan Crouse
209b1fc2839SJordan Crouse /*
210b1fc2839SJordan Crouse * Try to trigger preemption again in case there was a submit or
211b1fc2839SJordan Crouse * retire during ring switch
212b1fc2839SJordan Crouse */
213*cade05b2SRob Clark a5xx_preempt_trigger(gpu);
214b1fc2839SJordan Crouse }
215b1fc2839SJordan Crouse
a5xx_preempt_hw_init(struct msm_gpu * gpu)216b1fc2839SJordan Crouse void a5xx_preempt_hw_init(struct msm_gpu *gpu)
217b1fc2839SJordan Crouse {
218b1fc2839SJordan Crouse struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
219b1fc2839SJordan Crouse struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
220b1fc2839SJordan Crouse int i;
221b1fc2839SJordan Crouse
222b1fc2839SJordan Crouse /* Always come up on rb 0 */
223b1fc2839SJordan Crouse a5xx_gpu->cur_ring = gpu->rb[0];
224b1fc2839SJordan Crouse
22534221545SJordan Crouse /* No preemption if we only have one ring */
22634221545SJordan Crouse if (gpu->nr_rings == 1)
22734221545SJordan Crouse return;
228b1fc2839SJordan Crouse
229b1fc2839SJordan Crouse for (i = 0; i < gpu->nr_rings; i++) {
230b1fc2839SJordan Crouse a5xx_gpu->preempt[i]->data = 0;
231a5fc7aa9SJonathan Marek a5xx_gpu->preempt[i]->info = 0;
232b1fc2839SJordan Crouse a5xx_gpu->preempt[i]->wptr = 0;
233b1fc2839SJordan Crouse a5xx_gpu->preempt[i]->rptr = 0;
234b1fc2839SJordan Crouse a5xx_gpu->preempt[i]->rbase = gpu->rb[i]->iova;
235b1fc2839SJordan Crouse a5xx_gpu->preempt[i]->rptr_addr = shadowptr(a5xx_gpu, gpu->rb[i]);
23634221545SJordan Crouse }
23734221545SJordan Crouse
23834221545SJordan Crouse /* Write a 0 to signal that we aren't switching pagetables */
239a5fc7aa9SJonathan Marek gpu_write64(gpu, REG_A5XX_CP_CONTEXT_SWITCH_SMMU_INFO_LO, 0);
24034221545SJordan Crouse
241030af2b0SRob Clark /* Reset the preemption state */
24234221545SJordan Crouse set_preempt_state(a5xx_gpu, PREEMPT_NONE);
24334221545SJordan Crouse }
24434221545SJordan Crouse
preempt_init_ring(struct a5xx_gpu * a5xx_gpu,struct msm_ringbuffer * ring)2450815d774SJordan Crouse static int preempt_init_ring(struct a5xx_gpu *a5xx_gpu,
24634221545SJordan Crouse struct msm_ringbuffer *ring)
2470815d774SJordan Crouse {
248b1fc2839SJordan Crouse struct adreno_gpu *adreno_gpu = &a5xx_gpu->base;
24934221545SJordan Crouse struct msm_gpu *gpu = &adreno_gpu->base;
250b1fc2839SJordan Crouse struct a5xx_preempt_record *ptr;
251b1fc2839SJordan Crouse void *counters;
252b1fc2839SJordan Crouse struct drm_gem_object *bo = NULL, *counters_bo = NULL;
253b1fc2839SJordan Crouse u64 iova = 0, counters_iova = 0;
254b1fc2839SJordan Crouse
255b1fc2839SJordan Crouse ptr = msm_gem_kernel_new(gpu->dev,
256b1fc2839SJordan Crouse A5XX_PREEMPT_RECORD_SIZE + A5XX_PREEMPT_COUNTER_SIZE,
257b1fc2839SJordan Crouse MSM_BO_WC | MSM_BO_MAP_PRIV, gpu->aspace, &bo, &iova);
2588907afb4SJordan Crouse
2598907afb4SJordan Crouse if (IS_ERR(ptr))
2608907afb4SJordan Crouse return PTR_ERR(ptr);
26134221545SJordan Crouse
262b1fc2839SJordan Crouse /* The buffer to store counters needs to be unprivileged */
263b1fc2839SJordan Crouse counters = msm_gem_kernel_new(gpu->dev,
264b1fc2839SJordan Crouse A5XX_PREEMPT_COUNTER_SIZE,
265b1fc2839SJordan Crouse MSM_BO_WC, gpu->aspace, &counters_bo, &counters_iova);
266b1fc2839SJordan Crouse if (IS_ERR(counters)) {
267b1fc2839SJordan Crouse msm_gem_kernel_put(bo, gpu->aspace);
268b1fc2839SJordan Crouse return PTR_ERR(counters);
269b1fc2839SJordan Crouse }
270b1fc2839SJordan Crouse
271b1fc2839SJordan Crouse msm_gem_object_set_name(bo, "preempt");
27234221545SJordan Crouse msm_gem_object_set_name(counters_bo, "preempt_counters");
273030af2b0SRob Clark
274030af2b0SRob Clark a5xx_gpu->preempt_bo[ring->id] = bo;
27534221545SJordan Crouse a5xx_gpu->preempt_counters_bo[ring->id] = counters_bo;
276b1fc2839SJordan Crouse a5xx_gpu->preempt_iova[ring->id] = iova;
277b1fc2839SJordan Crouse a5xx_gpu->preempt[ring->id] = ptr;
278b1fc2839SJordan Crouse
279b1fc2839SJordan Crouse /* Set up the defaults on the preemption record */
280b1fc2839SJordan Crouse
281b1fc2839SJordan Crouse ptr->magic = A5XX_PREEMPT_RECORD_MAGIC;
282b1fc2839SJordan Crouse ptr->info = 0;
283b1fc2839SJordan Crouse ptr->data = 0;
284b1fc2839SJordan Crouse ptr->cntl = MSM_GPU_RB_CNTL_DEFAULT | AXXX_CP_RB_CNTL_NO_UPDATE;
285b1fc2839SJordan Crouse
286b1fc2839SJordan Crouse ptr->counter = counters_iova;
287b1fc2839SJordan Crouse
288b1fc2839SJordan Crouse return 0;
289b1fc2839SJordan Crouse }
290b1fc2839SJordan Crouse
a5xx_preempt_fini(struct msm_gpu * gpu)291b1fc2839SJordan Crouse void a5xx_preempt_fini(struct msm_gpu *gpu)
292b1fc2839SJordan Crouse {
293b1fc2839SJordan Crouse struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
294b1fc2839SJordan Crouse struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
295b1fc2839SJordan Crouse int i;
296b1fc2839SJordan Crouse
297b1fc2839SJordan Crouse for (i = 0; i < gpu->nr_rings; i++) {
298b1fc2839SJordan Crouse msm_gem_kernel_put(a5xx_gpu->preempt_bo[i], gpu->aspace);
299b1fc2839SJordan Crouse msm_gem_kernel_put(a5xx_gpu->preempt_counters_bo[i], gpu->aspace);
300b1fc2839SJordan Crouse }
301e99e88a9SKees Cook }
302b1fc2839SJordan Crouse
a5xx_preempt_init(struct msm_gpu * gpu)303 void a5xx_preempt_init(struct msm_gpu *gpu)
304 {
305 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
306 struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
307 int i;
308
309 /* No preemption if we only have one ring */
310 if (gpu->nr_rings <= 1)
311 return;
312
313 for (i = 0; i < gpu->nr_rings; i++) {
314 if (preempt_init_ring(a5xx_gpu, gpu->rb[i])) {
315 /*
316 * On any failure our adventure is over. Clean up and
317 * set nr_rings to 1 to force preemption off
318 */
319 a5xx_preempt_fini(gpu);
320 gpu->nr_rings = 1;
321
322 return;
323 }
324 }
325
326 spin_lock_init(&a5xx_gpu->preempt_start_lock);
327 timer_setup(&a5xx_gpu->preempt_timer, a5xx_preempt_timer, 0);
328 }
329