summaryrefslogtreecommitdiffstats
path: root/drivers/mailbox
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mailbox')
-rw-r--r--drivers/mailbox/ti-msgmgr.c93
1 files changed, 91 insertions, 2 deletions
diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c
index f860cd0c907a..ddac423ac1a9 100644
--- a/drivers/mailbox/ti-msgmgr.c
+++ b/drivers/mailbox/ti-msgmgr.c
@@ -2,7 +2,7 @@
/*
* Texas Instruments' Message Manager Driver
*
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/
+ * Copyright (C) 2015-2022 Texas Instruments Incorporated - https://www.ti.com/
* Nishanth Menon
*/
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/mailbox_controller.h>
#include <linux/module.h>
@@ -100,6 +101,7 @@ struct ti_msgmgr_desc {
* @queue_ctrl: Queue Control register
* @chan: Mailbox channel
* @rx_buff: Receive buffer pointer allocated at probe, max_message_size
+ * @polled_rx_mode: Use polling for rx instead of interrupts
*/
struct ti_queue_inst {
char name[30];
@@ -113,6 +115,7 @@ struct ti_queue_inst {
void __iomem *queue_ctrl;
struct mbox_chan *chan;
u32 *rx_buff;
+ bool polled_rx_mode;
};
/**
@@ -237,6 +240,26 @@ static int ti_msgmgr_queue_rx_data(struct mbox_chan *chan, struct ti_queue_inst
return 0;
}
+static int ti_msgmgr_queue_rx_poll_timeout(struct mbox_chan *chan, int timeout_us)
+{
+ struct device *dev = chan->mbox->dev;
+ struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
+ struct ti_queue_inst *qinst = chan->con_priv;
+ const struct ti_msgmgr_desc *desc = inst->desc;
+ int msg_count;
+ int ret;
+
+ ret = readl_poll_timeout_atomic(qinst->queue_state, msg_count,
+ (msg_count & desc->status_cnt_mask),
+ 10, timeout_us);
+ if (ret != 0)
+ return ret;
+
+ ti_msgmgr_queue_rx_data(chan, qinst, desc);
+
+ return 0;
+}
+
/**
* ti_msgmgr_queue_rx_interrupt() - Interrupt handler for receive Queue
* @irq: Interrupt number
@@ -346,6 +369,17 @@ static bool ti_msgmgr_last_tx_done(struct mbox_chan *chan)
return msg_count ? false : true;
}
+static bool ti_msgmgr_chan_has_polled_queue_rx(struct mbox_chan *chan)
+{
+ struct ti_queue_inst *qinst;
+
+ if (!chan)
+ return false;
+
+ qinst = chan->con_priv;
+ return qinst->polled_rx_mode;
+}
+
/**
* ti_msgmgr_send_data() - Send data
* @chan: Channel Pointer
@@ -363,6 +397,7 @@ static int ti_msgmgr_send_data(struct mbox_chan *chan, void *data)
struct ti_msgmgr_message *message = data;
void __iomem *data_reg;
u32 *word_data;
+ int ret = 0;
if (WARN_ON(!inst)) {
dev_err(dev, "no platform drv data??\n");
@@ -404,7 +439,12 @@ static int ti_msgmgr_send_data(struct mbox_chan *chan, void *data)
if (data_reg <= qinst->queue_buff_end)
writel(0, qinst->queue_buff_end);
- return 0;
+ /* If we are in polled mode, wait for a response before proceeding */
+ if (ti_msgmgr_chan_has_polled_queue_rx(message->chan_rx))
+ ret = ti_msgmgr_queue_rx_poll_timeout(message->chan_rx,
+ message->timeout_rx_ms * 1000);
+
+ return ret;
}
/**
@@ -652,6 +692,54 @@ static int ti_msgmgr_queue_setup(int idx, struct device *dev,
return 0;
}
+static int ti_msgmgr_queue_rx_set_polled_mode(struct ti_queue_inst *qinst, bool enable)
+{
+ if (enable) {
+ disable_irq(qinst->irq);
+ qinst->polled_rx_mode = true;
+ } else {
+ enable_irq(qinst->irq);
+ qinst->polled_rx_mode = false;
+ }
+
+ return 0;
+}
+
+static int ti_msgmgr_suspend(struct device *dev)
+{
+ struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
+ struct ti_queue_inst *qinst;
+ int i;
+
+ /*
+ * We must switch operation to polled mode now as drivers and the genpd
+ * layer may make late TI SCI calls to change clock and device states
+ * from the noirq phase of suspend.
+ */
+ for (qinst = inst->qinsts, i = 0; i < inst->num_valid_queues; qinst++, i++) {
+ if (!qinst->is_tx)
+ ti_msgmgr_queue_rx_set_polled_mode(qinst, true);
+ }
+
+ return 0;
+}
+
+static int ti_msgmgr_resume(struct device *dev)
+{
+ struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
+ struct ti_queue_inst *qinst;
+ int i;
+
+ for (qinst = inst->qinsts, i = 0; i < inst->num_valid_queues; qinst++, i++) {
+ if (!qinst->is_tx)
+ ti_msgmgr_queue_rx_set_polled_mode(qinst, false);
+ }
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ti_msgmgr_pm_ops, ti_msgmgr_suspend, ti_msgmgr_resume);
+
/* Queue operations */
static const struct mbox_chan_ops ti_msgmgr_chan_ops = {
.startup = ti_msgmgr_queue_startup,
@@ -839,6 +927,7 @@ static struct platform_driver ti_msgmgr_driver = {
.driver = {
.name = "ti-msgmgr",
.of_match_table = of_match_ptr(ti_msgmgr_of_match),
+ .pm = &ti_msgmgr_pm_ops,
},
};
module_platform_driver(ti_msgmgr_driver);