diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c index 979acc810f30..99b9421b8494 100644 --- a/drivers/mailbox/rockchip-mailbox.c +++ b/drivers/mailbox/rockchip-mailbox.c @@ -11,6 +11,7 @@ #include #include #include +#include #define MAILBOX_A2B_INTEN 0x00 #define MAILBOX_A2B_STATUS 0x04 @@ -24,7 +25,7 @@ struct rockchip_mbox_msg { u32 cmd; - int rx_size; + u32 data; }; struct rockchip_mbox_data { @@ -34,17 +35,13 @@ struct rockchip_mbox_data { struct rockchip_mbox_chan { int idx; int irq; - struct rockchip_mbox_msg *msg; - struct rockchip_mbox *mb; }; struct rockchip_mbox { struct mbox_controller mbox; struct clk *pclk; void __iomem *mbox_base; - - /* The maximum size of buf for each channel */ - u32 buf_size; + spinlock_t cfg_lock; /* Serialise access to the register */ struct rockchip_mbox_chan *chans; }; @@ -53,24 +50,23 @@ static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data) { struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); struct rockchip_mbox_msg *msg = data; - struct rockchip_mbox_chan *chans = mb->chans; + struct rockchip_mbox_chan *chans = chan->con_priv; + u32 status; if (!msg) return -EINVAL; - if (msg->rx_size > mb->buf_size) { - dev_err(mb->mbox.dev, "Transmit size over buf size(%d)\n", - mb->buf_size); - return -EINVAL; + status = readl_relaxed(mb->mbox_base + MAILBOX_A2B_STATUS); + if (status & (1U << chans->idx)) { + dev_err(mb->mbox.dev, "The mailbox channel is busy\n"); + return -EBUSY; } - dev_dbg(mb->mbox.dev, "Chan[%d]: A2B message, cmd 0x%08x\n", - chans->idx, msg->cmd); - - mb->chans[chans->idx].msg = msg; + dev_dbg(mb->mbox.dev, "Chan[%d]: A2B message, cmd 0x%08x, data 0x%08x\n", + chans->idx, msg->cmd, msg->data); writel_relaxed(msg->cmd, mb->mbox_base + MAILBOX_A2B_CMD(chans->idx)); - writel_relaxed(msg->rx_size, mb->mbox_base + + writel_relaxed(msg->data, mb->mbox_base + MAILBOX_A2B_DAT(chans->idx)); return 0; @@ -79,10 +75,15 @@ static int rockchip_mbox_send_data(struct mbox_chan *chan, void *data) static int rockchip_mbox_startup(struct mbox_chan *chan) { struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); + struct rockchip_mbox_chan *chans = chan->con_priv; + u32 val = 0U; - /* Enable all B2A interrupts */ - writel_relaxed((1 << mb->mbox.num_chans) - 1, - mb->mbox_base + MAILBOX_B2A_INTEN); + /* Enable the corresponding B2A interrupt */ + spin_lock(&mb->cfg_lock); + val = readl_relaxed(mb->mbox_base + MAILBOX_B2A_INTEN) | + (1U << chans->idx); + writel_relaxed(val, mb->mbox_base + MAILBOX_B2A_INTEN); + spin_unlock(&mb->cfg_lock); return 0; } @@ -90,12 +91,15 @@ static int rockchip_mbox_startup(struct mbox_chan *chan) static void rockchip_mbox_shutdown(struct mbox_chan *chan) { struct rockchip_mbox *mb = dev_get_drvdata(chan->mbox->dev); - struct rockchip_mbox_chan *chans = mb->chans; + struct rockchip_mbox_chan *chans = chan->con_priv; + u32 val = 0U; - /* Disable all B2A interrupts */ - writel_relaxed(0, mb->mbox_base + MAILBOX_B2A_INTEN); - - mb->chans[chans->idx].msg = NULL; + /* Disable the corresponding B2A interrupt */ + spin_lock(&mb->cfg_lock); + val = readl_relaxed(mb->mbox_base + MAILBOX_B2A_INTEN) & + ~(1U << chans->idx); + writel_relaxed(val, mb->mbox_base + MAILBOX_B2A_INTEN); + spin_unlock(&mb->cfg_lock); } static const struct mbox_chan_ops rockchip_mbox_chan_ops = { @@ -107,47 +111,30 @@ static const struct mbox_chan_ops rockchip_mbox_chan_ops = { static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id) { int idx; + struct rockchip_mbox_msg msg; struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; u32 status = readl_relaxed(mb->mbox_base + MAILBOX_B2A_STATUS); for (idx = 0; idx < mb->mbox.num_chans; idx++) { - if ((status & (1 << idx)) && (irq == mb->chans[idx].irq)) { + if ((status & (1U << idx)) && irq == mb->chans[idx].irq) { + /* Get cmd/data from the channel of B2A */ + msg.cmd = readl_relaxed(mb->mbox_base + + MAILBOX_B2A_CMD(idx)); + msg.data = readl_relaxed(mb->mbox_base + + MAILBOX_B2A_DAT(idx)); + + dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x, data 0x%08x\n", + idx, msg.cmd, msg.data); + + if (mb->mbox.chans[idx].cl) + mbox_chan_received_data(&mb->mbox.chans[idx], + &msg); /* Clear mbox interrupt */ - writel_relaxed(1 << idx, + writel_relaxed(1U << idx, mb->mbox_base + MAILBOX_B2A_STATUS); - return IRQ_WAKE_THREAD; } } - return IRQ_NONE; -} - -static irqreturn_t rockchip_mbox_isr(int irq, void *dev_id) -{ - int idx; - struct rockchip_mbox_msg *msg = NULL; - struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id; - - for (idx = 0; idx < mb->mbox.num_chans; idx++) { - if (irq != mb->chans[idx].irq) - continue; - - msg = mb->chans[idx].msg; - if (!msg) { - dev_err(mb->mbox.dev, - "Chan[%d]: B2A message is NULL\n", idx); - break; /* spurious */ - } - - mbox_chan_received_data(&mb->mbox.chans[idx], msg); - mb->chans[idx].msg = NULL; - - dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x\n", - idx, msg->cmd); - - break; - } - return IRQ_HANDLED; } @@ -195,6 +182,7 @@ static int rockchip_mbox_probe(struct platform_device *pdev) mb->mbox.num_chans = drv_data->num_chans; mb->mbox.ops = &rockchip_mbox_chan_ops; mb->mbox.txdone_irq = true; + spin_lock_init(&mb->cfg_lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -204,9 +192,6 @@ static int rockchip_mbox_probe(struct platform_device *pdev) if (IS_ERR(mb->mbox_base)) return PTR_ERR(mb->mbox_base); - /* Each channel has two buffers for A2B and B2A */ - mb->buf_size = (size_t)resource_size(res) / (drv_data->num_chans * 2); - mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox"); if (IS_ERR(mb->pclk)) { ret = PTR_ERR(mb->pclk); @@ -223,20 +208,26 @@ static int rockchip_mbox_probe(struct platform_device *pdev) for (i = 0; i < mb->mbox.num_chans; i++) { irq = platform_get_irq(pdev, i); - if (irq < 0) - return irq; - - ret = devm_request_threaded_irq(&pdev->dev, irq, - rockchip_mbox_irq, - rockchip_mbox_isr, IRQF_ONESHOT, - dev_name(&pdev->dev), mb); - if (ret < 0) - return ret; + if (irq < 0) { + /* For shared irq case, only could be got one time */ + if (i > 0 && irq == -ENXIO) + mb->chans[i].irq = mb->chans[0].irq; + else + return irq; + } else { + mb->chans[i].irq = irq; + ret = devm_request_threaded_irq(&pdev->dev, irq, + NULL, + rockchip_mbox_irq, + IRQF_ONESHOT, + dev_name(&pdev->dev), + mb); + if (ret < 0) + return ret; + } mb->chans[i].idx = i; - mb->chans[i].irq = irq; - mb->chans[i].mb = mb; - mb->chans[i].msg = NULL; + mb->mbox.chans[i].con_priv = &mb->chans[i]; } ret = devm_mbox_controller_register(&pdev->dev, &mb->mbox);