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:
commit
b20b8538b3
|
@ -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>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,6 +24,7 @@ properties:
|
|||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,milos-ipcc
|
||||
- qcom,qcs8300-ipcc
|
||||
- qcom,qdu1000-ipcc
|
||||
- qcom,sa8255p-ipcc
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue