- 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 as 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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE6EwehDt/SOnwFyTyf9lkf8eYP5UFAl7hrXUACgkQf9lkf8eY P5U+9g//TWG3xf8pTEDkKq5LDQbq2K58GeplYbS1M0dfF6kMQsl9jdPMh7F/8kdO fBE1yAeVQrv+uiL7TNdlM4AyfmFDO2if8snhvvLZD61LfTBzAQGfm48oz3zB7n3r f9pyrnCXvjYutQ4TpNT3WBOuuJSNLHcyfmJwhkpdLQYQiSywOhP+LAKrw3LMwaIk 0/5LkixC1IuPOdShTRJnkR2WA4fZ0jhUFbZmP0OwmZZc4I0V/CsQzeh7DqZyCnak zcOaUg8bfOPGlPQVo4MHJpFlC/irIjPxD/TXPVftwHHB2Zz+g0u2uSC9mZvKdszW vUDGtywP64arcsyuCuDC1C/WVww30WiPhIT08MInbbNtsdMaPPdVJrnyQzY53xqo ZfFDZJ+F9K11xi2xU5EUh5KwWV3yQvx7Y2c5Y1G67Vu73jryvR5aPkCCCdRgL9Pv qe3ux5iMWK5XrnKm7UJ6kUxcEyhiP/ReKmozsnYpQzRzDlG3v8Gjghr6/tTjEV60 At/f5zPn1GlX8kkkAakgORTHQ94MfsMrYh1CMY49P/ICntGKM3WYv9YGv1sDhxbn ih2G5VzngQtffnAQLyPsTGvuRLxFL1TEoVpI7/1OQq/ReksFxjN58X+Vc0W9PA7a mzhZROc/4KatjQNYx3YPejYWL9e9WqGecKRzO4TaxiDTElhgWvE= =FpYp -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
2dca74a40e
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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 */
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
...
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue