summaryrefslogtreecommitdiff
path: root/drivers/mailbox/apple-mailbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mailbox/apple-mailbox.c')
-rw-r--r--drivers/mailbox/apple-mailbox.c104
1 files changed, 93 insertions, 11 deletions
diff --git a/drivers/mailbox/apple-mailbox.c b/drivers/mailbox/apple-mailbox.c
index 496c4951ccb1..06c7ad3b1c03 100644
--- a/drivers/mailbox/apple-mailbox.c
+++ b/drivers/mailbox/apple-mailbox.c
@@ -17,6 +17,7 @@
*/
#include <linux/apple-mailbox.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
@@ -25,6 +26,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/spinlock.h>
#include <linux/types.h>
#define APPLE_ASC_MBOX_CONTROL_FULL BIT(16)
@@ -100,6 +102,9 @@ struct apple_mbox {
struct device *dev;
struct mbox_controller controller;
+ spinlock_t rx_lock;
+ spinlock_t tx_lock;
+ bool tx_pending;
};
static const struct of_device_id apple_mbox_of_match[];
@@ -112,6 +117,14 @@ static bool apple_mbox_hw_can_send(struct apple_mbox *apple_mbox)
return !(mbox_ctrl & apple_mbox->hw->control_full);
}
+static bool apple_mbox_hw_send_empty(struct apple_mbox *apple_mbox)
+{
+ u32 mbox_ctrl =
+ readl_relaxed(apple_mbox->regs + apple_mbox->hw->a2i_control);
+
+ return mbox_ctrl & apple_mbox->hw->control_empty;
+}
+
static int apple_mbox_hw_send(struct apple_mbox *apple_mbox,
struct apple_mbox_msg *msg)
{
@@ -155,11 +168,15 @@ static int apple_mbox_chan_send_data(struct mbox_chan *chan, void *data)
{
struct apple_mbox *apple_mbox = chan->con_priv;
struct apple_mbox_msg *msg = data;
+ unsigned long flags;
int ret;
+ spin_lock_irqsave(&apple_mbox->tx_lock, flags);
+ WARN_ON(apple_mbox->tx_pending);
+
ret = apple_mbox_hw_send(apple_mbox, msg);
if (ret)
- return ret;
+ goto err_unlock;
/*
* The interrupt is level triggered and will keep firing as long as the
@@ -174,9 +191,13 @@ static int apple_mbox_chan_send_data(struct mbox_chan *chan, void *data)
writel_relaxed(apple_mbox->hw->irq_bit_send_empty,
apple_mbox->regs + apple_mbox->hw->irq_ack);
}
+ apple_mbox->tx_pending = true;
enable_irq(apple_mbox->irq_send_empty);
- return 0;
+err_unlock:
+ spin_unlock_irqrestore(&apple_mbox->tx_lock, flags);
+
+ return ret;
}
static irqreturn_t apple_mbox_send_empty_irq(int irq, void *data)
@@ -191,17 +212,27 @@ static irqreturn_t apple_mbox_send_empty_irq(int irq, void *data)
* it at the main controller again.
*/
disable_irq_nosync(apple_mbox->irq_send_empty);
- mbox_chan_txdone(&apple_mbox->chan, 0);
+ spin_lock(&apple_mbox->tx_lock);
+ if (apple_mbox->tx_pending) {
+ apple_mbox->tx_pending = false;
+ spin_unlock(&apple_mbox->tx_lock);
+ mbox_chan_txdone(&apple_mbox->chan, 0);
+ } else {
+ spin_unlock(&apple_mbox->tx_lock);
+ }
return IRQ_HANDLED;
}
-static irqreturn_t apple_mbox_recv_irq(int irq, void *data)
+static int apple_mbox_poll(struct apple_mbox *apple_mbox)
{
- struct apple_mbox *apple_mbox = data;
+
struct apple_mbox_msg msg;
+ int ret = 0;
- while (apple_mbox_hw_recv(apple_mbox, &msg) == 0)
+ while (apple_mbox_hw_recv(apple_mbox, &msg) == 0) {
mbox_chan_received_data(&apple_mbox->chan, (void *)&msg);
+ ret++;
+ }
/*
* The interrupt will keep firing even if there are no more messages
@@ -216,9 +247,57 @@ static irqreturn_t apple_mbox_recv_irq(int irq, void *data)
apple_mbox->regs + apple_mbox->hw->irq_ack);
}
+ return ret;
+}
+
+static irqreturn_t apple_mbox_recv_irq(int irq, void *data)
+{
+ struct apple_mbox *apple_mbox = data;
+
+ spin_lock(&apple_mbox->rx_lock);
+ apple_mbox_poll(apple_mbox);
+ spin_unlock(&apple_mbox->rx_lock);
+
return IRQ_HANDLED;
}
+static bool apple_mbox_chan_peek_data(struct mbox_chan *chan)
+{
+ struct apple_mbox *apple_mbox = chan->con_priv;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&apple_mbox->rx_lock, flags);
+ ret = apple_mbox_poll(apple_mbox);
+ spin_unlock_irqrestore(&apple_mbox->rx_lock, flags);
+
+ return ret > 0;
+}
+
+static int apple_mbox_chan_flush(struct mbox_chan *chan, unsigned long timeout)
+{
+ struct apple_mbox *apple_mbox = chan->con_priv;
+ unsigned long deadline = jiffies + msecs_to_jiffies(timeout);
+ unsigned long flags;
+
+ while (time_before(jiffies, deadline)) {
+ if (apple_mbox_hw_send_empty(apple_mbox)) {
+ spin_lock_irqsave(&apple_mbox->tx_lock, flags);
+ if (apple_mbox->tx_pending) {
+ apple_mbox->tx_pending = false;
+ disable_irq_nosync(apple_mbox->irq_send_empty);
+ }
+ /* Mailbox subsystem will call txdone for us */
+ spin_unlock_irqrestore(&apple_mbox->tx_lock, flags);
+ return 0;
+ }
+
+ udelay(1);
+ }
+
+ return -ETIME;
+}
+
static int apple_mbox_chan_startup(struct mbox_chan *chan)
{
struct apple_mbox *apple_mbox = chan->con_priv;
@@ -250,6 +329,8 @@ static void apple_mbox_chan_shutdown(struct mbox_chan *chan)
static const struct mbox_chan_ops apple_mbox_ops = {
.send_data = apple_mbox_chan_send_data,
+ .peek_data = apple_mbox_chan_peek_data,
+ .flush = apple_mbox_chan_flush,
.startup = apple_mbox_chan_startup,
.shutdown = apple_mbox_chan_shutdown,
};
@@ -304,6 +385,8 @@ static int apple_mbox_probe(struct platform_device *pdev)
mbox->controller.txdone_irq = true;
mbox->controller.of_xlate = apple_mbox_of_xlate;
mbox->chan.con_priv = mbox;
+ spin_lock_init(&mbox->rx_lock);
+ spin_lock_init(&mbox->tx_lock);
irqname = devm_kasprintf(dev, GFP_KERNEL, "%s-recv", dev_name(dev));
if (!irqname)
@@ -311,8 +394,8 @@ static int apple_mbox_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(dev, mbox->irq_recv_not_empty, NULL,
apple_mbox_recv_irq,
- IRQF_NO_AUTOEN | IRQF_ONESHOT, irqname,
- mbox);
+ IRQF_NO_AUTOEN | IRQF_ONESHOT | IRQF_NO_SUSPEND,
+ irqname, mbox);
if (ret)
return ret;
@@ -320,9 +403,8 @@ static int apple_mbox_probe(struct platform_device *pdev)
if (!irqname)
return -ENOMEM;
- ret = devm_request_irq(dev, mbox->irq_send_empty,
- apple_mbox_send_empty_irq, IRQF_NO_AUTOEN,
- irqname, mbox);
+ ret = devm_request_irq(dev, mbox->irq_send_empty, apple_mbox_send_empty_irq,
+ IRQF_NO_AUTOEN | IRQF_NO_SUSPEND, irqname, mbox);
if (ret)
return ret;