mailbox: platform and core updates

PCC
 - Updates to transmission and interrupt handling, including
   dynamic txdone configuration, ->last_tx_done() wiring, and SHMEM
   initialization fixes. Reverted previous shared buffer patch.
 
 MediaTek
 - Introduce mtk-vcp-mailbox driver and bindings for MT8196 VCP.
 - Expand mtk-cmdq for MT8196 with GCE virtualization, mminfra_offset,
   and instruction generation data.
 
 Spreadtrum (SPRD)
 - Add Mailbox Revision 2 support and UMS9230 bindings.
 - Fix unhandled interrupt masking and TX done delivery flags.
 
 Microchip
 - Add pic64gx compatibility to MPFS.
 - Fix out-of-bounds access and smatch warnings in mchp-ipc-sbi.
 
 Core & Misc Platform Updates
 - Prevent out-of-bounds access in fw_mbox_index_xlate().
 - Add bindings for Qualcomm CPUCP (Kaanapali).
 - Simplify mtk-cmdq and zynqmp-ipi with scoped OF child iterators.
 - Consolidate various minor fixes, dead code removal, and typo
   corrections across Broadcom, NXP, Samsung, Xilinx, ARM, and
   core headers.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE6EwehDt/SOnwFyTyf9lkf8eYP5UFAmmP4QkACgkQf9lkf8eY
 P5XRlg//Ye7g0M4uLODJCPRBCG9WsBfHTi3FrpPi9yiaXXy0v5t8+2vwq8YEzevU
 /4UbuC1hANdVbDB8ETtyeI+kmpcIw9VbZz/B4MW5PNr0bNuO1UUKgueoalXwkT6g
 4S8zNX+Kx2dvjHjf4UF/tmBDDM0MAMKuTwEiugree2S7tuusrn0MFYt3XEeMVpKN
 0ob+JTzSi3kHIiWHLi6ZagK8PqtZtOJTdJEziAz0OgOXdLWg614M0lAwPwdnkqX0
 I+nIYeNaDqsR2tmMldPxRGv4A7sKKsZm/nnzrTC7OAvU/q+l7JNjF3ejeMJWehpF
 4I6/jFsnAhmBBr/4U+HuzMxwARI11VjexQYlTH+aoZFtfiAEGfCNVYLAnbzOX1jh
 7mnb1ccwWlgfuj0GtVYjYbrR0wjZVfsgSZX6mfbEOuDVLeQE/AWV1pq2V2Ptcl/s
 qzKQwzDtAuHIJRB73xYthAE4jWF/EigEHyedDPoEXE8K+pkv8G9zuVHQYsejYmNF
 oWlnoJZH/q+z7vMKCgblKQDPb++bUbXQr/x4PNLGW7J+ROikOOux0zOBJDaPSWoM
 Mpw2sUaWI40IgpmEswtoifqzspYUwN7t/C7nBM/vdyFy/13KVOjV0Jbk2oW7mtMA
 BzotYgVIThScG0P7iflXFvQz0fEMN8p4up/nYxbitgBFaBtHdU0=
 =9BUB
 -----END PGP SIGNATURE-----

Merge tag 'mailbox-v6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox

Pull mailbox updates from Jassi Brar:
 "Platform and core updates

  PCC:
   - Updates to transmission and interrupt handling, including dynamic
     txdone configuration, ->last_tx_done() wiring, and SHMEM
     initialization fixes. Reverted previous shared buffer patch

  MediaTek
   - Introduce mtk-vcp-mailbox driver and bindings for MT8196 VCP
   - Expand mtk-cmdq for MT8196 with GCE virtualization, mminfra_offset,
     and instruction generation data

  Spreadtrum (SPRD)
   - Add Mailbox Revision 2 support and UMS9230 bindings
   - Fix unhandled interrupt masking and TX done delivery flags

  Microchip
   - Add pic64gx compatibility to MPFS
   - Fix out-of-bounds access and smatch warnings in mchp-ipc-sbi

  Core & Misc Platform Updates
   - Prevent out-of-bounds access in fw_mbox_index_xlate()
   - Add bindings for Qualcomm CPUCP (Kaanapali)
   - Simplify mtk-cmdq and zynqmp-ipi with scoped OF child iterators
   - Consolidate various minor fixes, dead code removal, and typo
     corrections across Broadcom, NXP, Samsung, Xilinx, ARM, and core
     headers"

* tag 'mailbox-v6.20' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox: (34 commits)
  mailbox: sprd: mask interrupts that are not handled
  mailbox: sprd: add support for mailbox revision 2
  mailbox: sprd: clear delivery flag before handling TX done
  dt-bindings: mailbox: sprd: add compatible for UMS9230
  mailbox: bcm-ferxrm-mailbox: Use default primary handler
  mailbox: Remove mailbox_client.h from controller drivers
  mailbox: zynqmp-ipi: Simplify with scoped for each OF child loop
  mailbox: mtk-cmdq: Simplify with scoped for each OF child loop
  dt-bindings: mailbox: xlnx,zynqmp-ipi-mailbox: Document msg region requirement
  mailbox: Improve RISCV_SBI_MPXY_MBOX guidance
  mailbox: mchp-ipc-sbi: fix uninitialized symbol and other smatch warnings
  mailbox: arm_mhuv3: fix typo in comment
  mailbox: cix: fix typo in error message
  mailbox: imx: Skip the suspend flag for i.MX7ULP
  mailbox: exynos: drop unneeded runtime pointer (pclk)
  mailbox: pcc: Remove spurious IRQF_ONESHOT usage
  mailbox: mtk-cmdq: Add driver data to support for MT8196
  mailbox: mtk-cmdq: Add mminfra_offset configuration for DRAM transaction
  mailbox: mtk-cmdq: Add GCE hardware virtualization configuration
  mailbox: mtk-cmdq: Add cmdq private data to cmdq_pkt for generating instruction
  ...
This commit is contained in:
Linus Torvalds 2026-02-14 11:13:32 -08:00
commit f0a475aedd
22 changed files with 434 additions and 208 deletions

View file

@ -0,0 +1,49 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/mediatek,mt8196-vcp-mbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek Video Companion Processor (VCP) mailbox
maintainers:
- Jjian Zhou <Jjian.Zhou@mediatek.com>
description:
The MTK VCP mailbox enables the SoC to communicate with the VCP by passing
messages through 64 32-bit wide registers. It has 32 interrupt vectors in
either direction for signalling purposes.
properties:
compatible:
enum:
- mediatek,mt8196-vcp-mbox
reg:
maxItems: 1
interrupts:
maxItems: 1
"#mbox-cells":
const: 0
required:
- compatible
- reg
- interrupts
- "#mbox-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
mailbox@31b80000 {
compatible = "mediatek,mt8196-vcp-mbox";
reg = <0x31b80000 0x1000>;
interrupts = <GIC_SPI 789 IRQ_TYPE_LEVEL_HIGH 0>;
#mbox-cells = <0>;
};

View file

@ -11,7 +11,11 @@ maintainers:
properties: properties:
compatible: compatible:
const: microchip,mpfs-mailbox oneOf:
- items:
- const: microchip,pic64gx-mailbox
- const: microchip,mpfs-mailbox
- const: microchip,mpfs-mailbox
reg: reg:
oneOf: oneOf:

View file

@ -16,6 +16,7 @@ properties:
enum: enum:
- sprd,sc9860-mailbox - sprd,sc9860-mailbox
- sprd,sc9863a-mailbox - sprd,sc9863a-mailbox
- sprd,ums9230-mailbox
reg: reg:
items: items:

View file

@ -11,6 +11,17 @@ description: |
messaging between two Xilinx Zynq UltraScale+ MPSoC IPI agents. Each IPI messaging between two Xilinx Zynq UltraScale+ MPSoC IPI agents. Each IPI
agent owns registers used for notification and buffers for message. agent owns registers used for notification and buffers for message.
For Versal devices, there are two types of IPI channels:
- Buffered channels: Support message passing and require the "msg"
register region to be present on both the host and remote IPI agents.
- Buffer-less channels: Support notification only and do not require the
"msg" register region. For these channels, the "msg" region should be
omitted.
For message passing, both the host and remote IPI agents must define the "msg"
register region. If either agent omits the "msg" region, only notification
based communication is possible.
+-------------------------------------+ +-------------------------------------+
| Xilinx ZynqMP IPI Controller | | Xilinx ZynqMP IPI Controller |
+-------------------------------------+ +-------------------------------------+

View file

@ -199,7 +199,7 @@ config POLARFIRE_SOC_MAILBOX
tristate "PolarFire SoC (MPFS) Mailbox" tristate "PolarFire SoC (MPFS) Mailbox"
depends on HAS_IOMEM depends on HAS_IOMEM
depends on MFD_SYSCON depends on MFD_SYSCON
depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST depends on ARCH_MICROCHIP || COMPILE_TEST
help help
This driver adds support for the PolarFire SoC (MPFS) mailbox controller. This driver adds support for the PolarFire SoC (MPFS) mailbox controller.
@ -279,7 +279,7 @@ config MTK_ADSP_MBOX
tristate "MediaTek ADSP Mailbox Controller" tristate "MediaTek ADSP Mailbox Controller"
depends on ARCH_MEDIATEK || COMPILE_TEST depends on ARCH_MEDIATEK || COMPILE_TEST
help help
Say yes here to add support for "MediaTek ADSP Mailbox Controller. Say yes here to add support for MediaTek ADSP Mailbox Controller.
This mailbox driver is used to send notification or short message This mailbox driver is used to send notification or short message
between processors with ADSP. It will place the message to share between processors with ADSP. It will place the message to share
buffer and will access the ipc control. buffer and will access the ipc control.
@ -304,6 +304,15 @@ config MTK_GPUEB_MBOX
Say Y or m here if you want to support the MT8196 SoC in your kernel Say Y or m here if you want to support the MT8196 SoC in your kernel
build. build.
config MTK_VCP_MBOX
tristate "MediaTek VCP Mailbox Support"
depends on ARCH_MEDIATEK || COMPILE_TEST
help
Say yes here to add support for the MediaTek VCP mailbox driver.
The mailbox implementation provides access from the application
processor to Video Companion Processor Unit.
If unsure say N.
config ZYNQMP_IPI_MBOX config ZYNQMP_IPI_MBOX
tristate "Xilinx ZynqMP IPI Mailbox" tristate "Xilinx ZynqMP IPI Mailbox"
depends on ARCH_ZYNQMP && OF depends on ARCH_ZYNQMP && OF
@ -387,7 +396,7 @@ config RISCV_SBI_MPXY_MBOX
Mailbox driver implementation for RISC-V SBI Message Proxy (MPXY) Mailbox driver implementation for RISC-V SBI Message Proxy (MPXY)
extension. This mailbox driver is used to send messages to the extension. This mailbox driver is used to send messages to the
remote processor through the SBI implementation (M-mode firmware remote processor through the SBI implementation (M-mode firmware
or HS-mode hypervisor). Say Y here if you want to have this support. or HS-mode hypervisor). Say Y here, unless you are sure you do not
If unsure say N. need this.
endif endif

View file

@ -65,6 +65,8 @@ obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o
obj-$(CONFIG_MTK_GPUEB_MBOX) += mtk-gpueb-mailbox.o obj-$(CONFIG_MTK_GPUEB_MBOX) += mtk-gpueb-mailbox.o
obj-$(CONFIG_MTK_VCP_MBOX) += mtk-vcp-mailbox.o
obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o
obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o

View file

@ -333,7 +333,7 @@ struct mhuv3_extension {
* @rev: MHUv3 controller IIDR revision. * @rev: MHUv3 controller IIDR revision.
* @var: MHUv3 controller IIDR variant. * @var: MHUv3 controller IIDR variant.
* @prod_id: MHUv3 controller IIDR product_id. * @prod_id: MHUv3 controller IIDR product_id.
* @num_chans: The total number of channnels discovered across all extensions. * @num_chans: The total number of channels discovered across all extensions.
* @cmb_irq: Combined IRQ number if any found defined. * @cmb_irq: Combined IRQ number if any found defined.
* @ctrl: A reference to the MHUv3 control page for this block. * @ctrl: A reference to the MHUv3 control page for this block.
* @pbx: Base address of the PBX register mapping region. * @pbx: Base address of the PBX register mapping region.

View file

@ -26,7 +26,6 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox/brcm-message.h> #include <linux/mailbox/brcm-message.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/msi.h> #include <linux/msi.h>

View file

@ -346,7 +346,7 @@ static void cix_mbox_isr_fifo(struct mbox_chan *chan)
/* FIFO overflow is generated */ /* FIFO overflow is generated */
if (int_status & CIX_FIFO_OFLOW_INT) { if (int_status & CIX_FIFO_OFLOW_INT) {
status = cix_mbox_read(priv, CIX_FIFO_STAS); status = cix_mbox_read(priv, CIX_FIFO_STAS);
dev_err(priv->dev, "fifo overlow: int_stats %d\n", status); dev_err(priv->dev, "fifo overflow: int_stats %d\n", status);
cix_mbox_write(priv, CIX_FIFO_OFLOW_INT, CIX_INT_CLEAR); cix_mbox_write(priv, CIX_FIFO_OFLOW_INT, CIX_INT_CLEAR);
} }
} }

View file

@ -11,7 +11,6 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>

View file

@ -35,12 +35,10 @@
* struct exynos_mbox - driver's private data. * struct exynos_mbox - driver's private data.
* @regs: mailbox registers base address. * @regs: mailbox registers base address.
* @mbox: pointer to the mailbox controller. * @mbox: pointer to the mailbox controller.
* @pclk: pointer to the mailbox peripheral clock.
*/ */
struct exynos_mbox { struct exynos_mbox {
void __iomem *regs; void __iomem *regs;
struct mbox_controller *mbox; struct mbox_controller *mbox;
struct clk *pclk;
}; };
static int exynos_mbox_send_data(struct mbox_chan *chan, void *data) static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
@ -100,6 +98,7 @@ static int exynos_mbox_probe(struct platform_device *pdev)
struct exynos_mbox *exynos_mbox; struct exynos_mbox *exynos_mbox;
struct mbox_controller *mbox; struct mbox_controller *mbox;
struct mbox_chan *chans; struct mbox_chan *chans;
struct clk *pclk;
int i; int i;
exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL); exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL);
@ -119,9 +118,9 @@ static int exynos_mbox_probe(struct platform_device *pdev)
if (IS_ERR(exynos_mbox->regs)) if (IS_ERR(exynos_mbox->regs))
return PTR_ERR(exynos_mbox->regs); return PTR_ERR(exynos_mbox->regs);
exynos_mbox->pclk = devm_clk_get_enabled(dev, "pclk"); pclk = devm_clk_get_enabled(dev, "pclk");
if (IS_ERR(exynos_mbox->pclk)) if (IS_ERR(pclk))
return dev_err_probe(dev, PTR_ERR(exynos_mbox->pclk), return dev_err_probe(dev, PTR_ERR(pclk),
"Failed to enable clock.\n"); "Failed to enable clock.\n");
mbox->num_chans = EXYNOS_MBOX_CHAN_COUNT; mbox->num_chans = EXYNOS_MBOX_CHAN_COUNT;

View file

@ -122,6 +122,7 @@ struct imx_mu_dcfg {
u32 xRR; /* Receive Register0 */ u32 xRR; /* Receive Register0 */
u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */ u32 xSR[IMX_MU_xSR_MAX]; /* Status Registers */
u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */ u32 xCR[IMX_MU_xCR_MAX]; /* Control Registers */
bool skip_suspend_flag;
}; };
#define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x)))) #define IMX_MU_xSR_GIPn(type, x) (type & IMX_MU_V2 ? BIT(x) : BIT(28 + (3 - (x))))
@ -988,6 +989,7 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = {
.xRR = 0x40, .xRR = 0x40,
.xSR = {0x60, 0x60, 0x60, 0x60}, .xSR = {0x60, 0x60, 0x60, 0x60},
.xCR = {0x64, 0x64, 0x64, 0x64, 0x64}, .xCR = {0x64, 0x64, 0x64, 0x64, 0x64},
.skip_suspend_flag = true,
}; };
static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = { static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
@ -1071,7 +1073,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; if (!priv->dcfg->skip_suspend_flag)
priv->suspend = true;
return 0; return 0;
} }
@ -1094,7 +1097,8 @@ static int __maybe_unused imx_mu_resume_noirq(struct device *dev)
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; if (!priv->dcfg->skip_suspend_flag)
priv->suspend = false;
return 0; return 0;
} }

View file

@ -174,26 +174,30 @@ static irqreturn_t mchp_ipc_cluster_aggr_isr(int irq, void *data)
struct mchp_ipc_msg ipc_msg; struct mchp_ipc_msg ipc_msg;
struct mchp_ipc_status status_msg; struct mchp_ipc_status status_msg;
int ret; int ret;
unsigned long hartid;
u32 i, chan_index, chan_id; u32 i, chan_index, chan_id;
bool found = false;
/* Find out the hart that originated the irq */ /* Find out the hart that originated the irq */
for_each_online_cpu(i) { for_each_online_cpu(i) {
hartid = cpuid_to_hartid_map(i); if (irq == ipc->cluster_cfg[i].irq) {
if (irq == ipc->cluster_cfg[hartid].irq) found = true;
break; break;
}
} }
status_msg.cluster = hartid; if (unlikely(!found))
memcpy(ipc->cluster_cfg[hartid].buf_base, &status_msg, sizeof(struct mchp_ipc_status)); return IRQ_NONE;
ret = mchp_ipc_sbi_send(SBI_EXT_IPC_STATUS, ipc->cluster_cfg[hartid].buf_base_addr); status_msg.cluster = cpuid_to_hartid_map(i);
memcpy(ipc->cluster_cfg[i].buf_base, &status_msg, sizeof(struct mchp_ipc_status));
ret = mchp_ipc_sbi_send(SBI_EXT_IPC_STATUS, ipc->cluster_cfg[i].buf_base_addr);
if (ret < 0) { if (ret < 0) {
dev_err_ratelimited(ipc->dev, "could not get IHC irq status ret=%d\n", ret); dev_err_ratelimited(ipc->dev, "could not get IHC irq status ret=%d\n", ret);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
memcpy(&status_msg, ipc->cluster_cfg[hartid].buf_base, sizeof(struct mchp_ipc_status)); memcpy(&status_msg, ipc->cluster_cfg[i].buf_base, sizeof(struct mchp_ipc_status));
/* /*
* Iterate over each bit set in the IHC interrupt status register (IRQ_STATUS) to identify * Iterate over each bit set in the IHC interrupt status register (IRQ_STATUS) to identify
@ -321,13 +325,6 @@ static int mchp_ipc_startup(struct mbox_chan *chan)
goto fail_free_buf_msg_rx; goto fail_free_buf_msg_rx;
} }
if (ret) {
dev_err(ipc->dev, "failed to register interrupt(s)\n");
goto fail_free_buf_msg_rx;
}
return ret;
fail_free_buf_msg_rx: fail_free_buf_msg_rx:
kfree(chan_info->msg_buf_rx); kfree(chan_info->msg_buf_rx);
fail_free_buf_msg_tx: fail_free_buf_msg_tx:
@ -385,21 +382,21 @@ static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc)
if (ret <= 0) if (ret <= 0)
continue; continue;
ipc->cluster_cfg[hartid].irq = ret; ipc->cluster_cfg[cpuid].irq = ret;
ret = devm_request_irq(ipc->dev, ipc->cluster_cfg[hartid].irq, ret = devm_request_irq(ipc->dev, ipc->cluster_cfg[cpuid].irq,
mchp_ipc_cluster_aggr_isr, IRQF_SHARED, mchp_ipc_cluster_aggr_isr, IRQF_SHARED,
"miv-ihc-irq", ipc); "miv-ihc-irq", ipc);
if (ret) if (ret)
return ret; return ret;
ipc->cluster_cfg[hartid].buf_base = devm_kmalloc(ipc->dev, ipc->cluster_cfg[cpuid].buf_base = devm_kmalloc(ipc->dev,
sizeof(struct mchp_ipc_status), sizeof(struct mchp_ipc_status),
GFP_KERNEL); GFP_KERNEL);
if (!ipc->cluster_cfg[hartid].buf_base) if (!ipc->cluster_cfg[cpuid].buf_base)
return -ENOMEM; return -ENOMEM;
ipc->cluster_cfg[hartid].buf_base_addr = __pa(ipc->cluster_cfg[hartid].buf_base); ipc->cluster_cfg[cpuid].buf_base_addr = __pa(ipc->cluster_cfg[cpuid].buf_base);
irq_found = true; irq_found = true;
} }
@ -419,7 +416,7 @@ static int mchp_ipc_probe(struct platform_device *pdev)
ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY); ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY);
if (ret <= 0) if (ret <= 0)
return dev_err_probe(dev, ret, "Microchip SBI extension not detected\n"); return dev_err_probe(dev, -ENODEV, "Microchip SBI extension not detected\n");
ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
if (!ipc) if (!ipc)

View file

@ -489,12 +489,10 @@ EXPORT_SYMBOL_GPL(mbox_free_channel);
static struct mbox_chan *fw_mbox_index_xlate(struct mbox_controller *mbox, static struct mbox_chan *fw_mbox_index_xlate(struct mbox_controller *mbox,
const struct fwnode_reference_args *sp) const struct fwnode_reference_args *sp)
{ {
int ind = sp->args[0]; if (sp->nargs < 1 || sp->args[0] >= mbox->num_chans)
if (ind >= mbox->num_chans)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
return &mbox->chans[ind]; return &mbox->chans[sp->args[0]];
} }
/** /**

View file

@ -636,7 +636,7 @@ static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq) static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq)
{ {
static const char * const gce_name = "gce"; static const char * const gce_name = "gce";
struct device_node *node, *parent = dev->of_node->parent; struct device_node *parent = dev->of_node->parent;
struct clk_bulk_data *clks; struct clk_bulk_data *clks;
cmdq->clocks = devm_kcalloc(dev, cmdq->pdata->gce_num, cmdq->clocks = devm_kcalloc(dev, cmdq->pdata->gce_num,
@ -661,7 +661,7 @@ static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq)
* as the clock of the main GCE must be enabled for additional IPs * as the clock of the main GCE must be enabled for additional IPs
* to be reachable. * to be reachable.
*/ */
for_each_child_of_node(parent, node) { for_each_child_of_node_scoped(parent, node) {
int alias_id = of_alias_get_id(node, gce_name); int alias_id = of_alias_get_id(node, gce_name);
if (alias_id < 0 || alias_id >= cmdq->pdata->gce_num) if (alias_id < 0 || alias_id >= cmdq->pdata->gce_num)
@ -670,17 +670,13 @@ static int cmdq_get_clocks(struct device *dev, struct cmdq *cmdq)
clks = &cmdq->clocks[alias_id]; clks = &cmdq->clocks[alias_id];
clks->id = devm_kasprintf(dev, GFP_KERNEL, "gce%d", alias_id); clks->id = devm_kasprintf(dev, GFP_KERNEL, "gce%d", alias_id);
if (!clks->id) { if (!clks->id)
of_node_put(node);
return -ENOMEM; return -ENOMEM;
}
clks->clk = of_clk_get(node, 0); clks->clk = of_clk_get(node, 0);
if (IS_ERR(clks->clk)) { if (IS_ERR(clks->clk))
of_node_put(node);
return dev_err_probe(dev, PTR_ERR(clks->clk), return dev_err_probe(dev, PTR_ERR(clks->clk),
"failed to get gce%d clock\n", alias_id); "failed to get gce%d clock\n", alias_id);
}
} }
return 0; return 0;

View file

@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2025 MediaTek Corporation. All rights reserved.
* Author: Jjian Zhou <jjian.zhou.@mediatek.com>
*/
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/mtk-vcp-mailbox.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct mtk_vcp_mbox {
struct mbox_controller mbox;
void __iomem *base;
struct device *dev;
const struct mtk_vcp_mbox_cfg *cfg;
struct mtk_ipi_info ipi_recv;
struct mbox_chan chans;
};
struct mtk_vcp_mbox_cfg {
u16 set_in;
u16 clr_out;
};
static irqreturn_t mtk_vcp_mbox_irq_thread(int irq, void *data)
{
struct mtk_vcp_mbox *priv = data;
/* get irq status */
priv->ipi_recv.irq_status = readl(priv->base + priv->cfg->clr_out);
__ioread32_copy(priv->ipi_recv.msg, priv->base,
MTK_VCP_MBOX_SLOT_MAX_SIZE / 4);
mbox_chan_received_data(&priv->chans, &priv->ipi_recv);
/* clear irq status */
writel(priv->ipi_recv.irq_status, priv->base + priv->cfg->clr_out);
return IRQ_HANDLED;
}
static struct mbox_chan *mtk_vcp_mbox_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *sp)
{
if (sp->args_count)
return NULL;
return &mbox->chans[0];
}
static int mtk_vcp_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct mtk_vcp_mbox *priv = chan->con_priv;
struct mtk_ipi_info *ipi_info = data;
u32 status;
if (!ipi_info->msg) {
dev_err(priv->dev, "msg buffer is NULL.\n");
return -EINVAL;
}
status = readl(priv->base + priv->cfg->set_in);
if (status & BIT(ipi_info->index)) {
dev_warn(priv->dev, "mailbox IPI %d is busy.\n", ipi_info->id);
return -EBUSY;
}
if (ipi_info->slot_ofs + ipi_info->len > MTK_VCP_MBOX_SLOT_MAX_SIZE)
return -EINVAL;
__iowrite32_copy(priv->base + ipi_info->slot_ofs, ipi_info->msg,
ipi_info->len);
writel(BIT(ipi_info->index), priv->base + priv->cfg->set_in);
return 0;
}
static bool mtk_vcp_mbox_last_tx_done(struct mbox_chan *chan)
{
struct mtk_ipi_info *ipi_info = chan->active_req;
struct mtk_vcp_mbox *priv = chan->con_priv;
return !(readl(priv->base + priv->cfg->set_in) & BIT(ipi_info->index));
}
static const struct mbox_chan_ops mtk_vcp_mbox_chan_ops = {
.send_data = mtk_vcp_mbox_send_data,
.last_tx_done = mtk_vcp_mbox_last_tx_done,
};
static int mtk_vcp_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_vcp_mbox *priv;
struct mbox_controller *mbox;
int ret, irq;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->chans.con_priv = priv;
mbox = &priv->mbox;
mbox->dev = dev;
mbox->ops = &mtk_vcp_mbox_chan_ops;
mbox->txdone_irq = false;
mbox->txdone_poll = true;
mbox->of_xlate = mtk_vcp_mbox_xlate;
mbox->num_chans = 1;
mbox->chans = &priv->chans;
priv->ipi_recv.msg = devm_kzalloc(dev, MTK_VCP_MBOX_SLOT_MAX_SIZE,
GFP_KERNEL);
if (!priv->ipi_recv.msg)
return -ENOMEM;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->cfg = of_device_get_match_data(dev);
if (!priv->cfg)
return -EINVAL;
platform_set_drvdata(pdev, priv);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_threaded_irq(dev, irq, NULL,
mtk_vcp_mbox_irq_thread, IRQF_ONESHOT,
dev_name(dev), priv);
if (ret < 0)
return ret;
return devm_mbox_controller_register(dev, &priv->mbox);
}
static const struct mtk_vcp_mbox_cfg mt8196_cfg = {
.set_in = 0x100,
.clr_out = 0x10c,
};
static const struct of_device_id mtk_vcp_mbox_of_match[] = {
{ .compatible = "mediatek,mt8196-vcp-mbox", .data = &mt8196_cfg },
{},
};
MODULE_DEVICE_TABLE(of, mtk_vcp_mbox_of_match);
static struct platform_driver mtk_vcp_mbox_driver = {
.probe = mtk_vcp_mbox_probe,
.driver = {
.name = "mtk_vcp_mbox",
.of_match_table = mtk_vcp_mbox_of_match,
},
};
module_platform_driver(mtk_vcp_mbox_driver);
MODULE_AUTHOR("Jjian Zhou <jjian.zhou@mediatek.com>");
MODULE_DESCRIPTION("MTK VCP Mailbox Controller");
MODULE_LICENSE("GPL");

View file

@ -21,7 +21,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
#include <linux/mailbox_client.h>
#include "mailbox.h" #include "mailbox.h"

View file

@ -305,22 +305,6 @@ static void pcc_chan_acknowledge(struct pcc_chan_info *pchan)
pcc_chan_reg_read_modify_write(&pchan->db); pcc_chan_reg_read_modify_write(&pchan->db);
} }
static void *write_response(struct pcc_chan_info *pchan)
{
struct pcc_header pcc_header;
void *buffer;
int data_len;
memcpy_fromio(&pcc_header, pchan->chan.shmem,
sizeof(pcc_header));
data_len = pcc_header.length - sizeof(u32) + sizeof(struct pcc_header);
buffer = pchan->chan.rx_alloc(pchan->chan.mchan->cl, data_len);
if (buffer != NULL)
memcpy_fromio(buffer, pchan->chan.shmem, data_len);
return buffer;
}
/** /**
* pcc_mbox_irq - PCC mailbox interrupt handler * pcc_mbox_irq - PCC mailbox interrupt handler
* @irq: interrupt number * @irq: interrupt number
@ -332,8 +316,6 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
{ {
struct pcc_chan_info *pchan; struct pcc_chan_info *pchan;
struct mbox_chan *chan = p; struct mbox_chan *chan = p;
struct pcc_header *pcc_header = chan->active_req;
void *handle = NULL;
pchan = chan->con_priv; pchan = chan->con_priv;
@ -357,17 +339,8 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
* required to avoid any possible race in updatation of this flag. * required to avoid any possible race in updatation of this flag.
*/ */
pchan->chan_in_use = false; pchan->chan_in_use = false;
mbox_chan_received_data(chan, NULL);
if (pchan->chan.rx_alloc) mbox_chan_txdone(chan, 0);
handle = write_response(pchan);
if (chan->active_req) {
pcc_header = chan->active_req;
if (pcc_header->flags & PCC_CMD_COMPLETION_NOTIFY)
mbox_chan_txdone(chan, 0);
}
mbox_chan_received_data(chan, handle);
pcc_chan_acknowledge(pchan); pcc_chan_acknowledge(pchan);
@ -404,33 +377,20 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
} }
rc = mbox_bind_client(chan, cl);
if (rc)
return ERR_PTR(rc);
pcc_mchan = &pchan->chan; pcc_mchan = &pchan->chan;
pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr, pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr,
pcc_mchan->shmem_size); pcc_mchan->shmem_size);
if (!pcc_mchan->shmem) if (!pcc_mchan->shmem)
goto err; return ERR_PTR(-ENXIO);
pcc_mchan->manage_writes = false; rc = mbox_bind_client(chan, cl);
if (rc) {
/* This indicates that the channel is ready to accept messages. iounmap(pcc_mchan->shmem);
* This needs to happen after the channel has registered pcc_mchan->shmem = NULL;
* its callback. There is no access point to do that in return ERR_PTR(rc);
* the mailbox API. That implies that the mailbox client must }
* have set the allocate callback function prior to
* sending any messages.
*/
if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
pcc_chan_reg_read_modify_write(&pchan->cmd_update);
return pcc_mchan; return pcc_mchan;
err:
mbox_free_channel(chan);
return ERR_PTR(-ENXIO);
} }
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel); EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
@ -459,38 +419,8 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
} }
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
static int pcc_write_to_buffer(struct mbox_chan *chan, void *data)
{
struct pcc_chan_info *pchan = chan->con_priv;
struct pcc_mbox_chan *pcc_mbox_chan = &pchan->chan;
struct pcc_header *pcc_header = data;
if (!pchan->chan.manage_writes)
return 0;
/* The PCC header length includes the command field
* but not the other values from the header.
*/
int len = pcc_header->length - sizeof(u32) + sizeof(struct pcc_header);
u64 val;
pcc_chan_reg_read(&pchan->cmd_complete, &val);
if (!val) {
pr_info("%s pchan->cmd_complete not set", __func__);
return -1;
}
memcpy_toio(pcc_mbox_chan->shmem, data, len);
return 0;
}
/** /**
* pcc_send_data - Called from Mailbox Controller code. If * pcc_send_data - Called from Mailbox Controller code. Used
* pchan->chan.rx_alloc is set, then the command complete
* flag is checked and the data is written to the shared
* buffer io memory.
*
* If pchan->chan.rx_alloc is not set, then it is used
* here only to ring the channel doorbell. The PCC client * here only to ring the channel doorbell. The PCC client
* specific read/write is done in the client driver in * specific read/write is done in the client driver in
* order to maintain atomicity over PCC channel once * order to maintain atomicity over PCC channel once
@ -506,37 +436,24 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
int ret; int ret;
struct pcc_chan_info *pchan = chan->con_priv; struct pcc_chan_info *pchan = chan->con_priv;
ret = pcc_write_to_buffer(chan, data);
if (ret)
return ret;
ret = pcc_chan_reg_read_modify_write(&pchan->cmd_update); ret = pcc_chan_reg_read_modify_write(&pchan->cmd_update);
if (ret) if (ret)
return ret; return ret;
ret = pcc_chan_reg_read_modify_write(&pchan->db); ret = pcc_chan_reg_read_modify_write(&pchan->db);
if (!ret && pchan->plat_irq > 0) if (!ret && pchan->plat_irq > 0)
pchan->chan_in_use = true; pchan->chan_in_use = true;
return ret; return ret;
} }
static bool pcc_last_tx_done(struct mbox_chan *chan) static bool pcc_last_tx_done(struct mbox_chan *chan)
{ {
struct pcc_chan_info *pchan = chan->con_priv; struct pcc_chan_info *pchan = chan->con_priv;
u64 val;
pcc_chan_reg_read(&pchan->cmd_complete, &val); return pcc_mbox_cmd_complete_check(pchan);
if (!val)
return false;
else
return true;
} }
/** /**
* pcc_startup - Called from Mailbox Controller code. Used here * pcc_startup - Called from Mailbox Controller code. Used here
* to request the interrupt. * to request the interrupt.
@ -550,9 +467,15 @@ static int pcc_startup(struct mbox_chan *chan)
unsigned long irqflags; unsigned long irqflags;
int rc; int rc;
/*
* Clear and acknowledge any pending interrupts on responder channel
* before enabling the interrupt
*/
pcc_chan_acknowledge(pchan);
if (pchan->plat_irq > 0) { if (pchan->plat_irq > 0) {
irqflags = pcc_chan_plat_irq_can_be_shared(pchan) ? irqflags = pcc_chan_plat_irq_can_be_shared(pchan) ?
IRQF_SHARED | IRQF_ONESHOT : 0; IRQF_SHARED : 0;
rc = devm_request_irq(chan->mbox->dev, pchan->plat_irq, pcc_mbox_irq, rc = devm_request_irq(chan->mbox->dev, pchan->plat_irq, pcc_mbox_irq,
irqflags, MBOX_IRQ_NAME, chan); irqflags, MBOX_IRQ_NAME, chan);
if (unlikely(rc)) { if (unlikely(rc)) {
@ -877,8 +800,13 @@ static int pcc_mbox_probe(struct platform_device *pdev)
(unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct)); (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct));
acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl; acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL) if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL) {
pcc_mbox_ctrl->txdone_irq = true; pcc_mbox_ctrl->txdone_irq = true;
pcc_mbox_ctrl->txdone_poll = false;
} else {
pcc_mbox_ctrl->txdone_irq = false;
pcc_mbox_ctrl->txdone_poll = true;
}
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
struct pcc_chan_info *pchan = chan_info + i; struct pcc_chan_info *pchan = chan_info + i;

View file

@ -24,7 +24,8 @@
#define SPRD_MBOX_IRQ_STS 0x18 #define SPRD_MBOX_IRQ_STS 0x18
#define SPRD_MBOX_IRQ_MSK 0x1c #define SPRD_MBOX_IRQ_MSK 0x1c
#define SPRD_MBOX_LOCK 0x20 #define SPRD_MBOX_LOCK 0x20
#define SPRD_MBOX_FIFO_DEPTH 0x24 #define SPRD_MBOX_FIFO_DEPTH 0x24 /* outbox only */
#define SPRD_MBOX_IN_FIFO_STS2 0x24 /* inbox only, revision 2 */
/* Bit and mask definition for inbox's SPRD_MBOX_FIFO_STS register */ /* Bit and mask definition for inbox's SPRD_MBOX_FIFO_STS register */
#define SPRD_INBOX_FIFO_DELIVER_MASK GENMASK(23, 16) #define SPRD_INBOX_FIFO_DELIVER_MASK GENMASK(23, 16)
@ -32,8 +33,18 @@
#define SPRD_INBOX_FIFO_DELIVER_SHIFT 16 #define SPRD_INBOX_FIFO_DELIVER_SHIFT 16
#define SPRD_INBOX_FIFO_BUSY_MASK GENMASK(7, 0) #define SPRD_INBOX_FIFO_BUSY_MASK GENMASK(7, 0)
/* Bit and mask definition for R2 inbox's SPRD_MBOX_FIFO_RST register */
#define SPRD_INBOX_R2_FIFO_OVERFLOW_DELIVER_RST GENMASK(31, 0)
/* Bit and mask definition for R2 inbox's SPRD_MBOX_FIFO_STS register */
#define SPRD_INBOX_R2_FIFO_DELIVER_MASK GENMASK(15, 0)
/* Bit and mask definition for SPRD_MBOX_IN_FIFO_STS2 register */
#define SPRD_INBOX_R2_FIFO_OVERFLOW_MASK GENMASK(31, 16)
#define SPRD_INBOX_R2_FIFO_BUSY_MASK GENMASK(15, 0)
/* Bit and mask definition for SPRD_MBOX_IRQ_STS register */ /* Bit and mask definition for SPRD_MBOX_IRQ_STS register */
#define SPRD_MBOX_IRQ_CLR BIT(0) #define SPRD_MBOX_IRQ_CLR GENMASK(31, 0)
/* Bit and mask definition for outbox's SPRD_MBOX_FIFO_STS register */ /* Bit and mask definition for outbox's SPRD_MBOX_FIFO_STS register */
#define SPRD_OUTBOX_FIFO_FULL BIT(2) #define SPRD_OUTBOX_FIFO_FULL BIT(2)
@ -52,8 +63,18 @@
#define SPRD_OUTBOX_FIFO_IRQ_MASK GENMASK(4, 0) #define SPRD_OUTBOX_FIFO_IRQ_MASK GENMASK(4, 0)
#define SPRD_OUTBOX_BASE_SPAN 0x1000 #define SPRD_OUTBOX_BASE_SPAN 0x1000
#define SPRD_MBOX_CHAN_MAX 8 #define SPRD_MBOX_R1_CHAN_MAX 8
#define SPRD_SUPP_INBOX_ID_SC9863A 7 #define SPRD_MBOX_R2_CHAN_MAX 16
enum sprd_mbox_version {
SPRD_MBOX_R1,
SPRD_MBOX_R2,
};
struct sprd_mbox_info {
enum sprd_mbox_version version;
unsigned long supp_id;
};
struct sprd_mbox_priv { struct sprd_mbox_priv {
struct mbox_controller mbox; struct mbox_controller mbox;
@ -64,9 +85,11 @@ struct sprd_mbox_priv {
void __iomem *supp_base; void __iomem *supp_base;
u32 outbox_fifo_depth; u32 outbox_fifo_depth;
const struct sprd_mbox_info *info;
struct mutex lock; struct mutex lock;
u32 refcnt; u32 refcnt;
struct mbox_chan chan[SPRD_MBOX_CHAN_MAX]; struct mbox_chan chan[SPRD_MBOX_R2_CHAN_MAX];
}; };
static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox) static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox)
@ -154,18 +177,35 @@ static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data)
{ {
struct sprd_mbox_priv *priv = data; struct sprd_mbox_priv *priv = data;
struct mbox_chan *chan; struct mbox_chan *chan;
u32 fifo_sts, send_sts, busy, id; u32 fifo_sts, fifo_sts2, send_sts, busy, id;
fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS); fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS);
if (priv->info->version == SPRD_MBOX_R2)
fifo_sts2 = readl(priv->inbox_base + SPRD_MBOX_IN_FIFO_STS2);
/* Get the inbox data delivery status */ /* Get the inbox data delivery status */
send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >> if (priv->info->version == SPRD_MBOX_R2) {
SPRD_INBOX_FIFO_DELIVER_SHIFT; send_sts = fifo_sts & SPRD_INBOX_R2_FIFO_DELIVER_MASK;
} else {
send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >>
SPRD_INBOX_FIFO_DELIVER_SHIFT;
}
if (!send_sts) { if (!send_sts) {
dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n"); dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n");
return IRQ_NONE; return IRQ_NONE;
} }
/* Clear FIFO delivery and overflow status first */
if (priv->info->version == SPRD_MBOX_R2) {
writel(SPRD_INBOX_R2_FIFO_OVERFLOW_DELIVER_RST,
priv->inbox_base + SPRD_MBOX_FIFO_RST);
} else {
writel(fifo_sts & (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK),
priv->inbox_base + SPRD_MBOX_FIFO_RST);
}
while (send_sts) { while (send_sts) {
id = __ffs(send_sts); id = __ffs(send_sts);
send_sts &= (send_sts - 1); send_sts &= (send_sts - 1);
@ -176,16 +216,15 @@ static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data)
* Check if the message was fetched by remote target, if yes, * Check if the message was fetched by remote target, if yes,
* that means the transmission has been completed. * that means the transmission has been completed.
*/ */
busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK; if (priv->info->version == SPRD_MBOX_R2)
busy = fifo_sts2 & SPRD_INBOX_R2_FIFO_BUSY_MASK;
else
busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK;
if (!(busy & BIT(id))) if (!(busy & BIT(id)))
mbox_chan_txdone(chan, 0); mbox_chan_txdone(chan, 0);
} }
/* Clear FIFO delivery and overflow status */
writel(fifo_sts &
(SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK),
priv->inbox_base + SPRD_MBOX_FIFO_RST);
/* Clear irq status */ /* Clear irq status */
writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS); writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS);
@ -243,21 +282,19 @@ static int sprd_mbox_startup(struct mbox_chan *chan)
/* Select outbox FIFO mode and reset the outbox FIFO status */ /* Select outbox FIFO mode and reset the outbox FIFO status */
writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST);
/* Enable inbox FIFO overflow and delivery interrupt */ /* Enable inbox FIFO delivery interrupt */
val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); val = SPRD_INBOX_FIFO_IRQ_MASK;
val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); val &= ~SPRD_INBOX_FIFO_DELIVER_IRQ;
writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK);
/* Enable outbox FIFO not empty interrupt */ /* Enable outbox FIFO not empty interrupt */
val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); val = SPRD_OUTBOX_FIFO_IRQ_MASK;
val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ;
writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK);
/* Enable supplementary outbox as the fundamental one */ /* Enable supplementary outbox as the fundamental one */
if (priv->supp_base) { if (priv->supp_base) {
writel(0x0, priv->supp_base + SPRD_MBOX_FIFO_RST); writel(0x0, priv->supp_base + SPRD_MBOX_FIFO_RST);
val = readl(priv->supp_base + SPRD_MBOX_IRQ_MSK);
val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ;
writel(val, priv->supp_base + SPRD_MBOX_IRQ_MSK); writel(val, priv->supp_base + SPRD_MBOX_IRQ_MSK);
} }
} }
@ -295,7 +332,7 @@ static int sprd_mbox_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct sprd_mbox_priv *priv; struct sprd_mbox_priv *priv;
int ret, inbox_irq, outbox_irq, supp_irq; int ret, inbox_irq, outbox_irq, supp_irq;
unsigned long id, supp; unsigned long id;
struct clk *clk; struct clk *clk;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@ -305,6 +342,10 @@ static int sprd_mbox_probe(struct platform_device *pdev)
priv->dev = dev; priv->dev = dev;
mutex_init(&priv->lock); mutex_init(&priv->lock);
priv->info = of_device_get_match_data(dev);
if (!priv->info)
return -EINVAL;
/* /*
* Unisoc mailbox uses an inbox to send messages to the target * Unisoc mailbox uses an inbox to send messages to the target
* core, and uses (an) outbox(es) to receive messages from other * core, and uses (an) outbox(es) to receive messages from other
@ -362,12 +403,12 @@ static int sprd_mbox_probe(struct platform_device *pdev)
return ret; return ret;
} }
supp = (unsigned long) of_device_get_match_data(dev); if (!priv->info->supp_id) {
if (!supp) {
dev_err(dev, "no supplementary outbox specified\n"); dev_err(dev, "no supplementary outbox specified\n");
return -ENODEV; return -ENODEV;
} }
priv->supp_base = priv->outbox_base + (SPRD_OUTBOX_BASE_SPAN * supp); priv->supp_base = priv->outbox_base +
(SPRD_OUTBOX_BASE_SPAN * priv->info->supp_id);
} }
/* Get the default outbox FIFO depth */ /* Get the default outbox FIFO depth */
@ -375,11 +416,15 @@ static int sprd_mbox_probe(struct platform_device *pdev)
readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1; readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1;
priv->mbox.dev = dev; priv->mbox.dev = dev;
priv->mbox.chans = &priv->chan[0]; priv->mbox.chans = &priv->chan[0];
priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX;
priv->mbox.ops = &sprd_mbox_ops; priv->mbox.ops = &sprd_mbox_ops;
priv->mbox.txdone_irq = true; priv->mbox.txdone_irq = true;
for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++) if (priv->info->version == SPRD_MBOX_R2)
priv->mbox.num_chans = SPRD_MBOX_R2_CHAN_MAX;
else
priv->mbox.num_chans = SPRD_MBOX_R1_CHAN_MAX;
for (id = 0; id < priv->mbox.num_chans; id++)
priv->chan[id].con_priv = (void *)id; priv->chan[id].con_priv = (void *)id;
ret = devm_mbox_controller_register(dev, &priv->mbox); ret = devm_mbox_controller_register(dev, &priv->mbox);
@ -391,10 +436,24 @@ static int sprd_mbox_probe(struct platform_device *pdev)
return 0; return 0;
} }
static const struct sprd_mbox_info sc9860_mbox_info = {
.version = SPRD_MBOX_R1,
};
static const struct sprd_mbox_info sc9863a_mbox_info = {
.version = SPRD_MBOX_R1,
.supp_id = 7,
};
static const struct sprd_mbox_info ums9230_mbox_info = {
.version = SPRD_MBOX_R2,
.supp_id = 6,
};
static const struct of_device_id sprd_mbox_of_match[] = { static const struct of_device_id sprd_mbox_of_match[] = {
{ .compatible = "sprd,sc9860-mailbox" }, { .compatible = "sprd,sc9860-mailbox", .data = &sc9860_mbox_info },
{ .compatible = "sprd,sc9863a-mailbox", { .compatible = "sprd,sc9863a-mailbox", .data = &sc9863a_mbox_info },
.data = (void *)SPRD_SUPP_INBOX_ID_SC9863A }, { .compatible = "sprd,ums9230-mailbox", .data = &ums9230_mbox_info },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, sprd_mbox_of_match); MODULE_DEVICE_TABLE(of, sprd_mbox_of_match);

View file

@ -904,7 +904,7 @@ static void zynqmp_ipi_free_mboxes(struct zynqmp_ipi_pdata *pdata)
static int zynqmp_ipi_probe(struct platform_device *pdev) static int zynqmp_ipi_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *nc, *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct zynqmp_ipi_pdata *pdata; struct zynqmp_ipi_pdata *pdata;
struct of_phandle_args out_irq; struct of_phandle_args out_irq;
struct zynqmp_ipi_mbox *mbox; struct zynqmp_ipi_mbox *mbox;
@ -940,13 +940,12 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
pdata->num_mboxes = num_mboxes; pdata->num_mboxes = num_mboxes;
mbox = pdata->ipi_mboxes; mbox = pdata->ipi_mboxes;
for_each_available_child_of_node(np, nc) { for_each_available_child_of_node_scoped(np, nc) {
mbox->pdata = pdata; mbox->pdata = pdata;
mbox->setup_ipi_fn = ipi_fn; mbox->setup_ipi_fn = ipi_fn;
ret = zynqmp_ipi_mbox_probe(mbox, nc); ret = zynqmp_ipi_mbox_probe(mbox, nc);
if (ret) { if (ret) {
of_node_put(nc);
dev_err(dev, "failed to probe subdev.\n"); dev_err(dev, "failed to probe subdev.\n");
ret = -EINVAL; ret = -EINVAL;
goto free_mbox_dev; goto free_mbox_dev;

View file

@ -17,35 +17,6 @@ struct pcc_mbox_chan {
u32 latency; u32 latency;
u32 max_access_rate; u32 max_access_rate;
u16 min_turnaround_time; u16 min_turnaround_time;
/* Set to true to indicate that the mailbox should manage
* writing the dat to the shared buffer. This differs from
* the case where the drivesr are writing to the buffer and
* using send_data only to ring the doorbell. If this flag
* is set, then the void * data parameter of send_data must
* point to a kernel-memory buffer formatted in accordance with
* the PCC specification.
*
* The active buffer management will include reading the
* notify_on_completion flag, and will then
* call mbox_chan_txdone when the acknowledgment interrupt is
* received.
*/
bool manage_writes;
/* Optional callback that allows the driver
* to allocate the memory used for receiving
* messages. The return value is the location
* inside the buffer where the mailbox should write the data.
*/
void *(*rx_alloc)(struct mbox_client *cl, int size);
};
struct pcc_header {
u32 signature;
u32 flags;
u32 length;
u32 command;
}; };
/* Generic Communications Channel Shared Memory Region */ /* Generic Communications Channel Shared Memory Region */

View file

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
/*
* Copyright (c) 2025 MediaTek Inc.
*/
#ifndef __MTK_VCP_MAILBOX_H__
#define __MTK_VCP_MAILBOX_H__
#define MTK_VCP_MBOX_SLOT_MAX_SIZE 0x100 /* mbox max slot size */
/**
* struct mtk_ipi_info - mailbox message info for mtk-vcp-mailbox
* @msg: The share buffer between IPC and mailbox driver
* @len: Message length
* @id: This is for identification purposes and not actually used
* by the mailbox hardware.
* @index: The signal number of the mailbox message.
* @slot_ofs: Data slot offset.
* @irq_status: Captures incoming signals for the RX path.
*
* It is used between IPC with mailbox driver.
*/
struct mtk_ipi_info {
void *msg;
u32 len;
u32 id;
u32 index;
u32 slot_ofs;
u32 irq_status;
};
#endif