Commit 2dca74a4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mailbox-v5.8' of git://git.linaro.org/landing-teams/working/fujitsu/integration

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
parents e0154bd4 e9f901dc
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>;
};
# 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>;
};
# 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 */
};
# 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>;
};
...
...@@ -14187,6 +14187,14 @@ L: linux-arm-msm@vger.kernel.org ...@@ -14187,6 +14187,14 @@ L: linux-arm-msm@vger.kernel.org
S: Maintained S: Maintained
F: drivers/iommu/qcom_iommu.c 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 QUALCOMM RMNET DRIVER
M: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org> M: Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
M: Sean Tranchetti <stranche@codeaurora.org> M: Sean Tranchetti <stranche@codeaurora.org>
......
...@@ -236,4 +236,22 @@ config SUN6I_MSGBOX ...@@ -236,4 +236,22 @@ config SUN6I_MSGBOX
various Allwinner SoCs. This mailbox is used for communication various Allwinner SoCs. This mailbox is used for communication
between the application CPUs and the power management coprocessor. 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 endif
...@@ -50,3 +50,7 @@ obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o ...@@ -50,3 +50,7 @@ obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o
obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
#define IMX_MU_xSR_GIPn(x) BIT(28 + (3 - (x))) #define IMX_MU_xSR_GIPn(x) BIT(28 + (3 - (x)))
...@@ -66,6 +67,8 @@ struct imx_mu_priv { ...@@ -66,6 +67,8 @@ struct imx_mu_priv {
struct clk *clk; struct clk *clk;
int irq; int irq;
u32 xcr;
bool side_b; bool side_b;
}; };
...@@ -154,12 +157,17 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv, ...@@ -154,12 +157,17 @@ static int imx_mu_scu_tx(struct imx_mu_priv *priv,
switch (cp->type) { switch (cp->type) {
case IMX_MU_TYPE_TX: 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 * The real message size can be different to
* struct imx_sc_rpc_msg_max size * 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; return -EINVAL;
} }
...@@ -198,9 +206,8 @@ static int imx_mu_scu_rx(struct imx_mu_priv *priv, ...@@ -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)); imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_RIEn(0));
*data++ = imx_mu_read(priv, priv->dcfg->xRR[0]); *data++ = imx_mu_read(priv, priv->dcfg->xRR[0]);
if (msg.hdr.size > sizeof(msg)) { if (msg.hdr.size > sizeof(msg) / 4) {
dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n", dev_err(priv->dev, "Maximal message size (%zu bytes) exceeded on RX; got: %i bytes\n", sizeof(msg), msg.hdr.size << 2);
sizeof(msg), msg.hdr.size);
return -EINVAL; return -EINVAL;
} }
...@@ -285,8 +292,10 @@ static int imx_mu_startup(struct mbox_chan *chan) ...@@ -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_priv *priv = to_imx_mu_priv(chan->mbox);
struct imx_mu_con_priv *cp = chan->con_priv; struct imx_mu_con_priv *cp = chan->con_priv;
unsigned long irq_flag = IRQF_SHARED;
int ret; int ret;
pm_runtime_get_sync(priv->dev);
if (cp->type == IMX_MU_TYPE_TXDB) { if (cp->type == IMX_MU_TYPE_TXDB) {
/* Tx doorbell don't have ACK support */ /* Tx doorbell don't have ACK support */
tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet, tasklet_init(&cp->txdb_tasklet, imx_mu_txdb_tasklet,
...@@ -294,8 +303,12 @@ static int imx_mu_startup(struct mbox_chan *chan) ...@@ -294,8 +303,12 @@ static int imx_mu_startup(struct mbox_chan *chan)
return 0; return 0;
} }
ret = request_irq(priv->irq, imx_mu_isr, IRQF_SHARED | /* IPC MU should be with IRQF_NO_SUSPEND set */
IRQF_NO_SUSPEND, cp->irq_desc, chan); 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) { if (ret) {
dev_err(priv->dev, dev_err(priv->dev,
"Unable to acquire IRQ %d\n", priv->irq); "Unable to acquire IRQ %d\n", priv->irq);
...@@ -323,6 +336,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan) ...@@ -323,6 +336,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
if (cp->type == IMX_MU_TYPE_TXDB) { if (cp->type == IMX_MU_TYPE_TXDB) {
tasklet_kill(&cp->txdb_tasklet); tasklet_kill(&cp->txdb_tasklet);
pm_runtime_put_sync(priv->dev);
return; return;
} }
...@@ -341,6 +355,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan) ...@@ -341,6 +355,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
} }
free_irq(priv->irq, chan); free_irq(priv->irq, chan);
pm_runtime_put_sync(priv->dev);
} }
static const struct mbox_chan_ops imx_mu_ops = { 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, ...@@ -374,7 +389,7 @@ static struct mbox_chan *imx_mu_scu_xlate(struct mbox_controller *mbox,
break; break;
default: default:
dev_err(mbox->dev, "Invalid chan type: %d\n", type); dev_err(mbox->dev, "Invalid chan type: %d\n", type);
return NULL; return ERR_PTR(-EINVAL);
} }
if (chan >= mbox->num_chans) { if (chan >= mbox->num_chans) {
...@@ -508,14 +523,39 @@ static int imx_mu_probe(struct platform_device *pdev) ...@@ -508,14 +523,39 @@ static int imx_mu_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv); 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) static int imx_mu_remove(struct platform_device *pdev)
{ {
struct imx_mu_priv *priv = platform_get_drvdata(pdev); struct imx_mu_priv *priv = platform_get_drvdata(pdev);
clk_disable_unprepare(priv->clk); pm_runtime_disable(priv->dev);
return 0; return 0;
} }
...@@ -558,12 +598,69 @@ static const struct of_device_id imx_mu_dt_ids[] = { ...@@ -558,12 +598,69 @@ static const struct of_device_id imx_mu_dt_ids[] = {
}; };
MODULE_DEVICE_TABLE(of, 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 = { static struct platform_driver imx_mu_driver = {
.probe = imx_mu_probe, .probe = imx_mu_probe,
.remove = imx_mu_remove, .remove = imx_mu_remove,
.driver = { .driver = {
.name = "imx_mu", .name = "imx_mu",
.of_match_table = imx_mu_dt_ids, .of_match_table = imx_mu_dt_ids,
.pm = &imx_mu_pm_ops,
}, },
}; };
module_platform_driver(imx_mu_driver); module_platform_driver(imx_mu_driver);
......
...@@ -568,7 +568,7 @@ static int pcc_mbox_probe(struct platform_device *pdev) ...@@ -568,7 +568,7 @@ static int pcc_mbox_probe(struct platform_device *pdev)
return ret; return ret;
} }
struct platform_driver pcc_mbox_driver = { static struct platform_driver pcc_mbox_driver = {
.probe = pcc_mbox_probe, .probe = pcc_mbox_probe,
.driver = { .driver = {
.name = "PCCT", .name = "PCCT",
......
...@@ -24,6 +24,35 @@ struct qcom_apcs_ipc { ...@@ -24,6 +24,35 @@ struct qcom_apcs_ipc {
struct platform_device *clk; 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 = { static const struct regmap_config apcs_regmap_config = {
.reg_bits = 32, .reg_bits = 32,
.reg_stride = 4, .reg_stride = 4,
...@@ -48,17 +77,12 @@ static const struct mbox_chan_ops qcom_apcs_ipc_ops = { ...@@ -48,17 +77,12 @@ static const struct mbox_chan_ops qcom_apcs_ipc_ops = {
static int qcom_apcs_ipc_probe(struct platform_device *pdev) static int qcom_apcs_ipc_probe(struct platform_device *pdev)
{ {
struct qcom_apcs_ipc *apcs; struct qcom_apcs_ipc *apcs;
const struct qcom_apcs_ipc_data *apcs_data;
struct regmap *regmap; struct regmap *regmap;
struct resource *res; struct resource *res;
unsigned long offset;
void __iomem *base; void __iomem *base;
unsigned long i; unsigned long i;
int ret; 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); apcs = devm_kzalloc(&pdev->dev, sizeof(*apcs), GFP_KERNEL);
if (!apcs) if (!apcs)
...@@ -73,10 +97,10 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev) ...@@ -73,10 +97,10 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
if (IS_ERR(regmap)) if (IS_ERR(regmap))
return PTR_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->regmap = regmap;
apcs->offset = offset; apcs->offset = apcs_data->offset;
/* Initialize channel identifiers */ /* Initialize channel identifiers */
for (i = 0; i < ARRAY_SIZE(apcs->mbox_chans); i++) for (i = 0; i < ARRAY_SIZE(apcs->mbox_chans); i++)
...@@ -93,9 +117,9 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev) ...@@ -93,9 +117,9 @@ static int qcom_apcs_ipc_probe(struct platform_device *pdev)
return ret; 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, apcs->clk = platform_device_register_data(&pdev->dev,
"qcom-apcs-msm8916-clk", apcs_data->clk_name,
PLATFORM_DEVID_NONE, PLATFORM_DEVID_NONE,
NULL, 0); NULL, 0);
if (IS_ERR(apcs->clk)) if (IS_ERR(apcs->clk))
...@@ -119,14 +143,15 @@ static int qcom_apcs_ipc_remove(struct platform_device *pdev) ...@@ -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 */ /* .data is the offset of the ipc register within the global block */
static const struct of_device_id qcom_apcs_ipc_of_match[] = { static const struct of_device_id qcom_apcs_ipc_of_match[] = {
{ .compatible = "qcom,msm8916-apcs-kpss-global", .data = (void *)8 }, { .compatible = "qcom,ipq6018-apcs-apps-global", .data = &ipq6018_apcs_data },
{ .compatible = "qcom,msm8996-apcs-hmss-global", .data = (void *)16 }, { .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq8074_apcs_data },
{ .compatible = "qcom,msm8998-apcs-hmss-global", .data = (void *)8 }, { .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data },
{ .compatible = "qcom,qcs404-apcs-apps-global", .data = (void *)8 }, { .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data },
{ .compatible = "qcom,sc7180-apss-shared", .data = (void *)12 }, { .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8998_apcs_data },
{ .compatible = "qcom,sdm845-apss-shared", .data = (void *)12 }, { .compatible = "qcom,qcs404-apcs-apps-global", .data = &msm8916_apcs_data },
{ .compatible = "qcom,sm8150-apss-shared", .data = (void *)12 }, { .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data },
{ .compatible = "qcom,ipq8074-apcs-apps-global", .data = (void *)8 }, { .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); MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
......
// 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");
// 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");
...@@ -504,10 +504,9 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, ...@@ -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_size = resource_size(&res);
mchan->req_buf = devm_ioremap(mdev, res.start, mchan->req_buf = devm_ioremap(mdev, res.start,
mchan->req_buf_size); 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"); dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
ret = PTR_ERR(mchan->req_buf); return -ENOMEM;
return ret;
} }
} else if (ret != -ENODEV) { } else if (ret != -ENODEV) {
dev_err(mdev, "Unmatched resource %s, %d.\n", name, ret); 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, ...@@ -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_size = resource_size(&res);
mchan->resp_buf = devm_ioremap(mdev, res.start, mchan->resp_buf = devm_ioremap(mdev, res.start,
mchan->resp_buf_size); 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"); dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
ret = PTR_ERR(mchan->resp_buf); return -ENOMEM;
return ret;
} }
} else if (ret != -ENODEV) { } else if (ret != -ENODEV) {
dev_err(mdev, "Unmatched resource %s.\n", name); dev_err(mdev, "Unmatched resource %s.\n", name);
...@@ -543,10 +541,9 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, ...@@ -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_size = resource_size(&res);
mchan->req_buf = devm_ioremap(mdev, res.start, mchan->req_buf = devm_ioremap(mdev, res.start,
mchan->req_buf_size); 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"); dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
ret = PTR_ERR(mchan->req_buf); return -ENOMEM;
return ret;
} }
} else if (ret != -ENODEV) { } else if (ret != -ENODEV) {
dev_err(mdev, "Unmatched resource %s.\n", name); dev_err(mdev, "Unmatched resource %s.\n", name);
...@@ -559,10 +556,9 @@ static int zynqmp_ipi_mbox_probe(struct zynqmp_ipi_mbox *ipi_mbox, ...@@ -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_size = resource_size(&res);
mchan->resp_buf = devm_ioremap(mdev, res.start, mchan->resp_buf = devm_ioremap(mdev, res.start,
mchan->resp_buf_size); 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"); dev_err(mdev, "Unable to map IPI buffer I/O memory\n");
ret = PTR_ERR(mchan->resp_buf); return -ENOMEM;
return ret;
} }
} else if (ret != -ENODEV) { } else if (ret != -ENODEV) {
dev_err(mdev, "Unmatched resource %s.\n", name); dev_err(mdev, "Unmatched resource %s.\n", name);
...@@ -668,10 +664,9 @@ static int zynqmp_ipi_probe(struct platform_device *pdev) ...@@ -668,10 +664,9 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
/* IPI IRQ */ /* IPI IRQ */
ret = platform_get_irq(pdev, 0); ret = platform_get_irq(pdev, 0);
if (ret < 0) { if (ret < 0)
dev_err(dev, "unable to find IPI IRQ.\n");
goto free_mbox_dev; goto free_mbox_dev;
}
pdata->irq = ret; pdata->irq = ret;
ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt, ret = devm_request_irq(dev, pdata->irq, zynqmp_ipi_interrupt,
IRQF_SHARED, dev_name(dev), pdata); IRQF_SHARED, dev_name(dev), pdata);
......
/* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment