0001
0002
0003
0004
0005 #include "msm_gem.h"
0006 #include "a5xx_gpu.h"
0007
0008
0009
0010
0011
0012 static inline bool try_preempt_state(struct a5xx_gpu *a5xx_gpu,
0013 enum preempt_state old, enum preempt_state new)
0014 {
0015 enum preempt_state cur = atomic_cmpxchg(&a5xx_gpu->preempt_state,
0016 old, new);
0017
0018 return (cur == old);
0019 }
0020
0021
0022
0023
0024
0025 static inline void set_preempt_state(struct a5xx_gpu *gpu,
0026 enum preempt_state new)
0027 {
0028
0029
0030
0031
0032
0033 smp_mb__before_atomic();
0034 atomic_set(&gpu->preempt_state, new);
0035
0036 smp_mb__after_atomic();
0037 }
0038
0039
0040 static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
0041 {
0042 unsigned long flags;
0043 uint32_t wptr;
0044
0045 if (!ring)
0046 return;
0047
0048 spin_lock_irqsave(&ring->preempt_lock, flags);
0049 wptr = get_wptr(ring);
0050 spin_unlock_irqrestore(&ring->preempt_lock, flags);
0051
0052 gpu_write(gpu, REG_A5XX_CP_RB_WPTR, wptr);
0053 }
0054
0055
0056 static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu)
0057 {
0058 unsigned long flags;
0059 int i;
0060
0061 for (i = 0; i < gpu->nr_rings; i++) {
0062 bool empty;
0063 struct msm_ringbuffer *ring = gpu->rb[i];
0064
0065 spin_lock_irqsave(&ring->preempt_lock, flags);
0066 empty = (get_wptr(ring) == ring->memptrs->rptr);
0067 spin_unlock_irqrestore(&ring->preempt_lock, flags);
0068
0069 if (!empty)
0070 return ring;
0071 }
0072
0073 return NULL;
0074 }
0075
0076 static void a5xx_preempt_timer(struct timer_list *t)
0077 {
0078 struct a5xx_gpu *a5xx_gpu = from_timer(a5xx_gpu, t, preempt_timer);
0079 struct msm_gpu *gpu = &a5xx_gpu->base.base;
0080 struct drm_device *dev = gpu->dev;
0081
0082 if (!try_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED, PREEMPT_FAULTED))
0083 return;
0084
0085 DRM_DEV_ERROR(dev->dev, "%s: preemption timed out\n", gpu->name);
0086 kthread_queue_work(gpu->worker, &gpu->recover_work);
0087 }
0088
0089
0090 void a5xx_preempt_trigger(struct msm_gpu *gpu)
0091 {
0092 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
0093 struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
0094 unsigned long flags;
0095 struct msm_ringbuffer *ring;
0096
0097 if (gpu->nr_rings == 1)
0098 return;
0099
0100
0101
0102
0103
0104 if (!try_preempt_state(a5xx_gpu, PREEMPT_NONE, PREEMPT_START))
0105 return;
0106
0107
0108 ring = get_next_ring(gpu);
0109
0110
0111
0112
0113
0114 if (!ring || (a5xx_gpu->cur_ring == ring)) {
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127 set_preempt_state(a5xx_gpu, PREEMPT_ABORT);
0128 update_wptr(gpu, a5xx_gpu->cur_ring);
0129 set_preempt_state(a5xx_gpu, PREEMPT_NONE);
0130 return;
0131 }
0132
0133
0134 spin_lock_irqsave(&ring->preempt_lock, flags);
0135 a5xx_gpu->preempt[ring->id]->wptr = get_wptr(ring);
0136 spin_unlock_irqrestore(&ring->preempt_lock, flags);
0137
0138
0139 gpu_write64(gpu, REG_A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_LO,
0140 REG_A5XX_CP_CONTEXT_SWITCH_RESTORE_ADDR_HI,
0141 a5xx_gpu->preempt_iova[ring->id]);
0142
0143 a5xx_gpu->next_ring = ring;
0144
0145
0146 mod_timer(&a5xx_gpu->preempt_timer, jiffies + msecs_to_jiffies(10000));
0147
0148
0149 set_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED);
0150
0151
0152 wmb();
0153
0154
0155 gpu_write(gpu, REG_A5XX_CP_CONTEXT_SWITCH_CNTL, 1);
0156 }
0157
0158 void a5xx_preempt_irq(struct msm_gpu *gpu)
0159 {
0160 uint32_t status;
0161 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
0162 struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
0163 struct drm_device *dev = gpu->dev;
0164
0165 if (!try_preempt_state(a5xx_gpu, PREEMPT_TRIGGERED, PREEMPT_PENDING))
0166 return;
0167
0168
0169 del_timer(&a5xx_gpu->preempt_timer);
0170
0171
0172
0173
0174
0175
0176
0177 status = gpu_read(gpu, REG_A5XX_CP_CONTEXT_SWITCH_CNTL);
0178 if (unlikely(status)) {
0179 set_preempt_state(a5xx_gpu, PREEMPT_FAULTED);
0180 DRM_DEV_ERROR(dev->dev, "%s: Preemption failed to complete\n",
0181 gpu->name);
0182 kthread_queue_work(gpu->worker, &gpu->recover_work);
0183 return;
0184 }
0185
0186 a5xx_gpu->cur_ring = a5xx_gpu->next_ring;
0187 a5xx_gpu->next_ring = NULL;
0188
0189 update_wptr(gpu, a5xx_gpu->cur_ring);
0190
0191 set_preempt_state(a5xx_gpu, PREEMPT_NONE);
0192 }
0193
0194 void a5xx_preempt_hw_init(struct msm_gpu *gpu)
0195 {
0196 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
0197 struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
0198 int i;
0199
0200
0201 a5xx_gpu->cur_ring = gpu->rb[0];
0202
0203
0204 if (gpu->nr_rings == 1)
0205 return;
0206
0207 for (i = 0; i < gpu->nr_rings; i++) {
0208 a5xx_gpu->preempt[i]->wptr = 0;
0209 a5xx_gpu->preempt[i]->rptr = 0;
0210 a5xx_gpu->preempt[i]->rbase = gpu->rb[i]->iova;
0211 }
0212
0213
0214 gpu_write64(gpu, REG_A5XX_CP_CONTEXT_SWITCH_SMMU_INFO_LO,
0215 REG_A5XX_CP_CONTEXT_SWITCH_SMMU_INFO_HI, 0);
0216
0217
0218 set_preempt_state(a5xx_gpu, PREEMPT_NONE);
0219 }
0220
0221 static int preempt_init_ring(struct a5xx_gpu *a5xx_gpu,
0222 struct msm_ringbuffer *ring)
0223 {
0224 struct adreno_gpu *adreno_gpu = &a5xx_gpu->base;
0225 struct msm_gpu *gpu = &adreno_gpu->base;
0226 struct a5xx_preempt_record *ptr;
0227 void *counters;
0228 struct drm_gem_object *bo = NULL, *counters_bo = NULL;
0229 u64 iova = 0, counters_iova = 0;
0230
0231 ptr = msm_gem_kernel_new(gpu->dev,
0232 A5XX_PREEMPT_RECORD_SIZE + A5XX_PREEMPT_COUNTER_SIZE,
0233 MSM_BO_WC | MSM_BO_MAP_PRIV, gpu->aspace, &bo, &iova);
0234
0235 if (IS_ERR(ptr))
0236 return PTR_ERR(ptr);
0237
0238
0239 counters = msm_gem_kernel_new(gpu->dev,
0240 A5XX_PREEMPT_COUNTER_SIZE,
0241 MSM_BO_WC, gpu->aspace, &counters_bo, &counters_iova);
0242 if (IS_ERR(counters)) {
0243 msm_gem_kernel_put(bo, gpu->aspace);
0244 return PTR_ERR(counters);
0245 }
0246
0247 msm_gem_object_set_name(bo, "preempt");
0248 msm_gem_object_set_name(counters_bo, "preempt_counters");
0249
0250 a5xx_gpu->preempt_bo[ring->id] = bo;
0251 a5xx_gpu->preempt_counters_bo[ring->id] = counters_bo;
0252 a5xx_gpu->preempt_iova[ring->id] = iova;
0253 a5xx_gpu->preempt[ring->id] = ptr;
0254
0255
0256
0257 ptr->magic = A5XX_PREEMPT_RECORD_MAGIC;
0258 ptr->info = 0;
0259 ptr->data = 0;
0260 ptr->cntl = MSM_GPU_RB_CNTL_DEFAULT | AXXX_CP_RB_CNTL_NO_UPDATE;
0261
0262 ptr->rptr_addr = shadowptr(a5xx_gpu, ring);
0263 ptr->counter = counters_iova;
0264
0265 return 0;
0266 }
0267
0268 void a5xx_preempt_fini(struct msm_gpu *gpu)
0269 {
0270 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
0271 struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
0272 int i;
0273
0274 for (i = 0; i < gpu->nr_rings; i++) {
0275 msm_gem_kernel_put(a5xx_gpu->preempt_bo[i], gpu->aspace);
0276 msm_gem_kernel_put(a5xx_gpu->preempt_counters_bo[i], gpu->aspace);
0277 }
0278 }
0279
0280 void a5xx_preempt_init(struct msm_gpu *gpu)
0281 {
0282 struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
0283 struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
0284 int i;
0285
0286
0287 if (gpu->nr_rings <= 1)
0288 return;
0289
0290 for (i = 0; i < gpu->nr_rings; i++) {
0291 if (preempt_init_ring(a5xx_gpu, gpu->rb[i])) {
0292
0293
0294
0295
0296 a5xx_preempt_fini(gpu);
0297 gpu->nr_rings = 1;
0298
0299 return;
0300 }
0301 }
0302
0303 timer_setup(&a5xx_gpu->preempt_timer, a5xx_preempt_timer, 0);
0304 }