1*b843c749SSergey Zigachev /* 2*b843c749SSergey Zigachev * Copyright 2014 Advanced Micro Devices, Inc. 3*b843c749SSergey Zigachev * 4*b843c749SSergey Zigachev * Permission is hereby granted, free of charge, to any person obtaining a 5*b843c749SSergey Zigachev * copy of this software and associated documentation files (the "Software"), 6*b843c749SSergey Zigachev * to deal in the Software without restriction, including without limitation 7*b843c749SSergey Zigachev * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8*b843c749SSergey Zigachev * and/or sell copies of the Software, and to permit persons to whom the 9*b843c749SSergey Zigachev * Software is furnished to do so, subject to the following conditions: 10*b843c749SSergey Zigachev * 11*b843c749SSergey Zigachev * The above copyright notice and this permission notice shall be included in 12*b843c749SSergey Zigachev * all copies or substantial portions of the Software. 13*b843c749SSergey Zigachev * 14*b843c749SSergey Zigachev * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15*b843c749SSergey Zigachev * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16*b843c749SSergey Zigachev * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17*b843c749SSergey Zigachev * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18*b843c749SSergey Zigachev * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19*b843c749SSergey Zigachev * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20*b843c749SSergey Zigachev * OTHER DEALINGS IN THE SOFTWARE. 21*b843c749SSergey Zigachev * 22*b843c749SSergey Zigachev */ 23*b843c749SSergey Zigachev 24*b843c749SSergey Zigachev #include <drm/drmP.h> 25*b843c749SSergey Zigachev #include "amdgpu.h" 26*b843c749SSergey Zigachev #include "amdgpu_ih.h" 27*b843c749SSergey Zigachev #include "amdgpu_amdkfd.h" 28*b843c749SSergey Zigachev 29*b843c749SSergey Zigachev /** 30*b843c749SSergey Zigachev * amdgpu_ih_ring_alloc - allocate memory for the IH ring 31*b843c749SSergey Zigachev * 32*b843c749SSergey Zigachev * @adev: amdgpu_device pointer 33*b843c749SSergey Zigachev * 34*b843c749SSergey Zigachev * Allocate a ring buffer for the interrupt controller. 35*b843c749SSergey Zigachev * Returns 0 for success, errors for failure. 36*b843c749SSergey Zigachev */ 37*b843c749SSergey Zigachev static int amdgpu_ih_ring_alloc(struct amdgpu_device *adev) 38*b843c749SSergey Zigachev { 39*b843c749SSergey Zigachev int r; 40*b843c749SSergey Zigachev 41*b843c749SSergey Zigachev /* Allocate ring buffer */ 42*b843c749SSergey Zigachev if (adev->irq.ih.ring_obj == NULL) { 43*b843c749SSergey Zigachev r = amdgpu_bo_create_kernel(adev, adev->irq.ih.ring_size, 44*b843c749SSergey Zigachev PAGE_SIZE, AMDGPU_GEM_DOMAIN_GTT, 45*b843c749SSergey Zigachev &adev->irq.ih.ring_obj, 46*b843c749SSergey Zigachev &adev->irq.ih.gpu_addr, 47*b843c749SSergey Zigachev (void **)&adev->irq.ih.ring); 48*b843c749SSergey Zigachev if (r) { 49*b843c749SSergey Zigachev DRM_ERROR("amdgpu: failed to create ih ring buffer (%d).\n", r); 50*b843c749SSergey Zigachev return r; 51*b843c749SSergey Zigachev } 52*b843c749SSergey Zigachev } 53*b843c749SSergey Zigachev return 0; 54*b843c749SSergey Zigachev } 55*b843c749SSergey Zigachev 56*b843c749SSergey Zigachev /** 57*b843c749SSergey Zigachev * amdgpu_ih_ring_init - initialize the IH state 58*b843c749SSergey Zigachev * 59*b843c749SSergey Zigachev * @adev: amdgpu_device pointer 60*b843c749SSergey Zigachev * 61*b843c749SSergey Zigachev * Initializes the IH state and allocates a buffer 62*b843c749SSergey Zigachev * for the IH ring buffer. 63*b843c749SSergey Zigachev * Returns 0 for success, errors for failure. 64*b843c749SSergey Zigachev */ 65*b843c749SSergey Zigachev int amdgpu_ih_ring_init(struct amdgpu_device *adev, unsigned ring_size, 66*b843c749SSergey Zigachev bool use_bus_addr) 67*b843c749SSergey Zigachev { 68*b843c749SSergey Zigachev u32 rb_bufsz; 69*b843c749SSergey Zigachev int r; 70*b843c749SSergey Zigachev 71*b843c749SSergey Zigachev /* Align ring size */ 72*b843c749SSergey Zigachev rb_bufsz = order_base_2(ring_size / 4); 73*b843c749SSergey Zigachev ring_size = (1 << rb_bufsz) * 4; 74*b843c749SSergey Zigachev adev->irq.ih.ring_size = ring_size; 75*b843c749SSergey Zigachev adev->irq.ih.ptr_mask = adev->irq.ih.ring_size - 1; 76*b843c749SSergey Zigachev adev->irq.ih.rptr = 0; 77*b843c749SSergey Zigachev adev->irq.ih.use_bus_addr = use_bus_addr; 78*b843c749SSergey Zigachev 79*b843c749SSergey Zigachev if (adev->irq.ih.use_bus_addr) { 80*b843c749SSergey Zigachev if (!adev->irq.ih.ring) { 81*b843c749SSergey Zigachev /* add 8 bytes for the rptr/wptr shadows and 82*b843c749SSergey Zigachev * add them to the end of the ring allocation. 83*b843c749SSergey Zigachev */ 84*b843c749SSergey Zigachev adev->irq.ih.ring = pci_alloc_consistent(adev->pdev, 85*b843c749SSergey Zigachev adev->irq.ih.ring_size + 8, 86*b843c749SSergey Zigachev &adev->irq.ih.rb_dma_addr); 87*b843c749SSergey Zigachev if (adev->irq.ih.ring == NULL) 88*b843c749SSergey Zigachev return -ENOMEM; 89*b843c749SSergey Zigachev memset((void *)adev->irq.ih.ring, 0, adev->irq.ih.ring_size + 8); 90*b843c749SSergey Zigachev adev->irq.ih.wptr_offs = (adev->irq.ih.ring_size / 4) + 0; 91*b843c749SSergey Zigachev adev->irq.ih.rptr_offs = (adev->irq.ih.ring_size / 4) + 1; 92*b843c749SSergey Zigachev } 93*b843c749SSergey Zigachev return 0; 94*b843c749SSergey Zigachev } else { 95*b843c749SSergey Zigachev r = amdgpu_device_wb_get(adev, &adev->irq.ih.wptr_offs); 96*b843c749SSergey Zigachev if (r) { 97*b843c749SSergey Zigachev dev_err(adev->dev, "(%d) ih wptr_offs wb alloc failed\n", r); 98*b843c749SSergey Zigachev return r; 99*b843c749SSergey Zigachev } 100*b843c749SSergey Zigachev 101*b843c749SSergey Zigachev r = amdgpu_device_wb_get(adev, &adev->irq.ih.rptr_offs); 102*b843c749SSergey Zigachev if (r) { 103*b843c749SSergey Zigachev amdgpu_device_wb_free(adev, adev->irq.ih.wptr_offs); 104*b843c749SSergey Zigachev dev_err(adev->dev, "(%d) ih rptr_offs wb alloc failed\n", r); 105*b843c749SSergey Zigachev return r; 106*b843c749SSergey Zigachev } 107*b843c749SSergey Zigachev 108*b843c749SSergey Zigachev return amdgpu_ih_ring_alloc(adev); 109*b843c749SSergey Zigachev } 110*b843c749SSergey Zigachev } 111*b843c749SSergey Zigachev 112*b843c749SSergey Zigachev /** 113*b843c749SSergey Zigachev * amdgpu_ih_ring_fini - tear down the IH state 114*b843c749SSergey Zigachev * 115*b843c749SSergey Zigachev * @adev: amdgpu_device pointer 116*b843c749SSergey Zigachev * 117*b843c749SSergey Zigachev * Tears down the IH state and frees buffer 118*b843c749SSergey Zigachev * used for the IH ring buffer. 119*b843c749SSergey Zigachev */ 120*b843c749SSergey Zigachev void amdgpu_ih_ring_fini(struct amdgpu_device *adev) 121*b843c749SSergey Zigachev { 122*b843c749SSergey Zigachev if (adev->irq.ih.use_bus_addr) { 123*b843c749SSergey Zigachev if (adev->irq.ih.ring) { 124*b843c749SSergey Zigachev /* add 8 bytes for the rptr/wptr shadows and 125*b843c749SSergey Zigachev * add them to the end of the ring allocation. 126*b843c749SSergey Zigachev */ 127*b843c749SSergey Zigachev pci_free_consistent(adev->pdev, adev->irq.ih.ring_size + 8, 128*b843c749SSergey Zigachev (void *)adev->irq.ih.ring, 129*b843c749SSergey Zigachev adev->irq.ih.rb_dma_addr); 130*b843c749SSergey Zigachev adev->irq.ih.ring = NULL; 131*b843c749SSergey Zigachev } 132*b843c749SSergey Zigachev } else { 133*b843c749SSergey Zigachev amdgpu_bo_free_kernel(&adev->irq.ih.ring_obj, 134*b843c749SSergey Zigachev &adev->irq.ih.gpu_addr, 135*b843c749SSergey Zigachev (void **)&adev->irq.ih.ring); 136*b843c749SSergey Zigachev amdgpu_device_wb_free(adev, adev->irq.ih.wptr_offs); 137*b843c749SSergey Zigachev amdgpu_device_wb_free(adev, adev->irq.ih.rptr_offs); 138*b843c749SSergey Zigachev } 139*b843c749SSergey Zigachev } 140*b843c749SSergey Zigachev 141*b843c749SSergey Zigachev /** 142*b843c749SSergey Zigachev * amdgpu_ih_process - interrupt handler 143*b843c749SSergey Zigachev * 144*b843c749SSergey Zigachev * @adev: amdgpu_device pointer 145*b843c749SSergey Zigachev * 146*b843c749SSergey Zigachev * Interrupt hander (VI), walk the IH ring. 147*b843c749SSergey Zigachev * Returns irq process return code. 148*b843c749SSergey Zigachev */ 149*b843c749SSergey Zigachev int amdgpu_ih_process(struct amdgpu_device *adev) 150*b843c749SSergey Zigachev { 151*b843c749SSergey Zigachev struct amdgpu_iv_entry entry; 152*b843c749SSergey Zigachev u32 wptr; 153*b843c749SSergey Zigachev 154*b843c749SSergey Zigachev if (!adev->irq.ih.enabled || adev->shutdown) 155*b843c749SSergey Zigachev return IRQ_NONE; 156*b843c749SSergey Zigachev 157*b843c749SSergey Zigachev wptr = amdgpu_ih_get_wptr(adev); 158*b843c749SSergey Zigachev 159*b843c749SSergey Zigachev restart_ih: 160*b843c749SSergey Zigachev /* is somebody else already processing irqs? */ 161*b843c749SSergey Zigachev if (atomic_xchg(&adev->irq.ih.lock, 1)) 162*b843c749SSergey Zigachev return IRQ_NONE; 163*b843c749SSergey Zigachev 164*b843c749SSergey Zigachev DRM_DEBUG("%s: rptr %d, wptr %d\n", __func__, adev->irq.ih.rptr, wptr); 165*b843c749SSergey Zigachev 166*b843c749SSergey Zigachev /* Order reading of wptr vs. reading of IH ring data */ 167*b843c749SSergey Zigachev rmb(); 168*b843c749SSergey Zigachev 169*b843c749SSergey Zigachev while (adev->irq.ih.rptr != wptr) { 170*b843c749SSergey Zigachev u32 ring_index = adev->irq.ih.rptr >> 2; 171*b843c749SSergey Zigachev 172*b843c749SSergey Zigachev /* Prescreening of high-frequency interrupts */ 173*b843c749SSergey Zigachev if (!amdgpu_ih_prescreen_iv(adev)) { 174*b843c749SSergey Zigachev adev->irq.ih.rptr &= adev->irq.ih.ptr_mask; 175*b843c749SSergey Zigachev continue; 176*b843c749SSergey Zigachev } 177*b843c749SSergey Zigachev 178*b843c749SSergey Zigachev /* Before dispatching irq to IP blocks, send it to amdkfd */ 179*b843c749SSergey Zigachev amdgpu_amdkfd_interrupt(adev, 180*b843c749SSergey Zigachev (const void *) &adev->irq.ih.ring[ring_index]); 181*b843c749SSergey Zigachev 182*b843c749SSergey Zigachev entry.iv_entry = (const uint32_t *) 183*b843c749SSergey Zigachev &adev->irq.ih.ring[ring_index]; 184*b843c749SSergey Zigachev amdgpu_ih_decode_iv(adev, &entry); 185*b843c749SSergey Zigachev adev->irq.ih.rptr &= adev->irq.ih.ptr_mask; 186*b843c749SSergey Zigachev 187*b843c749SSergey Zigachev amdgpu_irq_dispatch(adev, &entry); 188*b843c749SSergey Zigachev } 189*b843c749SSergey Zigachev amdgpu_ih_set_rptr(adev); 190*b843c749SSergey Zigachev atomic_set(&adev->irq.ih.lock, 0); 191*b843c749SSergey Zigachev 192*b843c749SSergey Zigachev /* make sure wptr hasn't changed while processing */ 193*b843c749SSergey Zigachev wptr = amdgpu_ih_get_wptr(adev); 194*b843c749SSergey Zigachev if (wptr != adev->irq.ih.rptr) 195*b843c749SSergey Zigachev goto restart_ih; 196*b843c749SSergey Zigachev 197*b843c749SSergey Zigachev return IRQ_HANDLED; 198*b843c749SSergey Zigachev } 199*b843c749SSergey Zigachev 200*b843c749SSergey Zigachev /** 201*b843c749SSergey Zigachev * amdgpu_ih_add_fault - Add a page fault record 202*b843c749SSergey Zigachev * 203*b843c749SSergey Zigachev * @adev: amdgpu device pointer 204*b843c749SSergey Zigachev * @key: 64-bit encoding of PASID and address 205*b843c749SSergey Zigachev * 206*b843c749SSergey Zigachev * This should be called when a retry page fault interrupt is 207*b843c749SSergey Zigachev * received. If this is a new page fault, it will be added to a hash 208*b843c749SSergey Zigachev * table. The return value indicates whether this is a new fault, or 209*b843c749SSergey Zigachev * a fault that was already known and is already being handled. 210*b843c749SSergey Zigachev * 211*b843c749SSergey Zigachev * If there are too many pending page faults, this will fail. Retry 212*b843c749SSergey Zigachev * interrupts should be ignored in this case until there is enough 213*b843c749SSergey Zigachev * free space. 214*b843c749SSergey Zigachev * 215*b843c749SSergey Zigachev * Returns 0 if the fault was added, 1 if the fault was already known, 216*b843c749SSergey Zigachev * -ENOSPC if there are too many pending faults. 217*b843c749SSergey Zigachev */ 218*b843c749SSergey Zigachev int amdgpu_ih_add_fault(struct amdgpu_device *adev, u64 key) 219*b843c749SSergey Zigachev { 220*b843c749SSergey Zigachev unsigned long flags; 221*b843c749SSergey Zigachev int r = -ENOSPC; 222*b843c749SSergey Zigachev 223*b843c749SSergey Zigachev if (WARN_ON_ONCE(!adev->irq.ih.faults)) 224*b843c749SSergey Zigachev /* Should be allocated in <IP>_ih_sw_init on GPUs that 225*b843c749SSergey Zigachev * support retry faults and require retry filtering. 226*b843c749SSergey Zigachev */ 227*b843c749SSergey Zigachev return r; 228*b843c749SSergey Zigachev 229*b843c749SSergey Zigachev spin_lock_irqsave(&adev->irq.ih.faults->lock, flags); 230*b843c749SSergey Zigachev 231*b843c749SSergey Zigachev /* Only let the hash table fill up to 50% for best performance */ 232*b843c749SSergey Zigachev if (adev->irq.ih.faults->count >= (1 << (AMDGPU_PAGEFAULT_HASH_BITS-1))) 233*b843c749SSergey Zigachev goto unlock_out; 234*b843c749SSergey Zigachev 235*b843c749SSergey Zigachev r = chash_table_copy_in(&adev->irq.ih.faults->hash, key, NULL); 236*b843c749SSergey Zigachev if (!r) 237*b843c749SSergey Zigachev adev->irq.ih.faults->count++; 238*b843c749SSergey Zigachev 239*b843c749SSergey Zigachev /* chash_table_copy_in should never fail unless we're losing count */ 240*b843c749SSergey Zigachev WARN_ON_ONCE(r < 0); 241*b843c749SSergey Zigachev 242*b843c749SSergey Zigachev unlock_out: 243*b843c749SSergey Zigachev spin_unlock_irqrestore(&adev->irq.ih.faults->lock, flags); 244*b843c749SSergey Zigachev return r; 245*b843c749SSergey Zigachev } 246*b843c749SSergey Zigachev 247*b843c749SSergey Zigachev /** 248*b843c749SSergey Zigachev * amdgpu_ih_clear_fault - Remove a page fault record 249*b843c749SSergey Zigachev * 250*b843c749SSergey Zigachev * @adev: amdgpu device pointer 251*b843c749SSergey Zigachev * @key: 64-bit encoding of PASID and address 252*b843c749SSergey Zigachev * 253*b843c749SSergey Zigachev * This should be called when a page fault has been handled. Any 254*b843c749SSergey Zigachev * future interrupt with this key will be processed as a new 255*b843c749SSergey Zigachev * page fault. 256*b843c749SSergey Zigachev */ 257*b843c749SSergey Zigachev void amdgpu_ih_clear_fault(struct amdgpu_device *adev, u64 key) 258*b843c749SSergey Zigachev { 259*b843c749SSergey Zigachev unsigned long flags; 260*b843c749SSergey Zigachev int r; 261*b843c749SSergey Zigachev 262*b843c749SSergey Zigachev if (!adev->irq.ih.faults) 263*b843c749SSergey Zigachev return; 264*b843c749SSergey Zigachev 265*b843c749SSergey Zigachev spin_lock_irqsave(&adev->irq.ih.faults->lock, flags); 266*b843c749SSergey Zigachev 267*b843c749SSergey Zigachev r = chash_table_remove(&adev->irq.ih.faults->hash, key, NULL); 268*b843c749SSergey Zigachev if (!WARN_ON_ONCE(r < 0)) { 269*b843c749SSergey Zigachev adev->irq.ih.faults->count--; 270*b843c749SSergey Zigachev WARN_ON_ONCE(adev->irq.ih.faults->count < 0); 271*b843c749SSergey Zigachev } 272*b843c749SSergey Zigachev 273*b843c749SSergey Zigachev spin_unlock_irqrestore(&adev->irq.ih.faults->lock, flags); 274*b843c749SSergey Zigachev } 275