aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorKristofer Jonsson <kristofer.jonsson@arm.com>2020-08-20 17:25:23 +0200
committerKristofer Jonsson <kristofer.jonsson@arm.com>2020-08-27 13:58:01 +0200
commit116a635581f292cb4882ea1a086f842904f85c3c (patch)
tree96ed12cebd8dbf9f1dc7b8f116be7d528779a2bb /kernel
parentcd13a572fe223fe95cd58c5b55b659885fb7b4cd (diff)
downloadethos-u-linux-driver-stack-116a635581f292cb4882ea1a086f842904f85c3c.tar.gz
Initial commit20.08-rc220.08
Change-Id: I14b6becc908a0ac215769c32ee9c43db192ae6c8
Diffstat (limited to 'kernel')
-rw-r--r--kernel/.gitignore28
-rw-r--r--kernel/CMakeLists.txt45
-rw-r--r--kernel/Kbuild28
-rw-r--r--kernel/Kconfig24
-rw-r--r--kernel/ethosu_buffer.c309
-rw-r--r--kernel/ethosu_buffer.h106
-rw-r--r--kernel/ethosu_core_interface.h93
-rw-r--r--kernel/ethosu_device.c268
-rw-r--r--kernel/ethosu_device.h73
-rw-r--r--kernel/ethosu_driver.c161
-rw-r--r--kernel/ethosu_inference.c332
-rw-r--r--kernel/ethosu_inference.h110
-rw-r--r--kernel/ethosu_mailbox.c295
-rw-r--r--kernel/ethosu_mailbox.h107
-rw-r--r--kernel/ethosu_network.c201
-rw-r--r--kernel/ethosu_network.h81
-rw-r--r--kernel/uapi/ethosu.h104
17 files changed, 2365 insertions, 0 deletions
diff --git a/kernel/.gitignore b/kernel/.gitignore
new file mode 100644
index 0000000..dca5994
--- /dev/null
+++ b/kernel/.gitignore
@@ -0,0 +1,28 @@
+#
+# (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, you can access it online at
+# http://www.gnu.org/licenses/gpl-2.0.html.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+*.cmd
+*.ko
+*.mod
+*.mod.c
+*.mod.o
+*.o
+/modules.order
+/Module.symvers
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
new file mode 100644
index 0000000..705a30a
--- /dev/null
+++ b/kernel/CMakeLists.txt
@@ -0,0 +1,45 @@
+#
+# (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, you can access it online at
+# http://www.gnu.org/licenses/gpl-2.0.html.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+cmake_minimum_required(VERSION 3.0.2)
+
+# Set the project name and version
+project("ethosu_kernel" VERSION 1.0)
+
+# Make sure KDIR is set
+set(KDIR "" CACHE PATH "Path to Linux kernel sources")
+if (NOT EXISTS ${KDIR})
+ message(FATAL_ERROR "Can't build kernel module without KDIR.")
+endif()
+
+# Depend on all h and c files
+file(GLOB_RECURSE SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c" "*.h")
+
+# Build the kernel module
+add_custom_target(kernel ALL
+ COMMAND ${CMAKE_MAKE_PROGRAM} -C ${KDIR} M=${CMAKE_CURRENT_SOURCE_DIR} CONFIG_ETHOSU=m CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 modules
+ BYPRODUCTS ethosu.ko
+ DEPENDS ${SOURCES} Kbuild Kconfig
+ COMMENT "Building ethosu.ko"
+ VERBATIM)
+
+# Install the kernel object and headers
+install(FILES ethosu.ko DESTINATION "modules")
+install(FILES "uapi/ethosu.h" DESTINATION "include/uapi")
diff --git a/kernel/Kbuild b/kernel/Kbuild
new file mode 100644
index 0000000..933efee
--- /dev/null
+++ b/kernel/Kbuild
@@ -0,0 +1,28 @@
+#
+# (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, you can access it online at
+# http://www.gnu.org/licenses/gpl-2.0.html.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+obj-$(CONFIG_ETHOSU) = ethosu.o
+
+ethosu-objs := ethosu_driver.o \
+ ethosu_buffer.o \
+ ethosu_device.o \
+ ethosu_inference.o \
+ ethosu_mailbox.o \
+ ethosu_network.o
diff --git a/kernel/Kconfig b/kernel/Kconfig
new file mode 100644
index 0000000..695bd37
--- /dev/null
+++ b/kernel/Kconfig
@@ -0,0 +1,24 @@
+#
+# (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, you can access it online at
+# http://www.gnu.org/licenses/gpl-2.0.html.
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+
+config ETHOSU
+ tristate "Arm Ethos-U NPU support"
+ help
+ Arm Ethos-U NPU driver. \ No newline at end of file
diff --git a/kernel/ethosu_buffer.c b/kernel/ethosu_buffer.c
new file mode 100644
index 0000000..bcc7242
--- /dev/null
+++ b/kernel/ethosu_buffer.c
@@ -0,0 +1,309 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "ethosu_buffer.h"
+
+#include "ethosu_device.h"
+#include "uapi/ethosu.h"
+
+#include <linux/anon_inodes.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_address.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+/****************************************************************************
+ * Variables
+ ****************************************************************************/
+
+static int ethosu_buffer_release(struct inode *inode,
+ struct file *file);
+
+static int ethosu_buffer_mmap(struct file *file,
+ struct vm_area_struct *vma);
+
+static long ethosu_buffer_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+
+static const struct file_operations ethosu_buffer_fops = {
+ .release = &ethosu_buffer_release,
+ .mmap = &ethosu_buffer_mmap,
+ .unlocked_ioctl = &ethosu_buffer_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = &ethosu_buffer_ioctl,
+#endif
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+/*
+ * The 'dma-ranges' device tree property for shared dma memory does not seem
+ * to be fully supported for coherent memory. Therefor we apply the DMA range
+ * offset ourselves.
+ */
+static dma_addr_t ethosu_buffer_dma_ranges(struct device *dev,
+ dma_addr_t dma_addr)
+{
+ struct device_node *node = dev->of_node;
+ const __be32 *ranges;
+ int len;
+ int naddr;
+ int nsize;
+ int inc;
+ int i;
+
+ if (!node)
+ return dma_addr;
+
+ /* Get the #address-cells and #size-cells properties */
+ naddr = of_n_addr_cells(node);
+ nsize = of_n_size_cells(node);
+
+ /* Read the 'dma-ranges' property */
+ ranges = of_get_property(node, "dma-ranges", &len);
+ if (!ranges || len <= 0)
+ return dma_addr;
+
+ dev_dbg(dev, "ranges=%p, len=%d, naddr=%d, nsize=%d\n",
+ ranges, len, naddr, nsize);
+
+ len /= sizeof(*ranges);
+ inc = naddr + naddr + nsize;
+
+ for (i = 0; (i + inc) <= len; i += inc) {
+ dma_addr_t daddr;
+ dma_addr_t paddr;
+ dma_addr_t size;
+
+ daddr = of_read_number(&ranges[i], naddr);
+ paddr = of_read_number(&ranges[i + naddr], naddr);
+ size = of_read_number(&ranges[i + naddr + naddr], nsize);
+
+ dev_dbg(dev, "daddr=0x%llx, paddr=0x%llx, size=0x%llx\n",
+ daddr, paddr, size);
+
+ if (dma_addr >= paddr && dma_addr < (paddr + size))
+ return dma_addr + daddr - paddr;
+ }
+
+ return dma_addr;
+}
+
+static bool ethosu_buffer_verify(struct file *file)
+{
+ return file->f_op == &ethosu_buffer_fops;
+}
+
+static void ethosu_buffer_destroy(struct kref *kref)
+{
+ struct ethosu_buffer *buf =
+ container_of(kref, struct ethosu_buffer, kref);
+
+ dev_info(buf->edev->dev, "Buffer destroy. handle=0x%pK\n", buf);
+
+ dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr,
+ buf->dma_addr_orig);
+ devm_kfree(buf->edev->dev, buf);
+}
+
+static int ethosu_buffer_release(struct inode *inode,
+ struct file *file)
+{
+ struct ethosu_buffer *buf = file->private_data;
+
+ dev_info(buf->edev->dev, "Buffer release. handle=0x%pK\n", buf);
+
+ ethosu_buffer_put(buf);
+
+ return 0;
+}
+
+static int ethosu_buffer_mmap(struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct ethosu_buffer *buf = file->private_data;
+ int ret;
+
+ dev_info(buf->edev->dev, "Buffer mmap. handle=0x%pK\n", buf);
+
+ ret = dma_mmap_coherent(buf->edev->dev, vma, buf->cpu_addr,
+ buf->dma_addr_orig,
+ buf->capacity);
+
+ return ret;
+}
+
+static long ethosu_buffer_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct ethosu_buffer *buf = file->private_data;
+ void __user *udata = (void __user *)arg;
+ int ret = -EINVAL;
+
+ ret = mutex_lock_interruptible(&buf->edev->mutex);
+ if (ret)
+ return ret;
+
+ dev_info(buf->edev->dev, "Ioctl. cmd=%u, arg=%lu\n", cmd, arg);
+
+ switch (cmd) {
+ case ETHOSU_IOCTL_BUFFER_SET: {
+ struct ethosu_uapi_buffer uapi;
+
+ if (copy_from_user(&uapi, udata, sizeof(uapi)))
+ break;
+
+ dev_info(buf->edev->dev,
+ "Ioctl: Buffer set. size=%u, offset=%u\n",
+ uapi.size, uapi.offset);
+
+ ret = ethosu_buffer_resize(buf, uapi.size, uapi.offset);
+ break;
+ }
+ case ETHOSU_IOCTL_BUFFER_GET: {
+ struct ethosu_uapi_buffer uapi;
+
+ uapi.size = buf->size;
+ uapi.offset = buf->offset;
+
+ dev_info(buf->edev->dev,
+ "Ioctl: Buffer get. size=%u, offset=%u\n",
+ uapi.size, uapi.offset);
+
+ if (copy_to_user(udata, &uapi, sizeof(uapi)))
+ break;
+
+ ret = 0;
+ break;
+ }
+ default: {
+ dev_err(buf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
+ cmd, arg);
+ break;
+ }
+ }
+
+ mutex_unlock(&buf->edev->mutex);
+
+ return ret;
+}
+
+int ethosu_buffer_create(struct ethosu_device *edev,
+ size_t capacity)
+{
+ struct ethosu_buffer *buf;
+ int ret = -ENOMEM;
+
+ buf = devm_kzalloc(edev->dev, sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf->edev = edev;
+ buf->capacity = capacity;
+ buf->offset = 0;
+ buf->size = 0;
+ kref_init(&buf->kref);
+
+ buf->cpu_addr = dma_alloc_coherent(buf->edev->dev, capacity,
+ &buf->dma_addr_orig, GFP_KERNEL);
+ if (!buf->cpu_addr)
+ goto free_buf;
+
+ buf->dma_addr = ethosu_buffer_dma_ranges(buf->edev->dev,
+ buf->dma_addr_orig);
+
+ ret = anon_inode_getfd("ethosu-buffer", &ethosu_buffer_fops, buf,
+ O_RDWR | O_CLOEXEC);
+ if (ret < 0)
+ goto free_dma;
+
+ buf->file = fget(ret);
+ fput(buf->file);
+
+ dev_info(buf->edev->dev,
+ "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",
+ buf, capacity, buf->cpu_addr, buf->dma_addr,
+ buf->dma_addr_orig, virt_to_phys(buf->cpu_addr));
+
+ return ret;
+
+free_dma:
+ dma_free_coherent(buf->edev->dev, buf->capacity, buf->cpu_addr,
+ buf->dma_addr_orig);
+
+free_buf:
+ devm_kfree(buf->edev->dev, buf);
+
+ return ret;
+}
+
+struct ethosu_buffer *ethosu_buffer_get_from_fd(int fd)
+{
+ struct ethosu_buffer *buf;
+ struct file *file;
+
+ file = fget(fd);
+ if (!file)
+ return ERR_PTR(-EINVAL);
+
+ if (!ethosu_buffer_verify(file)) {
+ fput(file);
+
+ return ERR_PTR(-EINVAL);
+ }
+
+ buf = file->private_data;
+ ethosu_buffer_get(buf);
+ fput(file);
+
+ return buf;
+}
+
+void ethosu_buffer_get(struct ethosu_buffer *buf)
+{
+ kref_get(&buf->kref);
+}
+
+void ethosu_buffer_put(struct ethosu_buffer *buf)
+{
+ kref_put(&buf->kref, ethosu_buffer_destroy);
+}
+
+int ethosu_buffer_resize(struct ethosu_buffer *buf,
+ size_t size,
+ size_t offset)
+{
+ if ((size + offset) > buf->capacity)
+ return -EINVAL;
+
+ buf->size = size;
+ buf->offset = offset;
+
+ return 0;
+}
diff --git a/kernel/ethosu_buffer.h b/kernel/ethosu_buffer.h
new file mode 100644
index 0000000..14f26c2
--- /dev/null
+++ b/kernel/ethosu_buffer.h
@@ -0,0 +1,106 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef ETHOSU_BUFFER_H
+#define ETHOSU_BUFFER_H
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include <linux/kref.h>
+#include <linux/types.h>
+
+/****************************************************************************
+ * Types
+ ****************************************************************************/
+
+struct ethosu_device;
+struct device;
+
+/**
+ * struct ethosu_buffer - Buffer
+ * @dev: Device
+ * @file: File
+ * @kref: Reference counting
+ * @capacity: Maximum capacity of the buffer
+ * @offset: Offset to first byte of buffer
+ * @size: Size of the data in the buffer
+ * @cpu_addr: Kernel mapped address
+ * @dma_addr: DMA address
+ * @dma_addr_orig: Original DMA address before range mapping
+ *
+ * 'offset + size' must not be larger than 'capacity'.
+ */
+struct ethosu_buffer {
+ struct ethosu_device *edev;
+ struct file *file;
+ struct kref kref;
+ size_t capacity;
+ size_t offset;
+ size_t size;
+ void *cpu_addr;
+ dma_addr_t dma_addr;
+ dma_addr_t dma_addr_orig;
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+/**
+ * ethosu_buffer_create() - Create buffer
+ *
+ * This function must be called in the context of a user space process.
+ *
+ * Return: fd on success, else error code.
+ */
+int ethosu_buffer_create(struct ethosu_device *edev,
+ size_t capacity);
+
+/**
+ * ethosu_buffer_get_from_fd() - Get buffer handle from fd
+ *
+ * This function must be called from a user space context.
+ *
+ * Return: Pointer on success, else ERR_PTR.
+ */
+struct ethosu_buffer *ethosu_buffer_get_from_fd(int fd);
+
+/**
+ * ethosu_buffer_get() - Put buffer
+ */
+void ethosu_buffer_get(struct ethosu_buffer *buf);
+
+/**
+ * ethosu_buffer_put() - Put buffer
+ */
+void ethosu_buffer_put(struct ethosu_buffer *buf);
+
+/**
+ * ethosu_buffer_resize() - Resize and validate buffer
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_buffer_resize(struct ethosu_buffer *buf,
+ size_t size,
+ size_t offset);
+
+#endif /* ETHOSU_BUFFER_H */
diff --git a/kernel/ethosu_core_interface.h b/kernel/ethosu_core_interface.h
new file mode 100644
index 0000000..827ce4f
--- /dev/null
+++ b/kernel/ethosu_core_interface.h
@@ -0,0 +1,93 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef ETHOSU_CORE_INTERFACE_H
+#define ETHOSU_CORE_INTERFACE_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+/**
+ * enum ethosu_core_msg_type - Message types
+ *
+ * Types for the messages sent between the host and the core subsystem.
+ */
+enum ethosu_core_msg_type {
+ ETHOSU_CORE_MSG_PING = 1,
+ ETHOSU_CORE_MSG_PONG,
+ ETHOSU_CORE_MSG_INFERENCE_REQ,
+ ETHOSU_CORE_MSG_INFERENCE_RSP,
+ ETHOSU_CORE_MSG_MAX
+};
+
+/**
+ * struct ethosu_core_msg - Message header
+ */
+struct ethosu_core_msg {
+ uint32_t type;
+ uint32_t length;
+};
+
+/**
+ * struct ethosu_core_queue_header - Message queue header
+ */
+struct ethosu_core_queue_header {
+ uint32_t size;
+ uint32_t read;
+ uint32_t write;
+};
+
+/**
+ * struct ethosu_core_queue - Message queue
+ *
+ * Dynamically sized message queue.
+ */
+struct ethosu_core_queue {
+ struct ethosu_core_queue_header header;
+ uint8_t data[];
+};
+
+enum ethosu_core_status {
+ ETHOSU_CORE_STATUS_OK,
+ ETHOSU_CORE_STATUS_ERROR
+};
+
+struct ethosu_core_buffer {
+ uint32_t ptr;
+ uint32_t size;
+};
+
+struct ethosu_core_inference_req {
+ uint64_t user_arg;
+ struct ethosu_core_buffer ifm;
+ struct ethosu_core_buffer ofm;
+ struct ethosu_core_buffer network;
+};
+
+struct ethosu_core_inference_rsp {
+ uint64_t user_arg;
+ uint32_t ofm_size;
+ uint32_t status;
+};
+
+#endif
diff --git a/kernel/ethosu_device.c b/kernel/ethosu_device.c
new file mode 100644
index 0000000..5cdea2c
--- /dev/null
+++ b/kernel/ethosu_device.c
@@ -0,0 +1,268 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "ethosu_device.h"
+
+#include "ethosu_buffer.h"
+#include "ethosu_core_interface.h"
+#include "ethosu_inference.h"
+#include "ethosu_network.h"
+#include "uapi/ethosu.h"
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/uaccess.h>
+
+/****************************************************************************
+ * Defines
+ ****************************************************************************/
+
+#define MINOR_VERSION 0 /* Minor version starts at 0 */
+#define MINOR_COUNT 1 /* Allocate 1 minor version */
+#define DMA_ADDR_BITS 32 /* Number of address bits */
+
+/****************************************************************************
+ * Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+static int ethosu_handle_msg(struct ethosu_device *edev)
+{
+ struct ethosu_core_msg header;
+
+ union {
+ struct ethosu_core_inference_rsp inf;
+ } data;
+ int ret;
+
+ /* Read message */
+ ret = ethosu_mailbox_read(&edev->mailbox, &header, &data, sizeof(data));
+ if (ret)
+ return ret;
+
+ switch (header.type) {
+ case ETHOSU_CORE_MSG_PING:
+ dev_info(edev->dev, "Msg: Ping\n");
+ ret = ethosu_mailbox_ping(&edev->mailbox);
+ break;
+ case ETHOSU_CORE_MSG_PONG:
+ dev_info(edev->dev, "Msg: Pong\n");
+ break;
+ case ETHOSU_CORE_MSG_INFERENCE_RSP:
+ dev_info(edev->dev,
+ "Msg: Inference response. user_arg=0x%llx, ofm_size=%u, status=%u\n",
+ data.inf.user_arg, data.inf.ofm_size,
+ data.inf.status);
+ ethosu_inference_rsp(edev, &data.inf);
+ break;
+ default:
+ dev_warn(edev->dev,
+ "Msg: Unsupported msg type. type=%u, length=%u",
+ header.type, header.length);
+ break;
+ }
+
+ return ret;
+}
+
+static int ethosu_open(struct inode *inode,
+ struct file *file)
+{
+ struct ethosu_device *edev =
+ container_of(inode->i_cdev, struct ethosu_device, cdev);
+
+ file->private_data = edev;
+
+ dev_info(edev->dev, "Opening device node.\n");
+
+ return nonseekable_open(inode, file);
+}
+
+static long ethosu_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct ethosu_device *edev = file->private_data;
+ void __user *udata = (void __user *)arg;
+ int ret = -EINVAL;
+
+ ret = mutex_lock_interruptible(&edev->mutex);
+ if (ret)
+ return ret;
+
+ dev_info(edev->dev, "Ioctl. cmd=%u, arg=%lu\n", cmd, arg);
+
+ switch (cmd) {
+ case ETHOSU_IOCTL_PING: {
+ dev_info(edev->dev, "Ioctl: Send ping\n");
+ ret = ethosu_mailbox_ping(&edev->mailbox);
+ break;
+ }
+ case ETHOSU_IOCTL_BUFFER_CREATE: {
+ struct ethosu_uapi_buffer_create uapi;
+
+ dev_info(edev->dev, "Ioctl: Buffer create\n");
+
+ if (copy_from_user(&uapi, udata, sizeof(uapi)))
+ break;
+
+ dev_info(edev->dev, "Ioctl: Buffer. capacity=%u\n",
+ uapi.capacity);
+
+ ret = ethosu_buffer_create(edev, uapi.capacity);
+ break;
+ }
+ case ETHOSU_IOCTL_NETWORK_CREATE: {
+ struct ethosu_uapi_network_create uapi;
+
+ if (copy_from_user(&uapi, udata, sizeof(uapi)))
+ break;
+
+ dev_info(edev->dev, "Ioctl: Network. fd=%u\n", uapi.fd);
+
+ ret = ethosu_network_create(edev, &uapi);
+ break;
+ }
+ default: {
+ dev_err(edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
+ cmd, arg);
+ break;
+ }
+ }
+
+ mutex_unlock(&edev->mutex);
+
+ return ret;
+}
+
+static void ethosu_mbox_rx(void *user_arg)
+{
+ struct ethosu_device *edev = user_arg;
+ int ret;
+
+ mutex_lock(&edev->mutex);
+
+ do {
+ ret = ethosu_handle_msg(edev);
+ } while (ret == 0);
+
+ mutex_unlock(&edev->mutex);
+}
+
+int ethosu_dev_init(struct ethosu_device *edev,
+ struct device *dev,
+ struct class *class,
+ struct resource *in_queue,
+ struct resource *out_queue)
+{
+ static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .open = &ethosu_open,
+ .unlocked_ioctl = &ethosu_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = &ethosu_ioctl,
+#endif
+ };
+ struct device *sysdev;
+ int ret;
+
+ edev->dev = dev;
+ edev->class = class;
+ mutex_init(&edev->mutex);
+ INIT_LIST_HEAD(&edev->inference_list);
+
+ ret = of_reserved_mem_device_init(edev->dev);
+ if (ret)
+ return ret;
+
+ dma_set_mask_and_coherent(edev->dev, DMA_BIT_MASK(DMA_ADDR_BITS));
+
+ ret = ethosu_mailbox_init(&edev->mailbox, dev, in_queue, out_queue,
+ ethosu_mbox_rx, edev);
+ if (ret)
+ goto release_reserved_mem;
+
+ ret = alloc_chrdev_region(&edev->devt, MINOR_VERSION, MINOR_COUNT,
+ "ethosu");
+ if (ret) {
+ dev_err(edev->dev, "Failed to allocate chrdev region.\n");
+ goto deinit_mailbox;
+ }
+
+ cdev_init(&edev->cdev, &fops);
+ edev->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&edev->cdev, edev->devt, MINOR_COUNT);
+ if (ret) {
+ dev_err(edev->dev, "Failed to add character device.\n");
+ goto region_unregister;
+ }
+
+ sysdev = device_create(edev->class, NULL, edev->devt, edev,
+ "ethosu%d", MAJOR(edev->devt));
+ if (IS_ERR(sysdev)) {
+ dev_err(edev->dev, "Failed to create device.\n");
+ ret = PTR_ERR(sysdev);
+ goto del_cdev;
+ }
+
+ dev_info(edev->dev,
+ "Created Arm Ethos-U device. name=%s, major=%d, minor=%d\n",
+ dev_name(sysdev), MAJOR(edev->devt), MINOR(edev->devt));
+
+ return 0;
+
+del_cdev:
+ cdev_del(&edev->cdev);
+
+region_unregister:
+ unregister_chrdev_region(edev->devt, 1);
+
+deinit_mailbox:
+ ethosu_mailbox_deinit(&edev->mailbox);
+
+release_reserved_mem:
+ of_reserved_mem_device_release(edev->dev);
+
+ return ret;
+}
+
+void ethosu_dev_deinit(struct ethosu_device *edev)
+{
+ ethosu_mailbox_deinit(&edev->mailbox);
+ device_destroy(edev->class, edev->cdev.dev);
+ cdev_del(&edev->cdev);
+ unregister_chrdev_region(edev->devt, MINOR_COUNT);
+ of_reserved_mem_device_release(edev->dev);
+
+ dev_info(edev->dev, "%s\n", __FUNCTION__);
+}
diff --git a/kernel/ethosu_device.h b/kernel/ethosu_device.h
new file mode 100644
index 0000000..4e4f59d
--- /dev/null
+++ b/kernel/ethosu_device.h
@@ -0,0 +1,73 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef ETHOSU_DEVICE_H
+#define ETHOSU_DEVICE_H
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "ethosu_mailbox.h"
+
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+/****************************************************************************
+ * Types
+ ****************************************************************************/
+
+/**
+ * struct ethosu_device - Device structure
+ */
+struct ethosu_device {
+ struct device *dev;
+ struct cdev cdev;
+ struct class *class;
+ dev_t devt;
+ struct mutex mutex;
+ struct ethosu_mailbox mailbox;
+ struct list_head inference_list;
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+/**
+ * ethosu_dev_init() - Initialize the device
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_dev_init(struct ethosu_device *edev,
+ struct device *dev,
+ struct class *class,
+ struct resource *in_queue,
+ struct resource *out_queue);
+
+/**
+ * ethosu_dev_deinit() - Initialize the device
+ */
+void ethosu_dev_deinit(struct ethosu_device *edev);
+
+#endif /* ETHOSU_DEVICE_H */
diff --git a/kernel/ethosu_driver.c b/kernel/ethosu_driver.c
new file mode 100644
index 0000000..3fc752b
--- /dev/null
+++ b/kernel/ethosu_driver.c
@@ -0,0 +1,161 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "ethosu_device.h"
+
+/****************************************************************************
+ * Defines
+ ****************************************************************************/
+
+#define ETHOSU_DRIVER_VERSION "1.0"
+#define ETHOSU_DRIVER_NAME "ethosu"
+
+/****************************************************************************
+ * Variables
+ ****************************************************************************/
+
+struct class *ethosu_class;
+
+/****************************************************************************
+ * Arm Ethos-U
+ ****************************************************************************/
+
+static int ethosu_pdev_probe(struct platform_device *pdev)
+{
+ struct ethosu_device *edev;
+ struct resource *in_queue_res;
+ struct resource *out_queue_res;
+ int ret;
+
+ dev_info(&pdev->dev, "Probe\n");
+
+ /* Get path to TCM memory */
+ in_queue_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "in_queue");
+ if (IS_ERR(in_queue_res)) {
+ dev_err(&pdev->dev, "Failed to get in_queue resource.\n");
+
+ return PTR_ERR(in_queue_res);
+ }
+
+ out_queue_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "out_queue");
+ if (IS_ERR(out_queue_res)) {
+ dev_err(&pdev->dev, "Failed to get out_queue resource.\n");
+
+ return PTR_ERR(out_queue_res);
+ }
+
+ /* Allocate memory for Arm Ethos-U device */
+ edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL);
+ if (!edev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, edev);
+
+ /* Initialize device */
+ ret = ethosu_dev_init(edev, &pdev->dev, ethosu_class, in_queue_res,
+ out_queue_res);
+ if (ret)
+ goto free_dev;
+
+ return 0;
+
+free_dev:
+ devm_kfree(&pdev->dev, edev);
+
+ return ret;
+}
+
+static int ethosu_pdev_remove(struct platform_device *pdev)
+{
+ struct ethosu_device *edev = platform_get_drvdata(pdev);
+
+ dev_info(&pdev->dev, "Remove\n");
+
+ ethosu_dev_deinit(edev);
+
+ return 0;
+}
+
+static const struct of_device_id ethosu_pdev_match[] = {
+ { .compatible = "arm,ethosu" },
+ { /* Sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, ethosu_pdev_match);
+
+static struct platform_driver ethosu_pdev_driver = {
+ .probe = &ethosu_pdev_probe,
+ .remove = &ethosu_pdev_remove,
+ .driver = {
+ .name = ETHOSU_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ethosu_pdev_match),
+ },
+};
+
+/****************************************************************************
+ * Module init and exit
+ ****************************************************************************/
+
+static int __init ethosu_init(void)
+{
+ int ret;
+
+ ethosu_class = class_create(THIS_MODULE, ETHOSU_DRIVER_NAME);
+ if (IS_ERR(ethosu_class)) {
+ printk("Failed to create class '%s'.\n", ETHOSU_DRIVER_NAME);
+
+ return PTR_ERR(ethosu_class);
+ }
+
+ ret = platform_driver_register(&ethosu_pdev_driver);
+ if (ret) {
+ printk("Failed to register Arm Ethos-U platform driver.\n");
+ goto destroy_class;
+ }
+
+ return 0;
+
+destroy_class:
+ class_destroy(ethosu_class);
+
+ return ret;
+}
+
+static void __exit ethosu_exit(void)
+{
+ platform_driver_unregister(&ethosu_pdev_driver);
+ class_destroy(ethosu_class);
+}
+
+module_init(ethosu_init)
+module_exit(ethosu_exit)
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Arm Ltd");
+MODULE_DESCRIPTION("Arm Ethos-U NPU Driver");
+MODULE_VERSION(ETHOSU_DRIVER_VERSION);
diff --git a/kernel/ethosu_inference.c b/kernel/ethosu_inference.c
new file mode 100644
index 0000000..8efc22d
--- /dev/null
+++ b/kernel/ethosu_inference.c
@@ -0,0 +1,332 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "ethosu_inference.h"
+
+#include "ethosu_buffer.h"
+#include "ethosu_core_interface.h"
+#include "ethosu_device.h"
+#include "ethosu_network.h"
+#include "uapi/ethosu.h"
+
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+
+/****************************************************************************
+ * Variables
+ ****************************************************************************/
+
+static int ethosu_inference_release(struct inode *inode,
+ struct file *file);
+
+static unsigned int ethosu_inference_poll(struct file *file,
+ poll_table *wait);
+
+static long ethosu_inference_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+
+static const struct file_operations ethosu_inference_fops = {
+ .release = &ethosu_inference_release,
+ .poll = &ethosu_inference_poll,
+ .unlocked_ioctl = &ethosu_inference_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = &ethosu_inference_ioctl,
+#endif
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+static const char *status_to_string(const enum ethosu_uapi_status status)
+{
+ switch (status) {
+ case ETHOSU_UAPI_STATUS_OK: {
+ return "Ok";
+ }
+ case ETHOSU_UAPI_STATUS_ERROR: {
+ return "Error";
+ }
+ default: {
+ return "Unknown";
+ }
+ }
+}
+
+static int ethosu_inference_send(struct ethosu_inference *inf)
+{
+ int ret;
+
+ if (inf->pending)
+ return -EINVAL;
+
+ inf->status = ETHOSU_UAPI_STATUS_ERROR;
+
+ ret = ethosu_mailbox_inference(&inf->edev->mailbox, inf, inf->ifm,
+ inf->ofm, inf->net->buf);
+ if (ret)
+ return ret;
+
+ inf->pending = true;
+
+ ethosu_inference_get(inf);
+
+ return 0;
+}
+
+static int ethosu_inference_find(struct ethosu_inference *inf,
+ struct list_head *inference_list)
+{
+ struct ethosu_inference *cur;
+
+ list_for_each_entry(cur, inference_list, list) {
+ if (cur == inf)
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static bool ethosu_inference_verify(struct file *file)
+{
+ return file->f_op == &ethosu_inference_fops;
+}
+
+static void ethosu_inference_kref_destroy(struct kref *kref)
+{
+ struct ethosu_inference *inf =
+ container_of(kref, struct ethosu_inference, kref);
+
+ dev_info(inf->edev->dev,
+ "Inference destroy. handle=0x%pK, status=%d\n",
+ inf, inf->status);
+
+ list_del(&inf->list);
+ ethosu_buffer_put(inf->ifm);
+ ethosu_buffer_put(inf->ofm);
+ ethosu_network_put(inf->net);
+ devm_kfree(inf->edev->dev, inf);
+}
+
+static int ethosu_inference_release(struct inode *inode,
+ struct file *file)
+{
+ struct ethosu_inference *inf = file->private_data;
+
+ dev_info(inf->edev->dev,
+ "Inference release. handle=0x%pK, status=%d\n",
+ inf, inf->status);
+
+ ethosu_inference_put(inf);
+
+ return 0;
+}
+
+static unsigned int ethosu_inference_poll(struct file *file,
+ poll_table *wait)
+{
+ struct ethosu_inference *inf = file->private_data;
+ int ret = 0;
+
+ poll_wait(file, &inf->waitq, wait);
+
+ if (!inf->pending)
+ ret |= POLLIN;
+
+ return ret;
+}
+
+static long ethosu_inference_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct ethosu_inference *inf = file->private_data;
+ int ret = -EINVAL;
+
+ ret = mutex_lock_interruptible(&inf->edev->mutex);
+ if (ret)
+ return ret;
+
+ dev_info(inf->edev->dev, "Ioctl: cmd=%u, arg=%lu\n", cmd, arg);
+
+ switch (cmd) {
+ case ETHOSU_IOCTL_INFERENCE_STATUS: {
+ ret = inf->status;
+
+ dev_info(inf->edev->dev,
+ "Ioctl: Inference status. status=%s (%d)\n",
+ status_to_string(ret), ret);
+ break;
+ }
+ default: {
+ dev_err(inf->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
+ cmd, arg);
+ break;
+ }
+ }
+
+ mutex_unlock(&inf->edev->mutex);
+
+ return ret;
+}
+
+int ethosu_inference_create(struct ethosu_device *edev,
+ struct ethosu_network *net,
+ struct ethosu_uapi_inference_create *uapi)
+{
+ struct ethosu_inference *inf;
+ int fd;
+ int ret = -ENOMEM;
+
+ inf = devm_kzalloc(edev->dev, sizeof(*inf), GFP_KERNEL);
+ if (!inf)
+ return -ENOMEM;
+
+ inf->edev = edev;
+ inf->net = net;
+ inf->pending = false;
+ inf->status = ETHOSU_UAPI_STATUS_ERROR;
+ kref_init(&inf->kref);
+ init_waitqueue_head(&inf->waitq);
+
+ /* Get pointer to IFM buffer */
+ inf->ifm = ethosu_buffer_get_from_fd(uapi->ifm_fd);
+ if (IS_ERR(inf->ifm)) {
+ ret = PTR_ERR(inf->ifm);
+ goto free_inf;
+ }
+
+ /* Get pointer to OFM buffer */
+ inf->ofm = ethosu_buffer_get_from_fd(uapi->ofm_fd);
+ if (IS_ERR(inf->ofm)) {
+ ret = PTR_ERR(inf->ofm);
+ goto put_ifm;
+ }
+
+ /* Increment network reference count */
+ ethosu_network_get(net);
+
+ /* Create file descriptor */
+ ret = fd = anon_inode_getfd("ethosu-inference", &ethosu_inference_fops,
+ inf, O_RDWR | O_CLOEXEC);
+ if (ret < 0)
+ goto put_net;
+
+ /* Store pointer to file structure */
+ inf->file = fget(ret);
+ fput(inf->file);
+
+ /* Add inference to inference list */
+ list_add(&inf->list, &edev->inference_list);
+
+ /* Send inference request to Arm Ethos-U subsystem */
+ (void)ethosu_inference_send(inf);
+
+ dev_info(edev->dev, "Inference create. handle=0x%pK, fd=%d",
+ inf, fd);
+
+ return fd;
+
+put_net:
+ ethosu_network_put(inf->net);
+ ethosu_buffer_put(inf->ofm);
+
+put_ifm:
+ ethosu_buffer_put(inf->ifm);
+
+free_inf:
+ devm_kfree(edev->dev, inf);
+
+ return ret;
+}
+
+struct ethosu_inference *ethosu_inference_get_from_fd(int fd)
+{
+ struct ethosu_inference *inf;
+ struct file *file;
+
+ file = fget(fd);
+ if (!file)
+ return ERR_PTR(-EINVAL);
+
+ if (!ethosu_inference_verify(file)) {
+ fput(file);
+
+ return ERR_PTR(-EINVAL);
+ }
+
+ inf = file->private_data;
+ ethosu_inference_get(inf);
+ fput(file);
+
+ return inf;
+}
+
+void ethosu_inference_get(struct ethosu_inference *inf)
+{
+ kref_get(&inf->kref);
+}
+
+void ethosu_inference_put(struct ethosu_inference *inf)
+{
+ kref_put(&inf->kref, &ethosu_inference_kref_destroy);
+}
+
+void ethosu_inference_rsp(struct ethosu_device *edev,
+ struct ethosu_core_inference_rsp *rsp)
+{
+ struct ethosu_inference *inf =
+ (struct ethosu_inference *)rsp->user_arg;
+ int ret;
+
+ ret = ethosu_inference_find(inf, &edev->inference_list);
+ if (ret) {
+ dev_warn(edev->dev,
+ "Handle not found in inference list. handle=0x%p\n",
+ rsp);
+
+ return;
+ }
+
+ inf->pending = false;
+
+ if (rsp->status == ETHOSU_CORE_STATUS_OK) {
+ inf->status = ETHOSU_UAPI_STATUS_OK;
+
+ ret = ethosu_buffer_resize(inf->ofm,
+ inf->ofm->size + rsp->ofm_size,
+ inf->ofm->offset);
+ if (ret)
+ inf->status = ETHOSU_UAPI_STATUS_ERROR;
+ } else {
+ inf->status = ETHOSU_UAPI_STATUS_ERROR;
+ }
+
+ wake_up_interruptible(&inf->waitq);
+
+ ethosu_inference_put(inf);
+}
diff --git a/kernel/ethosu_inference.h b/kernel/ethosu_inference.h
new file mode 100644
index 0000000..b42f5ca
--- /dev/null
+++ b/kernel/ethosu_inference.h
@@ -0,0 +1,110 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef ETHOSU_INFERENCE_H
+#define ETHOSU_INFERENCE_H
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "uapi/ethosu.h"
+
+#include <linux/kref.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+/****************************************************************************
+ * Types
+ ****************************************************************************/
+
+struct ethosu_buffer;
+struct ethosu_core_inference_rsp;
+struct ethosu_device;
+struct ethosu_network;
+struct ethosu_uapi_inference_create;
+struct file;
+
+/**
+ * struct ethosu_inference - Inference struct
+ * @edev: Arm Ethos-U device
+ * @file: File handle
+ * @kref: Reference counter
+ * @waitq: Wait queue
+ * @ifm: Pointer to IFM buffer
+ * @ofm: Pointer to OFM buffer
+ * @net: Pointer to network
+ * @pending: Pending response from the firmware
+ * @status: Inference status
+ */
+struct ethosu_inference {
+ struct ethosu_device *edev;
+ struct file *file;
+ struct kref kref;
+ wait_queue_head_t waitq;
+ struct ethosu_buffer *ifm;
+ struct ethosu_buffer *ofm;
+ struct ethosu_network *net;
+ bool pending;
+ enum ethosu_uapi_status status;
+ struct list_head list;
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+/**
+ * ethosu_inference_create() - Create inference
+ *
+ * This function must be called in the context of a user space process.
+ *
+ * Return: fd on success, else error code.
+ */
+int ethosu_inference_create(struct ethosu_device *edev,
+ struct ethosu_network *net,
+ struct ethosu_uapi_inference_create *uapi);
+
+/**
+ * ethosu_inference_get_from_fd() - Get inference handle from fd
+ *
+ * This function must be called from a user space context.
+ *
+ * Return: Pointer on success, else ERR_PTR.
+ */
+struct ethosu_inference *ethosu_inference_get_from_fd(int fd);
+
+/**
+ * ethosu_inference_get() - Get inference
+ */
+void ethosu_inference_get(struct ethosu_inference *inf);
+
+/**
+ * ethosu_inference_put() - Put inference
+ */
+void ethosu_inference_put(struct ethosu_inference *inf);
+
+/**
+ * ethosu_inference_rsp() - Handle inference response
+ */
+void ethosu_inference_rsp(struct ethosu_device *edev,
+ struct ethosu_core_inference_rsp *rsp);
+
+#endif /* ETHOSU_INFERENCE_H */
diff --git a/kernel/ethosu_mailbox.c b/kernel/ethosu_mailbox.c
new file mode 100644
index 0000000..23a356e
--- /dev/null
+++ b/kernel/ethosu_mailbox.c
@@ -0,0 +1,295 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "ethosu_mailbox.h"
+
+#include "ethosu_buffer.h"
+#include "ethosu_core_interface.h"
+#include "ethosu_device.h"
+
+#include <linux/resource.h>
+#include <linux/uio.h>
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+static void ethosu_core_set_size(struct ethosu_buffer *buf,
+ struct ethosu_core_buffer *cbuf)
+{
+ cbuf->ptr = (uint32_t)buf->dma_addr + buf->offset;
+ cbuf->size = (uint32_t)buf->size;
+}
+
+static void ethosu_core_set_capacity(struct ethosu_buffer *buf,
+ struct ethosu_core_buffer *cbuf)
+{
+ cbuf->ptr = (uint32_t)buf->dma_addr + buf->offset + buf->size;
+ cbuf->size = (uint32_t)buf->capacity - buf->offset - buf->size;
+}
+
+static size_t ethosu_queue_available(struct ethosu_core_queue *queue)
+{
+ size_t size = queue->header.write - queue->header.read;
+
+ if (queue->header.read > queue->header.write)
+ size += queue->header.size;
+
+ return size;
+}
+
+static size_t ethosu_queue_capacity(struct ethosu_core_queue *queue)
+{
+ return queue->header.size - ethosu_queue_available(queue);
+}
+
+static int ethosu_queue_write(struct ethosu_mailbox *mbox,
+ const struct kvec *vec,
+ size_t length)
+{
+ struct ethosu_core_queue *queue = mbox->in_queue;
+ uint8_t *dst = &queue->data[0];
+ uint32_t wpos = queue->header.write;
+ size_t total_size;
+ size_t i;
+ int ret;
+
+ for (i = 0, total_size = 0; i < length; i++)
+ total_size += vec[i].iov_len;
+
+ if (total_size > ethosu_queue_capacity(queue))
+ return -EINVAL;
+
+ for (i = 0; i < length; i++) {
+ const uint8_t *src = vec[i].iov_base;
+ const uint8_t *end = src + vec[i].iov_len;
+
+ while (src < end) {
+ dst[wpos] = *src++;
+ wpos = (wpos + 1) % queue->header.size;
+ }
+ }
+
+ queue->header.write = wpos;
+
+ ret = mbox_send_message(mbox->tx, queue);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ethosu_queue_write_msg(struct ethosu_mailbox *mbox,
+ uint32_t type,
+ void *data,
+ size_t length)
+{
+ struct ethosu_core_msg msg = { .type = type, .length = length };
+ const struct kvec vec[2] = {
+ { &msg, sizeof(msg) },
+ { data, length }
+ };
+
+ return ethosu_queue_write(mbox, vec, 2);
+}
+
+static int ethosu_queue_read(struct ethosu_mailbox *mbox,
+ void *data,
+ size_t length)
+{
+ struct ethosu_core_queue *queue = mbox->out_queue;
+ uint8_t *src = &queue->data[0];
+ uint8_t *dst = (uint8_t *)data;
+ const uint8_t *end = dst + length;
+ uint32_t rpos = queue->header.read;
+
+ if (length > ethosu_queue_available(queue))
+ return -ENOMSG;
+
+ while (dst < end) {
+ *dst++ = src[rpos];
+ rpos = (rpos + 1) % queue->header.size;
+ }
+
+ queue->header.read = rpos;
+
+ return 0;
+}
+
+int ethosu_mailbox_read(struct ethosu_mailbox *mbox,
+ struct ethosu_core_msg *header,
+ void *data,
+ size_t length)
+{
+ int ret;
+
+ /* Read message header */
+ ret = ethosu_queue_read(mbox, header, sizeof(*header));
+ if (ret)
+ return ret;
+
+ dev_info(mbox->dev, "mbox: Read msg header. type=%u, length=%u",
+ header->type, header->length);
+
+ /* Check that payload is not larger than allocated buffer */
+ if (header->length > length)
+ return -ENOMEM;
+
+ /* Ready payload data */
+ ret = ethosu_queue_read(mbox, data, header->length);
+ if (ret)
+ return -EBADMSG;
+
+ return 0;
+}
+
+int ethosu_mailbox_ping(struct ethosu_mailbox *mbox)
+{
+ return ethosu_queue_write_msg(mbox, ETHOSU_CORE_MSG_PING, NULL, 0);
+}
+
+int ethosu_mailbox_inference(struct ethosu_mailbox *mbox,
+ void *user_arg,
+ struct ethosu_buffer *ifm,
+ struct ethosu_buffer *ofm,
+ struct ethosu_buffer *network)
+{
+ struct ethosu_core_inference_req inf;
+
+ inf.user_arg = (ptrdiff_t)user_arg;
+ ethosu_core_set_size(ifm, &inf.ifm);
+ ethosu_core_set_capacity(ofm, &inf.ofm);
+ ethosu_core_set_size(network, &inf.network);
+
+ return ethosu_queue_write_msg(mbox, ETHOSU_CORE_MSG_INFERENCE_REQ,
+ &inf, sizeof(inf));
+}
+
+static void ethosu_mailbox_rx_work(struct work_struct *work)
+{
+ struct ethosu_mailbox *mbox = container_of(work, typeof(*mbox), work);
+
+ mbox->callback(mbox->user_arg);
+}
+
+static void ethosu_mailbox_rx_callback(struct mbox_client *client,
+ void *message)
+{
+ struct ethosu_mailbox *mbox =
+ container_of(client, typeof(*mbox), client);
+
+ dev_info(mbox->dev, "mbox: Received message.\n");
+
+ queue_work(mbox->wq, &mbox->work);
+}
+
+static void ethosu_mailbox_tx_done(struct mbox_client *client,
+ void *message,
+ int r)
+{
+ if (r)
+ dev_warn(client->dev, "mbox: Failed sending message (%d)\n", r);
+ else
+ dev_info(client->dev, "mbox: Message sent\n");
+}
+
+int ethosu_mailbox_init(struct ethosu_mailbox *mbox,
+ struct device *dev,
+ struct resource *in_queue,
+ struct resource *out_queue,
+ ethosu_mailbox_cb callback,
+ void *user_arg)
+{
+ int ret;
+
+ mbox->dev = dev;
+ mbox->callback = callback;
+ mbox->user_arg = user_arg;
+
+ mbox->client.dev = dev;
+ mbox->client.rx_callback = ethosu_mailbox_rx_callback;
+ mbox->client.tx_prepare = NULL; /* preparation of data is handled
+ * through the
+ * queue functions */
+ mbox->client.tx_done = ethosu_mailbox_tx_done;
+ mbox->client.tx_block = true;
+ mbox->client.knows_txdone = false;
+ mbox->client.tx_tout = 500;
+
+ mbox->in_queue = devm_ioremap_resource(mbox->dev, in_queue);
+ if (IS_ERR(mbox->in_queue))
+ return PTR_ERR(mbox->in_queue);
+
+ mbox->out_queue = devm_ioremap_resource(mbox->dev, out_queue);
+ if (IS_ERR(mbox->out_queue)) {
+ ret = PTR_ERR(mbox->out_queue);
+ goto unmap_in_queue;
+ }
+
+ mbox->wq = create_singlethread_workqueue("ethosu_workqueue");
+ if (!mbox->wq) {
+ dev_err(mbox->dev, "Failed to create work queue\n");
+ ret = -EINVAL;
+ goto unmap_out_queue;
+ }
+
+ INIT_WORK(&mbox->work, ethosu_mailbox_rx_work);
+
+ mbox->tx = mbox_request_channel_byname(&mbox->client, "tx");
+ if (IS_ERR(mbox->tx)) {
+ dev_warn(mbox->dev, "mbox: Failed to request tx channel\n");
+ ret = PTR_ERR(mbox->tx);
+ goto workqueue_destroy;
+ }
+
+ mbox->rx = mbox_request_channel_byname(&mbox->client, "rx");
+ if (IS_ERR(mbox->rx)) {
+ dev_info(dev, "mbox: Using same channel for RX and TX\n");
+ mbox->rx = mbox->tx;
+ }
+
+ return 0;
+
+workqueue_destroy:
+ destroy_workqueue(mbox->wq);
+
+unmap_out_queue:
+ devm_iounmap(mbox->dev, mbox->out_queue);
+
+unmap_in_queue:
+ devm_iounmap(mbox->dev, mbox->in_queue);
+
+ return ret;
+}
+
+void ethosu_mailbox_deinit(struct ethosu_mailbox *mbox)
+{
+ if (mbox->rx != mbox->tx)
+ mbox_free_channel(mbox->rx);
+
+ mbox_free_channel(mbox->tx);
+ destroy_workqueue(mbox->wq);
+ devm_iounmap(mbox->dev, mbox->out_queue);
+ devm_iounmap(mbox->dev, mbox->in_queue);
+}
diff --git a/kernel/ethosu_mailbox.h b/kernel/ethosu_mailbox.h
new file mode 100644
index 0000000..c5865d0
--- /dev/null
+++ b/kernel/ethosu_mailbox.h
@@ -0,0 +1,107 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef ETHOSU_MAILBOX_H
+#define ETHOSU_MAILBOX_H
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include <linux/types.h>
+#include <linux/mailbox_client.h>
+#include <linux/workqueue.h>
+
+/****************************************************************************
+ * Types
+ ****************************************************************************/
+
+struct device;
+struct ethosu_buffer;
+struct ethosu_device;
+struct ethosu_core_msg;
+struct ethosu_core_queue;
+struct resource;
+
+typedef void (*ethosu_mailbox_cb)(void *user_arg);
+
+struct ethosu_mailbox {
+ struct device *dev;
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ struct ethosu_core_queue __iomem *in_queue;
+ struct ethosu_core_queue __iomem *out_queue;
+ struct mbox_client client;
+ struct mbox_chan *rx;
+ struct mbox_chan *tx;
+ ethosu_mailbox_cb callback;
+ void *user_arg;
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+/**
+ * ethosu_mailbox_init() - Initialize mailbox
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_mailbox_init(struct ethosu_mailbox *mbox,
+ struct device *dev,
+ struct resource *in_queue,
+ struct resource *out_queue,
+ ethosu_mailbox_cb callback,
+ void *user_arg);
+
+/**
+ * ethosu_mailbox_deinit() - Deinitialize mailbox
+ */
+void ethosu_mailbox_deinit(struct ethosu_mailbox *mbox);
+
+/**
+ * ethosu_mailbox_read() - Read message from mailbox
+ *
+ * Return: 0 message read, else error code.
+ */
+int ethosu_mailbox_read(struct ethosu_mailbox *mbox,
+ struct ethosu_core_msg *header,
+ void *data,
+ size_t length);
+
+/**
+ * ethosu_mailbox_ping() - Send ping message
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_mailbox_ping(struct ethosu_mailbox *mbox);
+
+/**
+ * ethosu_mailbox_inference() - Send inference
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_mailbox_inference(struct ethosu_mailbox *mbox,
+ void *user_arg,
+ struct ethosu_buffer *ifm,
+ struct ethosu_buffer *ofm,
+ struct ethosu_buffer *network);
+
+#endif /* ETHOSU_MAILBOX_H */
diff --git a/kernel/ethosu_network.c b/kernel/ethosu_network.c
new file mode 100644
index 0000000..851d4b7
--- /dev/null
+++ b/kernel/ethosu_network.c
@@ -0,0 +1,201 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include "ethosu_network.h"
+
+#include "ethosu_buffer.h"
+#include "ethosu_device.h"
+#include "ethosu_inference.h"
+#include "uapi/ethosu.h"
+
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+
+/****************************************************************************
+ * Variables
+ ****************************************************************************/
+
+static int ethosu_network_release(struct inode *inode,
+ struct file *file);
+
+static long ethosu_network_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+
+static const struct file_operations ethosu_network_fops = {
+ .release = &ethosu_network_release,
+ .unlocked_ioctl = &ethosu_network_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = &ethosu_network_ioctl,
+#endif
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+static bool ethosu_network_verify(struct file *file)
+{
+ return file->f_op == &ethosu_network_fops;
+}
+
+static void ethosu_network_destroy(struct kref *kref)
+{
+ struct ethosu_network *net =
+ container_of(kref, struct ethosu_network, kref);
+
+ dev_info(net->edev->dev, "Network destroy. handle=0x%pK\n", net);
+
+ ethosu_buffer_put(net->buf);
+ devm_kfree(net->edev->dev, net);
+}
+
+static int ethosu_network_release(struct inode *inode,
+ struct file *file)
+{
+ struct ethosu_network *net = file->private_data;
+
+ dev_info(net->edev->dev, "Network release. handle=0x%pK\n", net);
+
+ ethosu_network_put(net);
+
+ return 0;
+}
+
+static long ethosu_network_ioctl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct ethosu_network *net = file->private_data;
+ void __user *udata = (void __user *)arg;
+ int ret = -EINVAL;
+
+ ret = mutex_lock_interruptible(&net->edev->mutex);
+ if (ret)
+ return ret;
+
+ dev_info(net->edev->dev, "Ioctl: cmd=%u, arg=%lu\n", cmd, arg);
+
+ switch (cmd) {
+ case ETHOSU_IOCTL_INFERENCE_CREATE: {
+ struct ethosu_uapi_inference_create uapi;
+
+ if (copy_from_user(&uapi, udata, sizeof(uapi)))
+ break;
+
+ dev_info(net->edev->dev,
+ "Ioctl: Inference. ifm_fd=%u, ofm_fd=%u\n",
+ uapi.ifm_fd, uapi.ofm_fd);
+
+ ret = ethosu_inference_create(net->edev, net, &uapi);
+ break;
+ }
+ default: {
+ dev_err(net->edev->dev, "Invalid ioctl. cmd=%u, arg=%lu",
+ cmd, arg);
+ break;
+ }
+ }
+
+ mutex_unlock(&net->edev->mutex);
+
+ return ret;
+}
+
+int ethosu_network_create(struct ethosu_device *edev,
+ struct ethosu_uapi_network_create *uapi)
+{
+ struct ethosu_buffer *buf;
+ struct ethosu_network *net;
+ int ret = -ENOMEM;
+
+ buf = ethosu_buffer_get_from_fd(uapi->fd);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ net = devm_kzalloc(edev->dev, sizeof(*net), GFP_KERNEL);
+ if (!net) {
+ ret = -ENOMEM;
+ goto put_buf;
+ }
+
+ net->edev = edev;
+ net->buf = buf;
+ kref_init(&net->kref);
+
+ ret = anon_inode_getfd("ethosu-network", &ethosu_network_fops, net,
+ O_RDWR | O_CLOEXEC);
+ if (ret < 0)
+ goto free_net;
+
+ net->file = fget(ret);
+ fput(net->file);
+
+ dev_info(edev->dev, "Network create. handle=0x%pK",
+ net);
+
+ return ret;
+
+free_net:
+ devm_kfree(edev->dev, net);
+
+put_buf:
+ ethosu_buffer_put(buf);
+
+ return ret;
+}
+
+struct ethosu_network *ethosu_network_get_from_fd(int fd)
+{
+ struct ethosu_network *net;
+ struct file *file;
+
+ file = fget(fd);
+ if (!file)
+ return ERR_PTR(-EINVAL);
+
+ if (!ethosu_network_verify(file)) {
+ fput(file);
+
+ return ERR_PTR(-EINVAL);
+ }
+
+ net = file->private_data;
+ ethosu_network_get(net);
+ fput(file);
+
+ return net;
+}
+
+void ethosu_network_get(struct ethosu_network *net)
+{
+ kref_get(&net->kref);
+}
+
+void ethosu_network_put(struct ethosu_network *net)
+{
+ kref_put(&net->kref, ethosu_network_destroy);
+}
diff --git a/kernel/ethosu_network.h b/kernel/ethosu_network.h
new file mode 100644
index 0000000..bb70afc
--- /dev/null
+++ b/kernel/ethosu_network.h
@@ -0,0 +1,81 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef ETHOSU_NETWORK_H
+#define ETHOSU_NETWORK_H
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include <linux/kref.h>
+#include <linux/types.h>
+
+/****************************************************************************
+ * Types
+ ****************************************************************************/
+
+struct ethosu_buffer;
+struct ethosu_device;
+struct ethosu_uapi_network_create;
+struct device;
+struct file;
+
+struct ethosu_network {
+ struct ethosu_device *edev;
+ struct file *file;
+ struct kref kref;
+ struct ethosu_buffer *buf;
+};
+
+/****************************************************************************
+ * Functions
+ ****************************************************************************/
+
+/**
+ * ethosu_network_create() - Create network
+ *
+ * This function must be called in the context of a user space process.
+ *
+ * Return: fd on success, else error code.
+ */
+int ethosu_network_create(struct ethosu_device *edev,
+ struct ethosu_uapi_network_create *uapi);
+
+/**
+ * ethosu_network_get_from_fd() - Get network handle from fd
+ *
+ * This function must be called from a user space context.
+ *
+ * Return: Pointer on success, else ERR_PTR.
+ */
+struct ethosu_network *ethosu_network_get_from_fd(int fd);
+
+/**
+ * ethosu_network_get() - Get network
+ */
+void ethosu_network_get(struct ethosu_network *net);
+
+/**
+ * ethosu_network_put() - Put network
+ */
+void ethosu_network_put(struct ethosu_network *net);
+
+#endif /* ETHOSU_NETWORK_H */
diff --git a/kernel/uapi/ethosu.h b/kernel/uapi/ethosu.h
new file mode 100644
index 0000000..2eca2c4
--- /dev/null
+++ b/kernel/uapi/ethosu.h
@@ -0,0 +1,104 @@
+/*
+ * (C) COPYRIGHT 2020 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#ifndef ETHOSU_H
+#define ETHOSU_H
+
+/****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/****************************************************************************
+ * Defines
+ ****************************************************************************/
+
+#define ETHOSU_IOCTL_BASE 0x01
+#define ETHOSU_IO(nr) _IO(ETHOSU_IOCTL_BASE, nr)
+#define ETHOSU_IOR(nr, type) _IOR(ETHOSU_IOCTL_BASE, nr, type)
+#define ETHOSU_IOW(nr, type) _IOW(ETHOSU_IOCTL_BASE, nr, type)
+#define ETHOSU_IOWR(nr, type) _IOWR(ETHOSU_IOCTL_BASE, nr, type)
+
+#define ETHOSU_IOCTL_PING ETHOSU_IO(0x00)
+#define ETHOSU_IOCTL_BUFFER_CREATE ETHOSU_IOR(0x10, \
+ struct ethosu_uapi_buffer_create)
+#define ETHOSU_IOCTL_BUFFER_SET ETHOSU_IOR(0x11, \
+ struct ethosu_uapi_buffer)
+#define ETHOSU_IOCTL_BUFFER_GET ETHOSU_IOW(0x12, \
+ struct ethosu_uapi_buffer)
+#define ETHOSU_IOCTL_NETWORK_CREATE ETHOSU_IOR(0x20, \
+ struct ethosu_uapi_network_create)
+#define ETHOSU_IOCTL_INFERENCE_CREATE ETHOSU_IOR(0x30, \
+ struct ethosu_uapi_inference_create)
+#define ETHOSU_IOCTL_INFERENCE_STATUS ETHOSU_IO(0x31)
+
+/****************************************************************************
+ * Types
+ ****************************************************************************/
+
+/**
+ * enum ethosu_uapi_status - Status
+ */
+enum ethosu_uapi_status {
+ ETHOSU_UAPI_STATUS_OK,
+ ETHOSU_UAPI_STATUS_ERROR
+};
+
+/**
+ * struct ethosu_uapi_buffer_create - Create buffer request
+ * @capacity: Maximum capacity of the buffer
+ */
+struct ethosu_uapi_buffer_create {
+ __u32 capacity;
+};
+
+/**
+ * struct ethosu_uapi_buffer - Buffer descriptor
+ * @offset: Offset to where the data starts
+ * @size: Size of the data
+ *
+ * 'offset + size' must not exceed the capacity of the buffer.
+ */
+struct ethosu_uapi_buffer {
+ __u32 offset;
+ __u32 size;
+};
+
+/**
+ * struct ethosu_uapi_network_create - Create network request
+ * @fd: Buffer file descriptor
+ */
+struct ethosu_uapi_network_create {
+ __u32 fd;
+};
+
+/**
+ * struct ethosu_uapi_inference_create - Create network request
+ * @ifm_fd: IFM buffer file descriptor
+ * @ofm_fd: OFM buffer file descriptor
+ */
+struct ethosu_uapi_inference_create {
+ __u32 ifm_fd;
+ __u32 ofm_fd;
+};
+
+#endif