diff options
Diffstat (limited to 'kernel/ethosu_device.c')
-rw-r--r-- | kernel/ethosu_device.c | 166 |
1 files changed, 163 insertions, 3 deletions
diff --git a/kernel/ethosu_device.c b/kernel/ethosu_device.c index e82b4c0..7adc7a6 100644 --- a/kernel/ethosu_device.c +++ b/kernel/ethosu_device.c @@ -38,6 +38,7 @@ #include <linux/io.h> #include <linux/of_reserved_mem.h> #include <linux/uaccess.h> +#include <linux/slab.h> /**************************************************************************** * Defines @@ -47,6 +48,8 @@ #define MINOR_COUNT 1 /* Allocate 1 minor version */ #define DMA_ADDR_BITS 32 /* Number of address bits */ +#define CAPABILITIES_RESP_TIMEOUT_MS 50 + /**************************************************************************** * Types ****************************************************************************/ @@ -55,6 +58,71 @@ * Functions ****************************************************************************/ +static void ethosu_capabilities_destroy(struct kref *kref) +{ + struct ethosu_capabilities *cap = + container_of(kref, struct ethosu_capabilities, refcount); + + list_del(&cap->list); + + devm_kfree(cap->edev->dev, cap); +} + +static int ethosu_capabilities_find(struct ethosu_capabilities *cap, + struct list_head *capabilties_list) +{ + struct ethosu_capabilities *cur; + + list_for_each_entry(cur, capabilties_list, list) { + if (cur == cap) + return 0; + } + + return -EINVAL; +} + +static int ethosu_capability_rsp(struct ethosu_device *edev, + struct ethosu_core_msg_capabilities_rsp *msg) +{ + struct ethosu_capabilities *cap; + 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) { + dev_warn(edev->dev, + "Handle not found in capabilities list. handle=0x%p\n", + cap); + + /* NOTE: do not call complete or kref_put on invalid data! */ + return ret; + } + + capabilities = cap->capabilities; + + capabilities->hw_id.version_status = msg->version_status; + capabilities->hw_id.version_minor = msg->version_minor; + capabilities->hw_id.version_major = msg->version_major; + capabilities->hw_id.product_major = msg->product_major; + capabilities->hw_id.arch_patch_rev = msg->arch_patch_rev; + capabilities->hw_id.arch_minor_rev = msg->arch_minor_rev; + capabilities->hw_id.arch_major_rev = msg->arch_major_rev; + capabilities->driver_patch_rev = msg->driver_patch_rev; + capabilities->driver_minor_rev = msg->driver_minor_rev; + capabilities->driver_major_rev = msg->driver_major_rev; + capabilities->hw_cfg.macs_per_cc = msg->macs_per_cc; + capabilities->hw_cfg.cmd_stream_version = msg->cmd_stream_version; + capabilities->hw_cfg.shram_size = msg->shram_size; + capabilities->hw_cfg.custom_dma = msg->custom_dma; + + complete(&cap->done); + + kref_put(&cap->refcount, ethosu_capabilities_destroy); + + return 0; +} + /* Incoming messages */ static int ethosu_handle_msg(struct ethosu_device *edev) { @@ -62,9 +130,10 @@ static int ethosu_handle_msg(struct ethosu_device *edev) struct ethosu_core_msg header; union { - struct ethosu_core_msg_err error; - struct ethosu_core_inference_rsp inf; - struct ethosu_core_msg_version version; + struct ethosu_core_msg_err error; + struct ethosu_core_inference_rsp inf; + struct ethosu_core_msg_version version; + struct ethosu_core_msg_capabilities_rsp capabilities; } data; /* Read message */ @@ -133,7 +202,35 @@ static int ethosu_handle_msg(struct ethosu_device *edev) } break; + case ETHOSU_CORE_MSG_CAPABILITIES_RSP: + if (header.length != sizeof(data.capabilities)) { + dev_warn(edev->dev, + "Msg: Capabilities response of incorrect size. size=%u, expected=%zu\n", header.length, + sizeof(data.capabilities)); + ret = -EBADMSG; + break; + } + dev_info(edev->dev, + "Msg: Capabilities response ua%llx vs%hhu v%hhu.%hhu p%hhu av%hhu.%hhu.%hhu dv%hhu.%hhu.%hhu mcc%hhu csv%hhu ss%hhu cd%hhu\n", + data.capabilities.user_arg, + data.capabilities.version_status, + data.capabilities.version_major, + data.capabilities.version_minor, + data.capabilities.product_major, + data.capabilities.arch_major_rev, + data.capabilities.arch_minor_rev, + data.capabilities.arch_patch_rev, + data.capabilities.driver_major_rev, + data.capabilities.driver_minor_rev, + data.capabilities.driver_patch_rev, + data.capabilities.macs_per_cc, + data.capabilities.cmd_stream_version, + data.capabilities.shram_size, + data.capabilities.custom_dma); + + ret = ethosu_capability_rsp(edev, &data.capabilities); + break; default: /* This should not happen due to version checks */ dev_warn(edev->dev, "Msg: Protocol error\n"); @@ -157,6 +254,64 @@ 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 /* timed out*/) { + 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) @@ -176,6 +331,10 @@ static long ethosu_ioctl(struct file *file, dev_info(edev->dev, "Ioctl: Send version request\n"); ret = ethosu_mailbox_version_request(&edev->mailbox); break; + case ETHOSU_IOCTL_CAPABILITIES_REQ: + dev_info(edev->dev, "Ioctl: Send capabilities request\n"); + ret = ethosu_send_capabilities_request(edev, udata); + break; case ETHOSU_IOCTL_PING: { dev_info(edev->dev, "Ioctl: Send ping\n"); ret = ethosu_mailbox_ping(&edev->mailbox); @@ -257,6 +416,7 @@ int ethosu_dev_init(struct ethosu_device *edev, edev->dev = dev; edev->class = class; mutex_init(&edev->mutex); + INIT_LIST_HEAD(&edev->capabilities_list); INIT_LIST_HEAD(&edev->inference_list); ret = of_reserved_mem_device_init(edev->dev); |