bcm_vk_sg.c (6479B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright 2018-2020 Broadcom. 4 */ 5#include <linux/dma-mapping.h> 6#include <linux/mm.h> 7#include <linux/pagemap.h> 8#include <linux/pgtable.h> 9#include <linux/vmalloc.h> 10 11#include <asm/page.h> 12#include <asm/unaligned.h> 13 14#include <uapi/linux/misc/bcm_vk.h> 15 16#include "bcm_vk.h" 17#include "bcm_vk_msg.h" 18#include "bcm_vk_sg.h" 19 20/* 21 * Valkyrie has a hardware limitation of 16M transfer size. 22 * So limit the SGL chunks to 16M. 23 */ 24#define BCM_VK_MAX_SGL_CHUNK SZ_16M 25 26static int bcm_vk_dma_alloc(struct device *dev, 27 struct bcm_vk_dma *dma, 28 int dir, 29 struct _vk_data *vkdata); 30static int bcm_vk_dma_free(struct device *dev, struct bcm_vk_dma *dma); 31 32/* Uncomment to dump SGLIST */ 33/* #define BCM_VK_DUMP_SGLIST */ 34 35static int bcm_vk_dma_alloc(struct device *dev, 36 struct bcm_vk_dma *dma, 37 int direction, 38 struct _vk_data *vkdata) 39{ 40 dma_addr_t addr, sg_addr; 41 int err; 42 int i; 43 int offset; 44 u32 size; 45 u32 remaining_size; 46 u32 transfer_size; 47 u64 data; 48 unsigned long first, last; 49 struct _vk_data *sgdata; 50 51 /* Get 64-bit user address */ 52 data = get_unaligned(&vkdata->address); 53 54 /* offset into first page */ 55 offset = offset_in_page(data); 56 57 /* Calculate number of pages */ 58 first = (data & PAGE_MASK) >> PAGE_SHIFT; 59 last = ((data + vkdata->size - 1) & PAGE_MASK) >> PAGE_SHIFT; 60 dma->nr_pages = last - first + 1; 61 62 /* Allocate DMA pages */ 63 dma->pages = kmalloc_array(dma->nr_pages, 64 sizeof(struct page *), 65 GFP_KERNEL); 66 if (!dma->pages) 67 return -ENOMEM; 68 69 dev_dbg(dev, "Alloc DMA Pages [0x%llx+0x%x => %d pages]\n", 70 data, vkdata->size, dma->nr_pages); 71 72 dma->direction = direction; 73 74 /* Get user pages into memory */ 75 err = get_user_pages_fast(data & PAGE_MASK, 76 dma->nr_pages, 77 direction == DMA_FROM_DEVICE, 78 dma->pages); 79 if (err != dma->nr_pages) { 80 dma->nr_pages = (err >= 0) ? err : 0; 81 dev_err(dev, "get_user_pages_fast, err=%d [%d]\n", 82 err, dma->nr_pages); 83 return err < 0 ? err : -EINVAL; 84 } 85 86 /* Max size of sg list is 1 per mapped page + fields at start */ 87 dma->sglen = (dma->nr_pages * sizeof(*sgdata)) + 88 (sizeof(u32) * SGLIST_VKDATA_START); 89 90 /* Allocate sglist */ 91 dma->sglist = dma_alloc_coherent(dev, 92 dma->sglen, 93 &dma->handle, 94 GFP_KERNEL); 95 if (!dma->sglist) 96 return -ENOMEM; 97 98 dma->sglist[SGLIST_NUM_SG] = 0; 99 dma->sglist[SGLIST_TOTALSIZE] = vkdata->size; 100 remaining_size = vkdata->size; 101 sgdata = (struct _vk_data *)&dma->sglist[SGLIST_VKDATA_START]; 102 103 /* Map all pages into DMA */ 104 size = min_t(size_t, PAGE_SIZE - offset, remaining_size); 105 remaining_size -= size; 106 sg_addr = dma_map_page(dev, 107 dma->pages[0], 108 offset, 109 size, 110 dma->direction); 111 transfer_size = size; 112 if (unlikely(dma_mapping_error(dev, sg_addr))) { 113 __free_page(dma->pages[0]); 114 return -EIO; 115 } 116 117 for (i = 1; i < dma->nr_pages; i++) { 118 size = min_t(size_t, PAGE_SIZE, remaining_size); 119 remaining_size -= size; 120 addr = dma_map_page(dev, 121 dma->pages[i], 122 0, 123 size, 124 dma->direction); 125 if (unlikely(dma_mapping_error(dev, addr))) { 126 __free_page(dma->pages[i]); 127 return -EIO; 128 } 129 130 /* 131 * Compress SG list entry when pages are contiguous 132 * and transfer size less or equal to BCM_VK_MAX_SGL_CHUNK 133 */ 134 if ((addr == (sg_addr + transfer_size)) && 135 ((transfer_size + size) <= BCM_VK_MAX_SGL_CHUNK)) { 136 /* pages are contiguous, add to same sg entry */ 137 transfer_size += size; 138 } else { 139 /* pages are not contiguous, write sg entry */ 140 sgdata->size = transfer_size; 141 put_unaligned(sg_addr, (u64 *)&sgdata->address); 142 dma->sglist[SGLIST_NUM_SG]++; 143 144 /* start new sg entry */ 145 sgdata++; 146 sg_addr = addr; 147 transfer_size = size; 148 } 149 } 150 /* Write last sg list entry */ 151 sgdata->size = transfer_size; 152 put_unaligned(sg_addr, (u64 *)&sgdata->address); 153 dma->sglist[SGLIST_NUM_SG]++; 154 155 /* Update pointers and size field to point to sglist */ 156 put_unaligned((u64)dma->handle, &vkdata->address); 157 vkdata->size = (dma->sglist[SGLIST_NUM_SG] * sizeof(*sgdata)) + 158 (sizeof(u32) * SGLIST_VKDATA_START); 159 160#ifdef BCM_VK_DUMP_SGLIST 161 dev_dbg(dev, 162 "sgl 0x%llx handle 0x%llx, sglen: 0x%x sgsize: 0x%x\n", 163 (u64)dma->sglist, 164 dma->handle, 165 dma->sglen, 166 vkdata->size); 167 for (i = 0; i < vkdata->size / sizeof(u32); i++) 168 dev_dbg(dev, "i:0x%x 0x%x\n", i, dma->sglist[i]); 169#endif 170 171 return 0; 172} 173 174int bcm_vk_sg_alloc(struct device *dev, 175 struct bcm_vk_dma *dma, 176 int dir, 177 struct _vk_data *vkdata, 178 int num) 179{ 180 int i; 181 int rc = -EINVAL; 182 183 /* Convert user addresses to DMA SG List */ 184 for (i = 0; i < num; i++) { 185 if (vkdata[i].size && vkdata[i].address) { 186 /* 187 * If both size and address are non-zero 188 * then DMA alloc. 189 */ 190 rc = bcm_vk_dma_alloc(dev, 191 &dma[i], 192 dir, 193 &vkdata[i]); 194 } else if (vkdata[i].size || 195 vkdata[i].address) { 196 /* 197 * If one of size and address are zero 198 * there is a problem. 199 */ 200 dev_err(dev, 201 "Invalid vkdata %x 0x%x 0x%llx\n", 202 i, vkdata[i].size, vkdata[i].address); 203 rc = -EINVAL; 204 } else { 205 /* 206 * If size and address are both zero 207 * don't convert, but return success. 208 */ 209 rc = 0; 210 } 211 212 if (rc) 213 goto fail_alloc; 214 } 215 return rc; 216 217fail_alloc: 218 while (i > 0) { 219 i--; 220 if (dma[i].sglist) 221 bcm_vk_dma_free(dev, &dma[i]); 222 } 223 return rc; 224} 225 226static int bcm_vk_dma_free(struct device *dev, struct bcm_vk_dma *dma) 227{ 228 dma_addr_t addr; 229 int i; 230 int num_sg; 231 u32 size; 232 struct _vk_data *vkdata; 233 234 dev_dbg(dev, "free sglist=%p sglen=0x%x\n", dma->sglist, dma->sglen); 235 236 /* Unmap all pages in the sglist */ 237 num_sg = dma->sglist[SGLIST_NUM_SG]; 238 vkdata = (struct _vk_data *)&dma->sglist[SGLIST_VKDATA_START]; 239 for (i = 0; i < num_sg; i++) { 240 size = vkdata[i].size; 241 addr = get_unaligned(&vkdata[i].address); 242 243 dma_unmap_page(dev, addr, size, dma->direction); 244 } 245 246 /* Free allocated sglist */ 247 dma_free_coherent(dev, dma->sglen, dma->sglist, dma->handle); 248 249 /* Release lock on all pages */ 250 for (i = 0; i < dma->nr_pages; i++) 251 put_page(dma->pages[i]); 252 253 /* Free allocated dma pages */ 254 kfree(dma->pages); 255 dma->sglist = NULL; 256 257 return 0; 258} 259 260int bcm_vk_sg_free(struct device *dev, struct bcm_vk_dma *dma, int num, 261 int *proc_cnt) 262{ 263 int i; 264 265 *proc_cnt = 0; 266 /* Unmap and free all pages and sglists */ 267 for (i = 0; i < num; i++) { 268 if (dma[i].sglist) { 269 bcm_vk_dma_free(dev, &dma[i]); 270 *proc_cnt += 1; 271 } 272 } 273 274 return 0; 275}