blob: ac32aca9682e26447c18b31326f72e9a41e62191 [file] [log] [blame]
Kristofer Jonsson116a6352020-08-20 17:25:23 +02001/*
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +01002 * Copyright (c) 2020-2022 Arm Limited.
Kristofer Jonsson116a6352020-08-20 17:25:23 +02003 *
4 * This program is free software and is provided to you under the terms of the
5 * GNU General Public License version 2 as published by the Free Software
6 * Foundation, and any use by you of this program is subject to the terms
7 * of such GNU licence.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, you can access it online at
16 * http://www.gnu.org/licenses/gpl-2.0.html.
17 *
18 * SPDX-License-Identifier: GPL-2.0-only
19 */
20
21/****************************************************************************
22 * Includes
23 ****************************************************************************/
24
25#include "ethosu_buffer.h"
26
27#include "ethosu_device.h"
28#include "uapi/ethosu.h"
29
30#include <linux/anon_inodes.h>
31#include <linux/dma-mapping.h>
32#include <linux/of_address.h>
33#include <linux/file.h>
34#include <linux/fs.h>
35#include <linux/uaccess.h>
36
37/****************************************************************************
38 * Variables
39 ****************************************************************************/
40
41static int ethosu_buffer_release(struct inode *inode,
42 struct file *file);
43
44static int ethosu_buffer_mmap(struct file *file,
45 struct vm_area_struct *vma);
46
47static long ethosu_buffer_ioctl(struct file *file,
48 unsigned int cmd,
49 unsigned long arg);
50
51static const struct file_operations ethosu_buffer_fops = {
52 .release = &ethosu_buffer_release,
53 .mmap = &ethosu_buffer_mmap,
54 .unlocked_ioctl = &ethosu_buffer_ioctl,
55#ifdef CONFIG_COMPAT
56 .compat_ioctl = &ethosu_buffer_ioctl,
57#endif
58};
59
60/****************************************************************************
61 * Functions
62 ****************************************************************************/
63
64/*
65 * The 'dma-ranges' device tree property for shared dma memory does not seem
66 * to be fully supported for coherent memory. Therefor we apply the DMA range
67 * offset ourselves.
68 */
69static dma_addr_t ethosu_buffer_dma_ranges(struct device *dev,
Per Åstranded7d6c12021-11-09 13:56:45 +010070 dma_addr_t dma_addr,
71 size_t dma_buf_size)
Kristofer Jonsson116a6352020-08-20 17:25:23 +020072{
73 struct device_node *node = dev->of_node;
74 const __be32 *ranges;
75 int len;
76 int naddr;
77 int nsize;
78 int inc;
79 int i;
80
81 if (!node)
82 return dma_addr;
83
84 /* Get the #address-cells and #size-cells properties */
85 naddr = of_n_addr_cells(node);
86 nsize = of_n_size_cells(node);
87
88 /* Read the 'dma-ranges' property */
89 ranges = of_get_property(node, "dma-ranges", &len);
90 if (!ranges || len <= 0)
91 return dma_addr;
92
93 dev_dbg(dev, "ranges=%p, len=%d, naddr=%d, nsize=%d\n",
94 ranges, len, naddr, nsize);
95
96 len /= sizeof(*ranges);
97 inc = naddr + naddr + nsize;
98
99 for (i = 0; (i + inc) <= len; i += inc) {
100 dma_addr_t daddr;
101 dma_addr_t paddr;
102 dma_addr_t size;
103
104 daddr = of_read_number(&ranges[i], naddr);
105 paddr = of_read_number(&ranges[i + naddr], naddr);
106 size = of_read_number(&ranges[i + naddr + naddr], nsize);
107
108 dev_dbg(dev, "daddr=0x%llx, paddr=0x%llx, size=0x%llx\n",
109 daddr, paddr, size);
110
Per Åstranded7d6c12021-11-09 13:56:45 +0100111 if (dma_addr >= paddr &&
112 (dma_addr + dma_buf_size) < (paddr + size))
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200113 return dma_addr + daddr - paddr;
114 }
115
116 return dma_addr;
117}
118
119static bool ethosu_buffer_verify(struct file *file)
120{
121 return file->f_op == &ethosu_buffer_fops;
122}
123
124static void ethosu_buffer_destroy(struct kref *kref)
125{
126 struct ethosu_buffer *buf =
127 container_of(kref, struct ethosu_buffer, kref);
128
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200129 dev_info(buf->edev->dev, "Buffer destroy. buf=0x%pK\n", buf);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200130
131 dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr,
132 buf->dma_addr_orig);
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200133
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200134 devm_kfree(buf->edev->dev, buf);
135}
136
137static int ethosu_buffer_release(struct inode *inode,
138 struct file *file)
139{
140 struct ethosu_buffer *buf = file->private_data;
141
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200142 dev_info(buf->edev->dev, "Buffer release. file=0x%pK, buf=0x%pK\n",
143 file, buf);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200144
145 ethosu_buffer_put(buf);
146
147 return 0;
148}
149
150static int ethosu_buffer_mmap(struct file *file,
151 struct vm_area_struct *vma)
152{
153 struct ethosu_buffer *buf = file->private_data;
154 int ret;
155
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200156 dev_info(buf->edev->dev, "Buffer mmap. file=0x%pK, buf=0x%pK\n",
157 file, buf);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200158
159 ret = dma_mmap_coherent(buf->edev->dev, vma, buf->cpu_addr,
160 buf->dma_addr_orig,
161 buf->capacity);
162
163 return ret;
164}
165
166static long ethosu_buffer_ioctl(struct file *file,
167 unsigned int cmd,
168 unsigned long arg)
169{
170 struct ethosu_buffer *buf = file->private_data;
171 void __user *udata = (void __user *)arg;
172 int ret = -EINVAL;
173
174 ret = mutex_lock_interruptible(&buf->edev->mutex);
175 if (ret)
176 return ret;
177
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200178 dev_info(buf->edev->dev,
179 "Buffer ioctl. file=0x%pK, buf=0x%pK, cmd=0x%x, arg=%lu\n",
180 file, buf, cmd, arg);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200181
182 switch (cmd) {
183 case ETHOSU_IOCTL_BUFFER_SET: {
184 struct ethosu_uapi_buffer uapi;
185
186 if (copy_from_user(&uapi, udata, sizeof(uapi)))
187 break;
188
189 dev_info(buf->edev->dev,
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200190 "Buffer ioctl: Buffer set. size=%u, offset=%u\n",
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200191 uapi.size, uapi.offset);
192
193 ret = ethosu_buffer_resize(buf, uapi.size, uapi.offset);
194 break;
195 }
196 case ETHOSU_IOCTL_BUFFER_GET: {
197 struct ethosu_uapi_buffer uapi;
198
199 uapi.size = buf->size;
200 uapi.offset = buf->offset;
201
202 dev_info(buf->edev->dev,
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200203 "Buffer ioctl: Buffer get. size=%u, offset=%u\n",
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200204 uapi.size, uapi.offset);
205
206 if (copy_to_user(udata, &uapi, sizeof(uapi)))
207 break;
208
209 ret = 0;
210 break;
211 }
212 default: {
213 dev_err(buf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
214 cmd, arg);
215 break;
216 }
217 }
218
219 mutex_unlock(&buf->edev->mutex);
220
221 return ret;
222}
223
224int ethosu_buffer_create(struct ethosu_device *edev,
225 size_t capacity)
226{
227 struct ethosu_buffer *buf;
228 int ret = -ENOMEM;
229
Per Åstrandf50f25e2022-05-18 09:12:15 +0200230 if (!capacity)
231 return -EINVAL;
232
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200233 buf = devm_kzalloc(edev->dev, sizeof(*buf), GFP_KERNEL);
234 if (!buf)
235 return -ENOMEM;
236
237 buf->edev = edev;
238 buf->capacity = capacity;
239 buf->offset = 0;
240 buf->size = 0;
241 kref_init(&buf->kref);
242
243 buf->cpu_addr = dma_alloc_coherent(buf->edev->dev, capacity,
244 &buf->dma_addr_orig, GFP_KERNEL);
245 if (!buf->cpu_addr)
246 goto free_buf;
247
248 buf->dma_addr = ethosu_buffer_dma_ranges(buf->edev->dev,
Per Åstranded7d6c12021-11-09 13:56:45 +0100249 buf->dma_addr_orig,
250 buf->capacity);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200251
252 ret = anon_inode_getfd("ethosu-buffer", &ethosu_buffer_fops, buf,
253 O_RDWR | O_CLOEXEC);
254 if (ret < 0)
255 goto free_dma;
256
257 buf->file = fget(ret);
258 fput(buf->file);
259
260 dev_info(buf->edev->dev,
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200261 "Buffer create. file=0x%pK, fd=%d, buf=0x%pK, capacity=%zu, cpu_addr=0x%pK, dma_addr=0x%llx, dma_addr_orig=0x%llx, phys_addr=0x%llx\n",
262 buf->file, ret, buf, capacity, buf->cpu_addr, buf->dma_addr,
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200263 buf->dma_addr_orig, virt_to_phys(buf->cpu_addr));
264
265 return ret;
266
267free_dma:
268 dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr,
269 buf->dma_addr_orig);
270
271free_buf:
272 devm_kfree(buf->edev->dev, buf);
273
274 return ret;
275}
276
277struct ethosu_buffer *ethosu_buffer_get_from_fd(int fd)
278{
279 struct ethosu_buffer *buf;
280 struct file *file;
281
282 file = fget(fd);
283 if (!file)
284 return ERR_PTR(-EINVAL);
285
286 if (!ethosu_buffer_verify(file)) {
287 fput(file);
288
289 return ERR_PTR(-EINVAL);
290 }
291
292 buf = file->private_data;
293 ethosu_buffer_get(buf);
294 fput(file);
295
296 return buf;
297}
298
299void ethosu_buffer_get(struct ethosu_buffer *buf)
300{
301 kref_get(&buf->kref);
302}
303
304void ethosu_buffer_put(struct ethosu_buffer *buf)
305{
306 kref_put(&buf->kref, ethosu_buffer_destroy);
307}
308
309int ethosu_buffer_resize(struct ethosu_buffer *buf,
310 size_t size,
311 size_t offset)
312{
313 if ((size + offset) > buf->capacity)
314 return -EINVAL;
315
316 buf->size = size;
317 buf->offset = offset;
318
319 return 0;
320}