Merge tag 'mailbox-v5.8' of git://git.linaro.org/landing-teams/working/fujitsu/integr...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jun 2020 19:42:14 +0000 (12:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jun 2020 19:42:14 +0000 (12:42 -0700)
Pull mailbox updates from Jassi Brar:
 "qcom:
   - new controller driver for IPCC
   - reorg the of_device data
   - add support for ipq6018 platform

  spreadtrum:
   - new sprd controller driver

  imx:
   - implement suspend/resume PM support

  misc:
   - make pcc driver struct static
   - fix return value in imx_mu_scu
   - disable clock before bailout in imx probe
   - remove duplicate error mssg in zynqmp probe
   - fix header size in imx.scu
   - check for null instead of is-err in zynqmp"

* tag 'mailbox-v5.8' of git://git.linaro.org/landing-teams/working/fujitsu/integration:
  mailbox: qcom: Add ipq6018 apcs compatible
  mailbox: qcom: Add clock driver name in apcs mailbox driver data
  dt-bindings: mailbox: Add YAML schemas for QCOM APCS global block
  mailbox: imx: ONLY IPC MU needs IRQF_NO_SUSPEND flag
  mailbox: imx: Add runtime PM callback to handle MU clocks
  mailbox: imx: Add context save/restore for suspend/resume
  MAINTAINERS: Add entry for Qualcomm IPCC driver
  mailbox: Add support for Qualcomm IPCC
  dt-bindings: mailbox: Add devicetree binding for Qcom IPCC
  mailbox: zynqmp-ipi: Fix NULL vs IS_ERR() check in zynqmp_ipi_mbox_probe()
  mailbox: imx-mailbox: fix scu msg header size check
  mailbox: sprd: Add Spreadtrum mailbox driver
  dt-bindings: mailbox: Add the Spreadtrum mailbox documentation
  mailbox: ZynqMP IPI: Delete an error message in zynqmp_ipi_probe()
  mailbox: imx: Disable the clock on devm_mbox_controller_register() failure
  mailbox: imx: Fix return in imx_mu_scu_xlate()
  mailbox: imx: Support runtime PM
  mailbox: pcc: make pcc_mbox_driver static

14 files changed:
Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.txt [deleted file]
Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/mailbox/sprd-mailbox.yaml [new file with mode: 0644]
MAINTAINERS
drivers/mailbox/Kconfig
drivers/mailbox/Makefile
drivers/mailbox/imx-mailbox.c
drivers/mailbox/pcc.c
drivers/mailbox/qcom-apcs-ipc-mailbox.c
drivers/mailbox/qcom-ipcc.c [new file with mode: 0644]
drivers/mailbox/sprd-mailbox.c [new file with mode: 0644]
drivers/mailbox/zynqmp-ipi-mailbox.c
include/dt-bindings/mailbox/qcom-ipcc.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.txt b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.txt
deleted file mode 100644 (file)
index beec612..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-Binding for the Qualcomm APCS global block
-==========================================
-
-This binding describes the APCS "global" block found in various Qualcomm
-platforms.
-
-- compatible:
-       Usage: required
-       Value type: <string>
-       Definition: must be one of:
-                   "qcom,msm8916-apcs-kpss-global",
-                   "qcom,msm8996-apcs-hmss-global"
-                   "qcom,msm8998-apcs-hmss-global"
-                   "qcom,qcs404-apcs-apps-global"
-                   "qcom,sc7180-apss-shared"
-                   "qcom,sdm845-apss-shared"
-                   "qcom,sm8150-apss-shared"
-                   "qcom,ipq8074-apcs-apps-global"
-
-- reg:
-       Usage: required
-       Value type: <prop-encoded-array>
-       Definition: must specify the base address and size of the global block
-
-- clocks:
-       Usage: required if #clock-names property is present
-       Value type: <phandle array>
-       Definition: phandles to the two parent clocks of the clock driver.
-
-- #mbox-cells:
-       Usage: required
-       Value type: <u32>
-       Definition: as described in mailbox.txt, must be 1
-
-- #clock-cells:
-       Usage: optional
-       Value type: <u32>
-       Definition: as described in clock.txt, must be 0
-
-- clock-names:
-       Usage: required if the platform data based clock driver needs to
-       retrieve the parent clock names from device tree.
-       This will requires two mandatory clocks to be defined.
-       Value type: <string-array>
-       Definition: must be "pll" and "aux"
-
-= EXAMPLE
-The following example describes the APCS HMSS found in MSM8996 and part of the
-GLINK RPM referencing the "rpm_hlos" doorbell therein.
-
-       apcs_glb: mailbox@9820000 {
-               compatible = "qcom,msm8996-apcs-hmss-global";
-               reg = <0x9820000 0x1000>;
-
-               #mbox-cells = <1>;
-       };
-
-       rpm-glink {
-               compatible = "qcom,glink-rpm";
-
-               interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
-
-               qcom,rpm-msg-ram = <&rpm_msg_ram>;
-
-               mboxes = <&apcs_glb 0>;
-               mbox-names = "rpm_hlos";
-       };
-
-Below is another example of the APCS binding on MSM8916 platforms:
-
-       apcs: mailbox@b011000 {
-               compatible = "qcom,msm8916-apcs-kpss-global";
-               reg = <0xb011000 0x1000>;
-               #mbox-cells = <1>;
-               clocks = <&a53pll>;
-               #clock-cells = <0>;
-       };
-
-Below is another example of the APCS binding on QCS404 platforms:
-
-       apcs_glb: mailbox@b011000 {
-               compatible = "qcom,qcs404-apcs-apps-global", "syscon";
-               reg = <0x0b011000 0x1000>;
-               #mbox-cells = <1>;
-               clocks = <&apcs_hfpll>, <&gcc GCC_GPLL0_AO_OUT_MAIN>;
-               clock-names = "pll", "aux";
-               #clock-cells = <0>;
-       };
diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml
new file mode 100644 (file)
index 0000000..12eff94
--- /dev/null
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/mailbox/qcom,apcs-kpss-global.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcomm APCS global block bindings
+
+description:
+  This binding describes the APCS "global" block found in various Qualcomm
+  platforms.
+
+maintainers:
+  - Sivaprakash Murugesan <sivaprak@codeaurora.org>
+
+properties:
+  compatible:
+    enum:
+      - qcom,ipq8074-apcs-apps-global
+      - qcom,msm8916-apcs-kpss-global
+      - qcom,msm8996-apcs-hmss-global
+      - qcom,msm8998-apcs-hmss-global
+      - qcom,qcs404-apcs-apps-global
+      - qcom,sc7180-apss-shared
+      - qcom,sdm845-apss-shared
+      - qcom,sm8150-apss-shared
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    description: phandles to the parent clocks of the clock driver
+    items:
+      - description: primary pll parent of the clock driver
+      - description: auxiliary parent
+
+  '#mbox-cells':
+    const: 1
+
+  '#clock-cells':
+    const: 0
+
+  clock-names:
+    items:
+      - const: pll
+      - const: aux
+
+required:
+  - compatible
+  - reg
+  - '#mbox-cells'
+
+additionalProperties: false
+
+examples:
+
+  # Example apcs with msm8996
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    apcs_glb: mailbox@9820000 {
+        compatible = "qcom,msm8996-apcs-hmss-global";
+        reg = <0x9820000 0x1000>;
+
+        #mbox-cells = <1>;
+    };
+
+    rpm-glink {
+        compatible = "qcom,glink-rpm";
+        interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
+        qcom,rpm-msg-ram = <&rpm_msg_ram>;
+        mboxes = <&apcs_glb 0>;
+        mbox-names = "rpm_hlos";
+    };
+
+  # Example apcs with qcs404
+  - |
+    #define GCC_APSS_AHB_CLK_SRC  1
+    #define GCC_GPLL0_AO_OUT_MAIN 123
+    apcs: mailbox@b011000 {
+        compatible = "qcom,qcs404-apcs-apps-global";
+        reg = <0x0b011000 0x1000>;
+        #mbox-cells = <1>;
+        clocks = <&apcs_hfpll>, <&gcc GCC_GPLL0_AO_OUT_MAIN>;
+        clock-names = "pll", "aux";
+        #clock-cells = <0>;
+    };
diff --git a/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml b/Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml
new file mode 100644 (file)
index 0000000..4ac2123
--- /dev/null
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mailbox/qcom-ipcc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies, Inc. Inter-Processor Communication Controller
+
+maintainers:
+  - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+description:
+  The Inter-Processor Communication Controller (IPCC) is a centralized hardware
+  to route interrupts across various subsystems. It involves a three-level
+  addressing scheme called protocol, client and signal. For example, consider an
+  entity on the Application Processor Subsystem (APSS) that wants to listen to
+  Modem's interrupts via Shared Memory Point to Point (SMP2P) interface. In such
+  a case, the client would be Modem (client-id is 2) and the signal would be
+  SMP2P (signal-id is 2). The SMP2P itself falls under the Multiprocessor (MPROC)
+  protocol (protocol-id is 0). Refer include/dt-bindings/mailbox/qcom-ipcc.h
+  for the list of such IDs.
+
+properties:
+  compatible:
+    items:
+      - enum:
+        - qcom,sm8250-ipcc
+      - const: qcom,ipcc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 3
+    description:
+      The first cell is the client-id, the second cell is the signal-id and the
+      third cell is the interrupt type.
+
+  "#mbox-cells":
+    const: 2
+    description:
+      The first cell is the client-id, and the second cell is the signal-id.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - "#interrupt-cells"
+  - "#mbox-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+        #include <dt-bindings/interrupt-controller/arm-gic.h>
+        #include <dt-bindings/mailbox/qcom-ipcc.h>
+
+        mailbox@408000 {
+                compatible = "qcom,sm8250-ipcc", "qcom,ipcc";
+                reg = <0x408000 0x1000>;
+                interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>;
+                interrupt-controller;
+                #interrupt-cells = <3>;
+                #mbox-cells = <2>;
+        };
+
+        smp2p-modem {
+                compatible = "qcom,smp2p";
+                interrupts-extended = <&ipcc_mproc IPCC_CLIENT_MPSS
+                                IPCC_MPROC_SIGNAL_SMP2P IRQ_TYPE_EDGE_RISING>;
+                mboxes = <&ipcc_mproc IPCC_CLIENT_MPSS IPCC_MPROC_SIGNAL_SMP2P>;
+
+                /* Other SMP2P fields */
+        };
diff --git a/Documentation/devicetree/bindings/mailbox/sprd-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/sprd-mailbox.yaml
new file mode 100644 (file)
index 0000000..0f7451b
--- /dev/null
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/mailbox/sprd-mailbox.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Spreadtrum mailbox controller bindings
+
+maintainers:
+  - Orson Zhai <orsonzhai@gmail.com>
+  - Baolin Wang <baolin.wang7@gmail.com>
+  - Chunyan Zhang <zhang.lyra@gmail.com>
+
+properties:
+  compatible:
+    enum:
+      - sprd,sc9860-mailbox
+
+  reg:
+    items:
+      - description: inbox registers' base address
+      - description: outbox registers' base address
+
+  interrupts:
+    items:
+      - description: inbox interrupt
+      - description: outbox interrupt
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: enable
+
+  "#mbox-cells":
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#mbox-cells"
+  - clocks
+  - clock-names
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    mailbox: mailbox@400a0000 {
+      compatible = "sprd,sc9860-mailbox";
+      reg = <0 0x400a0000 0 0x8000>, <0 0x400a8000 0 0x8000>;
+      #mbox-cells = <1>;
+      clock-names = "enable";
+      clocks = <&aon_gate 53>;
+      interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+    };
+...
index 9b4e836..573cf64 100644 (file)
@@ -14187,6 +14187,14 @@ L:     linux-arm-msm@vger.kernel.org
 S:     Maintained
 F:     drivers/iommu/qcom_iommu.c
 
+QUALCOMM IPCC MAILBOX DRIVER
+M:     Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+L:     linux-arm-msm@vger.kernel.org
+S:     Supported
+F:     Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml
+F:     drivers/mailbox/qcom-ipcc.c
+F:     include/dt-bindings/mailbox/qcom-ipcc.h
+
 QUALCOMM RMNET DRIVER
 M:     Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
 M:     Sean Tranchetti <stranche@codeaurora.org>
index 5a577a6..05b1009 100644 (file)
@@ -236,4 +236,22 @@ config SUN6I_MSGBOX
          various Allwinner SoCs. This mailbox is used for communication
          between the application CPUs and the power management coprocessor.
 
+config SPRD_MBOX
+       tristate "Spreadtrum Mailbox"
+       depends on ARCH_SPRD || COMPILE_TEST
+       help
+         Mailbox driver implementation for the Spreadtrum platform. It is used
+         to send message between application processors and MCU. Say Y here if
+         you want to build the Spreatrum mailbox controller driver.
+
+config QCOM_IPCC
+       bool "Qualcomm Technologies, Inc. IPCC driver"
+       depends on ARCH_QCOM || COMPILE_TEST
+       help
+         Qualcomm Technologies, Inc. Inter-Processor Communication Controller
+         (IPCC) driver for MSM devices. The driver provides mailbox support for
+         sending interrupts to the clients. On the other hand, the driver also
+         acts as an interrupt controller for receiving interrupts from clients.
+         Say Y here if you want to build this driver.
+
 endif
index 2e4364e..60d224b 100644 (file)
@@ -50,3 +50,7 @@ obj-$(CONFIG_MTK_CMDQ_MBOX)   += mtk-cmdq-mailbox.o
 obj-$(CONFIG_ZYNQMP_IPI_MBOX)  += zynqmp-ipi-mailbox.o
 
 obj-$(CONFIG_SUN6I_MSGBOX)     += sun6i-msgbox.o
+
+obj-$(CONFIG_SPRD_MBOX)                += sprd-mailbox.o
+
+obj-$(CONFIG_QCOM_IPCC)                += qcom-ipcc.o
index 7906624..7205b82 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/mailbox_controller.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #define IMX_MU_xSR_GIPn(x)     BIT(28 + (3 - (x)))
@@ -66,6 +67,8 @@ struct imx_mu_priv {
        struct clk              *clk;
        int                     irq;
 
+       u32 xcr;
+
        bool                    side_b;
 };
 
@@ -154,12 +157,17 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv,
 
        switch (cp->type) {
        case IMX_MU_TYPE_TX:
-               if (msg->hdr.size > sizeof(*msg)) {
+               /*
+                * msg->hdr.size specifies the number of u32 words while
+                * sizeof yields bytes.
+                */
+
+               if (msg->hdr.size > sizeof(*msg) / 4) {
                        /*
                         * The real message size can be different to
                         * struct imx_sc_rpc_msg_max size
                         */
-                       dev_err(priv->dev, "Exceed max msg size (%zu) on TX, got: %i\n", sizeof(*msg), msg->hdr.size);
+                       dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on TX; got: %i bytes\n", sizeof(*msg), msg->hdr.size << 2);
                        return -EINVAL;
                }
 
@@ -198,9 +206,8 @@ static int imx_mu_scu_rx(struct imx_mu_priv *priv,
        imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_RIEn(0));
        *data++ = imx_mu_read(priv, priv->dcfg->xRR[0]);
 
-       if (msg.hdr.size > sizeof(msg)) {
-               dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n",
-                       sizeof(msg), msg.hdr.size);
+       if (msg.hdr.size > sizeof(msg) / 4) {
+               dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2);
                return -EINVAL;
        }
 
@@ -285,8 +292,10 @@ static int imx_mu_startup(struct mbox_chan *chan)
 {
        struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
        struct imx_mu_con_priv *cp = chan->con_priv;
+       unsigned long irq_flag = IRQF_SHARED;
        int ret;
 
+       pm_runtime_get_sync(priv->dev);
        if (cp->type == IMX_MU_TYPE_TXDB) {
                /* Tx doorbell don't have ACK support */
                tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet,
@@ -294,8 +303,12 @@ static int imx_mu_startup(struct mbox_chan *chan)
                return 0;
        }
 
-       ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED |
-                         IRQF_NO_SUSPEND, cp->irq_desc, chan);
+       /* IPC MU should be with IRQF_NO_SUSPEND set */
+       if (!priv->dev->pm_domain)
+               irq_flag |= IRQF_NO_SUSPEND;
+
+       ret = request_irq(priv->irq, imx_mu_isr, irq_flag,
+                         cp->irq_desc, chan);
        if (ret) {
                dev_err(priv->dev,
                        "Unable to acquire IRQ %d\n", priv->irq);
@@ -323,6 +336,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
 
        if (cp->type == IMX_MU_TYPE_TXDB) {
                tasklet_kill(&cp->txdb_tasklet);
+               pm_runtime_put_sync(priv->dev);
                return;
        }
 
@@ -341,6 +355,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
        }
 
        free_irq(priv->irq, chan);
+       pm_runtime_put_sync(priv->dev);
 }
 
 static const struct mbox_chan_ops imx_mu_ops = {
@@ -374,7 +389,7 @@ static struct mbox_chan *imx_mu_scu_xlate(struct mbox_controller *mbox,
                break;
        default:
                dev_err(mbox->dev, "Invalid chan type: %d\n", type);
-               return NULL;
+               return ERR_PTR(-EINVAL);
        }
 
        if (chan >= mbox->num_chans) {
@@ -508,14 +523,39 @@ static int imx_mu_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, priv);
 
-       return devm_mbox_controller_register(dev, &priv->mbox);
+       ret = devm_mbox_controller_register(dev, &priv->mbox);
+       if (ret) {
+               clk_disable_unprepare(priv->clk);
+               return ret;
+       }
+
+       pm_runtime_enable(dev);
+
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               pm_runtime_put_noidle(dev);
+               goto disable_runtime_pm;
+       }
+
+       ret = pm_runtime_put_sync(dev);
+       if (ret < 0)
+               goto disable_runtime_pm;
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+
+disable_runtime_pm:
+       pm_runtime_disable(dev);
+       clk_disable_unprepare(priv->clk);
+       return ret;
 }
 
 static int imx_mu_remove(struct platform_device *pdev)
 {
        struct imx_mu_priv *priv = platform_get_drvdata(pdev);
 
-       clk_disable_unprepare(priv->clk);
+       pm_runtime_disable(priv->dev);
 
        return 0;
 }
@@ -558,12 +598,69 @@ static const struct of_device_id imx_mu_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
 
+static int imx_mu_suspend_noirq(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+
+       if (!priv->clk)
+               priv->xcr = imx_mu_read(priv, priv->dcfg->xCR);
+
+       return 0;
+}
+
+static int imx_mu_resume_noirq(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+
+       /*
+        * ONLY restore MU when context lost, the TIE could
+        * be set during noirq resume as there is MU data
+        * communication going on, and restore the saved
+        * value will overwrite the TIE and cause MU data
+        * send failed, may lead to system freeze. This issue
+        * is observed by testing freeze mode suspend.
+        */
+       if (!imx_mu_read(priv, priv->dcfg->xCR) && !priv->clk)
+               imx_mu_write(priv, priv->xcr, priv->dcfg->xCR);
+
+       return 0;
+}
+
+static int imx_mu_runtime_suspend(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int imx_mu_runtime_resume(struct device *dev)
+{
+       struct imx_mu_priv *priv = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret)
+               dev_err(dev, "failed to enable clock\n");
+
+       return ret;
+}
+
+static const struct dev_pm_ops imx_mu_pm_ops = {
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_mu_suspend_noirq,
+                                     imx_mu_resume_noirq)
+       SET_RUNTIME_PM_OPS(imx_mu_runtime_suspend,
+                          imx_mu_runtime_resume, NULL)
+};
+
 static struct platform_driver imx_mu_driver = {
        .probe          = imx_mu_probe,
        .remove         = imx_mu_remove,
        .driver = {
                .name   = "imx_mu",
                .of_match_table = imx_mu_dt_ids,
+               .pm = &imx_mu_pm_ops,
        },
 };
 module_platform_driver(imx_mu_driver);
index 34844b7..8c7fac3 100644 (file)
@@ -568,7 +568,7 @@ static int pcc_mbox_probe(struct platform_device *pdev)
        return ret;
 }
 
-struct platform_driver pcc_mbox_driver = {
+static struct platform_driver pcc_mbox_driver = {
        .probe = pcc_mbox_probe,
        .driver = {
                .name = "PCCT",
index eeebafd..cec34f0 100644 (file)
@@ -24,6 +24,35 @@ struct qcom_apcs_ipc {
        struct platform_device *clk;
 };
 
+struct qcom_apcs_ipc_data {
+       int offset;
+       char *clk_name;
+};
+
+static const struct qcom_apcs_ipc_data ipq6018_apcs_data = {
+       .offset = 8, .clk_name = "qcom,apss-ipq6018-clk"
+};
+
+static const struct qcom_apcs_ipc_data ipq8074_apcs_data = {
+       .offset = 8, .clk_name = NULL
+};
+
+static const struct qcom_apcs_ipc_data msm8916_apcs_data = {
+       .offset = 8, .clk_name = "qcom-apcs-msm8916-clk"
+};
+
+static const struct qcom_apcs_ipc_data msm8996_apcs_data = {
+       .offset = 16, .clk_name = NULL
+};
+
+static const struct qcom_apcs_ipc_data msm8998_apcs_data = {
+       .offset = 8, .clk_name = NULL
+};
+
+static const struct qcom_apcs_ipc_data apps_shared_apcs_data = {
+       .offset = 12, .clk_name = NULL
+};
+
 static const struct regmap_config apcs_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
@@ -48,17 +77,12 @@ static const struct mbox_chan_ops qcom_apcs_ipc_ops = {
 static int qcom_apcs_ipc_probe(struct platform_device *pdev)
 {
        struct qcom_apcs_ipc *apcs;
+       const struct qcom_apcs_ipc_data *apcs_data;
        struct regmap *regmap;
        struct resource *res;
-       unsigned long offset;
        void __iomem *base;
        unsigned long i;
        int ret;
-       const struct of_device_id apcs_clk_match_table[] = {
-               { .compatible = "qcom,msm8916-apcs-kpss-global", },
-               { .compatible = "qcom,qcs404-apcs-apps-global", },
-               {}
-       };
 
        apcs = devm_kzalloc(&pdev->dev, sizeof(*apcs), GFP_KERNEL);
        if (!apcs)
@@ -73,10 +97,10 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
-       offset = (unsigned long)of_device_get_match_data(&pdev->dev);
+       apcs_data = of_device_get_match_data(&pdev->dev);
 
        apcs->regmap = regmap;
-       apcs->offset = offset;
+       apcs->offset = apcs_data->offset;
 
        /* Initialize channel identifiers */
        for (i = 0; i < ARRAY_SIZE(apcs->mbox_chans); i++)
@@ -93,9 +117,9 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
                return ret;
        }
 
-       if (of_match_device(apcs_clk_match_table, &pdev->dev)) {
+       if (apcs_data->clk_name) {
                apcs->clk = platform_device_register_data(&pdev->dev,
-                                                         "qcom-apcs-msm8916-clk",
+                                                         apcs_data->clk_name,
                                                          PLATFORM_DEVID_NONE,
                                                          NULL, 0);
                if (IS_ERR(apcs->clk))
@@ -119,14 +143,15 @@ static int qcom_apcs_ipc_remove(struct platform_device *pdev)
 
 /* .data is the offset of the ipc register within the global block */
 static const struct of_device_id qcom_apcs_ipc_of_match[] = {
-       { .compatible = "qcom,msm8916-apcs-kpss-global", .data = (void *)8 },
-       { .compatible = "qcom,msm8996-apcs-hmss-global", .data = (void *)16 },
-       { .compatible = "qcom,msm8998-apcs-hmss-global", .data = (void *)8 },
-       { .compatible = "qcom,qcs404-apcs-apps-global", .data = (void *)8 },
-       { .compatible = "qcom,sc7180-apss-shared", .data = (void *)12 },
-       { .compatible = "qcom,sdm845-apss-shared", .data = (void *)12 },
-       { .compatible = "qcom,sm8150-apss-shared", .data = (void *)12 },
-       { .compatible = "qcom,ipq8074-apcs-apps-global", .data = (void *)8 },
+       { .compatible = "qcom,ipq6018-apcs-apps-global", .data = &ipq6018_apcs_data },
+       { .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq8074_apcs_data },
+       { .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data },
+       { .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data },
+       { .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8998_apcs_data },
+       { .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data },
+       { .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data },
+       { .compatible = "qcom,sdm845-apss-shared", .data = &apps_shared_apcs_data },
+       { .compatible = "qcom,sm8150-apss-shared", .data = &apps_shared_apcs_data },
        {}
 };
 MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c
new file mode 100644 (file)
index 0000000..2d13c72
--- /dev/null
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <dt-bindings/mailbox/qcom-ipcc.h>
+
+#define IPCC_MBOX_MAX_CHAN             48
+
+/* IPCC Register offsets */
+#define IPCC_REG_SEND_ID               0x0c
+#define IPCC_REG_RECV_ID               0x10
+#define IPCC_REG_RECV_SIGNAL_ENABLE    0x14
+#define IPCC_REG_RECV_SIGNAL_DISABLE   0x18
+#define IPCC_REG_RECV_SIGNAL_CLEAR     0x1c
+#define IPCC_REG_CLIENT_CLEAR          0x38
+
+#define IPCC_SIGNAL_ID_MASK            GENMASK(15, 0)
+#define IPCC_CLIENT_ID_MASK            GENMASK(31, 16)
+
+#define IPCC_NO_PENDING_IRQ            GENMASK(31, 0)
+
+/**
+ * struct qcom_ipcc_chan_info - Per-mailbox-channel info
+ * @client_id: The client-id to which the interrupt has to be triggered
+ * @signal_id: The signal-id to which the interrupt has to be triggered
+ */
+struct qcom_ipcc_chan_info {
+       u16 client_id;
+       u16 signal_id;
+};
+
+/**
+ * struct qcom_ipcc - Holder for the mailbox driver
+ * @dev:               Device associated with this instance
+ * @base:              Base address of the IPCC frame associated to APSS
+ * @irq_domain:                The irq_domain associated with this instance
+ * @chan:              The mailbox channels array
+ * @mchan:             The per-mailbox channel info array
+ * @mbox:              The mailbox controller
+ * @irq:               Summary irq
+ */
+struct qcom_ipcc {
+       struct device *dev;
+       void __iomem *base;
+       struct irq_domain *irq_domain;
+       struct mbox_chan chan[IPCC_MBOX_MAX_CHAN];
+       struct qcom_ipcc_chan_info mchan[IPCC_MBOX_MAX_CHAN];
+       struct mbox_controller mbox;
+       int irq;
+};
+
+static inline struct qcom_ipcc *to_qcom_ipcc(struct mbox_controller *mbox)
+{
+       return container_of(mbox, struct qcom_ipcc, mbox);
+}
+
+static inline u32 qcom_ipcc_get_hwirq(u16 client_id, u16 signal_id)
+{
+       return FIELD_PREP(IPCC_CLIENT_ID_MASK, client_id) |
+              FIELD_PREP(IPCC_SIGNAL_ID_MASK, signal_id);
+}
+
+static irqreturn_t qcom_ipcc_irq_fn(int irq, void *data)
+{
+       struct qcom_ipcc *ipcc = data;
+       u32 hwirq;
+       int virq;
+
+       for (;;) {
+               hwirq = readl(ipcc->base + IPCC_REG_RECV_ID);
+               if (hwirq == IPCC_NO_PENDING_IRQ)
+                       break;
+
+               virq = irq_find_mapping(ipcc->irq_domain, hwirq);
+               writel(hwirq, ipcc->base + IPCC_REG_RECV_SIGNAL_CLEAR);
+               generic_handle_irq(virq);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void qcom_ipcc_mask_irq(struct irq_data *irqd)
+{
+       struct qcom_ipcc *ipcc = irq_data_get_irq_chip_data(irqd);
+       irq_hw_number_t hwirq = irqd_to_hwirq(irqd);
+
+       writel(hwirq, ipcc->base + IPCC_REG_RECV_SIGNAL_DISABLE);
+}
+
+static void qcom_ipcc_unmask_irq(struct irq_data *irqd)
+{
+       struct qcom_ipcc *ipcc = irq_data_get_irq_chip_data(irqd);
+       irq_hw_number_t hwirq = irqd_to_hwirq(irqd);
+
+       writel(hwirq, ipcc->base + IPCC_REG_RECV_SIGNAL_ENABLE);
+}
+
+static struct irq_chip qcom_ipcc_irq_chip = {
+       .name = "ipcc",
+       .irq_mask = qcom_ipcc_mask_irq,
+       .irq_unmask = qcom_ipcc_unmask_irq,
+       .flags = IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int qcom_ipcc_domain_map(struct irq_domain *d, unsigned int irq,
+                               irq_hw_number_t hw)
+{
+       struct qcom_ipcc *ipcc = d->host_data;
+
+       irq_set_chip_and_handler(irq, &qcom_ipcc_irq_chip, handle_level_irq);
+       irq_set_chip_data(irq, ipcc);
+       irq_set_noprobe(irq);
+
+       return 0;
+}
+
+static int qcom_ipcc_domain_xlate(struct irq_domain *d,
+                                 struct device_node *node, const u32 *intspec,
+                                 unsigned int intsize,
+                                 unsigned long *out_hwirq,
+                                 unsigned int *out_type)
+{
+       if (intsize != 3)
+               return -EINVAL;
+
+       *out_hwirq = qcom_ipcc_get_hwirq(intspec[0], intspec[1]);
+       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+
+       return 0;
+}
+
+static const struct irq_domain_ops qcom_ipcc_irq_ops = {
+       .map = qcom_ipcc_domain_map,
+       .xlate = qcom_ipcc_domain_xlate,
+};
+
+static int qcom_ipcc_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+       struct qcom_ipcc *ipcc = to_qcom_ipcc(chan->mbox);
+       struct qcom_ipcc_chan_info *mchan = chan->con_priv;
+       u32 hwirq;
+
+       hwirq = qcom_ipcc_get_hwirq(mchan->client_id, mchan->signal_id);
+       writel(hwirq, ipcc->base + IPCC_REG_SEND_ID);
+
+       return 0;
+}
+
+static struct mbox_chan *qcom_ipcc_mbox_xlate(struct mbox_controller *mbox,
+                                       const struct of_phandle_args *ph)
+{
+       struct qcom_ipcc *ipcc = to_qcom_ipcc(mbox);
+       struct qcom_ipcc_chan_info *mchan;
+       struct mbox_chan *chan;
+       unsigned int i;
+
+       if (ph->args_count != 2)
+               return ERR_PTR(-EINVAL);
+
+       for (i = 0; i < IPCC_MBOX_MAX_CHAN; i++) {
+               chan = &ipcc->chan[i];
+               if (!chan->con_priv) {
+                       mchan = &ipcc->mchan[i];
+                       mchan->client_id = ph->args[0];
+                       mchan->signal_id = ph->args[1];
+                       chan->con_priv = mchan;
+                       break;
+               }
+
+               chan = NULL;
+       }
+
+       return chan ?: ERR_PTR(-EBUSY);
+}
+
+static const struct mbox_chan_ops ipcc_mbox_chan_ops = {
+       .send_data = qcom_ipcc_mbox_send_data,
+};
+
+static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc)
+{
+       struct mbox_controller *mbox;
+       struct device *dev = ipcc->dev;
+
+       mbox = &ipcc->mbox;
+       mbox->dev = dev;
+       mbox->num_chans = IPCC_MBOX_MAX_CHAN;
+       mbox->chans = ipcc->chan;
+       mbox->ops = &ipcc_mbox_chan_ops;
+       mbox->of_xlate = qcom_ipcc_mbox_xlate;
+       mbox->txdone_irq = false;
+       mbox->txdone_poll = false;
+
+       return devm_mbox_controller_register(dev, mbox);
+}
+
+static int qcom_ipcc_probe(struct platform_device *pdev)
+{
+       struct qcom_ipcc *ipcc;
+       int ret;
+
+       ipcc = devm_kzalloc(&pdev->dev, sizeof(*ipcc), GFP_KERNEL);
+       if (!ipcc)
+               return -ENOMEM;
+
+       ipcc->dev = &pdev->dev;
+
+       ipcc->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(ipcc->base))
+               return PTR_ERR(ipcc->base);
+
+       ipcc->irq = platform_get_irq(pdev, 0);
+       if (ipcc->irq < 0)
+               return ipcc->irq;
+
+       ipcc->irq_domain = irq_domain_add_tree(pdev->dev.of_node,
+                                              &qcom_ipcc_irq_ops, ipcc);
+       if (!ipcc->irq_domain)
+               return -ENOMEM;
+
+       ret = qcom_ipcc_setup_mbox(ipcc);
+       if (ret)
+               goto err_mbox;
+
+       ret = devm_request_irq(&pdev->dev, ipcc->irq, qcom_ipcc_irq_fn,
+                              IRQF_TRIGGER_HIGH, "ipcc", ipcc);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret);
+               goto err_mbox;
+       }
+
+       enable_irq_wake(ipcc->irq);
+       platform_set_drvdata(pdev, ipcc);
+
+       return 0;
+
+err_mbox:
+       irq_domain_remove(ipcc->irq_domain);
+
+       return ret;
+}
+
+static int qcom_ipcc_remove(struct platform_device *pdev)
+{
+       struct qcom_ipcc *ipcc = platform_get_drvdata(pdev);
+
+       disable_irq_wake(ipcc->irq);
+       irq_domain_remove(ipcc->irq_domain);
+
+       return 0;
+}
+
+static const struct of_device_id qcom_ipcc_of_match[] = {
+       { .compatible = "qcom,ipcc"},
+       {}
+};
+MODULE_DEVICE_TABLE(of, qcom_ipcc_of_match);
+
+static struct platform_driver qcom_ipcc_driver = {
+       .probe = qcom_ipcc_probe,
+       .remove = qcom_ipcc_remove,
+       .driver = {
+               .name = "qcom-ipcc",
+               .of_match_table = qcom_ipcc_of_match,
+       },
+};
+
+static int __init qcom_ipcc_init(void)
+{
+       return platform_driver_register(&qcom_ipcc_driver);
+}
+arch_initcall(qcom_ipcc_init);
+
+MODULE_AUTHOR("Venkata Narendra Kumar Gutta <vnkgutta@codeaurora.org>");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. IPCC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mailbox/sprd-mailbox.c b/drivers/mailbox/sprd-mailbox.c
new file mode 100644 (file)
index 0000000..f6fab24
--- /dev/null
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Spreadtrum mailbox driver
+ *
+ * Copyright (c) 2020 Spreadtrum Communications Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#define SPRD_MBOX_ID           0x0
+#define SPRD_MBOX_MSG_LOW      0x4
+#define SPRD_MBOX_MSG_HIGH     0x8
+#define SPRD_MBOX_TRIGGER      0xc
+#define SPRD_MBOX_FIFO_RST     0x10
+#define SPRD_MBOX_FIFO_STS     0x14
+#define SPRD_MBOX_IRQ_STS      0x18
+#define SPRD_MBOX_IRQ_MSK      0x1c
+#define SPRD_MBOX_LOCK         0x20
+#define SPRD_MBOX_FIFO_DEPTH   0x24
+
+/* Bit and mask definiation for inbox's SPRD_MBOX_FIFO_STS register */
+#define SPRD_INBOX_FIFO_DELIVER_MASK           GENMASK(23, 16)
+#define SPRD_INBOX_FIFO_OVERLOW_MASK           GENMASK(15, 8)
+#define SPRD_INBOX_FIFO_DELIVER_SHIFT          16
+#define SPRD_INBOX_FIFO_BUSY_MASK              GENMASK(7, 0)
+
+/* Bit and mask definiation for SPRD_MBOX_IRQ_STS register */
+#define SPRD_MBOX_IRQ_CLR                      BIT(0)
+
+/* Bit and mask definiation for outbox's SPRD_MBOX_FIFO_STS register */
+#define SPRD_OUTBOX_FIFO_FULL                  BIT(0)
+#define SPRD_OUTBOX_FIFO_WR_SHIFT              16
+#define SPRD_OUTBOX_FIFO_RD_SHIFT              24
+#define SPRD_OUTBOX_FIFO_POS_MASK              GENMASK(7, 0)
+
+/* Bit and mask definiation for inbox's SPRD_MBOX_IRQ_MSK register */
+#define SPRD_INBOX_FIFO_BLOCK_IRQ              BIT(0)
+#define SPRD_INBOX_FIFO_OVERFLOW_IRQ           BIT(1)
+#define SPRD_INBOX_FIFO_DELIVER_IRQ            BIT(2)
+#define SPRD_INBOX_FIFO_IRQ_MASK               GENMASK(2, 0)
+
+/* Bit and mask definiation for outbox's SPRD_MBOX_IRQ_MSK register */
+#define SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ         BIT(0)
+#define SPRD_OUTBOX_FIFO_IRQ_MASK              GENMASK(4, 0)
+
+#define SPRD_MBOX_CHAN_MAX                     8
+
+struct sprd_mbox_priv {
+       struct mbox_controller  mbox;
+       struct device           *dev;
+       void __iomem            *inbox_base;
+       void __iomem            *outbox_base;
+       struct clk              *clk;
+       u32                     outbox_fifo_depth;
+
+       struct mbox_chan        chan[SPRD_MBOX_CHAN_MAX];
+};
+
+static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox)
+{
+       return container_of(mbox, struct sprd_mbox_priv, mbox);
+}
+
+static u32 sprd_mbox_get_fifo_len(struct sprd_mbox_priv *priv, u32 fifo_sts)
+{
+       u32 wr_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_WR_SHIFT) &
+               SPRD_OUTBOX_FIFO_POS_MASK;
+       u32 rd_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_RD_SHIFT) &
+               SPRD_OUTBOX_FIFO_POS_MASK;
+       u32 fifo_len;
+
+       /*
+        * If the read pointer is equal with write pointer, which means the fifo
+        * is full or empty.
+        */
+       if (wr_pos == rd_pos) {
+               if (fifo_sts & SPRD_OUTBOX_FIFO_FULL)
+                       fifo_len = priv->outbox_fifo_depth;
+               else
+                       fifo_len = 0;
+       } else if (wr_pos > rd_pos) {
+               fifo_len = wr_pos - rd_pos;
+       } else {
+               fifo_len = priv->outbox_fifo_depth - rd_pos + wr_pos;
+       }
+
+       return fifo_len;
+}
+
+static irqreturn_t sprd_mbox_outbox_isr(int irq, void *data)
+{
+       struct sprd_mbox_priv *priv = data;
+       struct mbox_chan *chan;
+       u32 fifo_sts, fifo_len, msg[2];
+       int i, id;
+
+       fifo_sts = readl(priv->outbox_base + SPRD_MBOX_FIFO_STS);
+
+       fifo_len = sprd_mbox_get_fifo_len(priv, fifo_sts);
+       if (!fifo_len) {
+               dev_warn_ratelimited(priv->dev, "spurious outbox interrupt\n");
+               return IRQ_NONE;
+       }
+
+       for (i = 0; i < fifo_len; i++) {
+               msg[0] = readl(priv->outbox_base + SPRD_MBOX_MSG_LOW);
+               msg[1] = readl(priv->outbox_base + SPRD_MBOX_MSG_HIGH);
+               id = readl(priv->outbox_base + SPRD_MBOX_ID);
+
+               chan = &priv->chan[id];
+               mbox_chan_received_data(chan, (void *)msg);
+
+               /* Trigger to update outbox FIFO pointer */
+               writel(0x1, priv->outbox_base + SPRD_MBOX_TRIGGER);
+       }
+
+       /* Clear irq status after reading all message. */
+       writel(SPRD_MBOX_IRQ_CLR, priv->outbox_base + SPRD_MBOX_IRQ_STS);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data)
+{
+       struct sprd_mbox_priv *priv = data;
+       struct mbox_chan *chan;
+       u32 fifo_sts, send_sts, busy, id;
+
+       fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS);
+
+       /* Get the inbox data delivery status */
+       send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >>
+               SPRD_INBOX_FIFO_DELIVER_SHIFT;
+       if (!send_sts) {
+               dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n");
+               return IRQ_NONE;
+       }
+
+       while (send_sts) {
+               id = __ffs(send_sts);
+               send_sts &= (send_sts - 1);
+
+               chan = &priv->chan[id];
+
+               /*
+                * Check if the message was fetched by remote traget, if yes,
+                * that means the transmission has been completed.
+                */
+               busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK;
+               if (!(busy & BIT(id)))
+                       mbox_chan_txdone(chan, 0);
+       }
+
+       /* Clear FIFO delivery and overflow status */
+       writel(fifo_sts &
+              (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK),
+              priv->inbox_base + SPRD_MBOX_FIFO_RST);
+
+       /* Clear irq status */
+       writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS);
+
+       return IRQ_HANDLED;
+}
+
+static int sprd_mbox_send_data(struct mbox_chan *chan, void *msg)
+{
+       struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);
+       unsigned long id = (unsigned long)chan->con_priv;
+       u32 *data = msg;
+
+       /* Write data into inbox FIFO, and only support 8 bytes every time */
+       writel(data[0], priv->inbox_base + SPRD_MBOX_MSG_LOW);
+       writel(data[1], priv->inbox_base + SPRD_MBOX_MSG_HIGH);
+
+       /* Set target core id */
+       writel(id, priv->inbox_base + SPRD_MBOX_ID);
+
+       /* Trigger remote request */
+       writel(0x1, priv->inbox_base + SPRD_MBOX_TRIGGER);
+
+       return 0;
+}
+
+static int sprd_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
+{
+       struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);
+       unsigned long id = (unsigned long)chan->con_priv;
+       u32 busy;
+
+       timeout = jiffies + msecs_to_jiffies(timeout);
+
+       while (time_before(jiffies, timeout)) {
+               busy = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS) &
+                       SPRD_INBOX_FIFO_BUSY_MASK;
+               if (!(busy & BIT(id))) {
+                       mbox_chan_txdone(chan, 0);
+                       return 0;
+               }
+
+               udelay(1);
+       }
+
+       return -ETIME;
+}
+
+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);
+
+       /* 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);
+
+       return 0;
+}
+
+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);
+}
+
+static const struct mbox_chan_ops sprd_mbox_ops = {
+       .send_data      = sprd_mbox_send_data,
+       .flush          = sprd_mbox_flush,
+       .startup        = sprd_mbox_startup,
+       .shutdown       = sprd_mbox_shutdown,
+};
+
+static void sprd_mbox_disable(void *data)
+{
+       struct sprd_mbox_priv *priv = data;
+
+       clk_disable_unprepare(priv->clk);
+}
+
+static int sprd_mbox_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct sprd_mbox_priv *priv;
+       int ret, inbox_irq, outbox_irq;
+       unsigned long id;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->dev = dev;
+
+       /*
+        * The Spreadtrum mailbox uses an inbox to send messages to the target
+        * core, and uses an outbox to receive messages from other cores.
+        *
+        * Thus the mailbox controller supplies 2 different register addresses
+        * and IRQ numbers for inbox and outbox.
+        */
+       priv->inbox_base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->inbox_base))
+               return PTR_ERR(priv->inbox_base);
+
+       priv->outbox_base = devm_platform_ioremap_resource(pdev, 1);
+       if (IS_ERR(priv->outbox_base))
+               return PTR_ERR(priv->outbox_base);
+
+       priv->clk = devm_clk_get(dev, "enable");
+       if (IS_ERR(priv->clk)) {
+               dev_err(dev, "failed to get mailbox clock\n");
+               return PTR_ERR(priv->clk);
+       }
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret)
+               return ret;
+
+       ret = devm_add_action_or_reset(dev, sprd_mbox_disable, priv);
+       if (ret) {
+               dev_err(dev, "failed to add mailbox disable action\n");
+               return ret;
+       }
+
+       inbox_irq = platform_get_irq(pdev, 0);
+       if (inbox_irq < 0)
+               return inbox_irq;
+
+       ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr,
+                              IRQF_NO_SUSPEND, dev_name(dev), priv);
+       if (ret) {
+               dev_err(dev, "failed to request inbox IRQ: %d\n", ret);
+               return ret;
+       }
+
+       outbox_irq = platform_get_irq(pdev, 1);
+       if (outbox_irq < 0)
+               return outbox_irq;
+
+       ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr,
+                              IRQF_NO_SUSPEND, dev_name(dev), priv);
+       if (ret) {
+               dev_err(dev, "failed to request outbox IRQ: %d\n", ret);
+               return ret;
+       }
+
+       /* Get the default outbox FIFO depth */
+       priv->outbox_fifo_depth =
+               readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1;
+       priv->mbox.dev = dev;
+       priv->mbox.chans = &priv->chan[0];
+       priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX;
+       priv->mbox.ops = &sprd_mbox_ops;
+       priv->mbox.txdone_irq = true;
+
+       for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++)
+               priv->chan[id].con_priv = (void *)id;
+
+       ret = devm_mbox_controller_register(dev, &priv->mbox);
+       if (ret) {
+               dev_err(dev, "failed to register mailbox: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id sprd_mbox_of_match[] = {
+       { .compatible = "sprd,sc9860-mailbox", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sprd_mbox_of_match);
+
+static struct platform_driver sprd_mbox_driver = {
+       .driver = {
+               .name = "sprd-mailbox",
+               .of_match_table = sprd_mbox_of_match,
+       },
+       .probe  = sprd_mbox_probe,
+};
+module_platform_driver(sprd_mbox_driver);
+
+MODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>");
+MODULE_DESCRIPTION("Spreadtrum mailbox driver");
+MODULE_LICENSE("GPL v2");
index 86887c9..f44079d 100644 (file)
@@ -504,10 +504,9 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox,
                mchan->req_buf_size = resource_size(&res);
                mchan->req_buf = devm_ioremap(mdev, res.start,
                                              mchan->req_buf_size);
-               if (IS_ERR(mchan->req_buf)) {
+               if (!mchan->req_buf) {
                        dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
-                       ret = PTR_ERR(mchan->req_buf);
-                       return ret;
+                       return -ENOMEM;
                }
        } else if (ret != -ENODEV) {
                dev_err(mdev, "Unmatched resource %s, %d.\n", name, ret);
@@ -520,10 +519,9 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox,
                mchan->resp_buf_size = resource_size(&res);
                mchan->resp_buf = devm_ioremap(mdev, res.start,
                                               mchan->resp_buf_size);
-               if (IS_ERR(mchan->resp_buf)) {
+               if (!mchan->resp_buf) {
                        dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
-                       ret = PTR_ERR(mchan->resp_buf);
-                       return ret;
+                       return -ENOMEM;
                }
        } else if (ret != -ENODEV) {
                dev_err(mdev, "Unmatched resource %s.\n", name);
@@ -543,10 +541,9 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox,
                mchan->req_buf_size = resource_size(&res);
                mchan->req_buf = devm_ioremap(mdev, res.start,
                                              mchan->req_buf_size);
-               if (IS_ERR(mchan->req_buf)) {
+               if (!mchan->req_buf) {
                        dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
-                       ret = PTR_ERR(mchan->req_buf);
-                       return ret;
+                       return -ENOMEM;
                }
        } else if (ret != -ENODEV) {
                dev_err(mdev, "Unmatched resource %s.\n", name);
@@ -559,10 +556,9 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox,
                mchan->resp_buf_size = resource_size(&res);
                mchan->resp_buf = devm_ioremap(mdev, res.start,
                                               mchan->resp_buf_size);
-               if (IS_ERR(mchan->resp_buf)) {
+               if (!mchan->resp_buf) {
                        dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
-                       ret = PTR_ERR(mchan->resp_buf);
-                       return ret;
+                       return -ENOMEM;
                }
        } else if (ret != -ENODEV) {
                dev_err(mdev, "Unmatched resource %s.\n", name);
@@ -668,10 +664,9 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
 
        /* IPI IRQ */
        ret = platform_get_irq(pdev, 0);
-       if (ret < 0) {
-               dev_err(dev, "unable to find IPI IRQ.\n");
+       if (ret < 0)
                goto free_mbox_dev;
-       }
+
        pdata->irq = ret;
        ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt,
                               IRQF_SHARED, dev_name(dev), pdata);
diff --git a/include/dt-bindings/mailbox/qcom-ipcc.h b/include/dt-bindings/mailbox/qcom-ipcc.h
new file mode 100644 (file)
index 0000000..4c23eef
--- /dev/null
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/*
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DT_BINDINGS_MAILBOX_IPCC_H
+#define __DT_BINDINGS_MAILBOX_IPCC_H
+
+/* Signal IDs for MPROC protocol */
+#define IPCC_MPROC_SIGNAL_GLINK_QMP    0
+#define IPCC_MPROC_SIGNAL_SMP2P                2
+#define IPCC_MPROC_SIGNAL_PING         3
+
+/* Client IDs */
+#define IPCC_CLIENT_AOP                        0
+#define IPCC_CLIENT_TZ                 1
+#define IPCC_CLIENT_MPSS               2
+#define IPCC_CLIENT_LPASS              3
+#define IPCC_CLIENT_SLPI               4
+#define IPCC_CLIENT_SDC                        5
+#define IPCC_CLIENT_CDSP               6
+#define IPCC_CLIENT_NPU                        7
+#define IPCC_CLIENT_APSS               8
+#define IPCC_CLIENT_GPU                        9
+#define IPCC_CLIENT_CVP                        10
+#define IPCC_CLIENT_CAM                        11
+#define IPCC_CLIENT_VPU                        12
+#define IPCC_CLIENT_PCIE0              13
+#define IPCC_CLIENT_PCIE1              14
+#define IPCC_CLIENT_PCIE2              15
+#define IPCC_CLIENT_SPSS               16
+
+#endif