summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-ux500/include/mach/mbox-db5500.h2
-rw-r--r--drivers/misc/mbox.c125
2 files changed, 115 insertions, 12 deletions
diff --git a/arch/arm/mach-ux500/include/mach/mbox-db5500.h b/arch/arm/mach-ux500/include/mach/mbox-db5500.h
index 24af854befe..2da180b8df6 100644
--- a/arch/arm/mach-ux500/include/mach/mbox-db5500.h
+++ b/arch/arm/mach-ux500/include/mach/mbox-db5500.h
@@ -89,5 +89,5 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv);
* specify "block" in order to block until send is possible).
*/
int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block);
-
+void mbox_state_reset(void);
#endif /*INC_STE_MBOX_H*/
diff --git a/drivers/misc/mbox.c b/drivers/misc/mbox.c
index 6e22653eed9..acea6962478 100644
--- a/drivers/misc/mbox.c
+++ b/drivers/misc/mbox.c
@@ -60,11 +60,13 @@
#define MBOX_LATCH 1
struct mbox_device_info {
+ struct mbox *mbox;
struct workqueue_struct *mbox_modem_rel_wq;
struct work_struct mbox_modem_rel;
struct completion mod_req_ack_work;
atomic_t ape_state;
atomic_t mod_req;
+ atomic_t mod_reset;
};
/* Global list of all mailboxes */
@@ -95,7 +97,7 @@ static void mbox_modem_rel_work(struct work_struct *work)
mutex_unlock(&modem_state_mutex);
}
-static void mbox_modem_req()
+static void mbox_modem_req(void)
{
mutex_lock(&modem_state_mutex);
if (!db5500_prcmu_is_modem_requested()) {
@@ -123,6 +125,11 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block)
int res = 0;
unsigned long flag;
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev,
+ "mbox_send called after modem reset\n");
+ return -EINVAL;
+ }
dev_dbg(&(mbox->pdev->dev),
"About to buffer 0x%X to mailbox 0x%X."
" ri = %d, wi = %d\n",
@@ -160,6 +167,12 @@ int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block)
* Indicate that we want an IRQ as soon as there is a slot
* in the FIFO
*/
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev,
+ "modem is in reset state, cannot proceed\n");
+ res -EINVAL;
+ goto exit;
+ }
writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
exit:
@@ -217,6 +230,10 @@ static ssize_t mbox_read_fifo(struct device *dev,
struct platform_device *pdev = to_platform_device(dev);
struct mbox *mbox = platform_get_drvdata(pdev);
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev, "modem crashed, returning\n");
+ return 0;
+ }
if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0)
return sprintf(buf, "Mailbox is empty\n");
@@ -251,6 +268,11 @@ static int mbox_show(struct seq_file *s, void *data)
continue;
}
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&m->pdev->dev, "modem crashed, returning\n");
+ spin_unlock(&m->lock);
+ return 0;
+ }
seq_printf(s,
"===========================\n"
" MAILBOX %d\n"
@@ -330,6 +352,10 @@ static irqreturn_t mbox_irq(int irq, void *arg)
int nbr_free;
struct mbox *mbox = (struct mbox *) arg;
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev, "modem in reset state\n");
+ return IRQ_HANDLED;
+ }
spin_lock(&mbox->lock);
dev_dbg(&(mbox->pdev->dev),
@@ -353,6 +379,11 @@ static irqreturn_t mbox_irq(int irq, void *arg)
while ((nbr_free > 0) &&
(mbox->read_index != mbox->write_index)) {
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev,
+ "modem in reset state\n");
+ goto exit;
+ }
/* Write the message and latch it into the FIFO */
writel(mbox->buffer[mbox->read_index],
(mbox->virtbase_peer + MBOX_FIFO_DATA));
@@ -368,6 +399,10 @@ static irqreturn_t mbox_irq(int irq, void *arg)
(mbox->read_index + 1) % MBOX_BUF_SIZE;
}
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev, "modem in reset state\n");
+ goto exit;
+ }
/*
* Check if we still want IRQ:s when there is free
* space to send
@@ -404,6 +439,10 @@ static irqreturn_t mbox_irq(int irq, void *arg)
hrtimer_start(&ape_timer, ktime_set(0, 10*NSEC_PER_MSEC),
HRTIMER_MODE_REL);
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev, "modem in reset state\n");
+ goto exit;
+ }
/* Check if we have any incoming messages */
nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7;
if (nbr_occup == 0)
@@ -417,6 +456,10 @@ redo:
}
atomic_set(&mb->ape_state, 1);
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev, "modem in reset state\n");
+ goto exit;
+ }
/* Read and acknowledge the message */
mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA);
writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE));
@@ -444,13 +487,20 @@ exit:
static void mbox_shutdown(struct mbox *mbox)
{
+ if (!mbox->allocated)
+ return;
#if defined(CONFIG_DEBUG_FS)
debugfs_remove(mbox->dentry);
device_remove_file(&mbox->pdev->dev, &dev_attr_fifo);
#endif
- writel(MBOX_DISABLE_IRQ, mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP);
- writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
- free_irq(mbox->irq, NULL);
+ /* TODO: Need to check if we can write after modem reset */
+ if (!atomic_read(&mb->mod_reset)) {
+ writel(MBOX_DISABLE_IRQ, mbox->virtbase_local +
+ MBOX_FIFO_THRES_OCCUP);
+ writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer +
+ MBOX_FIFO_THRES_FREE);
+ }
+ free_irq(mbox->irq, (void *)mbox);
mbox->client_blocked = 0;
iounmap(mbox->virtbase_local);
iounmap(mbox->virtbase_peer);
@@ -459,11 +509,39 @@ static void mbox_shutdown(struct mbox *mbox)
mbox->allocated = false;
}
-void mbox_reset_state(struct mbox *mbox)
+/** mbox_state_reset - Reset the mailbox state machine
+ *
+ * This function is called on receiving modem reset interrupt. Reset all
+ * the mailbox state machine, disable irq, cancel timers, shutdown the
+ * mailboxs and re-enable irq's.
+ */
+void mbox_state_reset(void)
{
+ struct mbox *mbox = mb->mbox;
+
+ /* Common for all mailbox */
+ atomic_set(&mb->mod_reset, 1);
+
+ /* Disable IRQ */
+ disable_irq_nosync(IRQ_DB5500_PRCMU_APE_REQ);
+ disable_irq_nosync(IRQ_DB5500_PRCMU_AC_WAKE_ACK);
+
+ /* Cancel sleep_req timers */
+ hrtimer_cancel(&modem_timer);
+ hrtimer_cancel(&ape_timer);
+
+ /* specific to each mailbox */
list_for_each_entry(mbox, &mboxs, list) {
mbox_shutdown(mbox);
}
+
+ /* Reset mailbox state machine */
+ atomic_set(&mb->mod_req, 0);
+ atomic_set(&mb->ape_state, 0);
+
+ /* Enable irq */
+ enable_irq(IRQ_DB5500_PRCMU_APE_REQ);
+ enable_irq(IRQ_DB5500_PRCMU_AC_WAKE_ACK);
}
@@ -474,6 +552,13 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
int res;
struct mbox *mbox;
+ /*
+ * set mod_reset flag to '0', clients calling this APE should make sure
+ * that modem is rebooted after MSR. Mailbox doesnt have any means of
+ * knowing the boot status of modem.
+ */
+ atomic_set(&mb->mod_reset, 0);
+
mbox = get_mbox_with_id(mbox_id);
if (mbox == NULL) {
dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n",
@@ -507,7 +592,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
dev_err(&(mbox->pdev->dev),
"Unable to retrieve mbox peer resource\n");
mbox = NULL;
- goto exit;
+ goto free_mbox;
}
dev_dbg(&(mbox->pdev->dev),
"Resource name: %s start: 0x%X, end: 0x%X\n",
@@ -516,7 +601,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
if (!mbox->virtbase_peer) {
dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n");
mbox = NULL;
- goto exit;
+ goto free_mbox;
}
dev_dbg(&(mbox->pdev->dev),
"ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n",
@@ -530,7 +615,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
dev_err(&(mbox->pdev->dev),
"Unable to retrieve mbox local resource\n");
mbox = NULL;
- goto exit;
+ goto free_map;
}
dev_dbg(&(mbox->pdev->dev),
"Resource name: %s start: 0x%X, end: 0x%X\n",
@@ -539,7 +624,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
if (!mbox->virtbase_local) {
dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n");
mbox = NULL;
- goto exit;
+ goto free_map;
}
dev_dbg(&(mbox->pdev->dev),
"ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n",
@@ -554,7 +639,7 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
dev_err(&(mbox->pdev->dev),
"Unable to retrieve mbox irq resource\n");
mbox = NULL;
- goto exit;
+ goto free_map1;
}
dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", mbox->irq);
@@ -568,6 +653,13 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
goto exit;
}
+ /* check if modem has reset */
+ if (atomic_read(&mb->mod_reset)) {
+ dev_err(&mbox->pdev->dev,
+ "modem is in reset state, cannot proceed\n");
+ mbox = NULL;
+ goto free_irq;
+ }
/* Set up mailbox to not launch IRQ on free space in mailbox */
writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
@@ -592,10 +684,19 @@ struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
mbox->dentry = debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL,
NULL, &mbox_operations);
#endif
-
dev_info(&(mbox->pdev->dev),
"Mailbox driver with index %d initiated!\n", mbox_id);
+ return mbox;
+free_irq:
+ free_irq(mbox->irq, (void *)mbox);
+free_map1:
+ iounmap(mbox->virtbase_local);
+free_map:
+ iounmap(mbox->virtbase_peer);
+free_mbox:
+ mbox->client_data = NULL;
+ mbox->cb = NULL;
exit:
return mbox;
}
@@ -637,6 +738,7 @@ int __init mbox_probe(struct platform_device *pdev)
spin_lock_init(&mbox->lock);
platform_set_drvdata(pdev, mbox);
+ mb->mbox = mbox;
dev_info(&(pdev->dev), "Mailbox driver loaded\n");
return res;
@@ -736,6 +838,7 @@ static int __init mbox_init(void)
atomic_set(&mb_di->ape_state, 0);
atomic_set(&mb_di->mod_req, 0);
+ atomic_set(&mb_di->mod_reset, 0);
err = request_irq(IRQ_DB5500_PRCMU_APE_REQ, mbox_prcmu_ape_req_handler,
IRQF_NO_SUSPEND, "ape_req", NULL);