Reset firmware

Reset the firmware if it becomes unresponsive. Use ping to send
keep alive requests.

Only monitor ping and inference request messages. The other messages
pass no resources to the firmware and can be cancelled without
resetting the firmware.

Change-Id: Ifbcc370f02d79a64f25598f11376a1dc84a7a066
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 @@
 	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 @@
 	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 @@
 			 "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 @@
 			 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 @@
 	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 @@
 
 	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 @@
 	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 @@
 		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 @@
 	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 @@
 		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));
diff --git a/kernel/ethosu_device.h b/kernel/ethosu_device.h
index 7d8791a..7c6c99d 100644
--- a/kernel/ethosu_device.h
+++ b/kernel/ethosu_device.h
@@ -40,6 +40,8 @@
  * Types
  ****************************************************************************/
 
+struct reset_control;
+
 /**
  * struct ethosu_device - Device structure
  */
@@ -51,9 +53,7 @@
 	struct mutex           mutex;
 	struct ethosu_mailbox  mailbox;
 	struct ethosu_watchdog watchdog;
-	struct list_head       capabilities_list;
-	struct list_head       inference_list;
-	struct list_head       network_info_list;
+	struct reset_control   *reset;
 };
 
 /**
@@ -64,7 +64,8 @@
 	struct completion                      done;
 	struct kref                            refcount;
 	struct ethosu_uapi_device_capabilities *capabilities;
-	struct list_head                       list;
+	struct ethosu_mailbox_msg              msg;
+	int                                    errno;
 };
 
 /****************************************************************************
diff --git a/kernel/ethosu_inference.c b/kernel/ethosu_inference.c
index ad31f06..3c18bbd 100644
--- a/kernel/ethosu_inference.c
+++ b/kernel/ethosu_inference.c
@@ -28,7 +28,6 @@
 #include "ethosu_core_interface.h"
 #include "ethosu_device.h"
 #include "ethosu_network.h"
-#include "uapi/ethosu.h"
 
 #include <linux/anon_inodes.h>
 #include <linux/file.h>
@@ -81,9 +80,6 @@
 {
 	int ret;
 
-	if (inf->pending)
-		return -EINVAL;
-
 	inf->status = ETHOSU_UAPI_STATUS_ERROR;
 
 	ret = ethosu_mailbox_inference(&inf->edev->mailbox, inf,
@@ -97,24 +93,51 @@
 	if (ret)
 		return ret;
 
-	inf->pending = true;
-
 	ethosu_inference_get(inf);
 
 	return 0;
 }
 
-static int ethosu_inference_find(struct ethosu_inference *inf,
-				 struct list_head *inference_list)
+static void ethosu_inference_fail(struct ethosu_mailbox_msg *msg)
 {
-	struct ethosu_inference *cur;
+	struct ethosu_inference *inf =
+		container_of(msg, typeof(*inf), msg);
+	int ret;
 
-	list_for_each_entry(cur, inference_list, list) {
-		if (cur == inf)
-			return 0;
+	/* Decrement reference count if inference was pending reponse */
+	if (!inf->done) {
+		ret = ethosu_inference_put(inf);
+		if (ret)
+			return;
 	}
 
-	return -EINVAL;
+	/* Fail inference and wake up any waiting process */
+	inf->status = ETHOSU_UAPI_STATUS_ERROR;
+	inf->done = true;
+	wake_up_interruptible(&inf->waitq);
+}
+
+static int ethosu_inference_resend(struct ethosu_mailbox_msg *msg)
+{
+	struct ethosu_inference *inf =
+		container_of(msg, typeof(*inf), msg);
+	int ret;
+
+	/* Don't resend request if response has already been received */
+	if (inf->done)
+		return 0;
+
+	/* Decrement reference count for pending request */
+	ret = ethosu_inference_put(inf);
+	if (ret)
+		return 0;
+
+	/* Resend request */
+	ret = ethosu_inference_send(inf);
+	if (ret)
+		return ret;
+
+	return 0;
 }
 
 static bool ethosu_inference_verify(struct file *file)
@@ -131,7 +154,7 @@
 		 "Inference destroy. handle=0x%pK, status=%d\n",
 		 inf, inf->status);
 
-	list_del(&inf->list);
+	list_del(&inf->msg.list);
 
 	while (inf->ifm_count-- > 0)
 		ethosu_buffer_put(inf->ifm[inf->ifm_count]);
@@ -165,7 +188,7 @@
 
 	poll_wait(file, &inf->waitq, wait);
 
-	if (!inf->pending)
+	if (inf->done)
 		ret |= POLLIN;
 
 	return ret;
@@ -237,10 +260,12 @@
 
 	inf->edev = edev;
 	inf->net = net;
-	inf->pending = false;
+	inf->done = false;
 	inf->status = ETHOSU_UAPI_STATUS_ERROR;
 	kref_init(&inf->kref);
 	init_waitqueue_head(&inf->waitq);
+	inf->msg.fail = ethosu_inference_fail;
+	inf->msg.resend = ethosu_inference_resend;
 
 	/* Get pointer to IFM buffers */
 	for (i = 0; i < uapi->ifm_count; i++) {
@@ -296,8 +321,8 @@
 	inf->file = fget(ret);
 	fput(inf->file);
 
-	/* Add inference to inference list */
-	list_add(&inf->list, &edev->inference_list);
+	/* Add inference to pending list */
+	list_add(&inf->msg.list, &edev->mailbox.pending_list);
 
 	/* Send inference request to Arm Ethos-U subsystem */
 	(void)ethosu_inference_send(inf);
@@ -350,9 +375,9 @@
 	kref_get(&inf->kref);
 }
 
-void ethosu_inference_put(struct ethosu_inference *inf)
+int ethosu_inference_put(struct ethosu_inference *inf)
 {
-	kref_put(&inf->kref, &ethosu_inference_kref_destroy);
+	return kref_put(&inf->kref, &ethosu_inference_kref_destroy);
 }
 
 void ethosu_inference_rsp(struct ethosu_device *edev,
@@ -363,7 +388,7 @@
 	int ret;
 	int i;
 
-	ret = ethosu_inference_find(inf, &edev->inference_list);
+	ret = ethosu_mailbox_find(&edev->mailbox, &inf->msg);
 	if (ret) {
 		dev_warn(edev->dev,
 			 "Handle not found in inference list. handle=0x%p\n",
@@ -372,8 +397,6 @@
 		return;
 	}
 
-	inf->pending = false;
-
 	if (rsp->status == ETHOSU_CORE_STATUS_OK &&
 	    inf->ofm_count <= ETHOSU_CORE_BUFFER_MAX) {
 		uint32_t i;
@@ -412,6 +435,8 @@
 		 "PMU cycle counter. enable=%u, count=%llu\n",
 		 inf->pmu_cycle_counter_enable,
 		 inf->pmu_cycle_counter_count);
+
+	inf->done = true;
 	wake_up_interruptible(&inf->waitq);
 
 	ethosu_inference_put(inf);
diff --git a/kernel/ethosu_inference.h b/kernel/ethosu_inference.h
index a41043a..66d4ff9 100644
--- a/kernel/ethosu_inference.h
+++ b/kernel/ethosu_inference.h
@@ -25,6 +25,7 @@
  * Includes
  ****************************************************************************/
 
+#include "ethosu_mailbox.h"
 #include "uapi/ethosu.h"
 
 #include <linux/kref.h>
@@ -48,10 +49,10 @@
  * @file:			File handle
  * @kref:			Reference counter
  * @waitq:			Wait queue
+ * @done:			Wait condition is done
  * @ifm:			Pointer to IFM buffer
  * @ofm:			Pointer to OFM buffer
  * @net:			Pointer to network
- * @pending:			Pending response from the firmware
  * @status:			Inference status
  * @pmu_event_config:		PMU event configuration
  * @pmu_event_count:		PMU event count after inference
@@ -59,22 +60,22 @@
  * @pmu_cycle_counter_count:	PMU cycle counter count after inference
  */
 struct ethosu_inference {
-	struct ethosu_device    *edev;
-	struct file             *file;
-	struct kref             kref;
-	wait_queue_head_t       waitq;
-	uint32_t                ifm_count;
-	struct ethosu_buffer    *ifm[ETHOSU_FD_MAX];
-	uint32_t                ofm_count;
-	struct ethosu_buffer    *ofm[ETHOSU_FD_MAX];
-	struct ethosu_network   *net;
-	bool                    pending;
-	enum ethosu_uapi_status status;
-	uint8_t                 pmu_event_config[ETHOSU_PMU_EVENT_MAX];
-	uint32_t                pmu_event_count[ETHOSU_PMU_EVENT_MAX];
-	uint32_t                pmu_cycle_counter_enable;
-	uint64_t                pmu_cycle_counter_count;
-	struct list_head        list;
+	struct ethosu_device      *edev;
+	struct file               *file;
+	struct kref               kref;
+	wait_queue_head_t         waitq;
+	bool                      done;
+	uint32_t                  ifm_count;
+	struct ethosu_buffer      *ifm[ETHOSU_FD_MAX];
+	uint32_t                  ofm_count;
+	struct ethosu_buffer      *ofm[ETHOSU_FD_MAX];
+	struct ethosu_network     *net;
+	enum ethosu_uapi_status   status;
+	uint8_t                   pmu_event_config[ETHOSU_PMU_EVENT_MAX];
+	uint32_t                  pmu_event_count[ETHOSU_PMU_EVENT_MAX];
+	uint32_t                  pmu_cycle_counter_enable;
+	uint64_t                  pmu_cycle_counter_count;
+	struct ethosu_mailbox_msg msg;
 };
 
 /****************************************************************************
@@ -108,8 +109,10 @@
 
 /**
  * ethosu_inference_put() - Put inference
+ *
+ * Return: 1 if object was removed, else 0.
  */
-void ethosu_inference_put(struct ethosu_inference *inf);
+int ethosu_inference_put(struct ethosu_inference *inf);
 
 /**
  * ethosu_inference_rsp() - Handle inference response
diff --git a/kernel/ethosu_mailbox.c b/kernel/ethosu_mailbox.c
index 7753baa..7355361 100644
--- a/kernel/ethosu_mailbox.c
+++ b/kernel/ethosu_mailbox.c
@@ -23,16 +23,29 @@
  ****************************************************************************/
 
 #include "ethosu_mailbox.h"
-#include "ethosu_watchdog.h"
 
 #include "ethosu_buffer.h"
 #include "ethosu_core_interface.h"
 #include "ethosu_device.h"
+#include "ethosu_watchdog.h"
 
+#include <linux/jiffies.h>
 #include <linux/resource.h>
 #include <linux/uio.h>
 
 /****************************************************************************
+ * Includes
+ ****************************************************************************/
+
+#ifndef fallthrough
+#if __has_attribute(__fallthrough__)
+#define fallthrough __attribute__((__fallthrough__))
+#else
+#define fallthrough do {} while (0)  /* fallthrough */
+#endif
+#endif
+
+/****************************************************************************
  * Functions
  ****************************************************************************/
 
@@ -41,10 +54,9 @@
 {
 	switch (type) {
 	case ETHOSU_CORE_MSG_PING:
+		mbox->ping_count++;
+		fallthrough;
 	case ETHOSU_CORE_MSG_INFERENCE_REQ:
-	case ETHOSU_CORE_MSG_VERSION_REQ:
-	case ETHOSU_CORE_MSG_CAPABILITIES_REQ:
-	case ETHOSU_CORE_MSG_NETWORK_INFO_REQ:
 		ethosu_watchdog_inc(mbox->wdog);
 		break;
 	default:
@@ -57,10 +69,9 @@
 {
 	switch (type) {
 	case ETHOSU_CORE_MSG_PONG:
+		mbox->ping_count--;
+		fallthrough;
 	case ETHOSU_CORE_MSG_INFERENCE_RSP:
-	case ETHOSU_CORE_MSG_VERSION_RSP:
-	case ETHOSU_CORE_MSG_CAPABILITIES_RSP:
-	case ETHOSU_CORE_MSG_NETWORK_INFO_RSP:
 		ethosu_watchdog_dec(mbox->wdog);
 		break;
 	default:
@@ -190,6 +201,36 @@
 	mbox->out_queue->header.read = mbox->out_queue->header.write;
 }
 
+void ethosu_mailbox_wait_prepare(struct ethosu_mailbox *mbox)
+{
+	mbox->in_queue->header.size = 0;
+	mbox->in_queue->header.read = 0xffffff;
+	mbox->in_queue->header.write = 0xffffff;
+}
+
+int ethosu_mailbox_wait_firmware(struct ethosu_mailbox *mbox)
+{
+	const unsigned long timeout = 1000;
+	const unsigned long end = jiffies + msecs_to_jiffies(timeout);
+	volatile struct ethosu_core_queue_header *hdr =
+		&mbox->in_queue->header;
+	int ret = -ETIMEDOUT;
+
+	/* Spin wait on mailbox initialization */
+	while ((end - jiffies) < timeout)
+		if (hdr->size != 0 &&
+		    hdr->read != 0xffffff &&
+		    hdr->write != 0xffffff) {
+			ret = 0;
+			break;
+		}
+
+	dev_info(mbox->dev, "mbox: Wait. ret=%d, size=%u, read=%u, write=%u",
+		 ret, hdr->size, hdr->read, hdr->write);
+
+	return ret;
+}
+
 int ethosu_mailbox_read(struct ethosu_mailbox *mbox,
 			struct ethosu_core_msg *header,
 			void *data,
@@ -241,6 +282,45 @@
 	return 0;
 }
 
+int ethosu_mailbox_find(struct ethosu_mailbox *mbox,
+			struct ethosu_mailbox_msg *msg)
+{
+	struct ethosu_mailbox_msg *cur;
+
+	list_for_each_entry(cur, &mbox->pending_list, list) {
+		if (cur == msg)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+void ethosu_mailbox_fail(struct ethosu_mailbox *mbox)
+{
+	struct ethosu_mailbox_msg *cur, *cur_tmp;
+
+	list_for_each_entry_safe(cur, cur_tmp, &mbox->pending_list, list) {
+		cur->fail(cur);
+	}
+}
+
+int ethosu_mailbox_resend(struct ethosu_mailbox *mbox)
+{
+	struct ethosu_mailbox_msg *cur, *cur_tmp;
+	int ret;
+
+	list_for_each_entry_safe(cur, cur_tmp, &mbox->pending_list, list) {
+		ret = cur->resend(cur);
+		if (ret) {
+			cur->fail(cur);
+
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 int ethosu_mailbox_ping(struct ethosu_mailbox *mbox)
 {
 	return ethosu_queue_write_msg(mbox, ETHOSU_CORE_MSG_PING, NULL, 0);
@@ -380,6 +460,8 @@
 	mbox->callback = callback;
 	mbox->user_arg = user_arg;
 	mbox->wdog = wdog;
+	mbox->ping_count = 0;
+	INIT_LIST_HEAD(&mbox->pending_list);
 
 	mbox->client.dev = dev;
 	mbox->client.rx_callback = ethosu_mailbox_rx_callback;
diff --git a/kernel/ethosu_mailbox.h b/kernel/ethosu_mailbox.h
index 956685b..55d4436 100644
--- a/kernel/ethosu_mailbox.h
+++ b/kernel/ethosu_mailbox.h
@@ -55,7 +55,15 @@
 	struct mbox_chan         *tx;
 	ethosu_mailbox_cb        callback;
 	void                     *user_arg;
+	struct list_head         pending_list;
 	struct ethosu_watchdog   *wdog;
+	unsigned                 ping_count;
+};
+
+struct ethosu_mailbox_msg {
+	struct list_head list;
+	void             (*fail)(struct ethosu_mailbox_msg *msg);
+	int              (*resend)(struct ethosu_mailbox_msg *msg);
 };
 
 /****************************************************************************
@@ -81,6 +89,26 @@
 void ethosu_mailbox_deinit(struct ethosu_mailbox *mbox);
 
 /**
+ * ethosu_mailbox_wait_prepare() - Prepare to wait on firmware
+ *
+ * This function must only be called when the firmware is in a
+ * stopped state. It invalidates the firmware queues setting
+ * size, read and write positions to illegal values.
+ */
+void ethosu_mailbox_wait_prepare(struct ethosu_mailbox *mbox);
+
+/**
+ * ethosu_mailbox_wait_firmware() - Waiting for firmware to initialize
+ *                                  message queues
+ *
+ * Following a call to ethosu_mailbox_wait_prepare() this function waits for
+ * the firmware to boot up and initialize the firmware queues.
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_mailbox_wait_firmware(struct ethosu_mailbox *mbox);
+
+/**
  * ethosu_mailbox_read() - Read message from mailbox
  *
  * Return: 0 message read, else error code.
@@ -91,6 +119,30 @@
 			size_t length);
 
 /**
+ * ethosu_mailbox_find() - Find mailbox message
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_mailbox_find(struct ethosu_mailbox *mbox,
+			struct ethosu_mailbox_msg *msg);
+
+/**
+ * ethosu_mailbox_fail() - Fail mailbox messages
+ *
+ * Call fail() callback on all messages in pending list.
+ */
+void ethosu_mailbox_fail(struct ethosu_mailbox *mbox);
+
+/**
+ * ethosu_mailbox_resend() - Resend mailbox messages
+ *
+ * Call resend() callback on all messages in pending list.
+ *
+ * Return: 0 on success, else error code.
+ */
+int ethosu_mailbox_resend(struct ethosu_mailbox *mbox);
+
+/**
  * ethosu_mailbox_reset() - Reset to end of queue
  */
 void ethosu_mailbox_reset(struct ethosu_mailbox *mbox);
diff --git a/kernel/ethosu_network.c b/kernel/ethosu_network.c
index 0654a79..86ae410 100644
--- a/kernel/ethosu_network.c
+++ b/kernel/ethosu_network.c
@@ -104,6 +104,9 @@
 	ret = ethosu_network_info_wait(info, 3000);
 	mutex_lock(&net->edev->mutex);
 
+	if (ret)
+		info->msg.fail(&info->msg);
+
 	ethosu_network_info_put(info);
 
 	return ret;
@@ -240,7 +243,7 @@
 	kref_get(&net->kref);
 }
 
-void ethosu_network_put(struct ethosu_network *net)
+int ethosu_network_put(struct ethosu_network *net)
 {
-	kref_put(&net->kref, ethosu_network_destroy);
+	return kref_put(&net->kref, ethosu_network_destroy);
 }
diff --git a/kernel/ethosu_network.h b/kernel/ethosu_network.h
index 7ddffb5..8ee6818 100644
--- a/kernel/ethosu_network.h
+++ b/kernel/ethosu_network.h
@@ -76,7 +76,9 @@
 
 /**
  * ethosu_network_put() - Put network
+ *
+ * Return: 1 if object was removed, else 0.
  */
-void ethosu_network_put(struct ethosu_network *net);
+int ethosu_network_put(struct ethosu_network *net);
 
 #endif /* ETHOSU_NETWORK_H */
diff --git a/kernel/ethosu_network_info.c b/kernel/ethosu_network_info.c
index 24be677..c2b6caa 100644
--- a/kernel/ethosu_network_info.c
+++ b/kernel/ethosu_network_info.c
@@ -40,7 +40,7 @@
 
 	dev_info(info->edev->dev, "Network info destroy. handle=0x%pK\n", info);
 
-	list_del(&info->list);
+	list_del(&info->msg.list);
 
 	ethosu_network_put(info->net);
 
@@ -59,8 +59,35 @@
 	if (ret)
 		return ret;
 
-	/* Increase reference count for the pending network info response */
-	ethosu_network_info_get(info);
+	return 0;
+}
+
+static void ethosu_network_info_fail(struct ethosu_mailbox_msg *msg)
+{
+	struct ethosu_network_info *info =
+		container_of(msg, typeof(*info), msg);
+
+	if (completion_done(&info->done))
+		return;
+
+	info->errno = -EFAULT;
+	complete(&info->done);
+}
+
+static int ethosu_network_info_resend(struct ethosu_mailbox_msg *msg)
+{
+	struct ethosu_network_info *info =
+		container_of(msg, typeof(*info), msg);
+	int ret;
+
+	/* Don't resend request if response has already been received */
+	if (completion_done(&info->done))
+		return 0;
+
+	/* Resend request */
+	ret = ethosu_network_info_send(info);
+	if (ret)
+		return ret;
 
 	return 0;
 }
@@ -82,9 +109,11 @@
 	info->uapi = uapi;
 	kref_init(&info->kref);
 	init_completion(&info->done);
+	info->msg.fail = ethosu_network_info_fail;
+	info->msg.resend = ethosu_network_info_resend;
 
 	/* Insert network info to network info list */
-	list_add(&info->list, &edev->network_info_list);
+	list_add(&info->msg.list, &edev->mailbox.pending_list);
 
 	/* Get reference to network */
 	ethosu_network_get(net);
@@ -103,27 +132,14 @@
 	return ERR_PTR(ret);
 }
 
-static int ethosu_network_info_find(struct ethosu_network_info *info,
-				    struct list_head *network_info_list)
-{
-	struct ethosu_network_info *cur;
-
-	list_for_each_entry(cur, network_info_list, list) {
-		if (cur == info)
-			return 0;
-	}
-
-	return -EINVAL;
-}
-
 void ethosu_network_info_get(struct ethosu_network_info *info)
 {
 	kref_get(&info->kref);
 }
 
-void ethosu_network_info_put(struct ethosu_network_info *info)
+int ethosu_network_info_put(struct ethosu_network_info *info)
 {
-	kref_put(&info->kref, ethosu_network_info_destroy);
+	return kref_put(&info->kref, ethosu_network_info_destroy);
 }
 
 int ethosu_network_info_wait(struct ethosu_network_info *info,
@@ -152,7 +168,7 @@
 	uint32_t i;
 	int ret;
 
-	ret = ethosu_network_info_find(info, &edev->network_info_list);
+	ret = ethosu_mailbox_find(&edev->mailbox, &info->msg);
 	if (0 != ret) {
 		dev_warn(edev->dev,
 			 "Handle not found in network info list. handle=0x%p\n",
@@ -161,6 +177,9 @@
 		return;
 	}
 
+	if (completion_done(&info->done))
+		return;
+
 	info->errno = 0;
 
 	if (rsp->status != ETHOSU_CORE_STATUS_OK) {
@@ -185,6 +204,4 @@
 
 signal_complete:
 	complete(&info->done);
-
-	ethosu_network_info_put(info);
 }
diff --git a/kernel/ethosu_network_info.h b/kernel/ethosu_network_info.h
index 2a333a6..680be80 100644
--- a/kernel/ethosu_network_info.h
+++ b/kernel/ethosu_network_info.h
@@ -26,6 +26,7 @@
  ****************************************************************************/
 
 #include "ethosu_core_interface.h"
+#include "ethosu_mailbox.h"
 
 #include <linux/kref.h>
 #include <linux/types.h>
@@ -44,9 +45,9 @@
 	struct ethosu_network           *net;
 	struct ethosu_uapi_network_info *uapi;
 	struct kref                     kref;
-	struct list_head                list;
 	struct completion               done;
 	int                             errno;
+	struct ethosu_mailbox_msg       msg;
 };
 
 /****************************************************************************
@@ -72,8 +73,10 @@
 
 /**
  * ethosu_network_info_put() - Put network info
+ *
+ * Return: 1 if object was removed, else 0.
  */
-void ethosu_network_info_put(struct ethosu_network_info *info);
+int ethosu_network_info_put(struct ethosu_network_info *info);
 
 /**
  * ethosu_network_info_wait() - Get network info