aboutsummaryrefslogtreecommitdiff
path: root/kernel/ethosu_device.c
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 /kernel/ethosu_device.c
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
Diffstat (limited to 'kernel/ethosu_device.c')
-rw-r--r--kernel/ethosu_device.c134
1 files changed, 115 insertions, 19 deletions
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);
}