diff options
Diffstat (limited to 'drivers/mailbox/apple-mailbox.c')
-rw-r--r-- | drivers/mailbox/apple-mailbox.c | 104 |
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; |