diff options
Diffstat (limited to 'kernel/ethosu_device.c')
-rw-r--r-- | kernel/ethosu_device.c | 260 |
1 files changed, 173 insertions, 87 deletions
diff --git a/kernel/ethosu_device.c b/kernel/ethosu_device.c index f440331..316df6f 100644 --- a/kernel/ethosu_device.c +++ b/kernel/ethosu_device.c @@ -38,8 +38,9 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of_reserved_mem.h> -#include <linux/uaccess.h> +#include <linux/reset.h> #include <linux/slab.h> +#include <linux/uaccess.h> /**************************************************************************** * Defines @@ -58,42 +59,71 @@ static void ethosu_capabilities_destroy(struct kref *kref) struct ethosu_capabilities *cap = container_of(kref, struct ethosu_capabilities, refcount); - list_del(&cap->list); + dev_info(cap->edev->dev, "Capabilities destroy. handle=0x%pK\n", cap); + + list_del(&cap->msg.list); devm_kfree(cap->edev->dev, cap); } -static int ethosu_capabilities_find(struct ethosu_capabilities *cap, - struct list_head *capabilties_list) +static int ethosu_capabilities_send(struct ethosu_capabilities *cap) { - struct ethosu_capabilities *cur; + int ret; - list_for_each_entry(cur, capabilties_list, list) { - if (cur == cap) - return 0; - } + ret = ethosu_mailbox_capabilities_request(&cap->edev->mailbox, cap); + if (ret) + return ret; - return -EINVAL; + return 0; } -static int ethosu_capability_rsp(struct ethosu_device *edev, - struct ethosu_core_msg_capabilities_rsp *msg) +static void ethosu_capabilities_fail(struct ethosu_mailbox_msg *msg) { - struct ethosu_capabilities *cap; + struct ethosu_capabilities *cap = + container_of(msg, typeof(*cap), msg); + + cap->errno = -EFAULT; + complete(&cap->done); +} + +static int ethosu_capabilities_resend(struct ethosu_mailbox_msg *msg) +{ + struct ethosu_capabilities *cap = + container_of(msg, typeof(*cap), msg); + int ret; + + /* Don't resend request if response has already been received */ + if (completion_done(&cap->done)) + return 0; + + /* Resend request */ + ret = ethosu_capabilities_send(cap); + if (ret) + return ret; + + return 0; +} + +static void ethosu_capability_rsp(struct ethosu_device *edev, + struct ethosu_core_msg_capabilities_rsp *msg) +{ + struct ethosu_capabilities *cap = + (struct ethosu_capabilities *)msg->user_arg; struct ethosu_uapi_device_capabilities *capabilities; int ret; - cap = (struct ethosu_capabilities *)msg->user_arg; - ret = ethosu_capabilities_find(cap, &edev->capabilities_list); - if (0 != ret) { + ret = ethosu_mailbox_find(&edev->mailbox, &cap->msg); + if (ret) { dev_warn(edev->dev, - "Handle not found in capabilities list. handle=0x%p\n", + "Capabilities not found in pending list. handle=0x%p\n", cap); - /* NOTE: do not call complete or kref_put on invalid data! */ - return ret; + return; } + if (completion_done(&cap->done)) + return; + capabilities = cap->capabilities; capabilities->hw_id.version_status = msg->version_status; @@ -110,11 +140,61 @@ static int ethosu_capability_rsp(struct ethosu_device *edev, capabilities->hw_cfg.cmd_stream_version = msg->cmd_stream_version; capabilities->hw_cfg.custom_dma = msg->custom_dma; + cap->errno = 0; complete(&cap->done); +} +static int ethosu_capabilities_request(struct ethosu_device *edev, + void __user *udata) +{ + struct ethosu_uapi_device_capabilities uapi; + struct ethosu_capabilities *cap; + int ret; + int timeout; + + cap = devm_kzalloc(edev->dev, sizeof(struct ethosu_capabilities), + GFP_KERNEL); + if (!cap) + return -ENOMEM; + + cap->edev = edev; + cap->capabilities = &uapi; + kref_init(&cap->refcount); + init_completion(&cap->done); + list_add(&cap->msg.list, &edev->mailbox.pending_list); + cap->msg.fail = ethosu_capabilities_fail; + cap->msg.resend = ethosu_capabilities_resend; + + ret = ethosu_capabilities_send(cap); + if (0 != ret) + goto put_kref; + + /* Unlock the mutex before going to block on the condition */ + mutex_unlock(&edev->mutex); + + /* wait for response to arrive back */ + timeout = wait_for_completion_timeout(&cap->done, + msecs_to_jiffies( + CAPABILITIES_RESP_TIMEOUT_MS)); + + /* take back the mutex before resuming to do anything */ + mutex_lock(&edev->mutex); + + if (0 == timeout) { + dev_warn(edev->dev, "Capabilities response timeout"); + ret = -EIO; + goto put_kref; + } + + if (cap->errno) + goto put_kref; + + ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0; + +put_kref: kref_put(&cap->refcount, ethosu_capabilities_destroy); - return 0; + return ret; } /* Incoming messages */ @@ -171,6 +251,7 @@ static int ethosu_handle_msg(struct ethosu_device *edev) "Msg: Inference response. user_arg=0x%llx, ofm_count=%u, status=%u\n", data.inf.user_arg, data.inf.ofm_count, data.inf.status); + ethosu_inference_rsp(edev, &data.inf); break; case ETHOSU_CORE_MSG_VERSION_RSP: @@ -223,7 +304,7 @@ static int ethosu_handle_msg(struct ethosu_device *edev) data.capabilities.cmd_stream_version, data.capabilities.custom_dma); - ret = ethosu_capability_rsp(edev, &data.capabilities); + ethosu_capability_rsp(edev, &data.capabilities); break; case ETHOSU_CORE_MSG_NETWORK_INFO_RSP: if (header.length != sizeof(data.network_info)) { @@ -252,6 +333,63 @@ static int ethosu_handle_msg(struct ethosu_device *edev) return ret; } +static int ethosu_firmware_reset(struct ethosu_device *edev) +{ + int ret; + + /* No reset control for this device */ + if (IS_ERR(edev->reset)) + return PTR_ERR(edev->reset); + + dev_info(edev->dev, "Resetting firmware."); + + ret = reset_control_assert(edev->reset); + if (ret) { + dev_warn(edev->dev, "Failed to reset assert firmware. ret=%d", + ret); + + return ret; + } + + /* Initialize mailbox header with illegal values */ + ethosu_mailbox_wait_prepare(&edev->mailbox); + + /* If this call fails we have a problem. We managed halt the firmware, + * but not to release the reset. + */ + ret = reset_control_deassert(edev->reset); + if (ret) { + dev_warn(edev->dev, "Failed to reset deassert firmware. ret=%d", + ret); + goto fail; + } + + /* Wait for firmware to boot up and initialize mailbox */ + ret = ethosu_mailbox_wait_firmware(&edev->mailbox); + if (ret) { + dev_warn(edev->dev, "Wait on firmware boot timed out. ret=%d", + ret); + goto fail; + } + + edev->mailbox.ping_count = 0; + ethosu_watchdog_reset(&edev->watchdog); + + ret = ethosu_mailbox_ping(&edev->mailbox); + if (ret) + goto fail; + + /* Resend messages */ + ethosu_mailbox_resend(&edev->mailbox); + + return ret; + +fail: + ethosu_mailbox_fail(&edev->mailbox); + + return ret; +} + static void ethosu_watchdog_callback(struct ethosu_watchdog *wdog) { struct ethosu_device *edev = @@ -259,7 +397,13 @@ static void ethosu_watchdog_callback(struct ethosu_watchdog *wdog) mutex_lock(&edev->mutex); - dev_warn(edev->dev, "Device watchdog timeout"); + dev_warn(edev->dev, "Device watchdog timeout. ping_count=%u", + edev->mailbox.ping_count); + + if (edev->mailbox.ping_count < 1) + ethosu_mailbox_ping(&edev->mailbox); + else + ethosu_firmware_reset(edev); mutex_unlock(&edev->mutex); } @@ -277,67 +421,6 @@ static int ethosu_open(struct inode *inode, return nonseekable_open(inode, file); } -static int ethosu_send_capabilities_request(struct ethosu_device *edev, - void __user *udata) -{ - struct ethosu_uapi_device_capabilities uapi; - struct ethosu_capabilities *cap; - int ret; - int timeout; - - cap = devm_kzalloc(edev->dev, sizeof(struct ethosu_capabilities), - GFP_KERNEL); - if (!cap) - return -ENOMEM; - - cap->edev = edev; - cap->capabilities = &uapi; - kref_init(&cap->refcount); - init_completion(&cap->done); - list_add(&cap->list, &edev->capabilities_list); - - ret = ethosu_mailbox_capabilities_request(&edev->mailbox, cap); - if (0 != ret) - goto put_kref; - - /* - * Increase ref counter since we sent the pointer out to - * response handler thread. That thread is responsible to - * decrease the ref counter before exiting. So the memory - * can be freed. - * - * NOTE: if no response is received back, the memory is leaked. - */ - kref_get(&cap->refcount); - - /* Unlock the mutex before going to block on the condition */ - mutex_unlock(&edev->mutex); - - /* wait for response to arrive back */ - timeout = wait_for_completion_timeout(&cap->done, - msecs_to_jiffies( - CAPABILITIES_RESP_TIMEOUT_MS)); - - /* take back the mutex before resuming to do anything */ - ret = mutex_lock_interruptible(&edev->mutex); - if (0 != ret) - goto put_kref; - - if (0 == timeout) { - dev_warn(edev->dev, - "Msg: Capabilities response lost - timeout\n"); - ret = -EIO; - goto put_kref; - } - - ret = copy_to_user(udata, &uapi, sizeof(uapi)) ? -EFAULT : 0; - -put_kref: - kref_put(&cap->refcount, ethosu_capabilities_destroy); - - return ret; -} - static long ethosu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -359,7 +442,7 @@ static long ethosu_ioctl(struct file *file, break; case ETHOSU_IOCTL_CAPABILITIES_REQ: dev_info(edev->dev, "Ioctl: Send capabilities request\n"); - ret = ethosu_send_capabilities_request(edev, udata); + ret = ethosu_capabilities_request(edev, udata); break; case ETHOSU_IOCTL_PING: { dev_info(edev->dev, "Ioctl: Send ping\n"); @@ -444,9 +527,10 @@ int ethosu_dev_init(struct ethosu_device *edev, edev->class = class; edev->devt = devt; mutex_init(&edev->mutex); - INIT_LIST_HEAD(&edev->capabilities_list); - INIT_LIST_HEAD(&edev->inference_list); - INIT_LIST_HEAD(&edev->network_info_list); + + edev->reset = devm_reset_control_get_by_index(edev->dev, 0); + if (IS_ERR(edev->reset)) + dev_warn(edev->dev, "No reset control found for this device."); ret = of_reserved_mem_device_init(edev->dev); if (ret) @@ -481,6 +565,8 @@ int ethosu_dev_init(struct ethosu_device *edev, goto del_cdev; } + ethosu_firmware_reset(edev); + 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)); |