blob: a83a95a6ee55f057b1ee25b1a1d79ba635fdd0c7 [file] [log] [blame]
Kristofer Jonsson116a6352020-08-20 17:25:23 +02001/*
Kristofer Jonssond779a082023-01-04 17:09:47 +01002 * Copyright 2020-2023 Arm Limited and/or its affiliates
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>
Kristofer Jonssond779a082023-01-04 17:09:47 +010035#include <linux/remoteproc.h>
Kristofer Jonsson116a6352020-08-20 17:25:23 +020036#include <linux/uaccess.h>
37
38/****************************************************************************
39 * Variables
40 ****************************************************************************/
41
42static int ethosu_buffer_release(struct inode *inode,
43 struct file *file);
44
45static int ethosu_buffer_mmap(struct file *file,
46 struct vm_area_struct *vma);
47
48static long ethosu_buffer_ioctl(struct file *file,
49 unsigned int cmd,
50 unsigned long arg);
51
52static const struct file_operations ethosu_buffer_fops = {
53 .release = &ethosu_buffer_release,
54 .mmap = &ethosu_buffer_mmap,
55 .unlocked_ioctl = &ethosu_buffer_ioctl,
56#ifdef CONFIG_COMPAT
57 .compat_ioctl = &ethosu_buffer_ioctl,
58#endif
59};
60
61/****************************************************************************
62 * Functions
63 ****************************************************************************/
64
Kristofer Jonssond779a082023-01-04 17:09:47 +010065__attribute__((used))
66static dma_addr_t ethosu_pa_to_da(struct device *dev,
67 phys_addr_t pa,
68 size_t len)
Kristofer Jonsson116a6352020-08-20 17:25:23 +020069{
Kristofer Jonssond779a082023-01-04 17:09:47 +010070 struct rproc *rproc = rproc_get_by_child(dev);
71 struct rproc_mem_entry *mem;
Kristofer Jonsson116a6352020-08-20 17:25:23 +020072
Kristofer Jonssond779a082023-01-04 17:09:47 +010073 list_for_each_entry(mem, &rproc->carveouts, node) {
74 ssize_t offset = pa - mem->dma;
Kristofer Jonsson116a6352020-08-20 17:25:23 +020075
Kristofer Jonssond779a082023-01-04 17:09:47 +010076 if (offset >= 0 && offset + len <= mem->len)
77 return mem->da + offset;
Kristofer Jonsson116a6352020-08-20 17:25:23 +020078 }
79
Kristofer Jonssond779a082023-01-04 17:09:47 +010080 return (dma_addr_t)-1;
Kristofer Jonsson116a6352020-08-20 17:25:23 +020081}
82
83static bool ethosu_buffer_verify(struct file *file)
84{
85 return file->f_op == &ethosu_buffer_fops;
86}
87
88static void ethosu_buffer_destroy(struct kref *kref)
89{
90 struct ethosu_buffer *buf =
91 container_of(kref, struct ethosu_buffer, kref);
92
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +020093 dev_info(buf->edev->dev, "Buffer destroy. buf=0x%pK\n", buf);
Kristofer Jonsson116a6352020-08-20 17:25:23 +020094
Kristofer Jonssond779a082023-01-04 17:09:47 +010095 dma_free_coherent(buf->dev, buf->capacity, buf->cpu_addr,
96 buf->dma_addr);
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +020097
Kristofer Jonsson116a6352020-08-20 17:25:23 +020098 devm_kfree(buf->edev->dev, buf);
99}
100
101static int ethosu_buffer_release(struct inode *inode,
102 struct file *file)
103{
104 struct ethosu_buffer *buf = file->private_data;
105
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200106 dev_info(buf->edev->dev, "Buffer release. file=0x%pK, buf=0x%pK\n",
107 file, buf);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200108
109 ethosu_buffer_put(buf);
110
111 return 0;
112}
113
114static int ethosu_buffer_mmap(struct file *file,
115 struct vm_area_struct *vma)
116{
117 struct ethosu_buffer *buf = file->private_data;
118 int ret;
119
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200120 dev_info(buf->edev->dev, "Buffer mmap. file=0x%pK, buf=0x%pK\n",
121 file, buf);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200122
123 ret = dma_mmap_coherent(buf->edev->dev, vma, buf->cpu_addr,
Kristofer Jonssond779a082023-01-04 17:09:47 +0100124 buf->dma_addr, buf->capacity);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200125
126 return ret;
127}
128
129static long ethosu_buffer_ioctl(struct file *file,
130 unsigned int cmd,
131 unsigned long arg)
132{
133 struct ethosu_buffer *buf = file->private_data;
134 void __user *udata = (void __user *)arg;
135 int ret = -EINVAL;
136
137 ret = mutex_lock_interruptible(&buf->edev->mutex);
138 if (ret)
139 return ret;
140
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200141 dev_info(buf->edev->dev,
142 "Buffer ioctl. file=0x%pK, buf=0x%pK, cmd=0x%x, arg=%lu\n",
143 file, buf, cmd, arg);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200144
145 switch (cmd) {
146 case ETHOSU_IOCTL_BUFFER_SET: {
147 struct ethosu_uapi_buffer uapi;
148
149 if (copy_from_user(&uapi, udata, sizeof(uapi)))
150 break;
151
152 dev_info(buf->edev->dev,
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200153 "Buffer ioctl: Buffer set. size=%u, offset=%u\n",
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200154 uapi.size, uapi.offset);
155
156 ret = ethosu_buffer_resize(buf, uapi.size, uapi.offset);
157 break;
158 }
159 case ETHOSU_IOCTL_BUFFER_GET: {
160 struct ethosu_uapi_buffer uapi;
161
162 uapi.size = buf->size;
163 uapi.offset = buf->offset;
164
165 dev_info(buf->edev->dev,
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200166 "Buffer ioctl: Buffer get. size=%u, offset=%u\n",
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200167 uapi.size, uapi.offset);
168
169 if (copy_to_user(udata, &uapi, sizeof(uapi)))
170 break;
171
172 ret = 0;
173 break;
174 }
175 default: {
176 dev_err(buf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
177 cmd, arg);
178 break;
179 }
180 }
181
182 mutex_unlock(&buf->edev->mutex);
183
184 return ret;
185}
186
187int ethosu_buffer_create(struct ethosu_device *edev,
188 size_t capacity)
189{
Kristofer Jonssond779a082023-01-04 17:09:47 +0100190 struct rproc *rproc = rproc_get_by_child(edev->dev);
191 struct device *dev = rproc->dev.parent;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200192 struct ethosu_buffer *buf;
193 int ret = -ENOMEM;
194
Per Åstrandf50f25e2022-05-18 09:12:15 +0200195 if (!capacity)
196 return -EINVAL;
197
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200198 buf = devm_kzalloc(edev->dev, sizeof(*buf), GFP_KERNEL);
199 if (!buf)
200 return -ENOMEM;
201
202 buf->edev = edev;
Kristofer Jonssond779a082023-01-04 17:09:47 +0100203 buf->dev = dev;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200204 buf->capacity = capacity;
205 buf->offset = 0;
206 buf->size = 0;
207 kref_init(&buf->kref);
208
Kristofer Jonssond779a082023-01-04 17:09:47 +0100209 buf->cpu_addr = dma_alloc_coherent(dev, capacity,
210 &buf->dma_addr, GFP_KERNEL);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200211 if (!buf->cpu_addr)
212 goto free_buf;
213
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200214 ret = anon_inode_getfd("ethosu-buffer", &ethosu_buffer_fops, buf,
215 O_RDWR | O_CLOEXEC);
216 if (ret < 0)
217 goto free_dma;
218
219 buf->file = fget(ret);
220 fput(buf->file);
221
222 dev_info(buf->edev->dev,
Kristofer Jonssond779a082023-01-04 17:09:47 +0100223 "Buffer create. file=0x%pK, fd=%d, buf=0x%pK, capacity=%zu, cpu_addr=0x%pK, dma_addr=0x%llx, phys_addr=0x%llx\n",
Kristofer Jonsson53fd03d2022-06-21 16:58:45 +0200224 buf->file, ret, buf, capacity, buf->cpu_addr, buf->dma_addr,
Kristofer Jonssond779a082023-01-04 17:09:47 +0100225 virt_to_phys(buf->cpu_addr));
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200226
227 return ret;
228
229free_dma:
230 dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr,
Kristofer Jonssond779a082023-01-04 17:09:47 +0100231 buf->dma_addr);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200232
233free_buf:
234 devm_kfree(buf->edev->dev, buf);
235
236 return ret;
237}
238
239struct ethosu_buffer *ethosu_buffer_get_from_fd(int fd)
240{
241 struct ethosu_buffer *buf;
242 struct file *file;
243
244 file = fget(fd);
245 if (!file)
246 return ERR_PTR(-EINVAL);
247
248 if (!ethosu_buffer_verify(file)) {
249 fput(file);
250
251 return ERR_PTR(-EINVAL);
252 }
253
254 buf = file->private_data;
255 ethosu_buffer_get(buf);
256 fput(file);
257
258 return buf;
259}
260
261void ethosu_buffer_get(struct ethosu_buffer *buf)
262{
263 kref_get(&buf->kref);
264}
265
266void ethosu_buffer_put(struct ethosu_buffer *buf)
267{
268 kref_put(&buf->kref, ethosu_buffer_destroy);
269}
270
271int ethosu_buffer_resize(struct ethosu_buffer *buf,
272 size_t size,
273 size_t offset)
274{
275 if ((size + offset) > buf->capacity)
276 return -EINVAL;
277
278 buf->size = size;
279 buf->offset = offset;
280
281 return 0;
282}