arm64: rockchip_defconfig: enable HARDENED_USERCOPY
[firefly-linux-kernel-4.4.55.git] / drivers / mailbox / rockchip_mailbox.c
index ca8cb4d1aeb363a2301be7ab9fefbf7527f23905..db9943179476772cb6dee32e04a67eee01823820 100644 (file)
@@ -23,6 +23,9 @@
 #include <linux/clk.h>
 
 #include <linux/rockchip-mailbox.h>
+#include <linux/scpi_protocol.h>
+
+#define MAILBOX_VERSION                        "V1.00"
 
 #define MAILBOX_A2B_INTEN              0x00
 #define MAILBOX_A2B_STATUS             0x04
@@ -62,6 +65,9 @@ struct rockchip_mbox {
        struct rockchip_mbox_chan *chans;
 };
 
+#define MBOX_CHAN_NUMS 4
+int idx_map_irq[MBOX_CHAN_NUMS] = {0, 0, 0, 0};
+
 static inline int chan_to_idx(struct rockchip_mbox *mb,
                              struct mbox_chan *chan)
 {
@@ -120,20 +126,38 @@ static struct mbox_chan_ops rockchip_mbox_chan_ops = {
 
 static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id)
 {
+       int idx;
        struct rockchip_mbox *mb = (struct rockchip_mbox *)dev_id;
        u32 status = readl_relaxed(mb->mbox_base + MAILBOX_B2A_STATUS);
-       int idx;
 
        for (idx = 0; idx < mb->mbox.num_chans; idx++) {
-               struct rockchip_mbox_msg *msg = mb->chans[idx].msg;
+               if ((status & (1 << idx)) && (irq == idx_map_irq[idx])) {
+                       /* Clear mbox interrupt */
+                       writel_relaxed(1 << idx,
+                                      mb->mbox_base + MAILBOX_B2A_STATUS);
+                       return IRQ_WAKE_THREAD;
+               }
+       }
 
-         /* Clear mbox interrupt */
-               writel_relaxed(1 << idx, mb->mbox_base + MAILBOX_B2A_STATUS);
+       return IRQ_NONE;
+}
 
-               if (!(status & (1 << idx)))
+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 != idx_map_irq[idx])
                        continue;
-               if (!msg)
-                       continue; /* spurious */
+
+               msg = mb->chans[idx].msg;
+               if (!msg) {
+                       dev_err(mb->mbox.dev,
+                               "Chan[%d]: B2A message is NULL\n", idx);
+                       break; /* spurious */
+               }
 
                if (msg->rx_buf)
                        memcpy(msg->rx_buf,
@@ -145,6 +169,8 @@ static irqreturn_t rockchip_mbox_irq(int irq, void *dev_id)
 
                dev_dbg(mb->mbox.dev, "Chan[%d]: B2A message, cmd 0x%08x\n",
                        idx, msg->cmd);
+
+               break;
        }
 
        return IRQ_HANDLED;
@@ -160,6 +186,30 @@ static struct of_device_id rockchip_mbox_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, rockchp_mbox_of_match);
 
+#ifdef CONFIG_PM
+static int rockchip_mbox_suspend(struct platform_device *pdev,
+                                pm_message_t state)
+{
+       struct rockchip_mbox *mb = platform_get_drvdata(pdev);
+
+       if (scpi_sys_set_mcu_state_suspend())
+               dev_err(mb->mbox.dev, "scpi_sys_set_mcu_state_suspend timeout.\n");
+       return 0;
+}
+
+static int rockchip_mbox_resume(struct platform_device *pdev)
+{
+       struct rockchip_mbox *mb = platform_get_drvdata(pdev);
+
+       writel_relaxed((1 << mb->mbox.num_chans) - 1,
+                      mb->mbox_base + MAILBOX_B2A_INTEN);
+
+       if (scpi_sys_set_mcu_state_resume())
+               dev_err(mb->mbox.dev, "scpi_sys_set_mcu_state_resume timeout.\n");
+       return 0;
+}
+#endif /* CONFIG_PM */
+
 static int rockchip_mbox_probe(struct platform_device *pdev)
 {
        struct rockchip_mbox *mb;
@@ -168,6 +218,9 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
        struct resource *res;
        int ret, irq, i;
 
+       dev_info(&pdev->dev,
+                "Rockchip mailbox initialize, version: "MAILBOX_VERSION"\n");
+
        if (!pdev->dev.of_node)
                return -ENODEV;
 
@@ -232,8 +285,9 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
                if (irq < 0)
                        return irq;
 
-               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-                                               rockchip_mbox_irq, IRQF_ONESHOT,
+               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;
@@ -241,6 +295,7 @@ static int rockchip_mbox_probe(struct platform_device *pdev)
                mb->chans[i].idx = i;
                mb->chans[i].mb = mb;
                mb->chans[i].msg = NULL;
+               idx_map_irq[i] = irq;
        }
 
        /* Enable all B2A interrupts */
@@ -266,6 +321,10 @@ static int rockchip_mbox_remove(struct platform_device *pdev)
 static struct platform_driver rockchip_mbox_driver = {
        .probe  = rockchip_mbox_probe,
        .remove = rockchip_mbox_remove,
+#ifdef CONFIG_PM
+       .suspend = rockchip_mbox_suspend,
+       .resume = rockchip_mbox_resume,
+#endif /* CONFIG_PM */
        .driver = {
                .name = "rockchip-mailbox",
                .of_match_table = of_match_ptr(rockchip_mbox_of_match),