mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
- qcom: add support for MSM8976
- mtk: enable mt8186 add ADSP controller driver - ti: use poll mode during suspend - tegra: fix tx channel flush - imx: add i.MX8 SECO MU support prepare for, and add iMX93 support -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE6EwehDt/SOnwFyTyf9lkf8eYP5UFAmItTzIACgkQf9lkf8eY P5WZdw/9HsyFdS3n478i0Iut8TgRgke88/ZrhiWekSNJfSrGmKiMbSn1QdDLpqMV A113m4rnduz4n4upe+SxWzBA76BbKePUpjW+Fsvu3OtxZ/6LwNmkKsa7z91+CF7n RQh+JXmmiO0rLwl10zIsYSXr3zjSz85mKNJUZFtdNFqaj0RZhJPWSVm2OUz36ONk WviDiDeWJuqJBtfRXIbqkqGv1YbWHa4CwsxYBA8R91OQOrb9Wd9A7roeUJjR2yvH PISGcGycMnDrfowNU3IU1rTVzNq/ojeNX3EfhR489MiKoLVvv5+O8Thr5UODf7TW ZtbdVANCywGO0KVpgbQb60TCJi1mzjmyw0UietDw/zz7d8TNKc3CbyR5I3CRMqSU epxpjpywAppdd3AZAKe/x44ctlefiSnzzCFqq3eNk2HnQr+p/mKnFW+JwnN8qEVK hTAq7L7kkBqQbWRdwPAkE4nAjT2PhdLL0CegOjygP2WBAVU43QdeDoutZMJFeBNZ AJUMGLSzbxDNM4Nxaq2xUbn6Sm7MoPRFPrUDIkuW+NFH7XqAYetTIgiNvhv/qGMS Qpwn0ZVEZAxEj+wQKBUID84zj2FTCgcCMqkm8PADahjDzjCDEPKOMmTYxiqjSwXa VM7Np5+cCSYIVyz6/uYQ3AYcBgfxt0M+rB4qGGEgBFGLT/SEFcQ= =mmKG -----END PGP SIGNATURE----- Merge tag 'mailbox-v5.18' of git://git.linaro.org/landing-teams/working/fujitsu/integration Pull mailbox updates from Jassi Brar: "qcom: - add support for MSM8976 mtk: - enable mt8186 - add ADSP controller driver ti: - use poll mode during suspend tegra: - fix tx channel flush imx: - add i.MX8 SECO MU support - prepare for, and add iMX93 support" * tag 'mailbox-v5.18' of git://git.linaro.org/landing-teams/working/fujitsu/integration: dt-bindings: mailbox: add definition for mt8186 mailbox: ti-msgmgr: Operate mailbox in polled mode during system suspend mailbox: ti-msgmgr: Refactor message read during interrupt handler mailbox: imx: support i.MX93 S401 MU mailbox: imx: support dual interrupts mailbox: imx: extend irq to an array dt-bindings: mailbox: imx-mu: add i.MX93 S4 MU support dt-bindings: mailbox: imx-mu: add i.MX93 MU mailbox: imx: add i.MX8 SECO MU support mailbox: imx: introduce rxdb callback dt-bindings: mailbox: imx-mu: add i.MX8 SECO MU support mailbox: imx: enlarge timeout while reading/writing messages to SCFW mailbox: imx: fix crash in resume on i.mx8ulp mailbox: imx: fix wakeup failure from freeze mode mailbox: mediatek: add support for adsp mailbox controller dt-bindings: mailbox: mtk,adsp-mbox: add mtk adsp-mbox document mailbox: qcom-apcs-ipc: Add compatible for MSM8976 SoC dt-bindings: mailbox: Add compatible for the MSM8976 mailbox: tegra-hsp: Flush whole channel
This commit is contained in:
commit
50d602d81f
12 changed files with 719 additions and 73 deletions
|
@ -28,7 +28,12 @@ properties:
|
||||||
- const: fsl,imx7ulp-mu
|
- const: fsl,imx7ulp-mu
|
||||||
- const: fsl,imx8ulp-mu
|
- const: fsl,imx8ulp-mu
|
||||||
- const: fsl,imx8-mu-scu
|
- const: fsl,imx8-mu-scu
|
||||||
|
- const: fsl,imx8-mu-seco
|
||||||
|
- const: fsl,imx93-mu-s4
|
||||||
- const: fsl,imx8ulp-mu-s4
|
- const: fsl,imx8ulp-mu-s4
|
||||||
|
- items:
|
||||||
|
- const: fsl,imx93-mu
|
||||||
|
- const: fsl,imx8ulp-mu
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
- fsl,imx7s-mu
|
- fsl,imx7s-mu
|
||||||
|
@ -51,7 +56,14 @@ properties:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
||||||
interrupts:
|
interrupts:
|
||||||
maxItems: 1
|
minItems: 1
|
||||||
|
maxItems: 2
|
||||||
|
|
||||||
|
interrupt-names:
|
||||||
|
minItems: 1
|
||||||
|
items:
|
||||||
|
- const: tx
|
||||||
|
- const: rx
|
||||||
|
|
||||||
"#mbox-cells":
|
"#mbox-cells":
|
||||||
description: |
|
description: |
|
||||||
|
@ -86,6 +98,27 @@ required:
|
||||||
- interrupts
|
- interrupts
|
||||||
- "#mbox-cells"
|
- "#mbox-cells"
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- if:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- fsl,imx93-mu-s4
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
interrupt-names:
|
||||||
|
minItems: 2
|
||||||
|
interrupts:
|
||||||
|
minItems: 2
|
||||||
|
|
||||||
|
else:
|
||||||
|
properties:
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
not:
|
||||||
|
required:
|
||||||
|
- interrupt-names
|
||||||
|
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
|
|
50
Documentation/devicetree/bindings/mailbox/mtk,adsp-mbox.yaml
Normal file
50
Documentation/devicetree/bindings/mailbox/mtk,adsp-mbox.yaml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/mailbox/mtk,adsp-mbox.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Mediatek ADSP mailbox
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Allen-KH Cheng <Allen-KH.Cheng@mediatek.com>
|
||||||
|
|
||||||
|
description: |
|
||||||
|
The MTK ADSP mailbox Inter-Processor Communication (IPC) enables the SoC
|
||||||
|
to ommunicate with ADSP by passing messages through two mailbox channels.
|
||||||
|
The MTK ADSP mailbox IPC also provides the ability for one processor to
|
||||||
|
signal the other processor using interrupts.
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
- const: mediatek,mt8195-adsp-mbox
|
||||||
|
|
||||||
|
"#mbox-cells":
|
||||||
|
const: 0
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- "#mbox-cells"
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
|
||||||
|
additionalProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
|
||||||
|
adsp_mailbox0:mailbox@10816000 {
|
||||||
|
compatible = "mediatek,mt8195-adsp-mbox";
|
||||||
|
#mbox-cells = <0>;
|
||||||
|
reg = <0x10816000 0x1000>;
|
||||||
|
interrupts = <GIC_SPI 702 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||||
|
};
|
|
@ -10,7 +10,8 @@ mailbox.txt for generic information about mailbox device-tree bindings.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: can be "mediatek,mt8173-gce", "mediatek,mt8183-gce",
|
- compatible: can be "mediatek,mt8173-gce", "mediatek,mt8183-gce",
|
||||||
"mediatek,mt8192-gce", "mediatek,mt8195-gce" or "mediatek,mt6779-gce".
|
"mediatek,mt8186-gce", "mediatek,mt8192-gce", "mediatek,mt8195-gce" or
|
||||||
|
"mediatek,mt6779-gce".
|
||||||
- reg: Address range of the GCE unit
|
- reg: Address range of the GCE unit
|
||||||
- interrupts: The interrupt signal from the GCE block
|
- interrupts: The interrupt signal from the GCE block
|
||||||
- clock: Clocks according to the common clock binding
|
- clock: Clocks according to the common clock binding
|
||||||
|
@ -40,8 +41,9 @@ Optional properties for a client mutex node:
|
||||||
defined in 'dt-bindings/gce/<chip>-gce.h'.
|
defined in 'dt-bindings/gce/<chip>-gce.h'.
|
||||||
|
|
||||||
Some vaules of properties are defined in 'dt-bindings/gce/mt8173-gce.h',
|
Some vaules of properties are defined in 'dt-bindings/gce/mt8173-gce.h',
|
||||||
'dt-bindings/gce/mt8183-gce.h', 'dt-bindings/gce/mt8192-gce.h',
|
'dt-bindings/gce/mt8183-gce.h', 'dt-bindings/gce/mt8186-gce.h'
|
||||||
'dt-bindings/gce/mt8195-gce.h' or 'dt-bindings/gce/mt6779-gce.h'.
|
'dt-bindings/gce/mt8192-gce.h', 'dt-bindings/gce/mt8195-gce.h' or
|
||||||
|
'dt-bindings/gce/mt6779-gce.h'.
|
||||||
Such as sub-system ids, thread priority, event ids.
|
Such as sub-system ids, thread priority, event ids.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
|
@ -21,6 +21,7 @@ properties:
|
||||||
- qcom,msm8916-apcs-kpss-global
|
- qcom,msm8916-apcs-kpss-global
|
||||||
- qcom,msm8939-apcs-kpss-global
|
- qcom,msm8939-apcs-kpss-global
|
||||||
- qcom,msm8953-apcs-kpss-global
|
- qcom,msm8953-apcs-kpss-global
|
||||||
|
- qcom,msm8976-apcs-kpss-global
|
||||||
- qcom,msm8994-apcs-kpss-global
|
- qcom,msm8994-apcs-kpss-global
|
||||||
- qcom,msm8996-apcs-hmss-global
|
- qcom,msm8996-apcs-hmss-global
|
||||||
- qcom,msm8998-apcs-hmss-global
|
- qcom,msm8998-apcs-hmss-global
|
||||||
|
|
|
@ -238,6 +238,15 @@ config STM32_IPCC
|
||||||
with hardware for Inter-Processor Communication Controller (IPCC)
|
with hardware for Inter-Processor Communication Controller (IPCC)
|
||||||
between processors. Say Y here if you want to have this support.
|
between processors. Say Y here if you want to have this support.
|
||||||
|
|
||||||
|
config MTK_ADSP_MBOX
|
||||||
|
tristate "MediaTek ADSP Mailbox Controller"
|
||||||
|
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||||
|
help
|
||||||
|
Say yes here to add support for "MediaTek ADSP Mailbox Controller.
|
||||||
|
This mailbox driver is used to send notification or short message
|
||||||
|
between processors with ADSP. It will place the message to share
|
||||||
|
buffer and will access the ipc control.
|
||||||
|
|
||||||
config MTK_CMDQ_MBOX
|
config MTK_CMDQ_MBOX
|
||||||
tristate "MediaTek CMDQ Mailbox Support"
|
tristate "MediaTek CMDQ Mailbox Support"
|
||||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||||
|
|
|
@ -49,6 +49,8 @@ obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o
|
||||||
|
|
||||||
obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
|
obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_MTK_ADSP_MBOX) += mtk-adsp-mailbox.o
|
||||||
|
|
||||||
obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
|
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
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
|
* Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
|
||||||
|
* Copyright 2022 NXP, Peng Fan <peng.fan@nxp.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
@ -9,11 +10,13 @@
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#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/pm_runtime.h>
|
||||||
|
#include <linux/suspend.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#define IMX_MU_CHANS 16
|
#define IMX_MU_CHANS 16
|
||||||
|
@ -23,11 +26,15 @@
|
||||||
#define IMX_MU_S4_CHANS 2
|
#define IMX_MU_S4_CHANS 2
|
||||||
#define IMX_MU_CHAN_NAME_SIZE 20
|
#define IMX_MU_CHAN_NAME_SIZE 20
|
||||||
|
|
||||||
|
#define IMX_MU_SECO_TX_TOUT (msecs_to_jiffies(3000))
|
||||||
|
#define IMX_MU_SECO_RX_TOUT (msecs_to_jiffies(3000))
|
||||||
|
|
||||||
|
/* Please not change TX & RX */
|
||||||
enum imx_mu_chan_type {
|
enum imx_mu_chan_type {
|
||||||
IMX_MU_TYPE_TX, /* Tx */
|
IMX_MU_TYPE_TX = 0, /* Tx */
|
||||||
IMX_MU_TYPE_RX, /* Rx */
|
IMX_MU_TYPE_RX = 1, /* Rx */
|
||||||
IMX_MU_TYPE_TXDB, /* Tx doorbell */
|
IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */
|
||||||
IMX_MU_TYPE_RXDB, /* Rx doorbell */
|
IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum imx_mu_xcr {
|
enum imx_mu_xcr {
|
||||||
|
@ -47,7 +54,7 @@ enum imx_mu_xsr {
|
||||||
|
|
||||||
struct imx_sc_rpc_msg_max {
|
struct imx_sc_rpc_msg_max {
|
||||||
struct imx_sc_rpc_msg hdr;
|
struct imx_sc_rpc_msg hdr;
|
||||||
u32 data[7];
|
u32 data[30];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct imx_s4_rpc_msg_max {
|
struct imx_s4_rpc_msg_max {
|
||||||
|
@ -75,7 +82,8 @@ struct imx_mu_priv {
|
||||||
struct imx_mu_con_priv con_priv[IMX_MU_CHANS];
|
struct imx_mu_con_priv con_priv[IMX_MU_CHANS];
|
||||||
const struct imx_mu_dcfg *dcfg;
|
const struct imx_mu_dcfg *dcfg;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
int irq;
|
int irq[IMX_MU_CHANS];
|
||||||
|
bool suspend;
|
||||||
|
|
||||||
u32 xcr[4];
|
u32 xcr[4];
|
||||||
|
|
||||||
|
@ -86,11 +94,13 @@ enum imx_mu_type {
|
||||||
IMX_MU_V1,
|
IMX_MU_V1,
|
||||||
IMX_MU_V2 = BIT(1),
|
IMX_MU_V2 = BIT(1),
|
||||||
IMX_MU_V2_S4 = BIT(15),
|
IMX_MU_V2_S4 = BIT(15),
|
||||||
|
IMX_MU_V2_IRQ = BIT(16),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct imx_mu_dcfg {
|
struct imx_mu_dcfg {
|
||||||
int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data);
|
int (*tx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data);
|
||||||
int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
|
int (*rx)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
|
||||||
|
int (*rxdb)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
|
||||||
void (*init)(struct imx_mu_priv *priv);
|
void (*init)(struct imx_mu_priv *priv);
|
||||||
enum imx_mu_type type;
|
enum imx_mu_type type;
|
||||||
u32 xTR; /* Transmit Register0 */
|
u32 xTR; /* Transmit Register0 */
|
||||||
|
@ -128,6 +138,55 @@ static u32 imx_mu_read(struct imx_mu_priv *priv, u32 offs)
|
||||||
return ioread32(priv->base + offs);
|
return ioread32(priv->base + offs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int imx_mu_tx_waiting_write(struct imx_mu_priv *priv, u32 val, u32 idx)
|
||||||
|
{
|
||||||
|
u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_TX_TOUT;
|
||||||
|
u32 status;
|
||||||
|
u32 can_write;
|
||||||
|
|
||||||
|
dev_dbg(priv->dev, "Trying to write %.8x to idx %d\n", val, idx);
|
||||||
|
|
||||||
|
do {
|
||||||
|
status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_TSR]);
|
||||||
|
can_write = status & IMX_MU_xSR_TEn(priv->dcfg->type, idx % 4);
|
||||||
|
} while (!can_write && time_is_after_jiffies64(timeout_time));
|
||||||
|
|
||||||
|
if (!can_write) {
|
||||||
|
dev_err(priv->dev, "timeout trying to write %.8x at %d(%.8x)\n",
|
||||||
|
val, idx, status);
|
||||||
|
return -ETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
imx_mu_write(priv, val, priv->dcfg->xTR + (idx % 4) * 4);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imx_mu_rx_waiting_read(struct imx_mu_priv *priv, u32 *val, u32 idx)
|
||||||
|
{
|
||||||
|
u64 timeout_time = get_jiffies_64() + IMX_MU_SECO_RX_TOUT;
|
||||||
|
u32 status;
|
||||||
|
u32 can_read;
|
||||||
|
|
||||||
|
dev_dbg(priv->dev, "Trying to read from idx %d\n", idx);
|
||||||
|
|
||||||
|
do {
|
||||||
|
status = imx_mu_read(priv, priv->dcfg->xSR[IMX_MU_RSR]);
|
||||||
|
can_read = status & IMX_MU_xSR_RFn(priv->dcfg->type, idx % 4);
|
||||||
|
} while (!can_read && time_is_after_jiffies64(timeout_time));
|
||||||
|
|
||||||
|
if (!can_read) {
|
||||||
|
dev_err(priv->dev, "timeout trying to read idx %d (%.8x)\n",
|
||||||
|
idx, status);
|
||||||
|
return -ETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = imx_mu_read(priv, priv->dcfg->xRR + (idx % 4) * 4);
|
||||||
|
dev_dbg(priv->dev, "Read %.8x\n", *val);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr)
|
static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, enum imx_mu_xcr type, u32 set, u32 clr)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -177,6 +236,16 @@ static int imx_mu_generic_rx(struct imx_mu_priv *priv,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int imx_mu_generic_rxdb(struct imx_mu_priv *priv,
|
||||||
|
struct imx_mu_con_priv *cp)
|
||||||
|
{
|
||||||
|
imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
|
||||||
|
priv->dcfg->xSR[IMX_MU_GSR]);
|
||||||
|
mbox_chan_received_data(cp->chan, NULL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data)
|
static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp, void *data)
|
||||||
{
|
{
|
||||||
u32 *arg = data;
|
u32 *arg = data;
|
||||||
|
@ -216,7 +285,7 @@ static int imx_mu_specific_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *
|
||||||
ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR],
|
ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_TSR],
|
||||||
xsr,
|
xsr,
|
||||||
xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr),
|
xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr),
|
||||||
0, 100);
|
0, 5 * USEC_PER_SEC);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(priv->dev, "Send data index: %d timeout\n", i);
|
dev_err(priv->dev, "Send data index: %d timeout\n", i);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -261,7 +330,8 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *
|
||||||
|
|
||||||
for (i = 1; i < size; i++) {
|
for (i = 1; i < size; i++) {
|
||||||
ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr,
|
ret = readl_poll_timeout(priv->base + priv->dcfg->xSR[IMX_MU_RSR], xsr,
|
||||||
xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0, 100);
|
xsr & IMX_MU_xSR_RFn(priv->dcfg->type, i % 4), 0,
|
||||||
|
5 * USEC_PER_SEC);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(priv->dev, "timeout read idx %d\n", i);
|
dev_err(priv->dev, "timeout read idx %d\n", i);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -275,6 +345,125 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int imx_mu_seco_tx(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct imx_sc_rpc_msg_max *msg = data;
|
||||||
|
u32 *arg = data;
|
||||||
|
u32 byte_size;
|
||||||
|
int err;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dev_dbg(priv->dev, "Sending message\n");
|
||||||
|
|
||||||
|
switch (cp->type) {
|
||||||
|
case IMX_MU_TYPE_TXDB:
|
||||||
|
byte_size = msg->hdr.size * sizeof(u32);
|
||||||
|
if (byte_size > sizeof(*msg)) {
|
||||||
|
/*
|
||||||
|
* 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), byte_size);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_hex_dump_debug("from client ", DUMP_PREFIX_OFFSET, 4, 4,
|
||||||
|
data, byte_size, false);
|
||||||
|
|
||||||
|
/* Send first word */
|
||||||
|
dev_dbg(priv->dev, "Sending header\n");
|
||||||
|
imx_mu_write(priv, *arg++, priv->dcfg->xTR);
|
||||||
|
|
||||||
|
/* Send signaling */
|
||||||
|
dev_dbg(priv->dev, "Sending signaling\n");
|
||||||
|
imx_mu_xcr_rmw(priv, IMX_MU_GCR,
|
||||||
|
IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx), 0);
|
||||||
|
|
||||||
|
/* Send words to fill the mailbox */
|
||||||
|
for (i = 1; i < 4 && i < msg->hdr.size; i++) {
|
||||||
|
dev_dbg(priv->dev, "Sending word %d\n", i);
|
||||||
|
imx_mu_write(priv, *arg++,
|
||||||
|
priv->dcfg->xTR + (i % 4) * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send rest of message waiting for remote read */
|
||||||
|
for (; i < msg->hdr.size; i++) {
|
||||||
|
dev_dbg(priv->dev, "Sending word %d\n", i);
|
||||||
|
err = imx_mu_tx_waiting_write(priv, *arg++, i);
|
||||||
|
if (err) {
|
||||||
|
dev_err(priv->dev, "Timeout tx %d\n", i);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simulate hack for mbox framework */
|
||||||
|
tasklet_schedule(&cp->txdb_tasklet);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_warn_ratelimited(priv->dev,
|
||||||
|
"Send data on wrong channel type: %d\n",
|
||||||
|
cp->type);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imx_mu_seco_rxdb(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp)
|
||||||
|
{
|
||||||
|
struct imx_sc_rpc_msg_max msg;
|
||||||
|
u32 *data = (u32 *)&msg;
|
||||||
|
u32 byte_size;
|
||||||
|
int err = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
dev_dbg(priv->dev, "Receiving message\n");
|
||||||
|
|
||||||
|
/* Read header */
|
||||||
|
dev_dbg(priv->dev, "Receiving header\n");
|
||||||
|
*data++ = imx_mu_read(priv, priv->dcfg->xRR);
|
||||||
|
byte_size = msg.hdr.size * sizeof(u32);
|
||||||
|
if (byte_size > sizeof(msg)) {
|
||||||
|
dev_err(priv->dev, "Exceed max msg size (%zu) on RX, got: %i\n",
|
||||||
|
sizeof(msg), byte_size);
|
||||||
|
err = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read message waiting they are written */
|
||||||
|
for (i = 1; i < msg.hdr.size; i++) {
|
||||||
|
dev_dbg(priv->dev, "Receiving word %d\n", i);
|
||||||
|
err = imx_mu_rx_waiting_read(priv, data++, i);
|
||||||
|
if (err) {
|
||||||
|
dev_err(priv->dev, "Timeout rx %d\n", i);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear GIP */
|
||||||
|
imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
|
||||||
|
priv->dcfg->xSR[IMX_MU_GSR]);
|
||||||
|
|
||||||
|
print_hex_dump_debug("to client ", DUMP_PREFIX_OFFSET, 4, 4,
|
||||||
|
&msg, byte_size, false);
|
||||||
|
|
||||||
|
/* send data to client */
|
||||||
|
dev_dbg(priv->dev, "Sending message to client\n");
|
||||||
|
mbox_chan_received_data(cp->chan, (void *)&msg);
|
||||||
|
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
error:
|
||||||
|
mbox_chan_received_data(cp->chan, ERR_PTR(err));
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static void imx_mu_txdb_tasklet(unsigned long data)
|
static void imx_mu_txdb_tasklet(unsigned long data)
|
||||||
{
|
{
|
||||||
struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data;
|
struct imx_mu_con_priv *cp = (struct imx_mu_con_priv *)data;
|
||||||
|
@ -326,14 +515,15 @@ static irqreturn_t imx_mu_isr(int irq, void *p)
|
||||||
priv->dcfg->rx(priv, cp);
|
priv->dcfg->rx(priv, cp);
|
||||||
} else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) &&
|
} else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) &&
|
||||||
(cp->type == IMX_MU_TYPE_RXDB)) {
|
(cp->type == IMX_MU_TYPE_RXDB)) {
|
||||||
imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
|
priv->dcfg->rxdb(priv, cp);
|
||||||
priv->dcfg->xSR[IMX_MU_GSR]);
|
|
||||||
mbox_chan_received_data(chan, NULL);
|
|
||||||
} else {
|
} else {
|
||||||
dev_warn_ratelimited(priv->dev, "Not handled interrupt\n");
|
dev_warn_ratelimited(priv->dev, "Not handled interrupt\n");
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (priv->suspend)
|
||||||
|
pm_system_wakeup();
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,7 +539,7 @@ 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;
|
unsigned long irq_flag = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pm_runtime_get_sync(priv->dev);
|
pm_runtime_get_sync(priv->dev);
|
||||||
|
@ -364,11 +554,12 @@ static int imx_mu_startup(struct mbox_chan *chan)
|
||||||
if (!priv->dev->pm_domain)
|
if (!priv->dev->pm_domain)
|
||||||
irq_flag |= IRQF_NO_SUSPEND;
|
irq_flag |= IRQF_NO_SUSPEND;
|
||||||
|
|
||||||
ret = request_irq(priv->irq, imx_mu_isr, irq_flag,
|
if (!(priv->dcfg->type & IMX_MU_V2_IRQ))
|
||||||
cp->irq_desc, chan);
|
irq_flag |= IRQF_SHARED;
|
||||||
|
|
||||||
|
ret = request_irq(priv->irq[cp->type], 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[cp->type]);
|
||||||
"Unable to acquire IRQ %d\n", priv->irq);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +602,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_irq(priv->irq, chan);
|
free_irq(priv->irq[cp->type], chan);
|
||||||
pm_runtime_put_sync(priv->dev);
|
pm_runtime_put_sync(priv->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,6 +670,27 @@ static struct mbox_chan * imx_mu_xlate(struct mbox_controller *mbox,
|
||||||
return &mbox->chans[chan];
|
return &mbox->chans[chan];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct mbox_chan *imx_mu_seco_xlate(struct mbox_controller *mbox,
|
||||||
|
const struct of_phandle_args *sp)
|
||||||
|
{
|
||||||
|
u32 type;
|
||||||
|
|
||||||
|
if (sp->args_count < 1) {
|
||||||
|
dev_err(mbox->dev, "Invalid argument count %d\n", sp->args_count);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
type = sp->args[0]; /* channel type */
|
||||||
|
|
||||||
|
/* Only supports TXDB and RXDB */
|
||||||
|
if (type == IMX_MU_TYPE_TX || type == IMX_MU_TYPE_RX) {
|
||||||
|
dev_err(mbox->dev, "Invalid type: %d\n", type);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return imx_mu_xlate(mbox, sp);
|
||||||
|
}
|
||||||
|
|
||||||
static void imx_mu_init_generic(struct imx_mu_priv *priv)
|
static void imx_mu_init_generic(struct imx_mu_priv *priv)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -529,13 +741,19 @@ static void imx_mu_init_specific(struct imx_mu_priv *priv)
|
||||||
imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
|
imx_mu_write(priv, 0, priv->dcfg->xCR[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void imx_mu_init_seco(struct imx_mu_priv *priv)
|
||||||
|
{
|
||||||
|
imx_mu_init_generic(priv);
|
||||||
|
priv->mbox.of_xlate = imx_mu_seco_xlate;
|
||||||
|
}
|
||||||
|
|
||||||
static int imx_mu_probe(struct platform_device *pdev)
|
static int imx_mu_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct imx_mu_priv *priv;
|
struct imx_mu_priv *priv;
|
||||||
const struct imx_mu_dcfg *dcfg;
|
const struct imx_mu_dcfg *dcfg;
|
||||||
int ret;
|
int i, ret;
|
||||||
u32 size;
|
u32 size;
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
@ -548,14 +766,25 @@ static int imx_mu_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(priv->base))
|
if (IS_ERR(priv->base))
|
||||||
return PTR_ERR(priv->base);
|
return PTR_ERR(priv->base);
|
||||||
|
|
||||||
priv->irq = platform_get_irq(pdev, 0);
|
|
||||||
if (priv->irq < 0)
|
|
||||||
return priv->irq;
|
|
||||||
|
|
||||||
dcfg = of_device_get_match_data(dev);
|
dcfg = of_device_get_match_data(dev);
|
||||||
if (!dcfg)
|
if (!dcfg)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
priv->dcfg = dcfg;
|
priv->dcfg = dcfg;
|
||||||
|
if (priv->dcfg->type & IMX_MU_V2_IRQ) {
|
||||||
|
priv->irq[IMX_MU_TYPE_TX] = platform_get_irq_byname(pdev, "tx");
|
||||||
|
if (priv->irq[IMX_MU_TYPE_TX] < 0)
|
||||||
|
return priv->irq[IMX_MU_TYPE_TX];
|
||||||
|
priv->irq[IMX_MU_TYPE_RX] = platform_get_irq_byname(pdev, "rx");
|
||||||
|
if (priv->irq[IMX_MU_TYPE_RX] < 0)
|
||||||
|
return priv->irq[IMX_MU_TYPE_RX];
|
||||||
|
} else {
|
||||||
|
ret = platform_get_irq(pdev, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < IMX_MU_CHANS; i++)
|
||||||
|
priv->irq[i] = ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (priv->dcfg->type & IMX_MU_V2_S4)
|
if (priv->dcfg->type & IMX_MU_V2_S4)
|
||||||
size = sizeof(struct imx_s4_rpc_msg_max);
|
size = sizeof(struct imx_s4_rpc_msg_max);
|
||||||
|
@ -633,6 +862,7 @@ static int imx_mu_remove(struct platform_device *pdev)
|
||||||
static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
|
static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
|
||||||
.tx = imx_mu_generic_tx,
|
.tx = imx_mu_generic_tx,
|
||||||
.rx = imx_mu_generic_rx,
|
.rx = imx_mu_generic_rx,
|
||||||
|
.rxdb = imx_mu_generic_rxdb,
|
||||||
.init = imx_mu_init_generic,
|
.init = imx_mu_init_generic,
|
||||||
.xTR = 0x0,
|
.xTR = 0x0,
|
||||||
.xRR = 0x10,
|
.xRR = 0x10,
|
||||||
|
@ -643,6 +873,7 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = {
|
||||||
static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
|
static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
|
||||||
.tx = imx_mu_generic_tx,
|
.tx = imx_mu_generic_tx,
|
||||||
.rx = imx_mu_generic_rx,
|
.rx = imx_mu_generic_rx,
|
||||||
|
.rxdb = imx_mu_generic_rxdb,
|
||||||
.init = imx_mu_init_generic,
|
.init = imx_mu_init_generic,
|
||||||
.xTR = 0x20,
|
.xTR = 0x20,
|
||||||
.xRR = 0x40,
|
.xRR = 0x40,
|
||||||
|
@ -653,7 +884,9 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
|
||||||
static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
|
static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
|
||||||
.tx = imx_mu_generic_tx,
|
.tx = imx_mu_generic_tx,
|
||||||
.rx = imx_mu_generic_rx,
|
.rx = imx_mu_generic_rx,
|
||||||
|
.rxdb = imx_mu_generic_rxdb,
|
||||||
.init = imx_mu_init_generic,
|
.init = imx_mu_init_generic,
|
||||||
|
.rxdb = imx_mu_generic_rxdb,
|
||||||
.type = IMX_MU_V2,
|
.type = IMX_MU_V2,
|
||||||
.xTR = 0x200,
|
.xTR = 0x200,
|
||||||
.xRR = 0x280,
|
.xRR = 0x280,
|
||||||
|
@ -672,10 +905,33 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp_s4 = {
|
||||||
.xCR = {0x110, 0x114, 0x120, 0x128},
|
.xCR = {0x110, 0x114, 0x120, 0x128},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct imx_mu_dcfg imx_mu_cfg_imx93_s4 = {
|
||||||
|
.tx = imx_mu_specific_tx,
|
||||||
|
.rx = imx_mu_specific_rx,
|
||||||
|
.init = imx_mu_init_specific,
|
||||||
|
.type = IMX_MU_V2 | IMX_MU_V2_S4 | IMX_MU_V2_IRQ,
|
||||||
|
.xTR = 0x200,
|
||||||
|
.xRR = 0x280,
|
||||||
|
.xSR = {0xC, 0x118, 0x124, 0x12C},
|
||||||
|
.xCR = {0x110, 0x114, 0x120, 0x128},
|
||||||
|
};
|
||||||
|
|
||||||
static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = {
|
static const struct imx_mu_dcfg imx_mu_cfg_imx8_scu = {
|
||||||
.tx = imx_mu_specific_tx,
|
.tx = imx_mu_specific_tx,
|
||||||
.rx = imx_mu_specific_rx,
|
.rx = imx_mu_specific_rx,
|
||||||
.init = imx_mu_init_specific,
|
.init = imx_mu_init_specific,
|
||||||
|
.rxdb = imx_mu_generic_rxdb,
|
||||||
|
.xTR = 0x0,
|
||||||
|
.xRR = 0x10,
|
||||||
|
.xSR = {0x20, 0x20, 0x20, 0x20},
|
||||||
|
.xCR = {0x24, 0x24, 0x24, 0x24},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct imx_mu_dcfg imx_mu_cfg_imx8_seco = {
|
||||||
|
.tx = imx_mu_seco_tx,
|
||||||
|
.rx = imx_mu_generic_rx,
|
||||||
|
.rxdb = imx_mu_seco_rxdb,
|
||||||
|
.init = imx_mu_init_seco,
|
||||||
.xTR = 0x0,
|
.xTR = 0x0,
|
||||||
.xRR = 0x10,
|
.xRR = 0x10,
|
||||||
.xSR = {0x20, 0x20, 0x20, 0x20},
|
.xSR = {0x20, 0x20, 0x20, 0x20},
|
||||||
|
@ -687,7 +943,9 @@ static const struct of_device_id imx_mu_dt_ids[] = {
|
||||||
{ .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx },
|
{ .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx },
|
||||||
{ .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp },
|
{ .compatible = "fsl,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp },
|
||||||
{ .compatible = "fsl,imx8ulp-mu-s4", .data = &imx_mu_cfg_imx8ulp_s4 },
|
{ .compatible = "fsl,imx8ulp-mu-s4", .data = &imx_mu_cfg_imx8ulp_s4 },
|
||||||
|
{ .compatible = "fsl,imx93-mu-s4", .data = &imx_mu_cfg_imx93_s4 },
|
||||||
{ .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu },
|
{ .compatible = "fsl,imx8-mu-scu", .data = &imx_mu_cfg_imx8_scu },
|
||||||
|
{ .compatible = "fsl,imx8-mu-seco", .data = &imx_mu_cfg_imx8_seco },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
|
MODULE_DEVICE_TABLE(of, imx_mu_dt_ids);
|
||||||
|
@ -702,6 +960,8 @@ static int __maybe_unused imx_mu_suspend_noirq(struct device *dev)
|
||||||
priv->xcr[i] = imx_mu_read(priv, priv->dcfg->xCR[i]);
|
priv->xcr[i] = imx_mu_read(priv, priv->dcfg->xCR[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv->suspend = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,11 +978,13 @@ static int __maybe_unused imx_mu_resume_noirq(struct device *dev)
|
||||||
* send failed, may lead to system freeze. This issue
|
* send failed, may lead to system freeze. This issue
|
||||||
* is observed by testing freeze mode suspend.
|
* is observed by testing freeze mode suspend.
|
||||||
*/
|
*/
|
||||||
if (!imx_mu_read(priv, priv->dcfg->xCR[0]) && !priv->clk) {
|
if (!priv->clk && !imx_mu_read(priv, priv->dcfg->xCR[0])) {
|
||||||
for (i = 0; i < IMX_MU_xCR_MAX; i++)
|
for (i = 0; i < IMX_MU_xCR_MAX; i++)
|
||||||
imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]);
|
imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv->suspend = false;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
176
drivers/mailbox/mtk-adsp-mailbox.c
Normal file
176
drivers/mailbox/mtk-adsp-mailbox.c
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 MediaTek Corporation. All rights reserved.
|
||||||
|
* Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/mailbox_controller.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
struct mtk_adsp_mbox_priv {
|
||||||
|
struct device *dev;
|
||||||
|
struct mbox_controller mbox;
|
||||||
|
void __iomem *va_mboxreg;
|
||||||
|
const struct mtk_adsp_mbox_cfg *cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mtk_adsp_mbox_cfg {
|
||||||
|
u32 set_in;
|
||||||
|
u32 set_out;
|
||||||
|
u32 clr_in;
|
||||||
|
u32 clr_out;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline struct mtk_adsp_mbox_priv *get_mtk_adsp_mbox_priv(struct mbox_controller *mbox)
|
||||||
|
{
|
||||||
|
return container_of(mbox, struct mtk_adsp_mbox_priv, mbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t mtk_adsp_mbox_irq(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct mbox_chan *chan = data;
|
||||||
|
struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox);
|
||||||
|
u32 op = readl(priv->va_mboxreg + priv->cfg->set_out);
|
||||||
|
|
||||||
|
writel(op, priv->va_mboxreg + priv->cfg->clr_out);
|
||||||
|
|
||||||
|
return IRQ_WAKE_THREAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t mtk_adsp_mbox_isr(int irq, void *data)
|
||||||
|
{
|
||||||
|
struct mbox_chan *chan = data;
|
||||||
|
|
||||||
|
mbox_chan_received_data(chan, NULL);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mbox_chan *mtk_adsp_mbox_xlate(struct mbox_controller *mbox,
|
||||||
|
const struct of_phandle_args *sp)
|
||||||
|
{
|
||||||
|
return mbox->chans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mtk_adsp_mbox_startup(struct mbox_chan *chan)
|
||||||
|
{
|
||||||
|
struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox);
|
||||||
|
|
||||||
|
/* Clear ADSP mbox command */
|
||||||
|
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in);
|
||||||
|
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mtk_adsp_mbox_shutdown(struct mbox_chan *chan)
|
||||||
|
{
|
||||||
|
struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox);
|
||||||
|
|
||||||
|
/* Clear ADSP mbox command */
|
||||||
|
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_in);
|
||||||
|
writel(0xFFFFFFFF, priv->va_mboxreg + priv->cfg->clr_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mtk_adsp_mbox_send_data(struct mbox_chan *chan, void *data)
|
||||||
|
{
|
||||||
|
struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox);
|
||||||
|
u32 *msg = data;
|
||||||
|
|
||||||
|
writel(*msg, priv->va_mboxreg + priv->cfg->set_in);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool mtk_adsp_mbox_last_tx_done(struct mbox_chan *chan)
|
||||||
|
{
|
||||||
|
struct mtk_adsp_mbox_priv *priv = get_mtk_adsp_mbox_priv(chan->mbox);
|
||||||
|
|
||||||
|
return readl(priv->va_mboxreg + priv->cfg->set_in) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mbox_chan_ops mtk_adsp_mbox_chan_ops = {
|
||||||
|
.send_data = mtk_adsp_mbox_send_data,
|
||||||
|
.startup = mtk_adsp_mbox_startup,
|
||||||
|
.shutdown = mtk_adsp_mbox_shutdown,
|
||||||
|
.last_tx_done = mtk_adsp_mbox_last_tx_done,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mtk_adsp_mbox_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct mtk_adsp_mbox_priv *priv;
|
||||||
|
const struct mtk_adsp_mbox_cfg *cfg;
|
||||||
|
struct mbox_controller *mbox;
|
||||||
|
int ret, irq;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mbox = &priv->mbox;
|
||||||
|
mbox->dev = dev;
|
||||||
|
mbox->ops = &mtk_adsp_mbox_chan_ops;
|
||||||
|
mbox->txdone_irq = false;
|
||||||
|
mbox->txdone_poll = true;
|
||||||
|
mbox->of_xlate = mtk_adsp_mbox_xlate;
|
||||||
|
mbox->num_chans = 1;
|
||||||
|
mbox->chans = devm_kzalloc(dev, sizeof(*mbox->chans), GFP_KERNEL);
|
||||||
|
if (!mbox->chans)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->va_mboxreg = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
if (IS_ERR(priv->va_mboxreg))
|
||||||
|
return PTR_ERR(priv->va_mboxreg);
|
||||||
|
|
||||||
|
cfg = of_device_get_match_data(dev);
|
||||||
|
if (!cfg)
|
||||||
|
return -EINVAL;
|
||||||
|
priv->cfg = cfg;
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
if (irq < 0)
|
||||||
|
return irq;
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(dev, irq, mtk_adsp_mbox_irq,
|
||||||
|
mtk_adsp_mbox_isr, IRQF_TRIGGER_NONE,
|
||||||
|
dev_name(dev), mbox->chans);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
|
return devm_mbox_controller_register(dev, &priv->mbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct mtk_adsp_mbox_cfg mt8195_adsp_mbox_cfg = {
|
||||||
|
.set_in = 0x00,
|
||||||
|
.set_out = 0x1c,
|
||||||
|
.clr_in = 0x04,
|
||||||
|
.clr_out = 0x20,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id mtk_adsp_mbox_of_match[] = {
|
||||||
|
{ .compatible = "mediatek,mt8195-adsp-mbox", .data = &mt8195_adsp_mbox_cfg },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, mtk_adsp_mbox_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver mtk_adsp_mbox_driver = {
|
||||||
|
.probe = mtk_adsp_mbox_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "mtk_adsp_mbox",
|
||||||
|
.of_match_table = mtk_adsp_mbox_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(mtk_adsp_mbox_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Allen-KH Cheng <Allen-KH.Cheng@mediatek.com>");
|
||||||
|
MODULE_DESCRIPTION("MTK ADSP Mailbox Controller");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -146,6 +146,7 @@ static const struct of_device_id qcom_apcs_ipc_of_match[] = {
|
||||||
{ .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data },
|
{ .compatible = "qcom,msm8916-apcs-kpss-global", .data = &msm8916_apcs_data },
|
||||||
{ .compatible = "qcom,msm8939-apcs-kpss-global", .data = &msm8916_apcs_data },
|
{ .compatible = "qcom,msm8939-apcs-kpss-global", .data = &msm8916_apcs_data },
|
||||||
{ .compatible = "qcom,msm8953-apcs-kpss-global", .data = &msm8994_apcs_data },
|
{ .compatible = "qcom,msm8953-apcs-kpss-global", .data = &msm8994_apcs_data },
|
||||||
|
{ .compatible = "qcom,msm8976-apcs-kpss-global", .data = &msm8994_apcs_data },
|
||||||
{ .compatible = "qcom,msm8994-apcs-kpss-global", .data = &msm8994_apcs_data },
|
{ .compatible = "qcom,msm8994-apcs-kpss-global", .data = &msm8994_apcs_data },
|
||||||
{ .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data },
|
{ .compatible = "qcom,msm8996-apcs-hmss-global", .data = &msm8996_apcs_data },
|
||||||
{ .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8994_apcs_data },
|
{ .compatible = "qcom,msm8998-apcs-hmss-global", .data = &msm8994_apcs_data },
|
||||||
|
|
|
@ -412,6 +412,11 @@ static int tegra_hsp_mailbox_flush(struct mbox_chan *chan,
|
||||||
value = tegra_hsp_channel_readl(ch, HSP_SM_SHRD_MBOX);
|
value = tegra_hsp_channel_readl(ch, HSP_SM_SHRD_MBOX);
|
||||||
if ((value & HSP_SM_SHRD_MBOX_FULL) == 0) {
|
if ((value & HSP_SM_SHRD_MBOX_FULL) == 0) {
|
||||||
mbox_chan_txdone(chan, 0);
|
mbox_chan_txdone(chan, 0);
|
||||||
|
|
||||||
|
/* Wait until channel is empty */
|
||||||
|
if (chan->active_req != NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
/*
|
/*
|
||||||
* Texas Instruments' Message Manager Driver
|
* Texas Instruments' Message Manager Driver
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/
|
* Copyright (C) 2015-2022 Texas Instruments Incorporated - https://www.ti.com/
|
||||||
* Nishanth Menon
|
* Nishanth Menon
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/mailbox_controller.h>
|
#include <linux/mailbox_controller.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -100,6 +101,7 @@ struct ti_msgmgr_desc {
|
||||||
* @queue_ctrl: Queue Control register
|
* @queue_ctrl: Queue Control register
|
||||||
* @chan: Mailbox channel
|
* @chan: Mailbox channel
|
||||||
* @rx_buff: Receive buffer pointer allocated at probe, max_message_size
|
* @rx_buff: Receive buffer pointer allocated at probe, max_message_size
|
||||||
|
* @polled_rx_mode: Use polling for rx instead of interrupts
|
||||||
*/
|
*/
|
||||||
struct ti_queue_inst {
|
struct ti_queue_inst {
|
||||||
char name[30];
|
char name[30];
|
||||||
|
@ -113,6 +115,7 @@ struct ti_queue_inst {
|
||||||
void __iomem *queue_ctrl;
|
void __iomem *queue_ctrl;
|
||||||
struct mbox_chan *chan;
|
struct mbox_chan *chan;
|
||||||
u32 *rx_buff;
|
u32 *rx_buff;
|
||||||
|
bool polled_rx_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -190,56 +193,17 @@ static inline bool ti_msgmgr_queue_is_error(const struct ti_msgmgr_desc *d,
|
||||||
return val ? true : false;
|
return val ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static int ti_msgmgr_queue_rx_data(struct mbox_chan *chan, struct ti_queue_inst *qinst,
|
||||||
* ti_msgmgr_queue_rx_interrupt() - Interrupt handler for receive Queue
|
const struct ti_msgmgr_desc *desc)
|
||||||
* @irq: Interrupt number
|
|
||||||
* @p: Channel Pointer
|
|
||||||
*
|
|
||||||
* Return: -EINVAL if there is no instance
|
|
||||||
* IRQ_NONE if the interrupt is not ours.
|
|
||||||
* IRQ_HANDLED if the rx interrupt was successfully handled.
|
|
||||||
*/
|
|
||||||
static irqreturn_t ti_msgmgr_queue_rx_interrupt(int irq, void *p)
|
|
||||||
{
|
{
|
||||||
struct mbox_chan *chan = p;
|
int num_words;
|
||||||
struct device *dev = chan->mbox->dev;
|
|
||||||
struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
|
|
||||||
struct ti_queue_inst *qinst = chan->con_priv;
|
|
||||||
const struct ti_msgmgr_desc *desc;
|
|
||||||
int msg_count, num_words;
|
|
||||||
struct ti_msgmgr_message message;
|
struct ti_msgmgr_message message;
|
||||||
void __iomem *data_reg;
|
void __iomem *data_reg;
|
||||||
u32 *word_data;
|
u32 *word_data;
|
||||||
|
|
||||||
if (WARN_ON(!inst)) {
|
|
||||||
dev_err(dev, "no platform drv data??\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do I have an invalid interrupt source? */
|
|
||||||
if (qinst->is_tx) {
|
|
||||||
dev_err(dev, "Cannot handle rx interrupt on tx channel %s\n",
|
|
||||||
qinst->name);
|
|
||||||
return IRQ_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
desc = inst->desc;
|
|
||||||
if (ti_msgmgr_queue_is_error(desc, qinst)) {
|
|
||||||
dev_err(dev, "Error on Rx channel %s\n", qinst->name);
|
|
||||||
return IRQ_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do I actually have messages to read? */
|
|
||||||
msg_count = ti_msgmgr_queue_get_num_messages(desc, qinst);
|
|
||||||
if (!msg_count) {
|
|
||||||
/* Shared IRQ? */
|
|
||||||
dev_dbg(dev, "Spurious event - 0 pending data!\n");
|
|
||||||
return IRQ_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I have no idea about the protocol being used to communicate with the
|
* I have no idea about the protocol being used to communicate with the
|
||||||
* remote producer - 0 could be valid data, so I won't make a judgement
|
* remote producer - 0 could be valid data, so I wont make a judgement
|
||||||
* of how many bytes I should be reading. Let the client figure this
|
* of how many bytes I should be reading. Let the client figure this
|
||||||
* out.. I just read the full message and pass it on..
|
* out.. I just read the full message and pass it on..
|
||||||
*/
|
*/
|
||||||
|
@ -273,6 +237,75 @@ static irqreturn_t ti_msgmgr_queue_rx_interrupt(int irq, void *p)
|
||||||
*/
|
*/
|
||||||
mbox_chan_received_data(chan, (void *)&message);
|
mbox_chan_received_data(chan, (void *)&message);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ti_msgmgr_queue_rx_poll_timeout(struct mbox_chan *chan, int timeout_us)
|
||||||
|
{
|
||||||
|
struct device *dev = chan->mbox->dev;
|
||||||
|
struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
|
||||||
|
struct ti_queue_inst *qinst = chan->con_priv;
|
||||||
|
const struct ti_msgmgr_desc *desc = inst->desc;
|
||||||
|
int msg_count;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = readl_poll_timeout_atomic(qinst->queue_state, msg_count,
|
||||||
|
(msg_count & desc->status_cnt_mask),
|
||||||
|
10, timeout_us);
|
||||||
|
if (ret != 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ti_msgmgr_queue_rx_data(chan, qinst, desc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ti_msgmgr_queue_rx_interrupt() - Interrupt handler for receive Queue
|
||||||
|
* @irq: Interrupt number
|
||||||
|
* @p: Channel Pointer
|
||||||
|
*
|
||||||
|
* Return: -EINVAL if there is no instance
|
||||||
|
* IRQ_NONE if the interrupt is not ours.
|
||||||
|
* IRQ_HANDLED if the rx interrupt was successfully handled.
|
||||||
|
*/
|
||||||
|
static irqreturn_t ti_msgmgr_queue_rx_interrupt(int irq, void *p)
|
||||||
|
{
|
||||||
|
struct mbox_chan *chan = p;
|
||||||
|
struct device *dev = chan->mbox->dev;
|
||||||
|
struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
|
||||||
|
struct ti_queue_inst *qinst = chan->con_priv;
|
||||||
|
const struct ti_msgmgr_desc *desc;
|
||||||
|
int msg_count;
|
||||||
|
|
||||||
|
if (WARN_ON(!inst)) {
|
||||||
|
dev_err(dev, "no platform drv data??\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do I have an invalid interrupt source? */
|
||||||
|
if (qinst->is_tx) {
|
||||||
|
dev_err(dev, "Cannot handle rx interrupt on tx channel %s\n",
|
||||||
|
qinst->name);
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc = inst->desc;
|
||||||
|
if (ti_msgmgr_queue_is_error(desc, qinst)) {
|
||||||
|
dev_err(dev, "Error on Rx channel %s\n", qinst->name);
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do I actually have messages to read? */
|
||||||
|
msg_count = ti_msgmgr_queue_get_num_messages(desc, qinst);
|
||||||
|
if (!msg_count) {
|
||||||
|
/* Shared IRQ? */
|
||||||
|
dev_dbg(dev, "Spurious event - 0 pending data!\n");
|
||||||
|
return IRQ_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ti_msgmgr_queue_rx_data(chan, qinst, desc);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,6 +369,17 @@ static bool ti_msgmgr_last_tx_done(struct mbox_chan *chan)
|
||||||
return msg_count ? false : true;
|
return msg_count ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ti_msgmgr_chan_has_polled_queue_rx(struct mbox_chan *chan)
|
||||||
|
{
|
||||||
|
struct ti_queue_inst *qinst;
|
||||||
|
|
||||||
|
if (!chan)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
qinst = chan->con_priv;
|
||||||
|
return qinst->polled_rx_mode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ti_msgmgr_send_data() - Send data
|
* ti_msgmgr_send_data() - Send data
|
||||||
* @chan: Channel Pointer
|
* @chan: Channel Pointer
|
||||||
|
@ -353,6 +397,7 @@ static int ti_msgmgr_send_data(struct mbox_chan *chan, void *data)
|
||||||
struct ti_msgmgr_message *message = data;
|
struct ti_msgmgr_message *message = data;
|
||||||
void __iomem *data_reg;
|
void __iomem *data_reg;
|
||||||
u32 *word_data;
|
u32 *word_data;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (WARN_ON(!inst)) {
|
if (WARN_ON(!inst)) {
|
||||||
dev_err(dev, "no platform drv data??\n");
|
dev_err(dev, "no platform drv data??\n");
|
||||||
|
@ -394,7 +439,12 @@ static int ti_msgmgr_send_data(struct mbox_chan *chan, void *data)
|
||||||
if (data_reg <= qinst->queue_buff_end)
|
if (data_reg <= qinst->queue_buff_end)
|
||||||
writel(0, qinst->queue_buff_end);
|
writel(0, qinst->queue_buff_end);
|
||||||
|
|
||||||
return 0;
|
/* If we are in polled mode, wait for a response before proceeding */
|
||||||
|
if (ti_msgmgr_chan_has_polled_queue_rx(message->chan_rx))
|
||||||
|
ret = ti_msgmgr_queue_rx_poll_timeout(message->chan_rx,
|
||||||
|
message->timeout_rx_ms * 1000);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -642,6 +692,54 @@ static int ti_msgmgr_queue_setup(int idx, struct device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ti_msgmgr_queue_rx_set_polled_mode(struct ti_queue_inst *qinst, bool enable)
|
||||||
|
{
|
||||||
|
if (enable) {
|
||||||
|
disable_irq(qinst->irq);
|
||||||
|
qinst->polled_rx_mode = true;
|
||||||
|
} else {
|
||||||
|
enable_irq(qinst->irq);
|
||||||
|
qinst->polled_rx_mode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ti_msgmgr_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
|
||||||
|
struct ti_queue_inst *qinst;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We must switch operation to polled mode now as drivers and the genpd
|
||||||
|
* layer may make late TI SCI calls to change clock and device states
|
||||||
|
* from the noirq phase of suspend.
|
||||||
|
*/
|
||||||
|
for (qinst = inst->qinsts, i = 0; i < inst->num_valid_queues; qinst++, i++) {
|
||||||
|
if (!qinst->is_tx)
|
||||||
|
ti_msgmgr_queue_rx_set_polled_mode(qinst, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ti_msgmgr_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ti_msgmgr_inst *inst = dev_get_drvdata(dev);
|
||||||
|
struct ti_queue_inst *qinst;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (qinst = inst->qinsts, i = 0; i < inst->num_valid_queues; qinst++, i++) {
|
||||||
|
if (!qinst->is_tx)
|
||||||
|
ti_msgmgr_queue_rx_set_polled_mode(qinst, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFINE_SIMPLE_DEV_PM_OPS(ti_msgmgr_pm_ops, ti_msgmgr_suspend, ti_msgmgr_resume);
|
||||||
|
|
||||||
/* Queue operations */
|
/* Queue operations */
|
||||||
static const struct mbox_chan_ops ti_msgmgr_chan_ops = {
|
static const struct mbox_chan_ops ti_msgmgr_chan_ops = {
|
||||||
.startup = ti_msgmgr_queue_startup,
|
.startup = ti_msgmgr_queue_startup,
|
||||||
|
@ -829,6 +927,7 @@ static struct platform_driver ti_msgmgr_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ti-msgmgr",
|
.name = "ti-msgmgr",
|
||||||
.of_match_table = of_match_ptr(ti_msgmgr_of_match),
|
.of_match_table = of_match_ptr(ti_msgmgr_of_match),
|
||||||
|
.pm = &ti_msgmgr_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
module_platform_driver(ti_msgmgr_driver);
|
module_platform_driver(ti_msgmgr_driver);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Texas Instruments' Message Manager
|
* Texas Instruments' Message Manager
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
|
* Copyright (C) 2015-2022 Texas Instruments Incorporated - https://www.ti.com/
|
||||||
* Nishanth Menon
|
* Nishanth Menon
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -17,10 +17,14 @@
|
||||||
#ifndef TI_MSGMGR_H
|
#ifndef TI_MSGMGR_H
|
||||||
#define TI_MSGMGR_H
|
#define TI_MSGMGR_H
|
||||||
|
|
||||||
|
struct mbox_chan;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ti_msgmgr_message - Message Manager structure
|
* struct ti_msgmgr_message - Message Manager structure
|
||||||
* @len: Length of data in the Buffer
|
* @len: Length of data in the Buffer
|
||||||
* @buf: Buffer pointer
|
* @buf: Buffer pointer
|
||||||
|
* @chan_rx: Expected channel for response, must be provided to use polled rx
|
||||||
|
* @timeout_rx_ms: Timeout value to use if polling for response
|
||||||
*
|
*
|
||||||
* This is the structure for data used in mbox_send_message
|
* This is the structure for data used in mbox_send_message
|
||||||
* the length of data buffer used depends on the SoC integration
|
* the length of data buffer used depends on the SoC integration
|
||||||
|
@ -30,6 +34,8 @@
|
||||||
struct ti_msgmgr_message {
|
struct ti_msgmgr_message {
|
||||||
size_t len;
|
size_t len;
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
|
struct mbox_chan *chan_rx;
|
||||||
|
int timeout_rx_ms;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* TI_MSGMGR_H */
|
#endif /* TI_MSGMGR_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue