Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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()

+1214 -63
+7 -7
Documentation/devicetree/bindings/mailbox/allwinner,sun6i-a31-msgbox.yaml
··· 68 68 #include <dt-bindings/reset/sun8i-h3-ccu.h> 69 69 70 70 msgbox: mailbox@1c17000 { 71 - compatible = "allwinner,sun8i-h3-msgbox", 72 - "allwinner,sun6i-a31-msgbox"; 73 - reg = <0x01c17000 0x1000>; 74 - clocks = <&ccu CLK_BUS_MSGBOX>; 75 - resets = <&ccu RST_BUS_MSGBOX>; 76 - interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>; 77 - #mbox-cells = <1>; 71 + compatible = "allwinner,sun8i-h3-msgbox", 72 + "allwinner,sun6i-a31-msgbox"; 73 + reg = <0x01c17000 0x1000>; 74 + clocks = <&ccu CLK_BUS_MSGBOX>; 75 + resets = <&ccu RST_BUS_MSGBOX>; 76 + interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>; 77 + #mbox-cells = <1>; 78 78 }; 79 79 80 80 ...
+5 -5
Documentation/devicetree/bindings/mailbox/amlogic,meson-gxbb-mhu.yaml
··· 27 27 maxItems: 1 28 28 29 29 interrupts: 30 - minItems: 3 30 + maxItems: 3 31 31 description: 32 32 Contains the interrupt information corresponding to each of the 3 links 33 33 of MHU. ··· 46 46 examples: 47 47 - | 48 48 mailbox@c883c404 { 49 - compatible = "amlogic,meson-gxbb-mhu"; 50 - reg = <0xc883c404 0x4c>; 51 - interrupts = <208>, <209>, <210>; 52 - #mbox-cells = <1>; 49 + compatible = "amlogic,meson-gxbb-mhu"; 50 + reg = <0xc883c404 0x4c>; 51 + interrupts = <208>, <209>, <210>; 52 + #mbox-cells = <1>; 53 53 };
+8 -8
Documentation/devicetree/bindings/mailbox/apple,mailbox.yaml
··· 78 78 79 79 examples: 80 80 - | 81 - mailbox@77408000 { 82 - compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; 83 - reg = <0x77408000 0x4000>; 84 - interrupts = <1 583 4>, <1 584 4>, <1 585 4>, <1 586 4>; 85 - interrupt-names = "send-empty", "send-not-empty", 86 - "recv-empty", "recv-not-empty"; 87 - #mbox-cells = <0>; 88 - }; 81 + mailbox@77408000 { 82 + compatible = "apple,t8103-asc-mailbox", "apple,asc-mailbox-v4"; 83 + reg = <0x77408000 0x4000>; 84 + interrupts = <1 583 4>, <1 584 4>, <1 585 4>, <1 586 4>; 85 + interrupt-names = "send-empty", "send-not-empty", 86 + "recv-empty", "recv-not-empty"; 87 + #mbox-cells = <0>; 88 + };
+68
Documentation/devicetree/bindings/mailbox/aspeed,ast2700-mailbox.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/mailbox/aspeed,ast2700-mailbox.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: ASPEED AST2700 mailbox controller 8 + 9 + maintainers: 10 + - Jammy Huang <jammy_huang@aspeedtech.com> 11 + 12 + description: > 13 + ASPEED AST2700 has multiple processors that need to communicate with each 14 + other. The mailbox controller provides a way for these processors to send 15 + messages to each other. It is a hardware-based inter-processor communication 16 + mechanism that allows processors to send and receive messages through 17 + dedicated channels. 18 + 19 + The mailbox's tx/rx are independent, meaning that one processor can send a 20 + message while another processor is receiving a message simultaneously. 21 + There are 4 channels available for both tx and rx operations. Each channel 22 + has a FIFO buffer that can hold messages of a fixed size (32 bytes in this 23 + case). 24 + 25 + The mailbox controller also supports interrupt generation, allowing 26 + processors to notify each other when a message is available or when an event 27 + occurs. 28 + 29 + properties: 30 + compatible: 31 + const: aspeed,ast2700-mailbox 32 + 33 + reg: 34 + items: 35 + - description: TX control register 36 + - description: RX control register 37 + 38 + reg-names: 39 + items: 40 + - const: tx 41 + - const: rx 42 + 43 + interrupts: 44 + maxItems: 1 45 + 46 + "#mbox-cells": 47 + const: 1 48 + 49 + required: 50 + - compatible 51 + - reg 52 + - reg-names 53 + - interrupts 54 + - "#mbox-cells" 55 + 56 + additionalProperties: false 57 + 58 + examples: 59 + - | 60 + #include <dt-bindings/interrupt-controller/arm-gic.h> 61 + 62 + mailbox@12c1c200 { 63 + compatible = "aspeed,ast2700-mailbox"; 64 + reg = <0x12c1c200 0x100>, <0x12c1c300 0x100>; 65 + reg-names = "tx", "rx"; 66 + interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>; 67 + #mbox-cells = <1>; 68 + };
+64
Documentation/devicetree/bindings/mailbox/brcm,bcm74110-mbox.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/mailbox/brcm,bcm74110-mbox.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Broadcom BCM74110 Mailbox 8 + 9 + maintainers: 10 + - Justin Chen <justin.chen@broadcom.com> 11 + - Florian Fainelli <florian.fainelli@broadcom.com> 12 + 13 + description: Broadcom mailbox hardware first introduced with 74110 14 + 15 + properties: 16 + compatible: 17 + enum: 18 + - brcm,bcm74110-mbox 19 + 20 + reg: 21 + maxItems: 1 22 + 23 + interrupts: 24 + items: 25 + - description: RX doorbell and watermark interrupts 26 + - description: TX doorbell and watermark interrupts 27 + 28 + "#mbox-cells": 29 + const: 2 30 + description: 31 + The first cell is channel type and second cell is shared memory slot 32 + 33 + brcm,rx: 34 + $ref: /schemas/types.yaml#/definitions/uint32 35 + description: RX Mailbox number 36 + 37 + brcm,tx: 38 + $ref: /schemas/types.yaml#/definitions/uint32 39 + description: TX Mailbox number 40 + 41 + required: 42 + - compatible 43 + - reg 44 + - interrupts 45 + - "#mbox-cells" 46 + - brcm,rx 47 + - brcm,tx 48 + 49 + additionalProperties: false 50 + 51 + examples: 52 + - | 53 + #include <dt-bindings/interrupt-controller/irq.h> 54 + #include <dt-bindings/interrupt-controller/arm-gic.h> 55 + 56 + mailbox@a552000 { 57 + compatible = "brcm,bcm74110-mbox"; 58 + reg = <0xa552000 0x1104>; 59 + interrupts = <GIC_SPI 0x67 IRQ_TYPE_LEVEL_HIGH>, 60 + <GIC_SPI 0x66 IRQ_TYPE_LEVEL_HIGH>; 61 + #mbox-cells = <0x2>; 62 + brcm,rx = <0x7>; 63 + brcm,tx = <0x6>; 64 + };
+1 -8
Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.yaml
··· 59 59 <dt-bindings/mailbox/tegra186-hsp.h> 60 60 61 61 properties: 62 - $nodename: 63 - pattern: "^hsp@[0-9a-f]+$" 64 - 65 62 compatible: 66 63 oneOf: 67 64 - enum: ··· 128 131 #include <dt-bindings/interrupt-controller/arm-gic.h> 129 132 #include <dt-bindings/mailbox/tegra186-hsp.h> 130 133 131 - hsp_top0: hsp@3c00000 { 134 + mailbox@3c00000 { 132 135 compatible = "nvidia,tegra186-hsp"; 133 136 reg = <0x03c00000 0xa0000>; 134 137 interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>; 135 138 interrupt-names = "doorbell"; 136 139 #mbox-cells = <2>; 137 - }; 138 - 139 - client { 140 - mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB TEGRA_HSP_DB_MASTER_CCPLEX>; 141 140 };
+1 -8
Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml
··· 251 251 # Example apcs with msm8996 252 252 - | 253 253 #include <dt-bindings/interrupt-controller/arm-gic.h> 254 - apcs_glb: mailbox@9820000 { 254 + mailbox@9820000 { 255 255 compatible = "qcom,msm8996-apcs-hmss-global"; 256 256 reg = <0x9820000 0x1000>; 257 257 258 258 #mbox-cells = <1>; 259 259 #clock-cells = <0>; 260 - }; 261 - 262 - rpm-glink { 263 - compatible = "qcom,glink-rpm"; 264 - interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>; 265 - qcom,rpm-msg-ram = <&rpm_msg_ram>; 266 - mboxes = <&apcs_glb 0>; 267 260 }; 268 261 269 262 # Example apcs with qcs404
+1
Documentation/devicetree/bindings/mailbox/qcom-ipcc.yaml
··· 24 24 compatible: 25 25 items: 26 26 - enum: 27 + - qcom,milos-ipcc 27 28 - qcom,qcs8300-ipcc 28 29 - qcom,qdu1000-ipcc 29 30 - qcom,sa8255p-ipcc
+3 -7
Documentation/devicetree/bindings/mailbox/ti,omap-mailbox.yaml
··· 242 242 - | 243 243 /* OMAP4 */ 244 244 #include <dt-bindings/interrupt-controller/arm-gic.h> 245 - mailbox: mailbox@4a0f4000 { 245 + mailbox@4a0f4000 { 246 246 compatible = "ti,omap4-mailbox"; 247 247 reg = <0x4a0f4000 0x200>; 248 248 interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>; ··· 260 260 }; 261 261 }; 262 262 263 - dsp { 264 - mboxes = <&mailbox &mbox_dsp>; 265 - }; 266 - 267 263 - | 268 264 /* AM33xx */ 269 - mailbox1: mailbox@480c8000 { 265 + mailbox@480c8000 { 270 266 compatible = "ti,omap4-mailbox"; 271 267 reg = <0x480c8000 0x200>; 272 268 interrupts = <77>; ··· 279 283 280 284 - | 281 285 /* AM65x */ 282 - mailbox0_cluster0: mailbox@31f80000 { 286 + mailbox@31f80000 { 283 287 compatible = "ti,am654-mailbox"; 284 288 reg = <0x31f80000 0x200>; 285 289 #mbox-cells = <1>;
+9 -9
Documentation/devicetree/bindings/mailbox/ti,secure-proxy.yaml
··· 36 36 - const: scfg 37 37 38 38 reg: 39 - minItems: 3 39 + maxItems: 3 40 40 41 41 interrupt-names: 42 42 minItems: 1 ··· 68 68 - | 69 69 #include <dt-bindings/interrupt-controller/arm-gic.h> 70 70 secure_proxy: mailbox@32c00000 { 71 - compatible = "ti,am654-secure-proxy"; 72 - #mbox-cells = <1>; 73 - reg-names = "target_data", "rt", "scfg"; 74 - reg = <0x32c00000 0x100000>, 75 - <0x32400000 0x100000>, 76 - <0x32800000 0x100000>; 77 - interrupt-names = "rx_011"; 78 - interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; 71 + compatible = "ti,am654-secure-proxy"; 72 + #mbox-cells = <1>; 73 + reg-names = "target_data", "rt", "scfg"; 74 + reg = <0x32c00000 0x100000>, 75 + <0x32400000 0x100000>, 76 + <0x32800000 0x100000>; 77 + interrupt-names = "rx_011"; 78 + interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; 79 79 };
+19
drivers/mailbox/Kconfig
··· 36 36 that provides different means of transports: supported extensions 37 37 will be discovered and possibly managed at probe-time. 38 38 39 + config AST2700_MBOX 40 + tristate "ASPEED AST2700 IPC driver" 41 + depends on ARCH_ASPEED || COMPILE_TEST 42 + help 43 + Mailbox driver implementation for ASPEED AST27XX SoCs. This driver 44 + can be used to send message between different processors in SoC. 45 + The driver provides mailbox support for sending interrupts to the 46 + clients. Say Y here if you want to build this driver. 47 + 39 48 config CV1800_MBOX 40 49 tristate "cv1800 mailbox" 41 50 depends on ARCH_SOPHGO || COMPILE_TEST ··· 358 349 11 mailbox channels with different operating mode and every channel 359 350 is unidirectional. Say Y here if you want to use the CIX Mailbox 360 351 support. 352 + 353 + config BCM74110_MAILBOX 354 + tristate "Brcmstb BCM74110 Mailbox" 355 + depends on ARCH_BRCMSTB || COMPILE_TEST 356 + default ARCH_BRCMSTB 357 + help 358 + Broadcom STB mailbox driver present starting with brcmstb bcm74110 359 + SoCs. The mailbox is a communication channel between the host 360 + processor and coprocessor that handles various power management task 361 + and more. 361 362 362 363 endif
+4
drivers/mailbox/Makefile
··· 11 11 12 12 obj-$(CONFIG_ARM_MHU_V3) += arm_mhuv3.o 13 13 14 + obj-$(CONFIG_AST2700_MBOX) += ast2700-mailbox.o 15 + 14 16 obj-$(CONFIG_CV1800_MBOX) += cv1800-mailbox.o 15 17 16 18 obj-$(CONFIG_EXYNOS_MBOX) += exynos-mailbox.o ··· 76 74 obj-$(CONFIG_THEAD_TH1520_MBOX) += mailbox-th1520.o 77 75 78 76 obj-$(CONFIG_CIX_MBOX) += cix-mailbox.o 77 + 78 + obj-$(CONFIG_BCM74110_MAILBOX) += bcm74110-mailbox.o
+235
drivers/mailbox/ast2700-mailbox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright Aspeed Technology Inc. (C) 2025. All rights reserved 4 + */ 5 + 6 + #include <linux/interrupt.h> 7 + #include <linux/io.h> 8 + #include <linux/iopoll.h> 9 + #include <linux/kernel.h> 10 + #include <linux/mailbox_controller.h> 11 + #include <linux/module.h> 12 + #include <linux/of.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/slab.h> 15 + 16 + /* Each bit in the register represents an IPC ID */ 17 + #define IPCR_TX_TRIG 0x00 18 + #define IPCR_ENABLE 0x04 19 + #define IPCR_STATUS 0x08 20 + #define RX_IRQ(n) BIT(n) 21 + #define RX_IRQ_MASK 0xf 22 + #define IPCR_DATA 0x10 23 + 24 + struct ast2700_mbox_data { 25 + u8 num_chans; 26 + u8 msg_size; 27 + }; 28 + 29 + struct ast2700_mbox { 30 + struct mbox_controller mbox; 31 + u8 msg_size; 32 + void __iomem *tx_regs; 33 + void __iomem *rx_regs; 34 + spinlock_t lock; 35 + }; 36 + 37 + static inline int ch_num(struct mbox_chan *chan) 38 + { 39 + return chan - chan->mbox->chans; 40 + } 41 + 42 + static inline bool ast2700_mbox_tx_done(struct ast2700_mbox *mb, int idx) 43 + { 44 + return !(readl(mb->tx_regs + IPCR_STATUS) & BIT(idx)); 45 + } 46 + 47 + static irqreturn_t ast2700_mbox_irq(int irq, void *p) 48 + { 49 + struct ast2700_mbox *mb = p; 50 + void __iomem *data_reg; 51 + int num_words = mb->msg_size / sizeof(u32); 52 + u32 *word_data; 53 + u32 status; 54 + int n, i; 55 + 56 + /* Only examine channels that are currently enabled. */ 57 + status = readl(mb->rx_regs + IPCR_ENABLE) & 58 + readl(mb->rx_regs + IPCR_STATUS); 59 + 60 + if (!(status & RX_IRQ_MASK)) 61 + return IRQ_NONE; 62 + 63 + for (n = 0; n < mb->mbox.num_chans; ++n) { 64 + struct mbox_chan *chan = &mb->mbox.chans[n]; 65 + 66 + if (!(status & RX_IRQ(n))) 67 + continue; 68 + 69 + data_reg = mb->rx_regs + IPCR_DATA + mb->msg_size * n; 70 + word_data = chan->con_priv; 71 + /* Read the message data */ 72 + for (i = 0; i < num_words; i++) 73 + word_data[i] = readl(data_reg + i * sizeof(u32)); 74 + 75 + mbox_chan_received_data(chan, chan->con_priv); 76 + 77 + /* The IRQ can be cleared only once the FIFO is empty. */ 78 + writel(RX_IRQ(n), mb->rx_regs + IPCR_STATUS); 79 + } 80 + 81 + return IRQ_HANDLED; 82 + } 83 + 84 + static int ast2700_mbox_send_data(struct mbox_chan *chan, void *data) 85 + { 86 + struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev); 87 + int idx = ch_num(chan); 88 + void __iomem *data_reg = mb->tx_regs + IPCR_DATA + mb->msg_size * idx; 89 + u32 *word_data = data; 90 + int num_words = mb->msg_size / sizeof(u32); 91 + int i; 92 + 93 + if (!(readl(mb->tx_regs + IPCR_ENABLE) & BIT(idx))) { 94 + dev_warn(mb->mbox.dev, "%s: Ch-%d not enabled yet\n", __func__, idx); 95 + return -ENODEV; 96 + } 97 + 98 + if (!(ast2700_mbox_tx_done(mb, idx))) { 99 + dev_warn(mb->mbox.dev, "%s: Ch-%d last data has not finished\n", __func__, idx); 100 + return -EBUSY; 101 + } 102 + 103 + /* Write the message data */ 104 + for (i = 0 ; i < num_words; i++) 105 + writel(word_data[i], data_reg + i * sizeof(u32)); 106 + 107 + writel(BIT(idx), mb->tx_regs + IPCR_TX_TRIG); 108 + dev_dbg(mb->mbox.dev, "%s: Ch-%d sent\n", __func__, idx); 109 + 110 + return 0; 111 + } 112 + 113 + static int ast2700_mbox_startup(struct mbox_chan *chan) 114 + { 115 + struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev); 116 + int idx = ch_num(chan); 117 + void __iomem *reg = mb->rx_regs + IPCR_ENABLE; 118 + unsigned long flags; 119 + 120 + spin_lock_irqsave(&mb->lock, flags); 121 + writel(readl(reg) | BIT(idx), reg); 122 + spin_unlock_irqrestore(&mb->lock, flags); 123 + 124 + return 0; 125 + } 126 + 127 + static void ast2700_mbox_shutdown(struct mbox_chan *chan) 128 + { 129 + struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev); 130 + int idx = ch_num(chan); 131 + void __iomem *reg = mb->rx_regs + IPCR_ENABLE; 132 + unsigned long flags; 133 + 134 + spin_lock_irqsave(&mb->lock, flags); 135 + writel(readl(reg) & ~BIT(idx), reg); 136 + spin_unlock_irqrestore(&mb->lock, flags); 137 + } 138 + 139 + static bool ast2700_mbox_last_tx_done(struct mbox_chan *chan) 140 + { 141 + struct ast2700_mbox *mb = dev_get_drvdata(chan->mbox->dev); 142 + int idx = ch_num(chan); 143 + 144 + return ast2700_mbox_tx_done(mb, idx); 145 + } 146 + 147 + static const struct mbox_chan_ops ast2700_mbox_chan_ops = { 148 + .send_data = ast2700_mbox_send_data, 149 + .startup = ast2700_mbox_startup, 150 + .shutdown = ast2700_mbox_shutdown, 151 + .last_tx_done = ast2700_mbox_last_tx_done, 152 + }; 153 + 154 + static int ast2700_mbox_probe(struct platform_device *pdev) 155 + { 156 + struct ast2700_mbox *mb; 157 + const struct ast2700_mbox_data *dev_data; 158 + struct device *dev = &pdev->dev; 159 + int irq, ret; 160 + 161 + if (!pdev->dev.of_node) 162 + return -ENODEV; 163 + 164 + dev_data = device_get_match_data(&pdev->dev); 165 + 166 + mb = devm_kzalloc(dev, sizeof(*mb), GFP_KERNEL); 167 + if (!mb) 168 + return -ENOMEM; 169 + 170 + mb->mbox.chans = devm_kcalloc(&pdev->dev, dev_data->num_chans, 171 + sizeof(*mb->mbox.chans), GFP_KERNEL); 172 + if (!mb->mbox.chans) 173 + return -ENOMEM; 174 + 175 + /* con_priv of each channel is used to store the message received */ 176 + for (int i = 0; i < dev_data->num_chans; i++) { 177 + mb->mbox.chans[i].con_priv = devm_kcalloc(dev, dev_data->msg_size, 178 + sizeof(u8), GFP_KERNEL); 179 + if (!mb->mbox.chans[i].con_priv) 180 + return -ENOMEM; 181 + } 182 + 183 + platform_set_drvdata(pdev, mb); 184 + 185 + mb->tx_regs = devm_platform_ioremap_resource_byname(pdev, "tx"); 186 + if (IS_ERR(mb->tx_regs)) 187 + return PTR_ERR(mb->tx_regs); 188 + 189 + mb->rx_regs = devm_platform_ioremap_resource_byname(pdev, "rx"); 190 + if (IS_ERR(mb->rx_regs)) 191 + return PTR_ERR(mb->rx_regs); 192 + 193 + mb->msg_size = dev_data->msg_size; 194 + mb->mbox.dev = dev; 195 + mb->mbox.num_chans = dev_data->num_chans; 196 + mb->mbox.ops = &ast2700_mbox_chan_ops; 197 + mb->mbox.txdone_irq = false; 198 + mb->mbox.txdone_poll = true; 199 + mb->mbox.txpoll_period = 5; 200 + spin_lock_init(&mb->lock); 201 + 202 + irq = platform_get_irq(pdev, 0); 203 + if (irq < 0) 204 + return irq; 205 + 206 + ret = devm_request_irq(dev, irq, ast2700_mbox_irq, 0, dev_name(dev), mb); 207 + if (ret) 208 + return ret; 209 + 210 + return devm_mbox_controller_register(dev, &mb->mbox); 211 + } 212 + 213 + static const struct ast2700_mbox_data ast2700_dev_data = { 214 + .num_chans = 4, 215 + .msg_size = 0x20, 216 + }; 217 + 218 + static const struct of_device_id ast2700_mbox_of_match[] = { 219 + { .compatible = "aspeed,ast2700-mailbox", .data = &ast2700_dev_data }, 220 + {} 221 + }; 222 + MODULE_DEVICE_TABLE(of, ast2700_mbox_of_match); 223 + 224 + static struct platform_driver ast2700_mbox_driver = { 225 + .driver = { 226 + .name = "ast2700-mailbox", 227 + .of_match_table = ast2700_mbox_of_match, 228 + }, 229 + .probe = ast2700_mbox_probe, 230 + }; 231 + module_platform_driver(ast2700_mbox_driver); 232 + 233 + MODULE_AUTHOR("Jammy Huang <jammy_huang@aspeedtech.com>"); 234 + MODULE_DESCRIPTION("ASPEED AST2700 IPC driver"); 235 + MODULE_LICENSE("GPL");
+656
drivers/mailbox/bcm74110-mailbox.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Broadcom BCM74110 Mailbox Driver 4 + * 5 + * Copyright (c) 2025 Broadcom 6 + */ 7 + #include <linux/list.h> 8 + #include <linux/types.h> 9 + #include <linux/workqueue.h> 10 + #include <linux/io-64-nonatomic-hi-lo.h> 11 + #include <linux/interrupt.h> 12 + #include <linux/module.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/of.h> 15 + #include <linux/delay.h> 16 + #include <linux/mailbox_controller.h> 17 + #include <linux/bitfield.h> 18 + #include <linux/slab.h> 19 + 20 + #define BCM_MBOX_BASE(sel) ((sel) * 0x40) 21 + #define BCM_MBOX_IRQ_BASE(sel) (((sel) * 0x20) + 0x800) 22 + 23 + #define BCM_MBOX_CFGA 0x0 24 + #define BCM_MBOX_CFGB 0x4 25 + #define BCM_MBOX_CFGC 0x8 26 + #define BCM_MBOX_CFGD 0xc 27 + #define BCM_MBOX_CTRL 0x10 28 + #define BCM_MBOX_CTRL_EN BIT(0) 29 + #define BCM_MBOX_CTRL_CLR BIT(1) 30 + #define BCM_MBOX_STATUS0 0x14 31 + #define BCM_MBOX_STATUS0_NOT_EMPTY BIT(28) 32 + #define BCM_MBOX_STATUS0_FULL BIT(29) 33 + #define BCM_MBOX_STATUS1 0x18 34 + #define BCM_MBOX_STATUS2 0x1c 35 + #define BCM_MBOX_WDATA 0x20 36 + #define BCM_MBOX_RDATA 0x28 37 + 38 + #define BCM_MBOX_IRQ_STATUS 0x0 39 + #define BCM_MBOX_IRQ_SET 0x4 40 + #define BCM_MBOX_IRQ_CLEAR 0x8 41 + #define BCM_MBOX_IRQ_MASK_STATUS 0xc 42 + #define BCM_MBOX_IRQ_MASK_SET 0x10 43 + #define BCM_MBOX_IRQ_MASK_CLEAR 0x14 44 + #define BCM_MBOX_IRQ_TIMEOUT BIT(0) 45 + #define BCM_MBOX_IRQ_NOT_EMPTY BIT(1) 46 + #define BCM_MBOX_IRQ_FULL BIT(2) 47 + #define BCM_MBOX_IRQ_LOW_WM BIT(3) 48 + #define BCM_MBOX_IRQ_HIGH_WM BIT(4) 49 + 50 + #define BCM_LINK_CODE0 0xbe0 51 + #define BCM_LINK_CODE1 0xbe1 52 + #define BCM_LINK_CODE2 0xbe2 53 + 54 + enum { 55 + BCM_MSG_FUNC_LINK_START = 0, 56 + BCM_MSG_FUNC_LINK_STOP, 57 + BCM_MSG_FUNC_SHMEM_TX, 58 + BCM_MSG_FUNC_SHMEM_RX, 59 + BCM_MSG_FUNC_SHMEM_STOP, 60 + BCM_MSG_FUNC_MAX, 61 + }; 62 + 63 + enum { 64 + BCM_MSG_SVC_INIT = 0, 65 + BCM_MSG_SVC_PMC, 66 + BCM_MSG_SVC_SCMI, 67 + BCM_MSG_SVC_DPFE, 68 + BCM_MSG_SVC_MAX, 69 + }; 70 + 71 + struct bcm74110_mbox_msg { 72 + struct list_head list_entry; 73 + #define BCM_MSG_VERSION_MASK GENMASK(31, 29) 74 + #define BCM_MSG_VERSION 0x1 75 + #define BCM_MSG_REQ_MASK BIT(28) 76 + #define BCM_MSG_RPLY_MASK BIT(27) 77 + #define BCM_MSG_SVC_MASK GENMASK(26, 24) 78 + #define BCM_MSG_FUNC_MASK GENMASK(23, 16) 79 + #define BCM_MSG_LENGTH_MASK GENMASK(15, 4) 80 + #define BCM_MSG_SLOT_MASK GENMASK(3, 0) 81 + 82 + #define BCM_MSG_SET_FIELD(hdr, field, val) \ 83 + do { \ 84 + hdr &= ~BCM_MSG_##field##_MASK; \ 85 + hdr |= FIELD_PREP(BCM_MSG_##field##_MASK, val); \ 86 + } while (0) 87 + 88 + #define BCM_MSG_GET_FIELD(hdr, field) \ 89 + FIELD_GET(BCM_MSG_##field##_MASK, hdr) 90 + u32 msg; 91 + }; 92 + 93 + struct bcm74110_mbox_chan { 94 + struct bcm74110_mbox *mbox; 95 + bool en; 96 + int slot; 97 + int type; 98 + }; 99 + 100 + struct bcm74110_mbox { 101 + struct platform_device *pdev; 102 + void __iomem *base; 103 + 104 + int tx_chan; 105 + int rx_chan; 106 + int rx_irq; 107 + struct list_head rx_svc_init_list; 108 + spinlock_t rx_svc_list_lock; 109 + 110 + struct mbox_controller controller; 111 + struct bcm74110_mbox_chan *mbox_chan; 112 + }; 113 + 114 + #define BCM74110_OFFSET_IO_WRITEL_MACRO(name, offset_base) \ 115 + static void bcm74110_##name##_writel(struct bcm74110_mbox *mbox,\ 116 + u32 val, u32 off) \ 117 + { \ 118 + writel_relaxed(val, mbox->base + offset_base + off); \ 119 + } 120 + BCM74110_OFFSET_IO_WRITEL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan)); 121 + BCM74110_OFFSET_IO_WRITEL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan)); 122 + 123 + #define BCM74110_OFFSET_IO_READL_MACRO(name, offset_base) \ 124 + static u32 bcm74110_##name##_readl(struct bcm74110_mbox *mbox, \ 125 + u32 off) \ 126 + { \ 127 + return readl_relaxed(mbox->base + offset_base + off); \ 128 + } 129 + BCM74110_OFFSET_IO_READL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan)); 130 + BCM74110_OFFSET_IO_READL_MACRO(rx, BCM_MBOX_BASE(mbox->rx_chan)); 131 + BCM74110_OFFSET_IO_READL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan)); 132 + 133 + static inline struct bcm74110_mbox *bcm74110_mbox_from_cntrl( 134 + struct mbox_controller *cntrl) 135 + { 136 + return container_of(cntrl, struct bcm74110_mbox, controller); 137 + } 138 + 139 + static void bcm74110_rx_push_init_msg(struct bcm74110_mbox *mbox, u32 val) 140 + { 141 + struct bcm74110_mbox_msg *msg; 142 + 143 + msg = kzalloc(sizeof(*msg), GFP_ATOMIC); 144 + if (!msg) 145 + return; 146 + 147 + INIT_LIST_HEAD(&msg->list_entry); 148 + msg->msg = val; 149 + 150 + spin_lock(&mbox->rx_svc_list_lock); 151 + list_add_tail(&msg->list_entry, &mbox->rx_svc_init_list); 152 + spin_unlock(&mbox->rx_svc_list_lock); 153 + } 154 + 155 + static void bcm74110_rx_process_msg(struct bcm74110_mbox *mbox) 156 + { 157 + struct device *dev = &mbox->pdev->dev; 158 + struct bcm74110_mbox_chan *chan_priv; 159 + struct mbox_chan *chan; 160 + u32 msg, status; 161 + int type; 162 + 163 + do { 164 + msg = bcm74110_rx_readl(mbox, BCM_MBOX_RDATA); 165 + status = bcm74110_rx_readl(mbox, BCM_MBOX_STATUS0); 166 + 167 + dev_dbg(dev, "rx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n", 168 + BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY), 169 + BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC), 170 + BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT)); 171 + 172 + type = BCM_MSG_GET_FIELD(msg, SVC); 173 + switch (type) { 174 + case BCM_MSG_SVC_INIT: 175 + bcm74110_rx_push_init_msg(mbox, msg); 176 + break; 177 + case BCM_MSG_SVC_PMC: 178 + case BCM_MSG_SVC_SCMI: 179 + case BCM_MSG_SVC_DPFE: 180 + chan = &mbox->controller.chans[type]; 181 + chan_priv = chan->con_priv; 182 + if (chan_priv->en) 183 + mbox_chan_received_data(chan, NULL); 184 + else 185 + dev_warn(dev, "Channel not enabled\n"); 186 + break; 187 + default: 188 + dev_warn(dev, "Unsupported msg received\n"); 189 + } 190 + } while (status & BCM_MBOX_STATUS0_NOT_EMPTY); 191 + } 192 + 193 + static irqreturn_t bcm74110_mbox_isr(int irq, void *data) 194 + { 195 + struct bcm74110_mbox *mbox = data; 196 + u32 status; 197 + 198 + status = bcm74110_irq_readl(mbox, BCM_MBOX_IRQ_STATUS); 199 + 200 + bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR); 201 + 202 + if (status & BCM_MBOX_IRQ_NOT_EMPTY) 203 + bcm74110_rx_process_msg(mbox); 204 + else 205 + dev_warn(&mbox->pdev->dev, "Spurious interrupt\n"); 206 + 207 + return IRQ_HANDLED; 208 + } 209 + 210 + static void bcm74110_mbox_mask_and_clear(struct bcm74110_mbox *mbox) 211 + { 212 + bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_MASK_SET); 213 + bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR); 214 + } 215 + 216 + static int bcm74110_rx_pop_init_msg(struct bcm74110_mbox *mbox, u32 func_type, 217 + u32 *val) 218 + { 219 + struct bcm74110_mbox_msg *msg, *msg_tmp; 220 + unsigned long flags; 221 + bool found = false; 222 + 223 + spin_lock_irqsave(&mbox->rx_svc_list_lock, flags); 224 + list_for_each_entry_safe(msg, msg_tmp, &mbox->rx_svc_init_list, 225 + list_entry) { 226 + if (BCM_MSG_GET_FIELD(msg->msg, FUNC) == func_type) { 227 + list_del(&msg->list_entry); 228 + found = true; 229 + break; 230 + } 231 + } 232 + spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags); 233 + 234 + if (!found) 235 + return -EINVAL; 236 + 237 + *val = msg->msg; 238 + kfree(msg); 239 + 240 + return 0; 241 + } 242 + 243 + static void bcm74110_rx_flush_msg(struct bcm74110_mbox *mbox) 244 + { 245 + struct bcm74110_mbox_msg *msg, *msg_tmp; 246 + LIST_HEAD(list_temp); 247 + unsigned long flags; 248 + 249 + spin_lock_irqsave(&mbox->rx_svc_list_lock, flags); 250 + list_splice_init(&mbox->rx_svc_init_list, &list_temp); 251 + spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags); 252 + 253 + list_for_each_entry_safe(msg, msg_tmp, &list_temp, list_entry) { 254 + list_del(&msg->list_entry); 255 + kfree(msg); 256 + } 257 + } 258 + 259 + #define BCM_DEQUEUE_TIMEOUT_MS 30 260 + static int bcm74110_rx_pop_init_msg_block(struct bcm74110_mbox *mbox, u32 func_type, 261 + u32 *val) 262 + { 263 + int ret, timeout = 0; 264 + 265 + do { 266 + ret = bcm74110_rx_pop_init_msg(mbox, func_type, val); 267 + 268 + if (!ret) 269 + return 0; 270 + 271 + /* TODO: Figure out what is a good sleep here. */ 272 + usleep_range(1000, 2000); 273 + timeout++; 274 + } while (timeout < BCM_DEQUEUE_TIMEOUT_MS); 275 + 276 + dev_warn(&mbox->pdev->dev, "Timeout waiting for service init response\n"); 277 + return -ETIMEDOUT; 278 + } 279 + 280 + static int bcm74110_mbox_create_msg(int req, int rply, int svc, int func, 281 + int length, int slot) 282 + { 283 + u32 msg = 0; 284 + 285 + BCM_MSG_SET_FIELD(msg, REQ, req); 286 + BCM_MSG_SET_FIELD(msg, RPLY, rply); 287 + BCM_MSG_SET_FIELD(msg, SVC, svc); 288 + BCM_MSG_SET_FIELD(msg, FUNC, func); 289 + BCM_MSG_SET_FIELD(msg, LENGTH, length); 290 + BCM_MSG_SET_FIELD(msg, SLOT, slot); 291 + 292 + return msg; 293 + } 294 + 295 + static int bcm74110_mbox_tx_msg(struct bcm74110_mbox *mbox, u32 msg) 296 + { 297 + int val; 298 + 299 + /* We can potentially poll with timeout here instead */ 300 + val = bcm74110_tx_readl(mbox, BCM_MBOX_STATUS0); 301 + if (val & BCM_MBOX_STATUS0_FULL) { 302 + dev_err(&mbox->pdev->dev, "Mailbox full\n"); 303 + return -EINVAL; 304 + } 305 + 306 + dev_dbg(&mbox->pdev->dev, "tx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n", 307 + BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY), 308 + BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC), 309 + BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT)); 310 + 311 + bcm74110_tx_writel(mbox, msg, BCM_MBOX_WDATA); 312 + 313 + return 0; 314 + } 315 + 316 + #define BCM_MBOX_LINK_TRAINING_RETRIES 5 317 + static int bcm74110_mbox_link_training(struct bcm74110_mbox *mbox) 318 + { 319 + int ret, retries = 0; 320 + u32 msg = 0, orig_len = 0, len = BCM_LINK_CODE0; 321 + 322 + do { 323 + switch (len) { 324 + case 0: 325 + retries++; 326 + dev_warn(&mbox->pdev->dev, 327 + "Link train failed, trying again... %d\n", 328 + retries); 329 + if (retries > BCM_MBOX_LINK_TRAINING_RETRIES) 330 + return -EINVAL; 331 + len = BCM_LINK_CODE0; 332 + fallthrough; 333 + case BCM_LINK_CODE0: 334 + case BCM_LINK_CODE1: 335 + case BCM_LINK_CODE2: 336 + msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 337 + BCM_MSG_FUNC_LINK_START, 338 + len, BCM_MSG_SVC_INIT); 339 + break; 340 + default: 341 + break; 342 + } 343 + 344 + bcm74110_mbox_tx_msg(mbox, msg); 345 + 346 + /* No response expected for LINK_CODE2 */ 347 + if (len == BCM_LINK_CODE2) 348 + return 0; 349 + 350 + orig_len = len; 351 + 352 + ret = bcm74110_rx_pop_init_msg_block(mbox, 353 + BCM_MSG_GET_FIELD(msg, FUNC), 354 + &msg); 355 + if (ret) { 356 + len = 0; 357 + continue; 358 + } 359 + 360 + if ((BCM_MSG_GET_FIELD(msg, SVC) != BCM_MSG_SVC_INIT) || 361 + (BCM_MSG_GET_FIELD(msg, FUNC) != BCM_MSG_FUNC_LINK_START) || 362 + (BCM_MSG_GET_FIELD(msg, SLOT) != 0) || 363 + (BCM_MSG_GET_FIELD(msg, RPLY) != 1) || 364 + (BCM_MSG_GET_FIELD(msg, REQ) != 0)) { 365 + len = 0; 366 + continue; 367 + } 368 + 369 + len = BCM_MSG_GET_FIELD(msg, LENGTH); 370 + 371 + /* Make sure sequence is good */ 372 + if (len != (orig_len + 1)) { 373 + len = 0; 374 + continue; 375 + } 376 + } while (1); 377 + 378 + return -EINVAL; 379 + } 380 + 381 + static int bcm74110_mbox_tx_msg_and_wait_ack(struct bcm74110_mbox *mbox, u32 msg) 382 + { 383 + int ret; 384 + u32 recv_msg; 385 + 386 + ret = bcm74110_mbox_tx_msg(mbox, msg); 387 + if (ret) 388 + return ret; 389 + 390 + ret = bcm74110_rx_pop_init_msg_block(mbox, BCM_MSG_GET_FIELD(msg, FUNC), 391 + &recv_msg); 392 + if (ret) 393 + return ret; 394 + 395 + /* 396 + * Modify tx message to verify rx ack. 397 + * Flip RPLY/REQ for synchronous messages 398 + */ 399 + if (BCM_MSG_GET_FIELD(msg, REQ) == 1) { 400 + BCM_MSG_SET_FIELD(msg, RPLY, 1); 401 + BCM_MSG_SET_FIELD(msg, REQ, 0); 402 + } 403 + 404 + if (msg != recv_msg) { 405 + dev_err(&mbox->pdev->dev, "Found ack, but ack is invalid\n"); 406 + return -EINVAL; 407 + } 408 + 409 + return 0; 410 + } 411 + 412 + /* Each index points to 0x100 of HAB MEM. IDX size counts from 0 */ 413 + #define BCM_MBOX_HAB_MEM_IDX_START 0x30 414 + #define BCM_MBOX_HAB_MEM_IDX_SIZE 0x0 415 + static int bcm74110_mbox_shmem_init(struct bcm74110_mbox *mbox) 416 + { 417 + u32 msg = 0; 418 + int ret; 419 + 420 + msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 421 + BCM_MSG_FUNC_SHMEM_STOP, 422 + 0, BCM_MSG_SVC_INIT); 423 + ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 424 + if (ret) 425 + return -EINVAL; 426 + 427 + msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 428 + BCM_MSG_FUNC_SHMEM_TX, 429 + BCM_MBOX_HAB_MEM_IDX_START, 430 + BCM_MBOX_HAB_MEM_IDX_SIZE); 431 + ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 432 + if (ret) 433 + return -EINVAL; 434 + 435 + msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 436 + BCM_MSG_FUNC_SHMEM_RX, 437 + BCM_MBOX_HAB_MEM_IDX_START, 438 + BCM_MBOX_HAB_MEM_IDX_SIZE); 439 + ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 440 + if (ret) 441 + return -EINVAL; 442 + 443 + return 0; 444 + } 445 + 446 + static int bcm74110_mbox_init(struct bcm74110_mbox *mbox) 447 + { 448 + int ret = 0; 449 + 450 + /* Disable queues tx/rx */ 451 + bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL); 452 + 453 + /* Clear status & restart tx/rx*/ 454 + bcm74110_tx_writel(mbox, BCM_MBOX_CTRL_EN | BCM_MBOX_CTRL_CLR, 455 + BCM_MBOX_CTRL); 456 + 457 + /* Unmask irq */ 458 + bcm74110_irq_writel(mbox, BCM_MBOX_IRQ_NOT_EMPTY, BCM_MBOX_IRQ_MASK_CLEAR); 459 + 460 + ret = bcm74110_mbox_link_training(mbox); 461 + if (ret) { 462 + dev_err(&mbox->pdev->dev, "Training failed\n"); 463 + return ret; 464 + } 465 + 466 + return bcm74110_mbox_shmem_init(mbox); 467 + } 468 + 469 + static int bcm74110_mbox_send_data(struct mbox_chan *chan, void *data) 470 + { 471 + struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 472 + u32 msg; 473 + 474 + switch (chan_priv->type) { 475 + case BCM_MSG_SVC_PMC: 476 + case BCM_MSG_SVC_SCMI: 477 + case BCM_MSG_SVC_DPFE: 478 + msg = bcm74110_mbox_create_msg(1, 0, chan_priv->type, 0, 479 + 128 + 28, chan_priv->slot); 480 + break; 481 + default: 482 + return -EINVAL; 483 + } 484 + 485 + return bcm74110_mbox_tx_msg(chan_priv->mbox, msg); 486 + } 487 + 488 + static int bcm74110_mbox_chan_startup(struct mbox_chan *chan) 489 + { 490 + struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 491 + 492 + chan_priv->en = true; 493 + 494 + return 0; 495 + } 496 + 497 + static void bcm74110_mbox_chan_shutdown(struct mbox_chan *chan) 498 + { 499 + struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 500 + 501 + chan_priv->en = false; 502 + } 503 + 504 + static const struct mbox_chan_ops bcm74110_mbox_chan_ops = { 505 + .send_data = bcm74110_mbox_send_data, 506 + .startup = bcm74110_mbox_chan_startup, 507 + .shutdown = bcm74110_mbox_chan_shutdown, 508 + }; 509 + 510 + static void bcm74110_mbox_shutdown(struct platform_device *pdev) 511 + { 512 + struct bcm74110_mbox *mbox = dev_get_drvdata(&pdev->dev); 513 + u32 msg; 514 + 515 + msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 516 + BCM_MSG_FUNC_LINK_STOP, 517 + 0, 0); 518 + 519 + bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 520 + 521 + /* Even if we don't receive ACK, lets shut it down */ 522 + 523 + bcm74110_mbox_mask_and_clear(mbox); 524 + 525 + /* Disable queues tx/rx */ 526 + bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL); 527 + 528 + /* Flush queues */ 529 + bcm74110_rx_flush_msg(mbox); 530 + } 531 + 532 + static struct mbox_chan *bcm74110_mbox_of_xlate(struct mbox_controller *cntrl, 533 + const struct of_phandle_args *p) 534 + { 535 + struct bcm74110_mbox *mbox = bcm74110_mbox_from_cntrl(cntrl); 536 + struct device *dev = &mbox->pdev->dev; 537 + struct bcm74110_mbox_chan *chan_priv; 538 + int slot, type; 539 + 540 + if (p->args_count != 2) { 541 + dev_err(dev, "Invalid arguments\n"); 542 + return ERR_PTR(-EINVAL); 543 + } 544 + 545 + type = p->args[0]; 546 + slot = p->args[1]; 547 + 548 + switch (type) { 549 + case BCM_MSG_SVC_PMC: 550 + case BCM_MSG_SVC_SCMI: 551 + case BCM_MSG_SVC_DPFE: 552 + if (slot > BCM_MBOX_HAB_MEM_IDX_SIZE) { 553 + dev_err(dev, "Not enough shared memory\n"); 554 + return ERR_PTR(-EINVAL); 555 + } 556 + chan_priv = cntrl->chans[type].con_priv; 557 + chan_priv->slot = slot; 558 + chan_priv->type = type; 559 + break; 560 + default: 561 + dev_err(dev, "Invalid channel type: %d\n", type); 562 + return ERR_PTR(-EINVAL); 563 + } 564 + 565 + return &cntrl->chans[type]; 566 + } 567 + 568 + static int bcm74110_mbox_probe(struct platform_device *pdev) 569 + { 570 + struct device *dev = &pdev->dev; 571 + struct bcm74110_mbox *mbox; 572 + int i, ret; 573 + 574 + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); 575 + if (!mbox) 576 + return -ENOMEM; 577 + 578 + mbox->pdev = pdev; 579 + platform_set_drvdata(pdev, mbox); 580 + 581 + mbox->base = devm_platform_ioremap_resource(pdev, 0); 582 + if (IS_ERR(mbox->base)) 583 + return dev_err_probe(dev, PTR_ERR(mbox->base), "Failed to iomap\n"); 584 + 585 + ret = of_property_read_u32(dev->of_node, "brcm,tx", &mbox->tx_chan); 586 + if (ret) 587 + return dev_err_probe(dev, ret, "Failed to find tx channel\n"); 588 + 589 + ret = of_property_read_u32(dev->of_node, "brcm,rx", &mbox->rx_chan); 590 + if (ret) 591 + return dev_err_probe(dev, ret, "Failed to find rx channel\n"); 592 + 593 + mbox->rx_irq = platform_get_irq(pdev, 0); 594 + if (mbox->rx_irq < 0) 595 + return mbox->rx_irq; 596 + 597 + INIT_LIST_HEAD(&mbox->rx_svc_init_list); 598 + spin_lock_init(&mbox->rx_svc_list_lock); 599 + bcm74110_mbox_mask_and_clear(mbox); 600 + 601 + ret = devm_request_irq(dev, mbox->rx_irq, bcm74110_mbox_isr, 602 + IRQF_NO_SUSPEND, pdev->name, mbox); 603 + if (ret) 604 + return dev_err_probe(dev, ret, "Failed to request irq\n"); 605 + 606 + mbox->controller.ops = &bcm74110_mbox_chan_ops; 607 + mbox->controller.dev = dev; 608 + mbox->controller.num_chans = BCM_MSG_SVC_MAX; 609 + mbox->controller.of_xlate = &bcm74110_mbox_of_xlate; 610 + mbox->controller.chans = devm_kcalloc(dev, BCM_MSG_SVC_MAX, 611 + sizeof(*mbox->controller.chans), 612 + GFP_KERNEL); 613 + if (!mbox->controller.chans) 614 + return -ENOMEM; 615 + 616 + mbox->mbox_chan = devm_kcalloc(dev, BCM_MSG_SVC_MAX, 617 + sizeof(*mbox->mbox_chan), 618 + GFP_KERNEL); 619 + if (!mbox->mbox_chan) 620 + return -ENOMEM; 621 + 622 + for (i = 0; i < BCM_MSG_SVC_MAX; i++) { 623 + mbox->mbox_chan[i].mbox = mbox; 624 + mbox->controller.chans[i].con_priv = &mbox->mbox_chan[i]; 625 + } 626 + 627 + ret = devm_mbox_controller_register(dev, &mbox->controller); 628 + if (ret) 629 + return ret; 630 + 631 + ret = bcm74110_mbox_init(mbox); 632 + if (ret) 633 + return ret; 634 + 635 + return 0; 636 + } 637 + 638 + static const struct of_device_id bcm74110_mbox_of_match[] = { 639 + { .compatible = "brcm,bcm74110-mbox", }, 640 + { /* sentinel */ }, 641 + }; 642 + MODULE_DEVICE_TABLE(of, bcm74110_mbox_of_match); 643 + 644 + static struct platform_driver bcm74110_mbox_driver = { 645 + .driver = { 646 + .name = "bcm74110-mbox", 647 + .of_match_table = bcm74110_mbox_of_match, 648 + }, 649 + .probe = bcm74110_mbox_probe, 650 + .shutdown = bcm74110_mbox_shutdown, 651 + }; 652 + module_platform_driver(bcm74110_mbox_driver); 653 + 654 + MODULE_AUTHOR("Justin Chen <justin.chen@broadcom.com>"); 655 + MODULE_DESCRIPTION("BCM74110 mailbox driver"); 656 + MODULE_LICENSE("GPL");
+5 -5
drivers/mailbox/mtk-cmdq-mailbox.c
··· 390 390 391 391 task = kzalloc(sizeof(*task), GFP_ATOMIC); 392 392 if (!task) { 393 - __pm_runtime_put_autosuspend(cmdq->mbox.dev); 393 + pm_runtime_put_autosuspend(cmdq->mbox.dev); 394 394 return -ENOMEM; 395 395 } 396 396 ··· 440 440 list_move_tail(&task->list_entry, &thread->task_busy_list); 441 441 442 442 pm_runtime_mark_last_busy(cmdq->mbox.dev); 443 - __pm_runtime_put_autosuspend(cmdq->mbox.dev); 443 + pm_runtime_put_autosuspend(cmdq->mbox.dev); 444 444 445 445 return 0; 446 446 } ··· 488 488 spin_unlock_irqrestore(&thread->chan->lock, flags); 489 489 490 490 pm_runtime_mark_last_busy(cmdq->mbox.dev); 491 - __pm_runtime_put_autosuspend(cmdq->mbox.dev); 491 + pm_runtime_put_autosuspend(cmdq->mbox.dev); 492 492 } 493 493 494 494 static int cmdq_mbox_flush(struct mbox_chan *chan, unsigned long timeout) ··· 528 528 out: 529 529 spin_unlock_irqrestore(&thread->chan->lock, flags); 530 530 pm_runtime_mark_last_busy(cmdq->mbox.dev); 531 - __pm_runtime_put_autosuspend(cmdq->mbox.dev); 531 + pm_runtime_put_autosuspend(cmdq->mbox.dev); 532 532 533 533 return 0; 534 534 ··· 543 543 return -EFAULT; 544 544 } 545 545 pm_runtime_mark_last_busy(cmdq->mbox.dev); 546 - __pm_runtime_put_autosuspend(cmdq->mbox.dev); 546 + pm_runtime_put_autosuspend(cmdq->mbox.dev); 547 547 return 0; 548 548 } 549 549
+98 -4
drivers/mailbox/pcc.c
··· 306 306 pcc_chan_reg_read_modify_write(&pchan->db); 307 307 } 308 308 309 + static void *write_response(struct pcc_chan_info *pchan) 310 + { 311 + struct pcc_header pcc_header; 312 + void *buffer; 313 + int data_len; 314 + 315 + memcpy_fromio(&pcc_header, pchan->chan.shmem, 316 + sizeof(pcc_header)); 317 + data_len = pcc_header.length - sizeof(u32) + sizeof(struct pcc_header); 318 + 319 + buffer = pchan->chan.rx_alloc(pchan->chan.mchan->cl, data_len); 320 + if (buffer != NULL) 321 + memcpy_fromio(buffer, pchan->chan.shmem, data_len); 322 + return buffer; 323 + } 324 + 309 325 /** 310 326 * pcc_mbox_irq - PCC mailbox interrupt handler 311 327 * @irq: interrupt number ··· 333 317 { 334 318 struct pcc_chan_info *pchan; 335 319 struct mbox_chan *chan = p; 320 + struct pcc_header *pcc_header = chan->active_req; 321 + void *handle = NULL; 336 322 337 323 pchan = chan->con_priv; 338 324 ··· 358 340 * required to avoid any possible race in updatation of this flag. 359 341 */ 360 342 pchan->chan_in_use = false; 361 - mbox_chan_received_data(chan, NULL); 343 + 344 + if (pchan->chan.rx_alloc) 345 + handle = write_response(pchan); 346 + 347 + if (chan->active_req) { 348 + pcc_header = chan->active_req; 349 + if (pcc_header->flags & PCC_CMD_COMPLETION_NOTIFY) 350 + mbox_chan_txdone(chan, 0); 351 + } 352 + 353 + mbox_chan_received_data(chan, handle); 362 354 363 355 pcc_chan_acknowledge(pchan); 364 356 ··· 412 384 pcc_mchan = &pchan->chan; 413 385 pcc_mchan->shmem = acpi_os_ioremap(pcc_mchan->shmem_base_addr, 414 386 pcc_mchan->shmem_size); 415 - if (pcc_mchan->shmem) 416 - return pcc_mchan; 387 + if (!pcc_mchan->shmem) 388 + goto err; 417 389 390 + pcc_mchan->manage_writes = false; 391 + 392 + /* This indicates that the channel is ready to accept messages. 393 + * This needs to happen after the channel has registered 394 + * its callback. There is no access point to do that in 395 + * the mailbox API. That implies that the mailbox client must 396 + * have set the allocate callback function prior to 397 + * sending any messages. 398 + */ 399 + if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE) 400 + pcc_chan_reg_read_modify_write(&pchan->cmd_update); 401 + 402 + return pcc_mchan; 403 + 404 + err: 418 405 mbox_free_channel(chan); 419 406 return ERR_PTR(-ENXIO); 420 407 } ··· 460 417 } 461 418 EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); 462 419 420 + static int pcc_write_to_buffer(struct mbox_chan *chan, void *data) 421 + { 422 + struct pcc_chan_info *pchan = chan->con_priv; 423 + struct pcc_mbox_chan *pcc_mbox_chan = &pchan->chan; 424 + struct pcc_header *pcc_header = data; 425 + 426 + if (!pchan->chan.manage_writes) 427 + return 0; 428 + 429 + /* The PCC header length includes the command field 430 + * but not the other values from the header. 431 + */ 432 + int len = pcc_header->length - sizeof(u32) + sizeof(struct pcc_header); 433 + u64 val; 434 + 435 + pcc_chan_reg_read(&pchan->cmd_complete, &val); 436 + if (!val) { 437 + pr_info("%s pchan->cmd_complete not set", __func__); 438 + return -1; 439 + } 440 + memcpy_toio(pcc_mbox_chan->shmem, data, len); 441 + return 0; 442 + } 443 + 444 + 463 445 /** 464 - * pcc_send_data - Called from Mailbox Controller code. Used 446 + * pcc_send_data - Called from Mailbox Controller code. If 447 + * pchan->chan.rx_alloc is set, then the command complete 448 + * flag is checked and the data is written to the shared 449 + * buffer io memory. 450 + * 451 + * If pchan->chan.rx_alloc is not set, then it is used 465 452 * here only to ring the channel doorbell. The PCC client 466 453 * specific read/write is done in the client driver in 467 454 * order to maintain atomicity over PCC channel once ··· 507 434 int ret; 508 435 struct pcc_chan_info *pchan = chan->con_priv; 509 436 437 + ret = pcc_write_to_buffer(chan, data); 438 + if (ret) 439 + return ret; 440 + 510 441 ret = pcc_chan_reg_read_modify_write(&pchan->cmd_update); 511 442 if (ret) 512 443 return ret; 513 444 514 445 ret = pcc_chan_reg_read_modify_write(&pchan->db); 446 + 515 447 if (!ret && pchan->plat_irq > 0) 516 448 pchan->chan_in_use = true; 517 449 518 450 return ret; 519 451 } 452 + 453 + 454 + static bool pcc_last_tx_done(struct mbox_chan *chan) 455 + { 456 + struct pcc_chan_info *pchan = chan->con_priv; 457 + u64 val; 458 + 459 + pcc_chan_reg_read(&pchan->cmd_complete, &val); 460 + if (!val) 461 + return false; 462 + else 463 + return true; 464 + } 465 + 466 + 520 467 521 468 /** 522 469 * pcc_startup - Called from Mailbox Controller code. Used here ··· 583 490 .send_data = pcc_send_data, 584 491 .startup = pcc_startup, 585 492 .shutdown = pcc_shutdown, 493 + .last_tx_done = pcc_last_tx_done, 586 494 }; 587 495 588 496 /**
+1 -2
drivers/mailbox/qcom-ipcc.c
··· 312 312 if (!name) 313 313 return -ENOMEM; 314 314 315 - ipcc->irq_domain = irq_domain_create_tree(of_fwnode_handle(pdev->dev.of_node), 316 - &qcom_ipcc_irq_ops, ipcc); 315 + ipcc->irq_domain = irq_domain_create_tree(dev_fwnode(&pdev->dev), &qcom_ipcc_irq_ops, ipcc); 317 316 if (!ipcc->irq_domain) 318 317 return -ENOMEM; 319 318
+29
include/acpi/pcc.h
··· 17 17 u32 latency; 18 18 u32 max_access_rate; 19 19 u16 min_turnaround_time; 20 + 21 + /* Set to true to indicate that the mailbox should manage 22 + * writing the dat to the shared buffer. This differs from 23 + * the case where the drivesr are writing to the buffer and 24 + * using send_data only to ring the doorbell. If this flag 25 + * is set, then the void * data parameter of send_data must 26 + * point to a kernel-memory buffer formatted in accordance with 27 + * the PCC specification. 28 + * 29 + * The active buffer management will include reading the 30 + * notify_on_completion flag, and will then 31 + * call mbox_chan_txdone when the acknowledgment interrupt is 32 + * received. 33 + */ 34 + bool manage_writes; 35 + 36 + /* Optional callback that allows the driver 37 + * to allocate the memory used for receiving 38 + * messages. The return value is the location 39 + * inside the buffer where the mailbox should write the data. 40 + */ 41 + void *(*rx_alloc)(struct mbox_client *cl, int size); 42 + }; 43 + 44 + struct pcc_header { 45 + u32 signature; 46 + u32 flags; 47 + u32 length; 48 + u32 command; 20 49 }; 21 50 22 51 /* Generic Communications Channel Shared Memory Region */