drivers: mailbox: retry on -EAGAIN

Mailbox framework makes requests to the underlying drivers by holding a
spinlock that prevents interrupts from happening at the same time. If
they both happen to occur on the same CPU, then the interrupt handler
would never get a chance to run. In cases where the request is pending
because the interrupt handler never got a chance to run and clear the
status of the controller, the msg_submit() call would fail and the
request would go back to the mailbox's queue. Additionally, in such a
case, if there are no following requests on the same mailbox client, the
request will remain pending forever in the mailbox queue. This would
hang the thread that is waiting on the completion of the request.

Hence return -EAGAIN from the mailbox controller driver and handle the
error condition as a special case, releasing the spinlock held by the
framework and retrying again.

Change-Id: Ia66031dc33650c5f392fc9ec538fc882678810f2
Signed-off-by: Lina Iyer <ilina@codeaurora.org>
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 6c7f6c4..d2cb1e8 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -60,6 +60,7 @@
 	void *data;
 	int err = -EBUSY;
 
+again:
 	spin_lock_irqsave(&chan->lock, flags);
 
 	if (!chan->msg_count || chan->active_req)
@@ -85,6 +86,16 @@
 exit:
 	spin_unlock_irqrestore(&chan->lock, flags);
 
+	/*
+	 * If the controller returns -EAGAIN, then it means, our spinlock
+	 * here is preventing the controller from receiving its interrupt,
+	 * that would help clear the controller channels that are currently
+	 * blocked waiting on the interrupt response.
+	 * Unlock and retry again.
+	 */
+	if (err == -EAGAIN)
+		goto again;
+
 	if (!err && (chan->txdone_method & TXDONE_BY_POLL))
 		/* kick start the timer immediately to avoid delays */
 		hrtimer_start(&chan->mbox->poll_hrt, ktime_set(0, 0),