| From 264bcf13067c2982f1a3188cb3a3bfd112cb6d68 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 19 Mar 2021 14:15:35 +0800 |
| Subject: mailbox: sprd: Introduce refcnt when clients requests/free channels |
| |
| From: Orson Zhai <orson.zhai@unisoc.com> |
| |
| [ Upstream commit 9468ab84032f96496e998cfa173cd1d0ac316bcd ] |
| |
| Unisoc mailbox has no way to be enabled/disabled for any single channel. |
| They can only be set to startup or shutdown as a whole device at same time. |
| |
| Add a variable to count references to avoid mailbox FIFO being reset |
| unexpectedly when clients are requesting or freeing channels. |
| |
| Also add a lock to dismiss possible conflicts from register r/w in |
| different startup or shutdown threads. And fix the crash problem when early |
| interrupts come from channel which has not been requested by client yet. |
| |
| Fixes: ca27fc26cd22 ("mailbox: sprd: Add Spreadtrum mailbox driver") |
| Signed-off-by: Orson Zhai <orson.zhai@unisoc.com> |
| Reviewed-by: Baolin Wang <baolin.wang7@gmail.com> |
| Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/mailbox/sprd-mailbox.c | 43 +++++++++++++++++++++++----------- |
| 1 file changed, 29 insertions(+), 14 deletions(-) |
| |
| diff --git a/drivers/mailbox/sprd-mailbox.c b/drivers/mailbox/sprd-mailbox.c |
| index 4c325301a2fe..94d9067dc8d0 100644 |
| --- a/drivers/mailbox/sprd-mailbox.c |
| +++ b/drivers/mailbox/sprd-mailbox.c |
| @@ -60,6 +60,8 @@ struct sprd_mbox_priv { |
| struct clk *clk; |
| u32 outbox_fifo_depth; |
| |
| + struct mutex lock; |
| + u32 refcnt; |
| struct mbox_chan chan[SPRD_MBOX_CHAN_MAX]; |
| }; |
| |
| @@ -115,7 +117,11 @@ static irqreturn_t sprd_mbox_outbox_isr(int irq, void *data) |
| id = readl(priv->outbox_base + SPRD_MBOX_ID); |
| |
| chan = &priv->chan[id]; |
| - mbox_chan_received_data(chan, (void *)msg); |
| + if (chan->cl) |
| + mbox_chan_received_data(chan, (void *)msg); |
| + else |
| + dev_warn_ratelimited(priv->dev, |
| + "message's been dropped at ch[%d]\n", id); |
| |
| /* Trigger to update outbox FIFO pointer */ |
| writel(0x1, priv->outbox_base + SPRD_MBOX_TRIGGER); |
| @@ -215,18 +221,22 @@ static int sprd_mbox_startup(struct mbox_chan *chan) |
| struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); |
| u32 val; |
| |
| - /* Select outbox FIFO mode and reset the outbox FIFO status */ |
| - writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); |
| + mutex_lock(&priv->lock); |
| + if (priv->refcnt++ == 0) { |
| + /* Select outbox FIFO mode and reset the outbox FIFO status */ |
| + writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); |
| |
| - /* Enable inbox FIFO overflow and delivery interrupt */ |
| - val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); |
| - val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); |
| - writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); |
| + /* Enable inbox FIFO overflow and delivery interrupt */ |
| + val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); |
| + val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); |
| + writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); |
| |
| - /* Enable outbox FIFO not empty interrupt */ |
| - val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); |
| - val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; |
| - writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); |
| + /* Enable outbox FIFO not empty interrupt */ |
| + val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); |
| + val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; |
| + writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); |
| + } |
| + mutex_unlock(&priv->lock); |
| |
| return 0; |
| } |
| @@ -235,9 +245,13 @@ static void sprd_mbox_shutdown(struct mbox_chan *chan) |
| { |
| struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); |
| |
| - /* Disable inbox & outbox interrupt */ |
| - writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK); |
| - writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK); |
| + mutex_lock(&priv->lock); |
| + if (--priv->refcnt == 0) { |
| + /* Disable inbox & outbox interrupt */ |
| + writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK); |
| + writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK); |
| + } |
| + mutex_unlock(&priv->lock); |
| } |
| |
| static const struct mbox_chan_ops sprd_mbox_ops = { |
| @@ -266,6 +280,7 @@ static int sprd_mbox_probe(struct platform_device *pdev) |
| return -ENOMEM; |
| |
| priv->dev = dev; |
| + mutex_init(&priv->lock); |
| |
| /* |
| * The Spreadtrum mailbox uses an inbox to send messages to the target |
| -- |
| 2.30.2 |
| |