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