1 /* $NetBSD: amdgpu_ih.c,v 1.10 2021/12/19 12:29:33 riastradh Exp $ */ 2 3 /* 4 * Copyright 2014 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 */ 25 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: amdgpu_ih.c,v 1.10 2021/12/19 12:29:33 riastradh Exp $"); 28 29 #include <linux/dma-mapping.h> 30 31 #include "amdgpu.h" 32 #include "amdgpu_ih.h" 33 34 /** 35 * amdgpu_ih_ring_init - initialize the IH state 36 * 37 * @adev: amdgpu_device pointer 38 * @ih: ih ring to initialize 39 * @ring_size: ring size to allocate 40 * @use_bus_addr: true when we can use dma_alloc_coherent 41 * 42 * Initializes the IH state and allocates a buffer 43 * for the IH ring buffer. 44 * Returns 0 for success, errors for failure. 45 */ 46 int amdgpu_ih_ring_init(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih, 47 unsigned ring_size, bool use_bus_addr) 48 { 49 u32 rb_bufsz; 50 int r; 51 52 /* Align ring size */ 53 rb_bufsz = order_base_2(ring_size / 4); 54 ring_size = (1 << rb_bufsz) * 4; 55 ih->ring_size = ring_size; 56 ih->ptr_mask = ih->ring_size - 1; 57 ih->rptr = 0; 58 ih->use_bus_addr = use_bus_addr; 59 60 if (use_bus_addr) { 61 dma_addr_t dma_addr; 62 63 if (ih->ring) 64 return 0; 65 66 /* add 8 bytes for the rptr/wptr shadows and 67 * add them to the end of the ring allocation. 68 */ 69 #ifdef __NetBSD__ 70 const bus_size_t size = ih->ring_size + 8; 71 int rseg __diagused; 72 void *kva; 73 r = -bus_dmamem_alloc(adev->ddev->dmat, size, PAGE_SIZE, 0, 74 &ih->ring_seg, 1, &rseg, BUS_DMA_WAITOK); 75 if (r) { 76 fail0: KASSERT(r); 77 return r; 78 } 79 KASSERT(rseg == 1); 80 r = -bus_dmamap_create(adev->ddev->dmat, size, 1, size, 0, 81 BUS_DMA_WAITOK, &ih->ring_map); 82 if (r) { 83 fail1: bus_dmamem_free(adev->ddev->dmat, &ih->ring_seg, 1); 84 goto fail0; 85 } 86 r = -bus_dmamem_map(adev->ddev->dmat, &ih->ring_seg, 1, size, 87 &kva, BUS_DMA_WAITOK|BUS_DMA_COHERENT); 88 if (r) { 89 fail2: bus_dmamap_destroy(adev->ddev->dmat, ih->ring_map); 90 ih->ring_map = NULL; 91 goto fail1; 92 } 93 r = -bus_dmamap_load(adev->ddev->dmat, ih->ring_map, kva, size, 94 NULL, BUS_DMA_WAITOK); 95 if (r) { 96 fail3: __unused bus_dmamem_unmap(adev->ddev->dmat, kva, size); 97 goto fail2; 98 } 99 ih->ring = kva; 100 dma_addr = ih->ring_map->dm_segs[0].ds_addr; 101 #else 102 ih->ring = dma_alloc_coherent(adev->dev, ih->ring_size + 8, 103 &dma_addr, GFP_KERNEL); 104 if (ih->ring == NULL) 105 return -ENOMEM; 106 #endif 107 108 ih->gpu_addr = dma_addr; 109 ih->wptr_addr = dma_addr + ih->ring_size; 110 ih->wptr_cpu = &ih->ring[ih->ring_size / 4]; 111 ih->rptr_addr = dma_addr + ih->ring_size + 4; 112 ih->rptr_cpu = &ih->ring[(ih->ring_size / 4) + 1]; 113 } else { 114 unsigned wptr_offs, rptr_offs; 115 116 r = amdgpu_device_wb_get(adev, &wptr_offs); 117 if (r) 118 return r; 119 120 r = amdgpu_device_wb_get(adev, &rptr_offs); 121 if (r) { 122 amdgpu_device_wb_free(adev, wptr_offs); 123 return r; 124 } 125 126 r = amdgpu_bo_create_kernel(adev, ih->ring_size, PAGE_SIZE, 127 AMDGPU_GEM_DOMAIN_GTT, 128 &ih->ring_obj, &ih->gpu_addr, 129 (void **)__UNVOLATILE(&ih->ring)); 130 if (r) { 131 amdgpu_device_wb_free(adev, rptr_offs); 132 amdgpu_device_wb_free(adev, wptr_offs); 133 return r; 134 } 135 136 ih->wptr_addr = adev->wb.gpu_addr + wptr_offs * 4; 137 ih->wptr_cpu = &adev->wb.wb[wptr_offs]; 138 ih->rptr_addr = adev->wb.gpu_addr + rptr_offs * 4; 139 ih->rptr_cpu = &adev->wb.wb[rptr_offs]; 140 } 141 return 0; 142 } 143 144 /** 145 * amdgpu_ih_ring_fini - tear down the IH state 146 * 147 * @adev: amdgpu_device pointer 148 * @ih: ih ring to tear down 149 * 150 * Tears down the IH state and frees buffer 151 * used for the IH ring buffer. 152 */ 153 void amdgpu_ih_ring_fini(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih) 154 { 155 if (ih->use_bus_addr) { 156 if (!ih->ring) 157 return; 158 159 /* add 8 bytes for the rptr/wptr shadows and 160 * add them to the end of the ring allocation. 161 */ 162 #ifdef __NetBSD__ 163 const bus_size_t size = ih->ring_size + 8; 164 void *kva = __UNVOLATILE(ih->ring); 165 bus_dmamap_unload(adev->ddev->dmat, ih->ring_map); 166 bus_dmamem_unmap(adev->ddev->dmat, kva, size); 167 bus_dmamap_destroy(adev->ddev->dmat, ih->ring_map); 168 bus_dmamem_free(adev->ddev->dmat, &ih->ring_seg, 1); 169 #else 170 dma_free_coherent(adev->dev, ih->ring_size + 8, 171 (void *)ih->ring, ih->gpu_addr); 172 #endif 173 ih->ring = NULL; 174 } else { 175 amdgpu_bo_free_kernel(&ih->ring_obj, &ih->gpu_addr, 176 (void **)__UNVOLATILE(&ih->ring)); 177 amdgpu_device_wb_free(adev, (ih->wptr_addr - ih->gpu_addr) / 4); 178 amdgpu_device_wb_free(adev, (ih->rptr_addr - ih->gpu_addr) / 4); 179 } 180 } 181 182 /** 183 * amdgpu_ih_process - interrupt handler 184 * 185 * @adev: amdgpu_device pointer 186 * @ih: ih ring to process 187 * 188 * Interrupt hander (VI), walk the IH ring. 189 * Returns irq process return code. 190 */ 191 int amdgpu_ih_process(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih) 192 { 193 unsigned int count = AMDGPU_IH_MAX_NUM_IVS; 194 u32 wptr; 195 196 if (!ih->enabled || adev->shutdown) 197 return IRQ_NONE; 198 199 wptr = amdgpu_ih_get_wptr(adev, ih); 200 201 restart_ih: 202 /* is somebody else already processing irqs? */ 203 if (atomic_xchg(&ih->lock, 1)) 204 return IRQ_NONE; 205 206 DRM_DEBUG("%s: rptr %d, wptr %d\n", __func__, ih->rptr, wptr); 207 208 /* Order reading of wptr vs. reading of IH ring data */ 209 rmb(); 210 211 while (ih->rptr != wptr && --count) { 212 amdgpu_irq_dispatch(adev, ih); 213 ih->rptr &= ih->ptr_mask; 214 } 215 216 amdgpu_ih_set_rptr(adev, ih); 217 atomic_set(&ih->lock, 0); 218 219 /* make sure wptr hasn't changed while processing */ 220 wptr = amdgpu_ih_get_wptr(adev, ih); 221 if (wptr != ih->rptr) 222 goto restart_ih; 223 224 return IRQ_HANDLED; 225 } 226 227