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