Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mailbox: sprd: Introduce refcnt when clients requests/free channels

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>

authored by

Orson Zhai and committed by
Jassi Brar
9468ab84 a8f96891

+29 -14
+29 -14
drivers/mailbox/sprd-mailbox.c
··· 60 60 struct clk *clk; 61 61 u32 outbox_fifo_depth; 62 62 63 + struct mutex lock; 64 + u32 refcnt; 63 65 struct mbox_chan chan[SPRD_MBOX_CHAN_MAX]; 64 66 }; 65 67 ··· 117 115 id = readl(priv->outbox_base + SPRD_MBOX_ID); 118 116 119 117 chan = &priv->chan[id]; 120 - mbox_chan_received_data(chan, (void *)msg); 118 + if (chan->cl) 119 + mbox_chan_received_data(chan, (void *)msg); 120 + else 121 + dev_warn_ratelimited(priv->dev, 122 + "message's been dropped at ch[%d]\n", id); 121 123 122 124 /* Trigger to update outbox FIFO pointer */ 123 125 writel(0x1, priv->outbox_base + SPRD_MBOX_TRIGGER); ··· 221 215 struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 222 216 u32 val; 223 217 224 - /* Select outbox FIFO mode and reset the outbox FIFO status */ 225 - writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); 218 + mutex_lock(&priv->lock); 219 + if (priv->refcnt++ == 0) { 220 + /* Select outbox FIFO mode and reset the outbox FIFO status */ 221 + writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); 226 222 227 - /* Enable inbox FIFO overflow and delivery interrupt */ 228 - val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); 229 - val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); 230 - writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 223 + /* Enable inbox FIFO overflow and delivery interrupt */ 224 + val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); 225 + val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); 226 + writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 231 227 232 - /* Enable outbox FIFO not empty interrupt */ 233 - val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); 234 - val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; 235 - writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 228 + /* Enable outbox FIFO not empty interrupt */ 229 + val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); 230 + val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; 231 + writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 232 + } 233 + mutex_unlock(&priv->lock); 236 234 237 235 return 0; 238 236 } ··· 245 235 { 246 236 struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 247 237 248 - /* Disable inbox & outbox interrupt */ 249 - writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 250 - writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 238 + mutex_lock(&priv->lock); 239 + if (--priv->refcnt == 0) { 240 + /* Disable inbox & outbox interrupt */ 241 + writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 242 + writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 243 + } 244 + mutex_unlock(&priv->lock); 251 245 } 252 246 253 247 static const struct mbox_chan_ops sprd_mbox_ops = { ··· 280 266 return -ENOMEM; 281 267 282 268 priv->dev = dev; 269 + mutex_init(&priv->lock); 283 270 284 271 /* 285 272 * The Spreadtrum mailbox uses an inbox to send messages to the target