- 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
|
@ -28,7 +28,12 @@ properties:
|
|||
- const: fsl,imx7ulp-mu
|
||||
- const: fsl,imx8ulp-mu
|
||||
- const: fsl,imx8-mu-scu
|
||||
- const: fsl,imx8-mu-seco
|
||||
- const: fsl,imx93-mu-s4
|
||||
- const: fsl,imx8ulp-mu-s4
|
||||
- items:
|
||||
- const: fsl,imx93-mu
|
||||
- const: fsl,imx8ulp-mu
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx7s-mu
|
||||
|
@ -51,7 +56,14 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: tx
|
||||
- const: rx
|
||||
|
||||
"#mbox-cells":
|
||||
description: |
|
||||
|
@ -86,6 +98,27 @@ required:
|
|||
- interrupts
|
||||
- "#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
|
||||
|
||||
examples:
|
||||
|
|
|
@ -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:
|
||||
- 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
|
||||
- interrupts: The interrupt signal from the GCE block
|
||||
- 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'.
|
||||
|
||||
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/mt8195-gce.h' or 'dt-bindings/gce/mt6779-gce.h'.
|
||||
'dt-bindings/gce/mt8183-gce.h', 'dt-bindings/gce/mt8186-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.
|
||||
|
||||
Example:
|
||||
|
|
|
@ -21,6 +21,7 @@ properties:
|
|||
- qcom,msm8916-apcs-kpss-global
|
||||
- qcom,msm8939-apcs-kpss-global
|
||||
- qcom,msm8953-apcs-kpss-global
|
||||
- qcom,msm8976-apcs-kpss-global
|
||||
- qcom,msm8994-apcs-kpss-global
|
||||
- qcom,msm8996-apcs-hmss-global
|
||||
- qcom,msm8998-apcs-hmss-global
|
||||
|
|
|
@ -238,6 +238,15 @@ config STM32_IPCC
|
|||
with hardware for Inter-Processor Communication Controller (IPCC)
|
||||
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
|
||||
tristate "MediaTek CMDQ Mailbox Support"
|
||||
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_MTK_ADSP_MBOX) += mtk-adsp-mailbox.o
|
||||
|
||||
obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
|
||||
|
||||
obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018 Pengutronix, Oleksij Rempel <o.rempel@pengutronix.de>
|
||||
* Copyright 2022 NXP, Peng Fan <peng.fan@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
|
@ -9,11 +10,13 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mailbox_controller.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define IMX_MU_CHANS 16
|
||||
|
@ -23,11 +26,15 @@
|
|||
#define IMX_MU_S4_CHANS 2
|
||||
#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 {
|
||||
IMX_MU_TYPE_TX, /* Tx */
|
||||
IMX_MU_TYPE_RX, /* Rx */
|
||||
IMX_MU_TYPE_TXDB, /* Tx doorbell */
|
||||
IMX_MU_TYPE_RXDB, /* Rx doorbell */
|
||||
IMX_MU_TYPE_TX = 0, /* Tx */
|
||||
IMX_MU_TYPE_RX = 1, /* Rx */
|
||||
IMX_MU_TYPE_TXDB = 2, /* Tx doorbell */
|
||||
IMX_MU_TYPE_RXDB = 3, /* Rx doorbell */
|
||||
};
|
||||
|
||||
enum imx_mu_xcr {
|
||||
|
@ -47,7 +54,7 @@ enum imx_mu_xsr {
|
|||
|
||||
struct imx_sc_rpc_msg_max {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
u32 data[7];
|
||||
u32 data[30];
|
||||
};
|
||||
|
||||
struct imx_s4_rpc_msg_max {
|
||||
|
@ -75,7 +82,8 @@ struct imx_mu_priv {
|
|||
struct imx_mu_con_priv con_priv[IMX_MU_CHANS];
|
||||
const struct imx_mu_dcfg *dcfg;
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
int irq[IMX_MU_CHANS];
|
||||
bool suspend;
|
||||
|
||||
u32 xcr[4];
|
||||
|
||||
|
@ -86,11 +94,13 @@ enum imx_mu_type {
|
|||
IMX_MU_V1,
|
||||
IMX_MU_V2 = BIT(1),
|
||||
IMX_MU_V2_S4 = BIT(15),
|
||||
IMX_MU_V2_IRQ = BIT(16),
|
||||
};
|
||||
|
||||
struct imx_mu_dcfg {
|
||||
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 (*rxdb)(struct imx_mu_priv *priv, struct imx_mu_con_priv *cp);
|
||||
void (*init)(struct imx_mu_priv *priv);
|
||||
enum imx_mu_type type;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -177,6 +236,16 @@ static int imx_mu_generic_rx(struct imx_mu_priv *priv,
|
|||
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)
|
||||
{
|
||||
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],
|
||||
xsr,
|
||||
xsr & IMX_MU_xSR_TEn(priv->dcfg->type, i % num_tr),
|
||||
0, 100);
|
||||
0, 5 * USEC_PER_SEC);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Send data index: %d timeout\n", i);
|
||||
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++) {
|
||||
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) {
|
||||
dev_err(priv->dev, "timeout read idx %d\n", i);
|
||||
return ret;
|
||||
|
@ -275,6 +345,125 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *
|
|||
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)
|
||||
{
|
||||
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);
|
||||
} else if ((val == IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx)) &&
|
||||
(cp->type == IMX_MU_TYPE_RXDB)) {
|
||||
imx_mu_write(priv, IMX_MU_xSR_GIPn(priv->dcfg->type, cp->idx),
|
||||
priv->dcfg->xSR[IMX_MU_GSR]);
|
||||
mbox_chan_received_data(chan, NULL);
|
||||
priv->dcfg->rxdb(priv, cp);
|
||||
} else {
|
||||
dev_warn_ratelimited(priv->dev, "Not handled interrupt\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (priv->suspend)
|
||||
pm_system_wakeup();
|
||||
|
||||
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_con_priv *cp = chan->con_priv;
|
||||
unsigned long irq_flag = IRQF_SHARED;
|
||||
unsigned long irq_flag = 0;
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_sync(priv->dev);
|
||||
|
@ -364,11 +554,12 @@ static int imx_mu_startup(struct mbox_chan *chan)
|
|||
if (!priv->dev->pm_domain)
|
||||
irq_flag |= IRQF_NO_SUSPEND;
|
||||
|
||||
ret = request_irq(priv->irq, imx_mu_isr, irq_flag,
|
||||
cp->irq_desc, chan);
|
||||
if (!(priv->dcfg->type & IMX_MU_V2_IRQ))
|
||||
irq_flag |= IRQF_SHARED;
|
||||
|
||||
ret = request_irq(priv->irq[cp->type], imx_mu_isr, irq_flag, cp->irq_desc, chan);
|
||||
if (ret) {
|
||||
dev_err(priv->dev,
|
||||
"Unable to acquire IRQ %d\n", priv->irq);
|
||||
dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq[cp->type]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -411,7 +602,7 @@ static void imx_mu_shutdown(struct mbox_chan *chan)
|
|||
break;
|
||||
}
|
||||
|
||||
free_irq(priv->irq, chan);
|
||||
free_irq(priv->irq[cp->type], chan);
|
||||
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];
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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]);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct imx_mu_priv *priv;
|
||||
const struct imx_mu_dcfg *dcfg;
|
||||
int ret;
|
||||
int i, ret;
|
||||
u32 size;
|
||||
|
||||
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))
|
||||
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);
|
||||
if (!dcfg)
|
||||
return -EINVAL;
|
||||
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)
|
||||
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 = {
|
||||
.tx = imx_mu_generic_tx,
|
||||
.rx = imx_mu_generic_rx,
|
||||
.rxdb = imx_mu_generic_rxdb,
|
||||
.init = imx_mu_init_generic,
|
||||
.xTR = 0x0,
|
||||
.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 = {
|
||||
.tx = imx_mu_generic_tx,
|
||||
.rx = imx_mu_generic_rx,
|
||||
.rxdb = imx_mu_generic_rxdb,
|
||||
.init = imx_mu_init_generic,
|
||||
.xTR = 0x20,
|
||||
.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 = {
|
||||
.tx = imx_mu_generic_tx,
|
||||
.rx = imx_mu_generic_rx,
|
||||
.rxdb = imx_mu_generic_rxdb,
|
||||
.init = imx_mu_init_generic,
|
||||
.rxdb = imx_mu_generic_rxdb,
|
||||
.type = IMX_MU_V2,
|
||||
.xTR = 0x200,
|
||||
.xRR = 0x280,
|
||||
|
@ -672,10 +905,33 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp_s4 = {
|
|||
.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 = {
|
||||
.tx = imx_mu_specific_tx,
|
||||
.rx = imx_mu_specific_rx,
|
||||
.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,
|
||||
.xRR = 0x10,
|
||||
.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,imx8ulp-mu", .data = &imx_mu_cfg_imx8ulp },
|
||||
{ .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-seco", .data = &imx_mu_cfg_imx8_seco },
|
||||
{ },
|
||||
};
|
||||
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->suspend = true;
|
||||
|
||||
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
|
||||
* 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++)
|
||||
imx_mu_write(priv, priv->xcr[i], priv->dcfg->xCR[i]);
|
||||
}
|
||||
|
||||
priv->suspend = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,msm8939-apcs-kpss-global", .data = &msm8916_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,msm8996-apcs-hmss-global", .data = &msm8996_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);
|
||||
if ((value & HSP_SM_SHRD_MBOX_FULL) == 0) {
|
||||
mbox_chan_txdone(chan, 0);
|
||||
|
||||
/* Wait until channel is empty */
|
||||
if (chan->active_req != NULL)
|
||||
continue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
|||
#include <linux/device.h>
|
||||
#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>
|
||||
|
@ -100,6 +101,7 @@ struct ti_msgmgr_desc {
|
|||
* @queue_ctrl: Queue Control register
|
||||
* @chan: Mailbox channel
|
||||
* @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 {
|
||||
char name[30];
|
||||
|
@ -113,6 +115,7 @@ struct ti_queue_inst {
|
|||
void __iomem *queue_ctrl;
|
||||
struct mbox_chan *chan;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static int ti_msgmgr_queue_rx_data(struct mbox_chan *chan, struct ti_queue_inst *qinst,
|
||||
const struct ti_msgmgr_desc *desc)
|
||||
{
|
||||
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, num_words;
|
||||
int num_words;
|
||||
struct ti_msgmgr_message message;
|
||||
void __iomem *data_reg;
|
||||
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
|
||||
* 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
|
||||
* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -336,6 +369,17 @@ static bool ti_msgmgr_last_tx_done(struct mbox_chan *chan)
|
|||
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
|
||||
* @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;
|
||||
void __iomem *data_reg;
|
||||
u32 *word_data;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON(!inst)) {
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
static const struct mbox_chan_ops ti_msgmgr_chan_ops = {
|
||||
.startup = ti_msgmgr_queue_startup,
|
||||
|
@ -829,6 +927,7 @@ static struct platform_driver ti_msgmgr_driver = {
|
|||
.driver = {
|
||||
.name = "ti-msgmgr",
|
||||
.of_match_table = of_match_ptr(ti_msgmgr_of_match),
|
||||
.pm = &ti_msgmgr_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_msgmgr_driver);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* 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
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -17,10 +17,14 @@
|
|||
#ifndef TI_MSGMGR_H
|
||||
#define TI_MSGMGR_H
|
||||
|
||||
struct mbox_chan;
|
||||
|
||||
/**
|
||||
* struct ti_msgmgr_message - Message Manager structure
|
||||
* @len: Length of data in the Buffer
|
||||
* @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
|
||||
* the length of data buffer used depends on the SoC integration
|
||||
|
@ -30,6 +34,8 @@
|
|||
struct ti_msgmgr_message {
|
||||
size_t len;
|
||||
u8 *buf;
|
||||
struct mbox_chan *chan_rx;
|
||||
int timeout_rx_ms;
|
||||
};
|
||||
|
||||
#endif /* TI_MSGMGR_H */
|
||||
|
|
Loading…
Reference in New Issue