Initial commit

Change-Id: I14b6becc908a0ac215769c32ee9c43db192ae6c8
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