blob: dfdaa58b34adfb30f7583d04f9f5c509974794cc [file] [log] [blame]
Kristofer Jonsson116a6352020-08-20 17:25:23 +02001/*
Kristofer Jonsson35de9e62022-03-08 13:25:45 +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_device.h"
26
27#include "ethosu_buffer.h"
28#include "ethosu_core_interface.h"
29#include "ethosu_inference.h"
30#include "ethosu_network.h"
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +010031#include "ethosu_network_info.h"
Kristofer Jonsson116a6352020-08-20 17:25:23 +020032#include "uapi/ethosu.h"
33
34#include <linux/delay.h>
35#include <linux/dma-mapping.h>
36#include <linux/errno.h>
37#include <linux/fs.h>
38#include <linux/interrupt.h>
39#include <linux/io.h>
40#include <linux/of_reserved_mem.h>
41#include <linux/uaccess.h>
Davide Grohmann35ce6c82021-06-01 15:03:51 +020042#include <linux/slab.h>
Kristofer Jonsson116a6352020-08-20 17:25:23 +020043
44/****************************************************************************
45 * Defines
46 ****************************************************************************/
47
Kristofer Jonsson116a6352020-08-20 17:25:23 +020048#define DMA_ADDR_BITS 32 /* Number of address bits */
49
Per Åstrandbb175272021-10-25 12:37:36 +020050#define CAPABILITIES_RESP_TIMEOUT_MS 2000
Davide Grohmann35ce6c82021-06-01 15:03:51 +020051
Kristofer Jonsson116a6352020-08-20 17:25:23 +020052/****************************************************************************
Kristofer Jonsson116a6352020-08-20 17:25:23 +020053 * Functions
54 ****************************************************************************/
55
Davide Grohmann35ce6c82021-06-01 15:03:51 +020056static void ethosu_capabilities_destroy(struct kref *kref)
57{
58 struct ethosu_capabilities *cap =
59 container_of(kref, struct ethosu_capabilities, refcount);
60
61 list_del(&cap->list);
62
63 devm_kfree(cap->edev->dev, cap);
64}
65
66static int ethosu_capabilities_find(struct ethosu_capabilities *cap,
67 struct list_head *capabilties_list)
68{
69 struct ethosu_capabilities *cur;
70
71 list_for_each_entry(cur, capabilties_list, list) {
72 if (cur == cap)
73 return 0;
74 }
75
76 return -EINVAL;
77}
78
79static int ethosu_capability_rsp(struct ethosu_device *edev,
80 struct ethosu_core_msg_capabilities_rsp *msg)
81{
82 struct ethosu_capabilities *cap;
83 struct ethosu_uapi_device_capabilities *capabilities;
84 int ret;
85
86 cap = (struct ethosu_capabilities *)msg->user_arg;
87 ret = ethosu_capabilities_find(cap, &edev->capabilities_list);
88 if (0 != ret) {
89 dev_warn(edev->dev,
90 "Handle not found in capabilities list. handle=0x%p\n",
91 cap);
92
93 /* NOTE: do not call complete or kref_put on invalid data! */
94 return ret;
95 }
96
97 capabilities = cap->capabilities;
98
99 capabilities->hw_id.version_status = msg->version_status;
100 capabilities->hw_id.version_minor = msg->version_minor;
101 capabilities->hw_id.version_major = msg->version_major;
102 capabilities->hw_id.product_major = msg->product_major;
103 capabilities->hw_id.arch_patch_rev = msg->arch_patch_rev;
104 capabilities->hw_id.arch_minor_rev = msg->arch_minor_rev;
105 capabilities->hw_id.arch_major_rev = msg->arch_major_rev;
106 capabilities->driver_patch_rev = msg->driver_patch_rev;
107 capabilities->driver_minor_rev = msg->driver_minor_rev;
108 capabilities->driver_major_rev = msg->driver_major_rev;
109 capabilities->hw_cfg.macs_per_cc = msg->macs_per_cc;
110 capabilities->hw_cfg.cmd_stream_version = msg->cmd_stream_version;
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200111 capabilities->hw_cfg.custom_dma = msg->custom_dma;
112
113 complete(&cap->done);
114
115 kref_put(&cap->refcount, ethosu_capabilities_destroy);
116
117 return 0;
118}
119
Jonny Svärd7c24c772021-01-14 19:53:17 +0100120/* Incoming messages */
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200121static int ethosu_handle_msg(struct ethosu_device *edev)
122{
Jonny Svärd7c24c772021-01-14 19:53:17 +0100123 int ret;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200124 struct ethosu_core_msg header;
125
126 union {
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200127 struct ethosu_core_msg_err error;
128 struct ethosu_core_inference_rsp inf;
129 struct ethosu_core_msg_version version;
130 struct ethosu_core_msg_capabilities_rsp capabilities;
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100131 struct ethosu_core_network_info_rsp network_info;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200132 } data;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200133
134 /* Read message */
135 ret = ethosu_mailbox_read(&edev->mailbox, &header, &data, sizeof(data));
136 if (ret)
137 return ret;
138
139 switch (header.type) {
Jonny Svärd7c24c772021-01-14 19:53:17 +0100140 case ETHOSU_CORE_MSG_ERR:
141 if (header.length != sizeof(data.error)) {
142 dev_warn(edev->dev,
143 "Msg: Error message of incorrect size. size=%u, expected=%zu\n", header.length,
144 sizeof(data.error));
145 ret = -EBADMSG;
146 break;
147 }
148
149 data.error.msg[sizeof(data.error.msg) - 1] = '\0';
150 dev_warn(edev->dev, "Msg: Error. type=%u, msg=\"%s\"\n",
151 data.error.type, data.error.msg);
152 ret = -EBADMSG;
153 break;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200154 case ETHOSU_CORE_MSG_PING:
155 dev_info(edev->dev, "Msg: Ping\n");
Jonny Svärd7c24c772021-01-14 19:53:17 +0100156 ret = ethosu_mailbox_pong(&edev->mailbox);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200157 break;
158 case ETHOSU_CORE_MSG_PONG:
159 dev_info(edev->dev, "Msg: Pong\n");
160 break;
161 case ETHOSU_CORE_MSG_INFERENCE_RSP:
Jonny Svärd7c24c772021-01-14 19:53:17 +0100162 if (header.length != sizeof(data.inf)) {
163 dev_warn(edev->dev,
164 "Msg: Inference response of incorrect size. size=%u, expected=%zu\n", header.length,
165 sizeof(data.inf));
166 ret = -EBADMSG;
167 break;
168 }
169
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200170 dev_info(edev->dev,
Kristofer Jonssonb74492c2020-09-10 13:26:01 +0200171 "Msg: Inference response. user_arg=0x%llx, ofm_count=%u, status=%u\n",
172 data.inf.user_arg, data.inf.ofm_count,
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200173 data.inf.status);
174 ethosu_inference_rsp(edev, &data.inf);
175 break;
Jonny Svärd7c24c772021-01-14 19:53:17 +0100176 case ETHOSU_CORE_MSG_VERSION_RSP:
177 if (header.length != sizeof(data.version)) {
178 dev_warn(edev->dev,
179 "Msg: Version response of incorrect size. size=%u, expected=%zu\n", header.length,
180 sizeof(data.version));
181 ret = -EBADMSG;
182 break;
183 }
184
185 dev_info(edev->dev, "Msg: Version response v%u.%u.%u\n",
186 data.version.major, data.version.minor,
187 data.version.patch);
188
189 /* Check major and minor version match, else return error */
190 if (data.version.major != ETHOSU_CORE_MSG_VERSION_MAJOR ||
191 data.version.minor != ETHOSU_CORE_MSG_VERSION_MINOR) {
192 dev_warn(edev->dev, "Msg: Version mismatch detected! ");
193 dev_warn(edev->dev, "Local version: v%u.%u.%u\n",
194 ETHOSU_CORE_MSG_VERSION_MAJOR,
195 ETHOSU_CORE_MSG_VERSION_MINOR,
196 ETHOSU_CORE_MSG_VERSION_PATCH);
197 }
198
199 break;
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200200 case ETHOSU_CORE_MSG_CAPABILITIES_RSP:
201 if (header.length != sizeof(data.capabilities)) {
202 dev_warn(edev->dev,
203 "Msg: Capabilities response of incorrect size. size=%u, expected=%zu\n", header.length,
204 sizeof(data.capabilities));
205 ret = -EBADMSG;
206 break;
207 }
Jonny Svärd7c24c772021-01-14 19:53:17 +0100208
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200209 dev_info(edev->dev,
Davide Grohmann40e23d12021-06-17 09:59:40 +0200210 "Msg: Capabilities response ua%llx vs%hhu v%hhu.%hhu p%hhu av%hhu.%hhu.%hhu dv%hhu.%hhu.%hhu mcc%hhu csv%hhu cd%hhu\n",
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200211 data.capabilities.user_arg,
212 data.capabilities.version_status,
213 data.capabilities.version_major,
214 data.capabilities.version_minor,
215 data.capabilities.product_major,
216 data.capabilities.arch_major_rev,
217 data.capabilities.arch_minor_rev,
218 data.capabilities.arch_patch_rev,
219 data.capabilities.driver_major_rev,
220 data.capabilities.driver_minor_rev,
221 data.capabilities.driver_patch_rev,
222 data.capabilities.macs_per_cc,
223 data.capabilities.cmd_stream_version,
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200224 data.capabilities.custom_dma);
225
226 ret = ethosu_capability_rsp(edev, &data.capabilities);
227 break;
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100228 case ETHOSU_CORE_MSG_NETWORK_INFO_RSP:
229 if (header.length != sizeof(data.network_info)) {
230 dev_warn(edev->dev,
231 "Msg: Network info response of incorrect size. size=%u, expected=%zu\n", header.length,
232 sizeof(data.network_info));
233 ret = -EBADMSG;
234 break;
235 }
236
237 dev_info(edev->dev,
238 "Msg: Network info response. user_arg=0x%llx, status=%u",
239 data.network_info.user_arg,
240 data.network_info.status);
241
242 ethosu_network_info_rsp(edev, &data.network_info);
243
244 break;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200245 default:
Jonny Svärd7c24c772021-01-14 19:53:17 +0100246 /* This should not happen due to version checks */
247 dev_warn(edev->dev, "Msg: Protocol error\n");
248 ret = -EPROTO;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200249 break;
250 }
251
252 return ret;
253}
254
255static int ethosu_open(struct inode *inode,
256 struct file *file)
257{
258 struct ethosu_device *edev =
259 container_of(inode->i_cdev, struct ethosu_device, cdev);
260
261 file->private_data = edev;
262
263 dev_info(edev->dev, "Opening device node.\n");
264
265 return nonseekable_open(inode, file);
266}
267
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200268static int ethosu_send_capabilities_request(struct ethosu_device *edev,
269 void __user *udata)
270{
271 struct ethosu_uapi_device_capabilities uapi;
272 struct ethosu_capabilities *cap;
273 int ret;
274 int timeout;
275
276 cap = devm_kzalloc(edev->dev, sizeof(struct ethosu_capabilities),
277 GFP_KERNEL);
278 if (!cap)
279 return -ENOMEM;
280
281 cap->edev = edev;
282 cap->capabilities = &uapi;
283 kref_init(&cap->refcount);
284 init_completion(&cap->done);
285 list_add(&cap->list, &edev->capabilities_list);
286
287 ret = ethosu_mailbox_capabilities_request(&edev->mailbox, cap);
288 if (0 != ret)
289 goto put_kref;
290
291 /*
292 * Increase ref counter since we sent the pointer out to
293 * response handler thread. That thread is responsible to
294 * decrease the ref counter before exiting. So the memory
295 * can be freed.
296 *
297 * NOTE: if no response is received back, the memory is leaked.
298 */
299 kref_get(&cap->refcount);
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100300
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200301 /* Unlock the mutex before going to block on the condition */
302 mutex_unlock(&edev->mutex);
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100303
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200304 /* wait for response to arrive back */
305 timeout = wait_for_completion_timeout(&cap->done,
306 msecs_to_jiffies(
307 CAPABILITIES_RESP_TIMEOUT_MS));
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100308
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200309 /* take back the mutex before resuming to do anything */
310 ret = mutex_lock_interruptible(&edev->mutex);
311 if (0 != ret)
312 goto put_kref;
313
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100314 if (0 == timeout) {
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200315 dev_warn(edev->dev,
316 "Msg: Capabilities response lost - timeout\n");
317 ret = -EIO;
318 goto put_kref;
319 }
320
321 ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0;
322
323put_kref:
324 kref_put(&cap->refcount, ethosu_capabilities_destroy);
325
326 return ret;
327}
328
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200329static long ethosu_ioctl(struct file *file,
330 unsigned int cmd,
331 unsigned long arg)
332{
333 struct ethosu_device *edev = file->private_data;
334 void __user *udata = (void __user *)arg;
335 int ret = -EINVAL;
336
337 ret = mutex_lock_interruptible(&edev->mutex);
338 if (ret)
339 return ret;
340
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100341 dev_info(edev->dev, "Ioctl. cmd=0x%x, arg=0x%lx\n", cmd, arg);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200342
343 switch (cmd) {
Jonny Svärd7c24c772021-01-14 19:53:17 +0100344 case ETHOSU_IOCTL_VERSION_REQ:
345 dev_info(edev->dev, "Ioctl: Send version request\n");
346 ret = ethosu_mailbox_version_request(&edev->mailbox);
347 break;
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200348 case ETHOSU_IOCTL_CAPABILITIES_REQ:
349 dev_info(edev->dev, "Ioctl: Send capabilities request\n");
350 ret = ethosu_send_capabilities_request(edev, udata);
351 break;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200352 case ETHOSU_IOCTL_PING: {
353 dev_info(edev->dev, "Ioctl: Send ping\n");
354 ret = ethosu_mailbox_ping(&edev->mailbox);
355 break;
356 }
357 case ETHOSU_IOCTL_BUFFER_CREATE: {
358 struct ethosu_uapi_buffer_create uapi;
359
360 dev_info(edev->dev, "Ioctl: Buffer create\n");
361
362 if (copy_from_user(&uapi, udata, sizeof(uapi)))
363 break;
364
365 dev_info(edev->dev, "Ioctl: Buffer. capacity=%u\n",
366 uapi.capacity);
367
368 ret = ethosu_buffer_create(edev, uapi.capacity);
369 break;
370 }
371 case ETHOSU_IOCTL_NETWORK_CREATE: {
372 struct ethosu_uapi_network_create uapi;
373
374 if (copy_from_user(&uapi, udata, sizeof(uapi)))
375 break;
376
377 dev_info(edev->dev, "Ioctl: Network. fd=%u\n", uapi.fd);
378
379 ret = ethosu_network_create(edev, &uapi);
380 break;
381 }
382 default: {
383 dev_err(edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
384 cmd, arg);
385 break;
386 }
387 }
388
389 mutex_unlock(&edev->mutex);
390
391 return ret;
392}
393
394static void ethosu_mbox_rx(void *user_arg)
395{
396 struct ethosu_device *edev = user_arg;
397 int ret;
398
399 mutex_lock(&edev->mutex);
400
401 do {
402 ret = ethosu_handle_msg(edev);
Jonny Svärd7c24c772021-01-14 19:53:17 +0100403 if (ret && ret != -ENOMSG)
404 /* Need to start over in case of error, empty the queue
405 * by fast-forwarding read position to write position.
406 * */
407 ethosu_mailbox_reset(&edev->mailbox);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200408 } while (ret == 0);
409
410 mutex_unlock(&edev->mutex);
411}
412
413int ethosu_dev_init(struct ethosu_device *edev,
414 struct device *dev,
415 struct class *class,
Kristofer Jonsson4aec3762021-10-13 17:09:27 +0200416 dev_t devt,
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200417 struct resource *in_queue,
418 struct resource *out_queue)
419{
420 static const struct file_operations fops = {
421 .owner = THIS_MODULE,
422 .open = &ethosu_open,
423 .unlocked_ioctl = &ethosu_ioctl,
424#ifdef CONFIG_COMPAT
425 .compat_ioctl = &ethosu_ioctl,
426#endif
427 };
428 struct device *sysdev;
429 int ret;
430
431 edev->dev = dev;
432 edev->class = class;
Kristofer Jonsson4aec3762021-10-13 17:09:27 +0200433 edev->devt = devt;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200434 mutex_init(&edev->mutex);
Davide Grohmann35ce6c82021-06-01 15:03:51 +0200435 INIT_LIST_HEAD(&edev->capabilities_list);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200436 INIT_LIST_HEAD(&edev->inference_list);
Kristofer Jonsson3c6a2602022-03-10 11:17:29 +0100437 INIT_LIST_HEAD(&edev->network_info_list);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200438
439 ret = of_reserved_mem_device_init(edev->dev);
440 if (ret)
441 return ret;
442
443 dma_set_mask_and_coherent(edev->dev, DMA_BIT_MASK(DMA_ADDR_BITS));
444
445 ret = ethosu_mailbox_init(&edev->mailbox, dev, in_queue, out_queue,
446 ethosu_mbox_rx, edev);
447 if (ret)
448 goto release_reserved_mem;
449
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200450 cdev_init(&edev->cdev, &fops);
451 edev->cdev.owner = THIS_MODULE;
452
Kristofer Jonsson4aec3762021-10-13 17:09:27 +0200453 ret = cdev_add(&edev->cdev, edev->devt, 1);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200454 if (ret) {
455 dev_err(edev->dev, "Failed to add character device.\n");
Kristofer Jonsson4aec3762021-10-13 17:09:27 +0200456 goto deinit_mailbox;
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200457 }
458
459 sysdev = device_create(edev->class, NULL, edev->devt, edev,
Kristofer Jonsson4aec3762021-10-13 17:09:27 +0200460 "ethosu%d", MINOR(edev->devt));
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200461 if (IS_ERR(sysdev)) {
462 dev_err(edev->dev, "Failed to create device.\n");
463 ret = PTR_ERR(sysdev);
464 goto del_cdev;
465 }
466
467 dev_info(edev->dev,
468 "Created Arm Ethos-U device. name=%s, major=%d, minor=%d\n",
469 dev_name(sysdev), MAJOR(edev->devt), MINOR(edev->devt));
470
471 return 0;
472
473del_cdev:
474 cdev_del(&edev->cdev);
475
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200476deinit_mailbox:
477 ethosu_mailbox_deinit(&edev->mailbox);
478
479release_reserved_mem:
480 of_reserved_mem_device_release(edev->dev);
481
482 return ret;
483}
484
485void ethosu_dev_deinit(struct ethosu_device *edev)
486{
487 ethosu_mailbox_deinit(&edev->mailbox);
488 device_destroy(edev->class, edev->cdev.dev);
489 cdev_del(&edev->cdev);
Kristofer Jonsson116a6352020-08-20 17:25:23 +0200490 of_reserved_mem_device_release(edev->dev);
491
492 dev_info(edev->dev, "%s\n", __FUNCTION__);
493}