blob: bcc7242218ec8945aee6057b7b61123c3b31c770 [file] [log] [blame]
Kristofer Jonsson116a6352020-08-20 17:25:23 +02001/*
2 * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
3 *
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,
70 dma_addr_t dma_addr)
71{
72 struct device_node *node = dev->of_node;
73 const __be32 *ranges;
74 int len;
75 int naddr;
76 int nsize;
77 int inc;
78 int i;
79
80 if (!node)
81 return dma_addr;
82
83 /* Get the #address-cells and #size-cells properties */
84 naddr = of_n_addr_cells(node);
85 nsize = of_n_size_cells(node);
86
87 /* Read the 'dma-ranges' property */
88 ranges = of_get_property(node, "dma-ranges", &len);
89 if (!ranges || len <= 0)
90 return dma_addr;
91
92 dev_dbg(dev, "ranges=%p, len=%d, naddr=%d, nsize=%d\n",
93 ranges, len, naddr, nsize);
94
95 len /= sizeof(*ranges);
96 inc = naddr + naddr + nsize;
97
98 for (i = 0; (i + inc) <= len; i += inc) {
99 dma_addr_t daddr;
100 dma_addr_t paddr;
101 dma_addr_t size;
102
103 daddr = of_read_number(&ranges[i], naddr);
104 paddr = of_read_number(&ranges[i + naddr], naddr);
105 size = of_read_number(&ranges[i + naddr + naddr], nsize);
106
107 dev_dbg(dev, "daddr=0x%llx, paddr=0x%llx, size=0x%llx\n",
108 daddr, paddr, size);
109
110 if (dma_addr >= paddr && dma_addr < (paddr + size))
111 return dma_addr + daddr - paddr;
112 }
113
114 return dma_addr;
115}
116
117static bool ethosu_buffer_verify(struct file *file)
118{
119 return file->f_op == &ethosu_buffer_fops;
120}
121
122static void ethosu_buffer_destroy(struct kref *kref)
123{
124 struct ethosu_buffer *buf =
125 container_of(kref, struct ethosu_buffer, kref);
126
127 dev_info(buf->edev->dev, "Buffer destroy. handle=0x%pK\n", buf);
128
129 dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr,
130 buf->dma_addr_orig);
131 devm_kfree(buf->edev->dev, buf);
132}
133
134static int ethosu_buffer_release(struct inode *inode,
135 struct file *file)
136{
137 struct ethosu_buffer *buf = file->private_data;
138
139 dev_info(buf->edev->dev, "Buffer release. handle=0x%pK\n", buf);
140
141 ethosu_buffer_put(buf);
142
143 return 0;
144}
145
146static int ethosu_buffer_mmap(struct file *file,
147 struct vm_area_struct *vma)
148{
149 struct ethosu_buffer *buf = file->private_data;
150 int ret;
151
152 dev_info(buf->edev->dev, "Buffer mmap. handle=0x%pK\n", buf);
153
154 ret = dma_mmap_coherent(buf->edev->dev, vma, buf->cpu_addr,
155 buf->dma_addr_orig,
156 buf->capacity);
157
158 return ret;
159}
160
161static long ethosu_buffer_ioctl(struct file *file,
162 unsigned int cmd,
163 unsigned long arg)
164{
165 struct ethosu_buffer *buf = file->private_data;
166 void __user *udata = (void __user *)arg;
167 int ret = -EINVAL;
168
169 ret = mutex_lock_interruptible(&buf->edev->mutex);
170 if (ret)
171 return ret;
172
173 dev_info(buf->edev->dev, "Ioctl. cmd=%u, arg=%lu\n", cmd, arg);
174
175 switch (cmd) {
176 case ETHOSU_IOCTL_BUFFER_SET: {
177 struct ethosu_uapi_buffer uapi;
178
179 if (copy_from_user(&uapi, udata, sizeof(uapi)))
180 break;
181
182 dev_info(buf->edev->dev,
183 "Ioctl: Buffer set. size=%u, offset=%u\n",
184 uapi.size, uapi.offset);
185
186 ret = ethosu_buffer_resize(buf, uapi.size, uapi.offset);
187 break;
188 }
189 case ETHOSU_IOCTL_BUFFER_GET: {
190 struct ethosu_uapi_buffer uapi;
191
192 uapi.size = buf->size;
193 uapi.offset = buf->offset;
194
195 dev_info(buf->edev->dev,
196 "Ioctl: Buffer get. size=%u, offset=%u\n",
197 uapi.size, uapi.offset);
198
199 if (copy_to_user(udata, &uapi, sizeof(uapi)))
200 break;
201
202 ret = 0;
203 break;
204 }
205 default: {
206 dev_err(buf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
207 cmd, arg);
208 break;
209 }
210 }
211
212 mutex_unlock(&buf->edev->mutex);
213
214 return ret;
215}
216
217int ethosu_buffer_create(struct ethosu_device *edev,
218 size_t capacity)
219{
220 struct ethosu_buffer *buf;
221 int ret = -ENOMEM;
222
223 buf = devm_kzalloc(edev->dev, sizeof(*buf), GFP_KERNEL);
224 if (!buf)
225 return -ENOMEM;
226
227 buf->edev = edev;
228 buf->capacity = capacity;
229 buf->offset = 0;
230 buf->size = 0;
231 kref_init(&buf->kref);
232
233 buf->cpu_addr = dma_alloc_coherent(buf->edev->dev, capacity,
234 &buf->dma_addr_orig, GFP_KERNEL);
235 if (!buf->cpu_addr)
236 goto free_buf;
237
238 buf->dma_addr = ethosu_buffer_dma_ranges(buf->edev->dev,
239 buf->dma_addr_orig);
240
241 ret = anon_inode_getfd("ethosu-buffer", &ethosu_buffer_fops, buf,
242 O_RDWR | O_CLOEXEC);
243 if (ret < 0)
244 goto free_dma;
245
246 buf->file = fget(ret);
247 fput(buf->file);
248
249 dev_info(buf->edev->dev,
250 "Buffer create. handle=0x%pK, capacity=%zu, cpu_addr=0x%pK, dma_addr=0x%llx, dma_addr_orig=0x%llx, phys_addr=0x%llx\n",
251 buf, capacity, buf->cpu_addr, buf->dma_addr,
252 buf->dma_addr_orig, virt_to_phys(buf->cpu_addr));
253
254 return ret;
255
256free_dma:
257 dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr,
258 buf->dma_addr_orig);
259
260free_buf:
261 devm_kfree(buf->edev->dev, buf);
262
263 return ret;
264}
265
266struct ethosu_buffer *ethosu_buffer_get_from_fd(int fd)
267{
268 struct ethosu_buffer *buf;
269 struct file *file;
270
271 file = fget(fd);
272 if (!file)
273 return ERR_PTR(-EINVAL);
274
275 if (!ethosu_buffer_verify(file)) {
276 fput(file);
277
278 return ERR_PTR(-EINVAL);
279 }
280
281 buf = file->private_data;
282 ethosu_buffer_get(buf);
283 fput(file);
284
285 return buf;
286}
287
288void ethosu_buffer_get(struct ethosu_buffer *buf)
289{
290 kref_get(&buf->kref);
291}
292
293void ethosu_buffer_put(struct ethosu_buffer *buf)
294{
295 kref_put(&buf->kref, ethosu_buffer_destroy);
296}
297
298int ethosu_buffer_resize(struct ethosu_buffer *buf,
299 size_t size,
300 size_t offset)
301{
302 if ((size + offset) > buf->capacity)
303 return -EINVAL;
304
305 buf->size = size;
306 buf->offset = offset;
307
308 return 0;
309}