aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristofer Jonsson <kristofer.jonsson@arm.com>2023-01-23 13:05:36 +0100
committerKristofer Jonsson <kristofer.jonsson@arm.com>2023-02-02 16:30:40 +0100
commit074ef905e834cff238e708f2e673e100884218ba (patch)
treea99762832b461441b0319e984b3ecdb908efe433
parentec47704ab3fd50a9ef8339f33139ddae4caa00b6 (diff)
downloadethos-u-linux-driver-stack-074ef905e834cff238e708f2e673e100884218ba.tar.gz
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
-rw-r--r--kernel/ethosu_buffer.c28
-rw-r--r--kernel/ethosu_cancel_inference.c6
-rw-r--r--kernel/ethosu_device.c134
-rw-r--r--kernel/ethosu_device.h2
-rw-r--r--kernel/ethosu_driver.c19
5 files changed, 128 insertions, 61 deletions
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 @@ static const struct file_operations ethosu_buffer_fops = {
* 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 @@ static void ethosu_buffer_destroy(struct kref *kref)
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 @@ static long ethosu_buffer_ioctl(struct file *file,
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 @@ int ethosu_buffer_create(struct device *dev,
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 @@ int ethosu_buffer_create(struct device *dev,
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 @@ int ethosu_cancel_inference_request(struct device *dev,
/* 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 @@ int ethosu_cancel_inference_request(struct device *dev,
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 @@ deregister:
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 @@ static int ethosu_handle_rpmsg(struct rpmsg_device *rpdev,
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 @@ static int ethosu_handle_rpmsg(struct rpmsg_device *rpdev,
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 @@ static int ethosu_handle_rpmsg(struct rpmsg_device *rpdev,
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 @@ static int ethosu_open(struct inode *inode,
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 @@ static long ethosu_ioctl(struct file *file,
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 @@ static const struct file_operations fops = {
#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 @@ int ethosu_dev_init(struct rpmsg_device *rpdev,
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 @@ del_cdev:
deinit_mailbox:
ethosu_mailbox_deinit(&edev->mailbox);
+device_unregister:
+ device_unregister(dev);
+
+free_edev:
+ kfree(edev);
+
return ret;
}
@@ -398,8 +494,8 @@ void ethosu_dev_deinit(struct rpmsg_device *rpdev)
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 struct class *ethosu_class;
static dev_t devt;
-static DECLARE_BITMAP(minors, MINOR_COUNT);
-
/****************************************************************************
* Rpmsg driver
****************************************************************************/
@@ -57,35 +53,24 @@ static DECLARE_BITMAP(minors, MINOR_COUNT);
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);
}