File Index
Files and directories
Sign in
FILE

bcm_vk_sg.c

File preview with syntax highlighting.

linux-6.19.14/drivers/misc/bcm-vk/bcm_vk_sg.c C
  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 <linux/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}
~/ Read-only file preview.