Create device for Ethos-U kernel driver

When the Ethos-U kernel driver is probed it creates a /dev/ethosu<nr>
device node, which user space use ioctl to communicate with. When
the driver is removed the Ethos-U resources must live on until all
open file handles have been closed.

The rpmsg device can be removed for a number of reasons, for example
if the firmware is stopped or the remoteproc driver is removed. To
allow the remove to complete without waiting for all file handles to
close, a new 'struct device' is created by the kernel driver. This
device will be used to memory allocations and will live on until the
last file handle has been closed.

Change-Id: I790d8219960f25fe64f58c11a865eb65c7b08974
diff --git a/kernel/ethosu_buffer.c b/kernel/ethosu_buffer.c
index 0fcbf3b..fb4a8b4 100644
--- a/kernel/ethosu_buffer.c
+++ b/kernel/ethosu_buffer.c
@@ -62,24 +62,6 @@
  * Functions
  ****************************************************************************/
 
-__attribute__((used))
-static dma_addr_t ethosu_pa_to_da(struct device *dev,
-				  phys_addr_t pa,
-				  size_t len)
-{
-	struct rproc *rproc = rproc_get_by_child(dev);
-	struct rproc_mem_entry *mem;
-
-	list_for_each_entry(mem, &rproc->carveouts, node) {
-		ssize_t offset = pa - mem->dma;
-
-		if (offset >= 0 && offset + len <= mem->len)
-			return mem->da + offset;
-	}
-
-	return (dma_addr_t)-1;
-}
-
 static bool ethosu_buffer_verify(struct file *file)
 {
 	return file->f_op == &ethosu_buffer_fops;
@@ -90,11 +72,10 @@
 	struct ethosu_buffer *buf =
 		container_of(kref, struct ethosu_buffer, kref);
 	struct device *dev = buf->dev;
-	struct rproc *rproc = rproc_get_by_child(dev);
 
-	dev_info(dev, "Buffer destroy. buf=0x%pK\n", buf);
+	dev_info(dev, "Buffer destroy. buf=0x%pK", buf);
 
-	dma_free_coherent(rproc->dev.parent, buf->capacity, buf->cpu_addr,
+	dma_free_coherent(dev, buf->capacity, buf->cpu_addr,
 			  buf->dma_addr);
 
 	devm_kfree(dev, buf);
@@ -192,7 +173,6 @@
 int ethosu_buffer_create(struct device *dev,
 			 size_t capacity)
 {
-	struct rproc *rproc = rproc_get_by_child(dev);
 	struct ethosu_buffer *buf;
 	int ret = -ENOMEM;
 
@@ -209,7 +189,7 @@
 	buf->size = 0;
 	kref_init(&buf->kref);
 
-	buf->cpu_addr = dma_alloc_coherent(rproc->dev.parent, capacity,
+	buf->cpu_addr = dma_alloc_coherent(dev, capacity,
 					   &buf->dma_addr, GFP_KERNEL);
 	if (!buf->cpu_addr)
 		goto free_buf;
@@ -230,7 +210,7 @@
 	return ret;
 
 free_dma:
-	dma_free_coherent(rproc->dev.parent, buf->capacity, buf->cpu_addr,
+	dma_free_coherent(dev, buf->capacity, buf->cpu_addr,
 			  buf->dma_addr);
 
 free_buf:
diff --git a/kernel/ethosu_cancel_inference.c b/kernel/ethosu_cancel_inference.c
index ee630f5..6661522 100644
--- a/kernel/ethosu_cancel_inference.c
+++ b/kernel/ethosu_cancel_inference.c
@@ -28,6 +28,7 @@
 #include "ethosu_device.h"
 #include "ethosu_inference.h"
 
+#include <linux/remoteproc.h>
 #include <linux/wait.h>
 
 /****************************************************************************
@@ -110,6 +111,7 @@
 
 	/* Unlock the mutex before going to block on the condition */
 	device_unlock(dev);
+
 	/* wait for response to arrive back */
 	timeout = wait_for_completion_timeout(&cancellation->done,
 					      msecs_to_jiffies(
@@ -123,11 +125,14 @@
 		dev_warn(dev,
 			 "Msg: Cancel Inference response lost - timeoutdev");
 		ret = -EIO;
+
+		rproc_report_crash(rproc_get_by_child(dev), RPROC_FATAL_ERROR);
 		goto deregister;
 	}
 
 	if (cancellation->errno) {
 		ret = cancellation->errno;
+		rproc_report_crash(rproc_get_by_child(dev), RPROC_FATAL_ERROR);
 		goto deregister;
 	}
 
@@ -138,6 +143,7 @@
 kfree:
 	dev_info(dev,
 		 "Cancel inference destroy. cancel=0x%pK", cancellation);
+
 	/* decrease the reference on the inference we are refering to */
 	ethosu_inference_put(cancellation->inf);
 	devm_kfree(dev, cancellation);
diff --git a/kernel/ethosu_device.c b/kernel/ethosu_device.c
index e99f8c0..96bd8b4 100644
--- a/kernel/ethosu_device.c
+++ b/kernel/ethosu_device.c
@@ -33,7 +33,6 @@
 #include "ethosu_network_info.h"
 #include "uapi/ethosu.h"
 
-#include <linux/dma-mapping.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/io.h>
@@ -43,6 +42,19 @@
 #include <linux/uaccess.h>
 
 /****************************************************************************
+ * Defines
+ ****************************************************************************/
+
+#define MINOR_BASE      0 /* Minor version starts at 0 */
+#define MINOR_COUNT    64 /* Allocate minor versions */
+
+/****************************************************************************
+ * Variables
+ ****************************************************************************/
+
+static DECLARE_BITMAP(minors, MINOR_COUNT);
+
+/****************************************************************************
  * Functions
  ****************************************************************************/
 
@@ -54,7 +66,7 @@
 			       u32 src)
 {
 	struct ethosu_device *edev = dev_get_drvdata(&rpdev->dev);
-	struct device *dev = &rpdev->dev;
+	struct device *dev = &edev->dev;
 	struct ethosu_core_rpmsg *rpmsg = data;
 	int length = len - sizeof(rpmsg->header);
 	int ret;
@@ -64,7 +76,7 @@
 		 rpmsg->header.magic, rpmsg->header.type, rpmsg->header.msg_id);
 
 	switch (rpmsg->header.type) {
-	case ETHOSU_CORE_MSG_ERR: {
+	case ETHOSU_CORE_MSG_ERR:
 		if (length != sizeof(rpmsg->error)) {
 			dev_warn(dev,
 				 "Msg: Error message of incorrect size. size=%u, expected=%zu", length,
@@ -76,8 +88,9 @@
 		rpmsg->error.msg[sizeof(rpmsg->error.msg) - 1] = '\0';
 		dev_warn(dev, "Msg: Error. type=%u, msg=\"%s\"",
 			 rpmsg->error.type, rpmsg->error.msg);
+
+		rproc_report_crash(rproc_get_by_child(dev), RPROC_FATAL_ERROR);
 		break;
-	}
 	case ETHOSU_CORE_MSG_PING:
 		dev_info(dev, "Msg: Ping");
 		ret = ethosu_mailbox_pong(&edev->mailbox);
@@ -205,7 +218,7 @@
 	struct ethosu_device *edev = container_of(cdev, struct ethosu_device,
 						  cdev);
 	struct rpmsg_device *rpdev = edev->rpdev;
-	struct device *dev = &rpdev->dev;
+	struct device *dev = &edev->dev;
 
 	dev_info(dev, "Device open. file=0x%pK", file);
 
@@ -219,8 +232,8 @@
 			 unsigned long arg)
 {
 	struct rpmsg_device *rpdev = file->private_data;
-	struct device *dev = &rpdev->dev;
-	struct ethosu_device *edev = dev_get_drvdata(dev);
+	struct ethosu_device *edev = dev_get_drvdata(&rpdev->dev);
+	struct device *dev = &edev->dev;
 	void __user *udata = (void __user *)arg;
 	int ret = -EINVAL;
 
@@ -325,6 +338,58 @@
 #endif
 };
 
+static void ethosu_dev_release(struct device *dev)
+{
+	struct ethosu_device *edev = dev_get_drvdata(dev);
+
+	dev_info(dev, "%s", __FUNCTION__);
+
+	clear_bit(MINOR(edev->cdev.dev), minors);
+
+	ethosu_mailbox_deinit(&edev->mailbox);
+	device_destroy(edev->class, edev->cdev.dev);
+	kfree(edev);
+}
+
+static int ethosu_device_register(struct device *dev,
+				  struct device *parent,
+				  void *drvdata,
+				  dev_t devt)
+{
+	struct rproc *rproc = rproc_get_by_child(parent);
+	int ret;
+
+	dev->parent = parent;
+	dev->release = ethosu_dev_release;
+	dev_set_drvdata(dev, drvdata);
+
+	ret = dev_set_name(dev, "ethosu%d", MINOR(devt));
+	if (ret) {
+		dev_err(parent, "Failed to set device name. ret=%d", ret);
+
+		return ret;
+	}
+
+	/* Inherit DMA settings for rproc device */
+	ret = of_reserved_mem_device_init_by_idx(dev,
+						 rproc->dev.parent->of_node, 0);
+	if (ret) {
+		dev_err(parent, "Failed to initialize reserved memory. ret=%d",
+			ret);
+
+		return ret;
+	}
+
+	ret = device_register(dev);
+	if (ret) {
+		dev_err(parent, "Failed to register device. ret=%d", ret);
+
+		return ret;
+	}
+
+	return 0;
+}
+
 int ethosu_dev_init(struct rpmsg_device *rpdev,
 		    struct class *class,
 		    dev_t devt)
@@ -332,51 +397,76 @@
 	struct device *dev = &rpdev->dev;
 	struct ethosu_device *edev;
 	struct device *sysdev;
+	int minor;
 	int ret;
 
 	dev_info(dev, "%s", __FUNCTION__);
 
+	/* Reserve minor number for device node */
+	minor = find_first_zero_bit(minors, MINOR_COUNT);
+	if (minor >= MINOR_COUNT) {
+		dev_err(dev, "No more minor numbers.");
+
+		return -ENOMEM;
+	}
+
+	devt = MKDEV(MAJOR(devt), minor);
+
 	/* Allocate and create Ethos-U device */
-	edev = devm_kzalloc(dev, sizeof(*edev), GFP_KERNEL);
+	edev = kzalloc(sizeof(*edev), GFP_KERNEL);
 	if (!edev)
 		return -ENOMEM;
 
-	dev_set_drvdata(dev, edev);
+	dev_set_drvdata(&rpdev->dev, edev);
 
 	edev->rpdev = rpdev;
 	edev->class = class;
-	edev->devt = devt;
+
+	/* Create device object */
+	ret = ethosu_device_register(&edev->dev, &rpdev->dev, edev,
+				     devt);
+	if (ret)
+		goto free_edev;
+
+	/* Continue with new device */
+	dev = &edev->dev;
 
 	/* Create RPMsg endpoint */
 	edev->ept = ethosu_create_ept(rpdev);
-	if (IS_ERR(edev->ept))
-		return PTR_ERR(edev->ept);
+	if (IS_ERR(edev->ept)) {
+		ret = PTR_ERR(edev->ept);
+		goto device_unregister;
+	}
 
 	ret = ethosu_mailbox_init(&edev->mailbox, dev, edev->ept);
 	if (ret)
-		return ret;
+		goto device_unregister;
 
 	/* Create device node */
 	cdev_init(&edev->cdev, &fops);
 	edev->cdev.owner = THIS_MODULE;
 
-	ret = cdev_add(&edev->cdev, edev->devt, 1);
+	cdev_set_parent(&edev->cdev, &dev->kobj);
+
+	ret = cdev_add(&edev->cdev, devt, 1);
 	if (ret) {
 		dev_err(dev, "Failed to add character device.");
 		goto deinit_mailbox;
 	}
 
-	sysdev = device_create(edev->class, NULL, edev->devt, rpdev,
-			       "ethosu%d", MINOR(edev->devt));
+	sysdev = device_create(edev->class, NULL, devt, rpdev,
+			       "ethosu%d", MINOR(devt));
 	if (IS_ERR(sysdev)) {
 		dev_err(dev, "Failed to create device.");
 		ret = PTR_ERR(sysdev);
 		goto del_cdev;
 	}
 
+	set_bit(minor, minors);
+
 	dev_info(dev,
 		 "Created Arm Ethos-U device. name=%s, major=%d, minor=%d",
-		 dev_name(sysdev), MAJOR(edev->devt), MINOR(edev->devt));
+		 dev_name(sysdev), MAJOR(devt), MINOR(devt));
 
 	ethosu_mailbox_ping(&edev->mailbox);
 
@@ -388,6 +478,12 @@
 deinit_mailbox:
 	ethosu_mailbox_deinit(&edev->mailbox);
 
+device_unregister:
+	device_unregister(dev);
+
+free_edev:
+	kfree(edev);
+
 	return ret;
 }
 
@@ -398,8 +494,8 @@
 
 	dev_info(dev, "%s", __FUNCTION__);
 
-	ethosu_mailbox_deinit(&edev->mailbox);
+	ethosu_mailbox_fail(&edev->mailbox);
 	rpmsg_destroy_ept(edev->ept);
-	device_destroy(edev->class, edev->cdev.dev);
 	cdev_del(&edev->cdev);
+	device_unregister(&edev->dev);
 }
diff --git a/kernel/ethosu_device.h b/kernel/ethosu_device.h
index d02f29d..d866514 100644
--- a/kernel/ethosu_device.h
+++ b/kernel/ethosu_device.h
@@ -42,11 +42,11 @@
  * struct ethosu_device - Device structure
  */
 struct ethosu_device {
+	struct device         dev;
 	struct rpmsg_device   *rpdev;
 	struct rpmsg_endpoint *ept;
 	struct cdev           cdev;
 	struct                class *class;
-	dev_t                 devt;
 	struct ethosu_mailbox mailbox;
 };
 
diff --git a/kernel/ethosu_driver.c b/kernel/ethosu_driver.c
index 6e5bfb9..342f501 100644
--- a/kernel/ethosu_driver.c
+++ b/kernel/ethosu_driver.c
@@ -38,8 +38,6 @@
 #define MINOR_BASE      0 /* Minor version starts at 0 */
 #define MINOR_COUNT    64 /* Allocate minor versions */
 
-#define DMA_ADDR_BITS 32  /* Number of address bits */
-
 /****************************************************************************
  * Variables
  ****************************************************************************/
@@ -48,8 +46,6 @@
 
 static dev_t devt;
 
-static DECLARE_BITMAP(minors, MINOR_COUNT);
-
 /****************************************************************************
  * Rpmsg driver
  ****************************************************************************/
@@ -57,35 +53,24 @@
 static int ethosu_rpmsg_probe(struct rpmsg_device *rpdev)
 {
 	struct device *dev = &rpdev->dev;
-	int minor;
 	int ret;
 
-	/* Reserve minor number for device node */
-	minor = find_first_zero_bit(minors, MINOR_COUNT);
-	if (minor >= MINOR_COUNT) {
-		dev_err(dev, "No more minor numbers.");
-
-		return -ENOMEM;
-	}
+	dev_info(dev, "%s", __FUNCTION__);
 
 	/* Initialize device */
-	ret = ethosu_dev_init(rpdev, ethosu_class, MKDEV(MAJOR(devt), minor));
+	ret = ethosu_dev_init(rpdev, ethosu_class, devt);
 	if (ret)
 		return ret;
 
-	set_bit(minor, minors);
-
 	return 0;
 }
 
 static void ethosu_rpmsg_remove(struct rpmsg_device *rpdev)
 {
 	struct device *dev = &rpdev->dev;
-	struct ethosu_device *edev = dev_get_drvdata(dev);
 
 	dev_info(dev, "%s", __FUNCTION__);
 
-	clear_bit(MINOR(edev->devt), minors);
 	ethosu_dev_deinit(rpdev);
 }