diff options
-rw-r--r-- | arch/arm/mach-ux500/include/mach/mbox-db5500.h | 2 | ||||
-rw-r--r-- | drivers/misc/mbox.c | 125 |
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); |