aspeed: add driver and bindings for ast2700

broadcom: add driver and bindings for bcm74110
 mediatek: fix RPM api usage
 qcom: use dev_fwnode
 pcc: support shared buffer
 misc: dt-bindings cleanup
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE6EwehDt/SOnwFyTyf9lkf8eYP5UFAmiWIEIACgkQf9lkf8eY
 P5UeTA/8DKjwtvDpVIyjFDiTZg1gxn4OfMhC56+uZdxtJti4SETW2oEfLtCfKUVv
 erKqvsCxhUfrhWFJ4obH3WHHaKBzO7Uoc85i+T6g9j8/CMH54gYMLl0oV+HBcpR1
 qhPGPFjt0MkiSDQtUedxMItHmg1vGGiUWHdXbrZAuiNEZCXDSOhhmUTWOlHeOx5I
 15656yBtZB6/lU2zb83Kd9qu5PsrIv9LVpmOBsqoAPGfQG53ICEm5TvdHaPYVNjD
 GgS0I6WcklLh309woGPq/DNQ0QiUBxlCHaWXdigD0hzabzJCdA67EjeYJWQOABww
 Uh4TU+/PP2UP9sWbN8N6syCcnL0Awq/XM2igMwYrqg87EV1VdjG57zrrD6SHQcP7
 rRf0mhnEIkSaslrQakAxnxkr70YnXyh0F1puHf51y+4/ZLWsFu13KqZJ+tDceefN
 SV4ZuuMg683k5NM/dKK78wGM/5KOmN6d5N3z6gH1FTyunn18VoktJTT55ilU0+Hy
 GJVaOubEukyLT0WE1kd8AeEIiFLT8jHTcPWS6LrrkyoLq/tiI2Xq7L+UHw5lWqIC
 0wo3yjOzVrl35kEGgJxAt5g1wYyhrYClpNlcFH9bdlWZWmXoDLpjmE+hEAGSfQrB
 AYdUQWVmqswLfXPVXwMoAuYkeTNwXL+FP8MR64ABV8a0cPN/zWM=
 =F7Ms
 -----END PGP SIGNATURE-----

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

Pull mailbox updates from Jassi Brar:

 - aspeed: add driver and bindings for ast2700

 - broadcom: add driver and bindings for bcm74110

 - mediatek: fix RPM api usage

 - qcom: use dev_fwnode

 - pcc: support shared buffer

 - misc dt-bindings cleanup

* tag 'mailbox-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox:
  mailbox/pcc: support mailbox management of the shared buffer
  mailbox: bcm74110: Fix spelling mistake
  mailbox: bcm74110: remove unneeded semicolon
  mailbox: aspeed: add mailbox driver for AST27XX series SoC
  dt-bindings: mailbox: Add ASPEED AST2700 series SoC
  dt-bindings: mailbox: Drop consumers example DTS
  dt-bindings: mailbox: nvidia,tegra186-hsp: Use generic node name
  dt-bindings: mailbox: Correct example indentation
  dt-bindings: mailbox: ti,secure-proxy: Add missing reg maxItems
  dt-bindings: mailbox: amlogic,meson-gxbb-mhu: Add missing interrupts maxItems
  dt-bindings: mailbox: qcom-ipcc: document the Milos Inter-Processor Communication Controller
  mailbox: Add support for bcm74110
  dt-bindings: mailbox: Add support for bcm74110
  mailbox: Use dev_fwnode()
  mailbox: mtk-cmdq: Switch to pm_runtime_put_autosuspend()
This commit is contained in:
Linus Torvalds 2025-08-09 08:37:17 +03:00
commit b20b8538b3
18 changed files with 1214 additions and 63 deletions

View File

@ -68,13 +68,13 @@ examples:
#include <dt-bindings/reset/sun8i-h3-ccu.h>
msgbox: mailbox@1c17000 {
compatible = "allwinner,sun8i-h3-msgbox",
"allwinner,sun6i-a31-msgbox";
reg = <0x01c17000 0x1000>;
clocks = <&ccu CLK_BUS_MSGBOX>;
resets = <&ccu RST_BUS_MSGBOX>;
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
compatible = "allwinner,sun8i-h3-msgbox",
"allwinner,sun6i-a31-msgbox";
reg = <0x01c17000 0x1000>;
clocks = <&ccu CLK_BUS_MSGBOX>;
resets = <&ccu RST_BUS_MSGBOX>;
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
};
...

View File

@ -27,7 +27,7 @@ properties:
maxItems: 1
interrupts:
minItems: 3
maxItems: 3
description:
Contains the interrupt information corresponding to each of the 3 links
of MHU.
@ -46,8 +46,8 @@ additionalProperties: false
examples:
- |
mailbox@c883c404 {
compatible = "amlogic,meson-gxbb-mhu";
reg = <0xc883c404 0x4c>;
interrupts = <208>, <209>, <210>;
#mbox-cells = <1>;
compatible = "amlogic,meson-gxbb-mhu";
reg = <0xc883c404 0x4c>;
interrupts = <208>, <209>, <210>;
#mbox-cells = <1>;
};

View File

@ -78,11 +78,11 @@ additionalProperties: false
examples:
- |
mailbox@77408000 {
compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4";
reg = <0x77408000 0x4000>;
interrupts = <1 583 4>, <1 584 4>, <1 585 4>, <1 586 4>;
interrupt-names = "send-empty", "send-not-empty",
"recv-empty", "recv-not-empty";
#mbox-cells = <0>;
};
mailbox@77408000 {
compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4";
reg = <0x77408000 0x4000>;
interrupts = <1 583 4>, <1 584 4>, <1 585 4>, <1 586 4>;
interrupt-names = "send-empty", "send-not-empty",
"recv-empty", "recv-not-empty";
#mbox-cells = <0>;
};

View File

@ -0,0 +1,68 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/aspeed,ast2700-mailbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ASPEED AST2700 mailbox controller
maintainers:
- Jammy Huang <jammy_huang@aspeedtech.com>
description: >
ASPEED AST2700 has multiple processors that need to communicate with each
other. The mailbox controller provides a way for these processors to send
messages to each other. It is a hardware-based inter-processor communication
mechanism that allows processors to send and receive messages through
dedicated channels.
The mailbox's tx/rx are independent, meaning that one processor can send a
message while another processor is receiving a message simultaneously.
There are 4 channels available for both tx and rx operations. Each channel
has a FIFO buffer that can hold messages of a fixed size (32 bytes in this
case).
The mailbox controller also supports interrupt generation, allowing
processors to notify each other when a message is available or when an event
occurs.
properties:
compatible:
const: aspeed,ast2700-mailbox
reg:
items:
- description: TX control register
- description: RX control register
reg-names:
items:
- const: tx
- const: rx
interrupts:
maxItems: 1
"#mbox-cells":
const: 1
required:
- compatible
- reg
- reg-names
- interrupts
- "#mbox-cells"
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
mailbox@12c1c200 {
compatible = "aspeed,ast2700-mailbox";
reg = <0x12c1c200 0x100>, <0x12c1c300 0x100>;
reg-names = "tx", "rx";
interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
};

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/brcm,bcm74110-mbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom BCM74110 Mailbox
maintainers:
- Justin Chen <justin.chen@broadcom.com>
- Florian Fainelli <florian.fainelli@broadcom.com>
description: Broadcom mailbox hardware first introduced with 74110
properties:
compatible:
enum:
- brcm,bcm74110-mbox
reg:
maxItems: 1
interrupts:
items:
- description: RX doorbell and watermark interrupts
- description: TX doorbell and watermark interrupts
"#mbox-cells":
const: 2
description:
The first cell is channel type and second cell is shared memory slot
brcm,rx:
$ref: /schemas/types.yaml#/definitions/uint32
description: RX Mailbox number
brcm,tx:
$ref: /schemas/types.yaml#/definitions/uint32
description: TX Mailbox number
required:
- compatible
- reg
- interrupts
- "#mbox-cells"
- brcm,rx
- brcm,tx
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
mailbox@a552000 {
compatible = "brcm,bcm74110-mbox";
reg = <0xa552000 0x1104>;
interrupts = <GIC_SPI 0x67 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 0x66 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <0x2>;
brcm,rx = <0x7>;
brcm,tx = <0x6>;
};

View File

@ -59,9 +59,6 @@ description: |
<dt-bindings/mailbox/tegra186-hsp.h>
properties:
$nodename:
pattern: "^hsp@[0-9a-f]+$"
compatible:
oneOf:
- enum:
@ -131,14 +128,10 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/mailbox/tegra186-hsp.h>
hsp_top0: hsp@3c00000 {
mailbox@3c00000 {
compatible = "nvidia,tegra186-hsp";
reg = <0x03c00000 0xa0000>;
interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "doorbell";
#mbox-cells = <2>;
};
client {
mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB TEGRA_HSP_DB_MASTER_CCPLEX>;
};

View File

@ -251,7 +251,7 @@ examples:
# Example apcs with msm8996
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
apcs_glb: mailbox@9820000 {
mailbox@9820000 {
compatible = "qcom,msm8996-apcs-hmss-global";
reg = <0x9820000 0x1000>;
@ -259,13 +259,6 @@ examples:
#clock-cells = <0>;
};
rpm-glink {
compatible = "qcom,glink-rpm";
interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
qcom,rpm-msg-ram = <&rpm_msg_ram>;
mboxes = <&apcs_glb 0>;
};
# Example apcs with qcs404
- |
#define GCC_APSS_AHB_CLK_SRC 1

View File

@ -24,6 +24,7 @@ properties:
compatible:
items:
- enum:
- qcom,milos-ipcc
- qcom,qcs8300-ipcc
- qcom,qdu1000-ipcc
- qcom,sa8255p-ipcc

View File

@ -242,7 +242,7 @@ examples:
- |
/* OMAP4 */
#include <dt-bindings/interrupt-controller/arm-gic.h>
mailbox: mailbox@4a0f4000 {
mailbox@4a0f4000 {
compatible = "ti,omap4-mailbox";
reg = <0x4a0f4000 0x200>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
@ -260,13 +260,9 @@ examples:
};
};
dsp {
mboxes = <&mailbox &mbox_dsp>;
};
- |
/* AM33xx */
mailbox1: mailbox@480c8000 {
mailbox@480c8000 {
compatible = "ti,omap4-mailbox";
reg = <0x480c8000 0x200>;
interrupts = <77>;
@ -283,7 +279,7 @@ examples:
- |
/* AM65x */
mailbox0_cluster0: mailbox@31f80000 {
mailbox@31f80000 {
compatible = "ti,am654-mailbox";
reg = <0x31f80000 0x200>;
#mbox-cells = <1>;

View File

@ -36,7 +36,7 @@ properties:
- const: scfg
reg:
minItems: 3
maxItems: 3
interrupt-names:
minItems: 1
@ -68,12 +68,12 @@ examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
secure_proxy: mailbox@32c00000 {
compatible = "ti,am654-secure-proxy";
#mbox-cells = <1>;
reg-names = "target_data", "rt", "scfg";
reg = <0x32c00000 0x100000>,
<0x32400000 0x100000>,
<0x32800000 0x100000>;
interrupt-names = "rx_011";
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
compatible = "ti,am654-secure-proxy";
#mbox-cells = <1>;
reg-names = "target_data", "rt", "scfg";
reg = <0x32c00000 0x100000>,
<0x32400000 0x100000>,
<0x32800000 0x100000>;
interrupt-names = "rx_011";
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
};

View File

@ -36,6 +36,15 @@ config ARM_MHU_V3
that provides different means of transports: supported extensions
will be discovered and possibly managed at probe-time.
config AST2700_MBOX
tristate "ASPEED AST2700 IPC driver"
depends on ARCH_ASPEED || COMPILE_TEST
help
Mailbox driver implementation for ASPEED AST27XX SoCs. This driver
can be used to send message between different processors in SoC.
The driver provides mailbox support for sending interrupts to the
clients. Say Y here if you want to build this driver.
config CV1800_MBOX
tristate "cv1800 mailbox"
depends on ARCH_SOPHGO || COMPILE_TEST
@ -350,4 +359,14 @@ config CIX_MBOX
is unidirectional. Say Y here if you want to use the CIX Mailbox
support.
config BCM74110_MAILBOX
tristate "Brcmstb BCM74110 Mailbox"
depends on ARCH_BRCMSTB || COMPILE_TEST
default ARCH_BRCMSTB
help
Broadcom STB mailbox driver present starting with brcmstb bcm74110
SoCs. The mailbox is a communication channel between the host
processor and coprocessor that handles various power management task
and more.
endif

View File

@ -11,6 +11,8 @@ obj-$(CONFIG_ARM_MHU_V2) += arm_mhuv2.o
obj-$(CONFIG_ARM_MHU_V3) += arm_mhuv3.o
obj-$(CONFIG_AST2700_MBOX) += ast2700-mailbox.o
obj-$(CONFIG_CV1800_MBOX) += cv1800-mailbox.o
obj-$(CONFIG_EXYNOS_MBOX) += exynos-mailbox.o
@ -74,3 +76,5 @@ obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
obj-$(CONFIG_THEAD_TH1520_MBOX) += mailbox-th1520.o
obj-$(CONFIG_CIX_MBOX) += cix-mailbox.o
obj-$(CONFIG_BCM74110_MAILBOX) += bcm74110-mailbox.o

View File

@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright Aspeed Technology Inc. (C) 2025. All rights reserved
*/
#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.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* Each bit in the register represents an IPC ID */
#define IPCR_TX_TRIG 0x00
#define IPCR_ENABLE 0x04
#define IPCR_STATUS 0x08
#define RX_IRQ(n) BIT(n)
#define RX_IRQ_MASK 0xf
#define IPCR_DATA 0x10
struct ast2700_mbox_data {
u8 num_chans;
u8 msg_size;
};
struct ast2700_mbox {
struct mbox_controller mbox;
u8 msg_size;
void __iomem *tx_regs;
void __iomem *rx_regs;
spinlock_t lock;
};
static inline int ch_num(struct mbox_chan *chan)
{
return chan - chan->mbox->chans;
}
static inline bool ast2700_mbox_tx_done(struct ast2700_mbox *mb, int idx)
{
return !(readl(mb->tx_regs + IPCR_STATUS) & BIT(idx));
}
static irqreturn_t ast2700_mbox_irq(int irq, void *p)
{
struct ast2700_mbox *mb = p;
void __iomem *data_reg;
int num_words = mb->msg_size / sizeof(u32);
u32 *word_data;
u32 status;
int n, i;
/* Only examine channels that are currently enabled. */
status = readl(mb->rx_regs + IPCR_ENABLE) &
readl(mb->rx_regs + IPCR_STATUS);
if (!(status & RX_IRQ_MASK))
return IRQ_NONE;
for (n = 0; n < mb->mbox.num_chans; ++n) {
struct mbox_chan *chan = &mb->mbox.chans[n];
if (!(status & RX_IRQ(n)))
continue;
data_reg = mb->rx_regs + IPCR_DATA + mb->msg_size * n;
word_data = chan->con_priv;
/* Read the message data */
for (i = 0; i < num_words; i++)
word_data[i] = readl(data_reg + i * sizeof(u32));
mbox_chan_received_data(chan, chan->con_priv);
/* The IRQ can be cleared only once the FIFO is empty. */
writel(RX_IRQ(n), mb->rx_regs + IPCR_STATUS);
}
return IRQ_HANDLED;
}
static int ast2700_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
int idx = ch_num(chan);
void __iomem *data_reg = mb->tx_regs + IPCR_DATA + mb->msg_size * idx;
u32 *word_data = data;
int num_words = mb->msg_size / sizeof(u32);
int i;
if (!(readl(mb->tx_regs + IPCR_ENABLE) & BIT(idx))) {
dev_warn(mb->mbox.dev, "%s: Ch-%d not enabled yet\n", __func__, idx);
return -ENODEV;
}
if (!(ast2700_mbox_tx_done(mb, idx))) {
dev_warn(mb->mbox.dev, "%s: Ch-%d last data has not finished\n", __func__, idx);
return -EBUSY;
}
/* Write the message data */
for (i = 0 ; i < num_words; i++)
writel(word_data[i], data_reg + i * sizeof(u32));
writel(BIT(idx), mb->tx_regs + IPCR_TX_TRIG);
dev_dbg(mb->mbox.dev, "%s: Ch-%d sent\n", __func__, idx);
return 0;
}
static int ast2700_mbox_startup(struct mbox_chan *chan)
{
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
int idx = ch_num(chan);
void __iomem *reg = mb->rx_regs + IPCR_ENABLE;
unsigned long flags;
spin_lock_irqsave(&mb->lock, flags);
writel(readl(reg) | BIT(idx), reg);
spin_unlock_irqrestore(&mb->lock, flags);
return 0;
}
static void ast2700_mbox_shutdown(struct mbox_chan *chan)
{
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
int idx = ch_num(chan);
void __iomem *reg = mb->rx_regs + IPCR_ENABLE;
unsigned long flags;
spin_lock_irqsave(&mb->lock, flags);
writel(readl(reg) & ~BIT(idx), reg);
spin_unlock_irqrestore(&mb->lock, flags);
}
static bool ast2700_mbox_last_tx_done(struct mbox_chan *chan)
{
struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev);
int idx = ch_num(chan);
return ast2700_mbox_tx_done(mb, idx);
}
static const struct mbox_chan_ops ast2700_mbox_chan_ops = {
.send_data = ast2700_mbox_send_data,
.startup = ast2700_mbox_startup,
.shutdown = ast2700_mbox_shutdown,
.last_tx_done = ast2700_mbox_last_tx_done,
};
static int ast2700_mbox_probe(struct platform_device *pdev)
{
struct ast2700_mbox *mb;
const struct ast2700_mbox_data *dev_data;
struct device *dev = &pdev->dev;
int irq, ret;
if (!pdev->dev.of_node)
return -ENODEV;
dev_data = device_get_match_data(&pdev->dev);
mb = devm_kzalloc(dev, sizeof(*mb), GFP_KERNEL);
if (!mb)
return -ENOMEM;
mb->mbox.chans = devm_kcalloc(&pdev->dev, dev_data->num_chans,
sizeof(*mb->mbox.chans), GFP_KERNEL);
if (!mb->mbox.chans)
return -ENOMEM;
/* con_priv of each channel is used to store the message received */
for (int i = 0; i < dev_data->num_chans; i++) {
mb->mbox.chans[i].con_priv = devm_kcalloc(dev, dev_data->msg_size,
sizeof(u8), GFP_KERNEL);
if (!mb->mbox.chans[i].con_priv)
return -ENOMEM;
}
platform_set_drvdata(pdev, mb);
mb->tx_regs = devm_platform_ioremap_resource_byname(pdev, "tx");
if (IS_ERR(mb->tx_regs))
return PTR_ERR(mb->tx_regs);
mb->rx_regs = devm_platform_ioremap_resource_byname(pdev, "rx");
if (IS_ERR(mb->rx_regs))
return PTR_ERR(mb->rx_regs);
mb->msg_size = dev_data->msg_size;
mb->mbox.dev = dev;
mb->mbox.num_chans = dev_data->num_chans;
mb->mbox.ops = &ast2700_mbox_chan_ops;
mb->mbox.txdone_irq = false;
mb->mbox.txdone_poll = true;
mb->mbox.txpoll_period = 5;
spin_lock_init(&mb->lock);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, irq, ast2700_mbox_irq, 0, dev_name(dev), mb);
if (ret)
return ret;
return devm_mbox_controller_register(dev, &mb->mbox);
}
static const struct ast2700_mbox_data ast2700_dev_data = {
.num_chans = 4,
.msg_size = 0x20,
};
static const struct of_device_id ast2700_mbox_of_match[] = {
{ .compatible = "aspeed,ast2700-mailbox", .data = &ast2700_dev_data },
{}
};
MODULE_DEVICE_TABLE(of, ast2700_mbox_of_match);
static struct platform_driver ast2700_mbox_driver = {
.driver = {
.name = "ast2700-mailbox",
.of_match_table = ast2700_mbox_of_match,
},
.probe = ast2700_mbox_probe,
};
module_platform_driver(ast2700_mbox_driver);
MODULE_AUTHOR("Jammy Huang <jammy_huang@aspeedtech.com>");
MODULE_DESCRIPTION("ASPEED AST2700 IPC driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,656 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Broadcom BCM74110 Mailbox Driver
*
* Copyright (c) 2025 Broadcom
*/
#include <linux/list.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/mailbox_controller.h>
#include <linux/bitfield.h>
#include <linux/slab.h>
#define BCM_MBOX_BASE(sel) ((sel) * 0x40)
#define BCM_MBOX_IRQ_BASE(sel) (((sel) * 0x20) + 0x800)
#define BCM_MBOX_CFGA 0x0
#define BCM_MBOX_CFGB 0x4
#define BCM_MBOX_CFGC 0x8
#define BCM_MBOX_CFGD 0xc
#define BCM_MBOX_CTRL 0x10
#define BCM_MBOX_CTRL_EN BIT(0)
#define BCM_MBOX_CTRL_CLR BIT(1)
#define BCM_MBOX_STATUS0 0x14
#define BCM_MBOX_STATUS0_NOT_EMPTY BIT(28)
#define BCM_MBOX_STATUS0_FULL BIT(29)
#define BCM_MBOX_STATUS1 0x18
#define BCM_MBOX_STATUS2 0x1c
#define BCM_MBOX_WDATA 0x20
#define BCM_MBOX_RDATA 0x28
#define BCM_MBOX_IRQ_STATUS 0x0
#define BCM_MBOX_IRQ_SET 0x4
#define BCM_MBOX_IRQ_CLEAR 0x8
#define BCM_MBOX_IRQ_MASK_STATUS 0xc
#define BCM_MBOX_IRQ_MASK_SET 0x10
#define BCM_MBOX_IRQ_MASK_CLEAR 0x14
#define BCM_MBOX_IRQ_TIMEOUT BIT(0)
#define BCM_MBOX_IRQ_NOT_EMPTY BIT(1)
#define BCM_MBOX_IRQ_FULL BIT(2)
#define BCM_MBOX_IRQ_LOW_WM BIT(3)
#define BCM_MBOX_IRQ_HIGH_WM BIT(4)
#define BCM_LINK_CODE0 0xbe0
#define BCM_LINK_CODE1 0xbe1
#define BCM_LINK_CODE2 0xbe2
enum {
BCM_MSG_FUNC_LINK_START = 0,
BCM_MSG_FUNC_LINK_STOP,
BCM_MSG_FUNC_SHMEM_TX,
BCM_MSG_FUNC_SHMEM_RX,
BCM_MSG_FUNC_SHMEM_STOP,
BCM_MSG_FUNC_MAX,
};
enum {
BCM_MSG_SVC_INIT = 0,
BCM_MSG_SVC_PMC,
BCM_MSG_SVC_SCMI,
BCM_MSG_SVC_DPFE,
BCM_MSG_SVC_MAX,
};
struct bcm74110_mbox_msg {
struct list_head list_entry;
#define BCM_MSG_VERSION_MASK GENMASK(31, 29)
#define BCM_MSG_VERSION 0x1
#define BCM_MSG_REQ_MASK BIT(28)
#define BCM_MSG_RPLY_MASK BIT(27)
#define BCM_MSG_SVC_MASK GENMASK(26, 24)
#define BCM_MSG_FUNC_MASK GENMASK(23, 16)
#define BCM_MSG_LENGTH_MASK GENMASK(15, 4)
#define BCM_MSG_SLOT_MASK GENMASK(3, 0)
#define BCM_MSG_SET_FIELD(hdr, field, val) \
do { \
hdr &= ~BCM_MSG_##field##_MASK; \
hdr |= FIELD_PREP(BCM_MSG_##field##_MASK, val); \
} while (0)
#define BCM_MSG_GET_FIELD(hdr, field) \
FIELD_GET(BCM_MSG_##field##_MASK, hdr)
u32 msg;
};
struct bcm74110_mbox_chan {
struct bcm74110_mbox *mbox;
bool en;
int slot;
int type;
};
struct bcm74110_mbox {
struct platform_device *pdev;
void __iomem *base;
int tx_chan;
int rx_chan;
int rx_irq;
struct list_head rx_svc_init_list;
spinlock_t rx_svc_list_lock;
struct mbox_controller controller;
struct bcm74110_mbox_chan *mbox_chan;
};
#define BCM74110_OFFSET_IO_WRITEL_MACRO(name, offset_base) \
static void bcm74110_##name##_writel(struct bcm74110_mbox *mbox,\
u32 val, u32 off) \
{ \
writel_relaxed(val, mbox->base + offset_base + off); \
}
BCM74110_OFFSET_IO_WRITEL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan));
BCM74110_OFFSET_IO_WRITEL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan));
#define BCM74110_OFFSET_IO_READL_MACRO(name, offset_base) \
static u32 bcm74110_##name##_readl(struct bcm74110_mbox *mbox, \
u32 off) \
{ \
return readl_relaxed(mbox->base + offset_base + off); \
}
BCM74110_OFFSET_IO_READL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan));
BCM74110_OFFSET_IO_READL_MACRO(rx, BCM_MBOX_BASE(mbox->rx_chan));
BCM74110_OFFSET_IO_READL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan));
static inline struct bcm74110_mbox *bcm74110_mbox_from_cntrl(
struct mbox_controller *cntrl)
{
return container_of(cntrl, struct bcm74110_mbox, controller);
}
static void bcm74110_rx_push_init_msg(struct bcm74110_mbox *mbox, u32 val)
{
struct bcm74110_mbox_msg *msg;
msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
if (!msg)
return;
INIT_LIST_HEAD(&msg->list_entry);
msg->msg = val;
spin_lock(&mbox->rx_svc_list_lock);
list_add_tail(&msg->list_entry, &mbox->rx_svc_init_list);
spin_unlock(&mbox->rx_svc_list_lock);
}
static void bcm74110_rx_process_msg(struct bcm74110_mbox *mbox)
{
struct device *dev = &mbox->pdev->dev;
struct bcm74110_mbox_chan *chan_priv;
struct mbox_chan *chan;
u32 msg, status;
int type;
do {
msg = bcm74110_rx_readl(mbox, BCM_MBOX_RDATA);
status = bcm74110_rx_readl(mbox, BCM_MBOX_STATUS0);
dev_dbg(dev, "rx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n",
BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY),
BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC),
BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT));
type = BCM_MSG_GET_FIELD(msg, SVC);
switch (type) {
case BCM_MSG_SVC_INIT:
bcm74110_rx_push_init_msg(mbox, msg);
break;
case BCM_MSG_SVC_PMC:
case BCM_MSG_SVC_SCMI:
case BCM_MSG_SVC_DPFE:
chan = &mbox->controller.chans[type];
chan_priv = chan->con_priv;
if (chan_priv->en)
mbox_chan_received_data(chan, NULL);
else
dev_warn(dev, "Channel not enabled\n");
break;
default:
dev_warn(dev, "Unsupported msg received\n");
}
} while (status & BCM_MBOX_STATUS0_NOT_EMPTY);
}
static irqreturn_t bcm74110_mbox_isr(int irq, void *data)
{
struct bcm74110_mbox *mbox = data;
u32 status;
status = bcm74110_irq_readl(mbox, BCM_MBOX_IRQ_STATUS);
bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR);
if (status & BCM_MBOX_IRQ_NOT_EMPTY)
bcm74110_rx_process_msg(mbox);
else
dev_warn(&mbox->pdev->dev, "Spurious interrupt\n");
return IRQ_HANDLED;
}
static void bcm74110_mbox_mask_and_clear(struct bcm74110_mbox *mbox)
{
bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_MASK_SET);
bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR);
}
static int bcm74110_rx_pop_init_msg(struct bcm74110_mbox *mbox, u32 func_type,
u32 *val)
{
struct bcm74110_mbox_msg *msg, *msg_tmp;
unsigned long flags;
bool found = false;
spin_lock_irqsave(&mbox->rx_svc_list_lock, flags);
list_for_each_entry_safe(msg, msg_tmp, &mbox->rx_svc_init_list,
list_entry) {
if (BCM_MSG_GET_FIELD(msg->msg, FUNC) == func_type) {
list_del(&msg->list_entry);
found = true;
break;
}
}
spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags);
if (!found)
return -EINVAL;
*val = msg->msg;
kfree(msg);
return 0;
}
static void bcm74110_rx_flush_msg(struct bcm74110_mbox *mbox)
{
struct bcm74110_mbox_msg *msg, *msg_tmp;
LIST_HEAD(list_temp);
unsigned long flags;
spin_lock_irqsave(&mbox->rx_svc_list_lock, flags);
list_splice_init(&mbox->rx_svc_init_list, &list_temp);
spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags);
list_for_each_entry_safe(msg, msg_tmp, &list_temp, list_entry) {
list_del(&msg->list_entry);
kfree(msg);
}
}
#define BCM_DEQUEUE_TIMEOUT_MS 30
static int bcm74110_rx_pop_init_msg_block(struct bcm74110_mbox *mbox, u32 func_type,
u32 *val)
{
int ret, timeout = 0;
do {
ret = bcm74110_rx_pop_init_msg(mbox, func_type, val);
if (!ret)
return 0;
/* TODO: Figure out what is a good sleep here. */
usleep_range(1000, 2000);
timeout++;
} while (timeout < BCM_DEQUEUE_TIMEOUT_MS);
dev_warn(&mbox->pdev->dev, "Timeout waiting for service init response\n");
return -ETIMEDOUT;
}
static int bcm74110_mbox_create_msg(int req, int rply, int svc, int func,
int length, int slot)
{
u32 msg = 0;
BCM_MSG_SET_FIELD(msg, REQ, req);
BCM_MSG_SET_FIELD(msg, RPLY, rply);
BCM_MSG_SET_FIELD(msg, SVC, svc);
BCM_MSG_SET_FIELD(msg, FUNC, func);
BCM_MSG_SET_FIELD(msg, LENGTH, length);
BCM_MSG_SET_FIELD(msg, SLOT, slot);
return msg;
}
static int bcm74110_mbox_tx_msg(struct bcm74110_mbox *mbox, u32 msg)
{
int val;
/* We can potentially poll with timeout here instead */
val = bcm74110_tx_readl(mbox, BCM_MBOX_STATUS0);
if (val & BCM_MBOX_STATUS0_FULL) {
dev_err(&mbox->pdev->dev, "Mailbox full\n");
return -EINVAL;
}
dev_dbg(&mbox->pdev->dev, "tx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n",
BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY),
BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC),
BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT));
bcm74110_tx_writel(mbox, msg, BCM_MBOX_WDATA);
return 0;
}
#define BCM_MBOX_LINK_TRAINING_RETRIES 5
static int bcm74110_mbox_link_training(struct bcm74110_mbox *mbox)
{
int ret, retries = 0;
u32 msg = 0, orig_len = 0, len = BCM_LINK_CODE0;
do {
switch (len) {
case 0:
retries++;
dev_warn(&mbox->pdev->dev,
"Link train failed, trying again... %d\n",
retries);
if (retries > BCM_MBOX_LINK_TRAINING_RETRIES)
return -EINVAL;
len = BCM_LINK_CODE0;
fallthrough;
case BCM_LINK_CODE0:
case BCM_LINK_CODE1:
case BCM_LINK_CODE2:
msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
BCM_MSG_FUNC_LINK_START,
len, BCM_MSG_SVC_INIT);
break;
default:
break;
}
bcm74110_mbox_tx_msg(mbox, msg);
/* No response expected for LINK_CODE2 */
if (len == BCM_LINK_CODE2)
return 0;
orig_len = len;
ret = bcm74110_rx_pop_init_msg_block(mbox,
BCM_MSG_GET_FIELD(msg, FUNC),
&msg);
if (ret) {
len = 0;
continue;
}
if ((BCM_MSG_GET_FIELD(msg, SVC) != BCM_MSG_SVC_INIT) ||
(BCM_MSG_GET_FIELD(msg, FUNC) != BCM_MSG_FUNC_LINK_START) ||
(BCM_MSG_GET_FIELD(msg, SLOT) != 0) ||
(BCM_MSG_GET_FIELD(msg, RPLY) != 1) ||
(BCM_MSG_GET_FIELD(msg, REQ) != 0)) {
len = 0;
continue;
}
len = BCM_MSG_GET_FIELD(msg, LENGTH);
/* Make sure sequence is good */
if (len != (orig_len + 1)) {
len = 0;
continue;
}
} while (1);
return -EINVAL;
}
static int bcm74110_mbox_tx_msg_and_wait_ack(struct bcm74110_mbox *mbox, u32 msg)
{
int ret;
u32 recv_msg;
ret = bcm74110_mbox_tx_msg(mbox, msg);
if (ret)
return ret;
ret = bcm74110_rx_pop_init_msg_block(mbox, BCM_MSG_GET_FIELD(msg, FUNC),
&recv_msg);
if (ret)
return ret;
/*
* Modify tx message to verify rx ack.
* Flip RPLY/REQ for synchronous messages
*/
if (BCM_MSG_GET_FIELD(msg, REQ) == 1) {
BCM_MSG_SET_FIELD(msg, RPLY, 1);
BCM_MSG_SET_FIELD(msg, REQ, 0);
}
if (msg != recv_msg) {
dev_err(&mbox->pdev->dev, "Found ack, but ack is invalid\n");
return -EINVAL;
}
return 0;
}
/* Each index points to 0x100 of HAB MEM. IDX size counts from 0 */
#define BCM_MBOX_HAB_MEM_IDX_START 0x30
#define BCM_MBOX_HAB_MEM_IDX_SIZE 0x0
static int bcm74110_mbox_shmem_init(struct bcm74110_mbox *mbox)
{
u32 msg = 0;
int ret;
msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
BCM_MSG_FUNC_SHMEM_STOP,
0, BCM_MSG_SVC_INIT);
ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
if (ret)
return -EINVAL;
msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
BCM_MSG_FUNC_SHMEM_TX,
BCM_MBOX_HAB_MEM_IDX_START,
BCM_MBOX_HAB_MEM_IDX_SIZE);
ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
if (ret)
return -EINVAL;
msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
BCM_MSG_FUNC_SHMEM_RX,
BCM_MBOX_HAB_MEM_IDX_START,
BCM_MBOX_HAB_MEM_IDX_SIZE);
ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
if (ret)
return -EINVAL;
return 0;
}
static int bcm74110_mbox_init(struct bcm74110_mbox *mbox)
{
int ret = 0;
/* Disable queues tx/rx */
bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL);
/* Clear status & restart tx/rx*/
bcm74110_tx_writel(mbox, BCM_MBOX_CTRL_EN | BCM_MBOX_CTRL_CLR,
BCM_MBOX_CTRL);
/* Unmask irq */
bcm74110_irq_writel(mbox, BCM_MBOX_IRQ_NOT_EMPTY, BCM_MBOX_IRQ_MASK_CLEAR);
ret = bcm74110_mbox_link_training(mbox);
if (ret) {
dev_err(&mbox->pdev->dev, "Training failed\n");
return ret;
}
return bcm74110_mbox_shmem_init(mbox);
}
static int bcm74110_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct bcm74110_mbox_chan *chan_priv = chan->con_priv;
u32 msg;
switch (chan_priv->type) {
case BCM_MSG_SVC_PMC:
case BCM_MSG_SVC_SCMI:
case BCM_MSG_SVC_DPFE:
msg = bcm74110_mbox_create_msg(1, 0, chan_priv->type, 0,
128 + 28, chan_priv->slot);
break;
default:
return -EINVAL;
}
return bcm74110_mbox_tx_msg(chan_priv->mbox, msg);
}
static int bcm74110_mbox_chan_startup(struct mbox_chan *chan)
{
struct bcm74110_mbox_chan *chan_priv = chan->con_priv;
chan_priv->en = true;
return 0;
}
static void bcm74110_mbox_chan_shutdown(struct mbox_chan *chan)
{
struct bcm74110_mbox_chan *chan_priv = chan->con_priv;
chan_priv->en = false;
}
static const struct mbox_chan_ops bcm74110_mbox_chan_ops = {
.send_data = bcm74110_mbox_send_data,
.startup = bcm74110_mbox_chan_startup,
.shutdown = bcm74110_mbox_chan_shutdown,
};
static void bcm74110_mbox_shutdown(struct platform_device *pdev)
{
struct bcm74110_mbox *mbox = dev_get_drvdata(&pdev->dev);
u32 msg;
msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT,
BCM_MSG_FUNC_LINK_STOP,
0, 0);
bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg);
/* Even if we don't receive ACK, lets shut it down */
bcm74110_mbox_mask_and_clear(mbox);
/* Disable queues tx/rx */
bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL);
/* Flush queues */
bcm74110_rx_flush_msg(mbox);
}
static struct mbox_chan *bcm74110_mbox_of_xlate(struct mbox_controller *cntrl,
const struct of_phandle_args *p)
{
struct bcm74110_mbox *mbox = bcm74110_mbox_from_cntrl(cntrl);
struct device *dev = &mbox->pdev->dev;
struct bcm74110_mbox_chan *chan_priv;
int slot, type;
if (p->args_count != 2) {
dev_err(dev, "Invalid arguments\n");
return ERR_PTR(-EINVAL);
}
type = p->args[0];
slot = p->args[1];
switch (type) {
case BCM_MSG_SVC_PMC:
case BCM_MSG_SVC_SCMI:
case BCM_MSG_SVC_DPFE:
if (slot > BCM_MBOX_HAB_MEM_IDX_SIZE) {
dev_err(dev, "Not enough shared memory\n");
return ERR_PTR(-EINVAL);
}
chan_priv = cntrl->chans[type].con_priv;
chan_priv->slot = slot;
chan_priv->type = type;
break;
default:
dev_err(dev, "Invalid channel type: %d\n", type);
return ERR_PTR(-EINVAL);
}
return &cntrl->chans[type];
}
static int bcm74110_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct bcm74110_mbox *mbox;
int i, ret;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
if (!mbox)
return -ENOMEM;
mbox->pdev = pdev;
platform_set_drvdata(pdev, mbox);
mbox->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->base))
return dev_err_probe(dev, PTR_ERR(mbox->base), "Failed to iomap\n");
ret = of_property_read_u32(dev->of_node, "brcm,tx", &mbox->tx_chan);
if (ret)
return dev_err_probe(dev, ret, "Failed to find tx channel\n");
ret = of_property_read_u32(dev->of_node, "brcm,rx", &mbox->rx_chan);
if (ret)
return dev_err_probe(dev, ret, "Failed to find rx channel\n");
mbox->rx_irq = platform_get_irq(pdev, 0);
if (mbox->rx_irq < 0)
return mbox->rx_irq;
INIT_LIST_HEAD(&mbox->rx_svc_init_list);
spin_lock_init(&mbox->rx_svc_list_lock);
bcm74110_mbox_mask_and_clear(mbox);
ret = devm_request_irq(dev, mbox->rx_irq, bcm74110_mbox_isr,
IRQF_NO_SUSPEND, pdev->name, mbox);
if (ret)
return dev_err_probe(dev, ret, "Failed to request irq\n");
mbox->controller.ops = &bcm74110_mbox_chan_ops;
mbox->controller.dev = dev;
mbox->controller.num_chans = BCM_MSG_SVC_MAX;
mbox->controller.of_xlate = &bcm74110_mbox_of_xlate;
mbox->controller.chans = devm_kcalloc(dev, BCM_MSG_SVC_MAX,
sizeof(*mbox->controller.chans),
GFP_KERNEL);
if (!mbox->controller.chans)
return -ENOMEM;
mbox->mbox_chan = devm_kcalloc(dev, BCM_MSG_SVC_MAX,
sizeof(*mbox->mbox_chan),
GFP_KERNEL);
if (!mbox->mbox_chan)
return -ENOMEM;
for (i = 0; i < BCM_MSG_SVC_MAX; i++) {
mbox->mbox_chan[i].mbox = mbox;
mbox->controller.chans[i].con_priv = &mbox->mbox_chan[i];
}
ret = devm_mbox_controller_register(dev, &mbox->controller);
if (ret)
return ret;
ret = bcm74110_mbox_init(mbox);
if (ret)
return ret;
return 0;
}
static const struct of_device_id bcm74110_mbox_of_match[] = {
{ .compatible = "brcm,bcm74110-mbox", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, bcm74110_mbox_of_match);
static struct platform_driver bcm74110_mbox_driver = {
.driver = {
.name = "bcm74110-mbox",
.of_match_table = bcm74110_mbox_of_match,
},
.probe = bcm74110_mbox_probe,
.shutdown = bcm74110_mbox_shutdown,
};
module_platform_driver(bcm74110_mbox_driver);
MODULE_AUTHOR("Justin Chen <justin.chen@broadcom.com>");
MODULE_DESCRIPTION("BCM74110 mailbox driver");
MODULE_LICENSE("GPL");

View File

@ -390,7 +390,7 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
task = kzalloc(sizeof(*task), GFP_ATOMIC);
if (!task) {
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
pm_runtime_put_autosuspend(cmdq->mbox.dev);
return -ENOMEM;
}
@ -440,7 +440,7 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
list_move_tail(&task->list_entry, &thread->task_busy_list);
pm_runtime_mark_last_busy(cmdq->mbox.dev);
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
pm_runtime_put_autosuspend(cmdq->mbox.dev);
return 0;
}
@ -488,7 +488,7 @@ done:
spin_unlock_irqrestore(&thread->chan->lock, flags);
pm_runtime_mark_last_busy(cmdq->mbox.dev);
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
pm_runtime_put_autosuspend(cmdq->mbox.dev);
}
static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
@ -528,7 +528,7 @@ static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
out:
spin_unlock_irqrestore(&thread->chan->lock, flags);
pm_runtime_mark_last_busy(cmdq->mbox.dev);
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
pm_runtime_put_autosuspend(cmdq->mbox.dev);
return 0;
@ -543,7 +543,7 @@ wait:
return -EFAULT;
}
pm_runtime_mark_last_busy(cmdq->mbox.dev);
__pm_runtime_put_autosuspend(cmdq->mbox.dev);
pm_runtime_put_autosuspend(cmdq->mbox.dev);
return 0;
}

View File

@ -306,6 +306,22 @@ static void pcc_chan_acknowledge(struct pcc_chan_info *pchan)
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
* @irq: interrupt number
@ -317,6 +333,8 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
{
struct pcc_chan_info *pchan;
struct mbox_chan *chan = p;
struct pcc_header *pcc_header = chan->active_req;
void *handle = NULL;
pchan = chan->con_priv;
@ -340,7 +358,17 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
* required to avoid any possible race in updatation of this flag.
*/
pchan->chan_in_use = false;
mbox_chan_received_data(chan, NULL);
if (pchan->chan.rx_alloc)
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);
@ -384,9 +412,24 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
pcc_mchan = &pchan->chan;
pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr,
pcc_mchan->shmem_size);
if (pcc_mchan->shmem)
return pcc_mchan;
if (!pcc_mchan->shmem)
goto err;
pcc_mchan->manage_writes = false;
/* This indicates that the channel is ready to accept messages.
* This needs to happen after the channel has registered
* its callback. There is no access point to do that in
* 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;
err:
mbox_free_channel(chan);
return ERR_PTR(-ENXIO);
}
@ -417,8 +460,38 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
}
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. Used
* pcc_send_data - Called from Mailbox Controller code. If
* 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
* specific read/write is done in the client driver in
* order to maintain atomicity over PCC channel once
@ -434,17 +507,37 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)
int ret;
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);
if (ret)
return ret;
ret = pcc_chan_reg_read_modify_write(&pchan->db);
if (!ret && pchan->plat_irq > 0)
pchan->chan_in_use = true;
return ret;
}
static bool pcc_last_tx_done(struct mbox_chan *chan)
{
struct pcc_chan_info *pchan = chan->con_priv;
u64 val;
pcc_chan_reg_read(&pchan->cmd_complete, &val);
if (!val)
return false;
else
return true;
}
/**
* pcc_startup - Called from Mailbox Controller code. Used here
* to request the interrupt.
@ -490,6 +583,7 @@ static const struct mbox_chan_ops pcc_chan_ops = {
.send_data = pcc_send_data,
.startup = pcc_startup,
.shutdown = pcc_shutdown,
.last_tx_done = pcc_last_tx_done,
};
/**

View File

@ -312,8 +312,7 @@ static int qcom_ipcc_probe(struct platform_device *pdev)
if (!name)
return -ENOMEM;
ipcc->irq_domain = irq_domain_create_tree(of_fwnode_handle(pdev->dev.of_node),
&qcom_ipcc_irq_ops, ipcc);
ipcc->irq_domain = irq_domain_create_tree(dev_fwnode(&pdev->dev), &qcom_ipcc_irq_ops, ipcc);
if (!ipcc->irq_domain)
return -ENOMEM;

View File

@ -17,6 +17,35 @@ struct pcc_mbox_chan {
u32 latency;
u32 max_access_rate;
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 */