- 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:
Linus Torvalds 2022-03-27 14:21:57 -07:00
commit 50d602d81f
12 changed files with 719 additions and 73 deletions

View File

@ -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:

View 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>;
};

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View 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");

View File

@ -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 },

View File

@ -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;
}

View File

@ -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);

View File

@ -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 */