samsung: add gs101-mbox driver

microchip: add sbi-ipc driver
 zynqmp: fix invalid __percpu annotation
 qcom: add IPQ5424 APCS compatible
 mpfs fix copy and paste bug
 th1520: Fix NULL vs IS_ERR() and a memory corruption bug
 tegra-hsp: clear mailbox before using message
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE6EwehDt/SOnwFyTyf9lkf8eYP5UFAmeTDB4ACgkQf9lkf8eY
 P5UfghAAgusrJTWyRCeBC/pg6dwkacvl9zAOi+eIVWb6IJ/sZcEyHvWjo9jsqAw2
 mX2tqvcltUnh6ID1GdFz6bH1OYTQfcXhs0LO/ESkGCkVsrAh2uuBFVvxLwqahY/w
 64bEc+S/xVLpAp9/XA9HXgtyAoCueDLB0dreLB4umG9ps8j9n1g46GVAauf2ftmV
 5BN0ybnPd4nYzQ4ergD59jWMI1QOrb/F2JpK9M8RLCBt135xqZ1rz5cCiYZcN4LY
 gxX6HlbkVSaZ45ZEe7++iGK8c8M+uWsxOhjE0PXT1SeeNPyXz09Spsny75r1uUZo
 04t9kQ2yzcrBkHd7AInNBpiKQgFSvuhhjZDI0gQZLcD0aDsNvTTR86PoOuH7pP8B
 qGg+wCU1Puy1Olv9nelc+M5nSLMSBHcXvSDVN8kOCGOXGPgoPLEOD6K8L0s9WPRZ
 VnzQiddMcBuYhFXbGNv73EblrjGyMwJPeUl06yDnMBoaZXvqHirvYwANzJgvhZn2
 piq5bhX2eUrN0UIP+VInU2RAWFsN+ORAoL5TH/JTkA5S7+yzOCK0R/wkUsMcmvco
 gBmNtKOvuHEDl4M50TC9baL/3p07VM/hxXf9j6zGeQVpfITAQGh++/vMNug95ITM
 FlgWWWJziNb9v3F1XU2bLESqrTSjwNC5MXPDV+CraVorwzyeao0=
 =XBAn
 -----END PGP SIGNATURE-----

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

Pull mailbox updates from Jassi Brar:

 - samsung: add gs101-mbox driver

 - microchip: add sbi-ipc driver

 - zynqmp: fix invalid __percpu annotation

 - qcom: add IPQ5424 APCS compatible

 - mpfs fix copy and paste bug

 - th1520: Fix NULL vs IS_ERR() and a memory corruption bug

 - tegra-hsp: clear mailbox before using message

* tag 'mailbox-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/jassibrar/mailbox:
  riscv: export __cpuid_to_hartid_map
  riscv: sbi: vendorid_list: Add Microchip Technology to the vendor list
  mailbox: th1520: Fix memory corruption due to incorrect array size
  mailbox: zynqmp: Remove invalid __percpu annotation in zynqmp_ipi_probe()
  MAINTAINERS: add entry for Samsung Exynos mailbox driver
  mailbox: add Samsung Exynos driver
  dt-bindings: mailbox: add google,gs101-mbox
  mailbox: qcom: Add support for IPQ5424 APCS IPC
  dt-bindings: mailbox: qcom: Add IPQ5424 APCS compatible
  mailbox: qcom-ipcc: Reset CLEAR_ON_RECV_RD if set from boot firmware
  mailbox: add Microchip IPC support
  dt-bindings: mailbox: add binding for Microchip IPC mailbox controller
  mailbox: tegra-hsp: Clear mailbox before using message
  mailbox: mpfs: fix copy and paste bug in probe
  mailbox: th1520: Fix a NULL vs IS_ERR() bug
This commit is contained in:
Linus Torvalds 2025-01-24 16:04:40 -08:00
commit 917846e9f0
18 changed files with 973 additions and 6 deletions

View File

@ -0,0 +1,69 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2024 Linaro Ltd.
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/google,gs101-mbox.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos Mailbox Controller
maintainers:
- Tudor Ambarus <tudor.ambarus@linaro.org>
description:
The Samsung Exynos mailbox controller, used on Google GS101 SoC, has 16 flag
bits for hardware interrupt generation and a shared register for passing
mailbox messages. When the controller is used by the ACPM interface
the shared register is ignored and the mailbox controller acts as a doorbell.
The controller just raises the interrupt to the firmware after the
ACPM interface has written the message to SRAM.
properties:
compatible:
const: google,gs101-mbox
reg:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: pclk
interrupts:
description: IRQ line for the RX mailbox.
maxItems: 1
'#mbox-cells':
const: 0
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
- '#mbox-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/google,gs101.h>
soc {
#address-cells = <1>;
#size-cells = <1>;
ap2apm_mailbox: mailbox@17610000 {
compatible = "google,gs101-mbox";
reg = <0x17610000 0x1000>;
clocks = <&cmu_apm CLK_GOUT_APM_MAILBOX_APM_AP_PCLK>;
clock-names = "pclk";
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH 0>;
#mbox-cells = <0>;
};
};

View File

@ -0,0 +1,123 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mailbox/microchip,sbi-ipc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip Inter-processor communication (IPC) mailbox controller
maintainers:
- Valentina Fernandez <valentina.fernandezalanis@microchip.com>
description:
The Microchip Inter-processor Communication (IPC) facilitates
message passing between processors using an interrupt signaling
mechanism.
properties:
compatible:
oneOf:
- description:
Intended for use by software running in supervisor privileged
mode (s-mode). This SBI interface is compatible with the Mi-V
Inter-hart Communication (IHC) IP.
const: microchip,sbi-ipc
- description:
Intended for use by the SBI implementation in machine mode
(m-mode), this compatible string is for the MIV_IHC Soft-IP.
const: microchip,miv-ihc-rtl-v2
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 5
interrupt-names:
minItems: 1
maxItems: 5
items:
enum:
- hart-0
- hart-1
- hart-2
- hart-3
- hart-4
- hart-5
"#mbox-cells":
description: >
For "microchip,sbi-ipc", the cell represents the global "logical"
channel IDs. The meaning of channel IDs are platform firmware dependent.
For "microchip,miv-ihc-rtl-v2", the cell represents the physical
channel and does not vary based on the platform firmware.
const: 1
microchip,ihc-chan-disabled-mask:
description: >
Represents the enable/disable state of the bi-directional IHC
channels within the MIV-IHC IP configuration.
A bit set to '1' indicates that the corresponding channel is disabled,
and any read or write operations to that channel will return zero.
A bit set to '0' indicates that the corresponding channel is enabled
and will be accessible through its dedicated address range registers.
The actual enable/disable state of each channel is determined by the
IP blocks configuration.
$ref: /schemas/types.yaml#/definitions/uint16
maximum: 0x7fff
default: 0
required:
- compatible
- interrupts
- interrupt-names
- "#mbox-cells"
allOf:
- if:
properties:
compatible:
contains:
const: microchip,sbi-ipc
then:
properties:
reg:
not: {}
description:
The 'microchip,sbi-ipc' operates in a programming model
that does not require memory-mapped I/O (MMIO) registers
since it uses SBI ecalls provided by the m-mode/firmware
SBI implementation to access hardware registers.
microchip,ihc-chan-disabled-mask: false
else:
required:
- reg
- microchip,ihc-chan-disabled-mask
additionalProperties: false
examples:
- |
mailbox {
compatible = "microchip,sbi-ipc";
interrupt-parent = <&plic>;
interrupts = <180>, <179>, <178>;
interrupt-names = "hart-1", "hart-2", "hart-3";
#mbox-cells = <1>;
};
- |
mailbox@50000000 {
compatible = "microchip,miv-ihc-rtl-v2";
microchip,ihc-chan-disabled-mask = /bits/ 16 <0>;
reg = <0x50000000 0x1c000>;
interrupt-parent = <&plic>;
interrupts = <180>, <179>, <178>;
interrupt-names = "hart-1", "hart-2", "hart-3";
#mbox-cells = <1>;
};

View File

@ -20,6 +20,7 @@ properties:
- enum:
- qcom,ipq5018-apcs-apps-global
- qcom,ipq5332-apcs-apps-global
- qcom,ipq5424-apcs-apps-global
- qcom,ipq8074-apcs-apps-global
- qcom,ipq9574-apcs-apps-global
- const: qcom,ipq6018-apcs-apps-global

View File

@ -3064,6 +3064,7 @@ F: drivers/*/*s3c24*
F: drivers/*/*s3c64xx*
F: drivers/*/*s5pv210*
F: drivers/clocksource/samsung_pwm_timer.c
F: drivers/mailbox/exynos-mailbox.c
F: drivers/memory/samsung/
F: drivers/pwm/pwm-samsung.c
F: drivers/soc/samsung/
@ -20826,6 +20827,15 @@ F: arch/arm64/boot/dts/exynos/exynos850*
F: drivers/clk/samsung/clk-exynos850.c
F: include/dt-bindings/clock/exynos850.h
SAMSUNG EXYNOS MAILBOX DRIVER
M: Tudor Ambarus <tudor.ambarus@linaro.org>
L: linux-kernel@vger.kernel.org
L: linux-samsung-soc@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/mailbox/google,gs101-mbox.yaml
F: drivers/mailbox/exynos-mailbox.c
F: include/linux/mailbox/exynos-message.h
SAMSUNG EXYNOS PSEUDO RANDOM NUMBER GENERATOR (RNG) DRIVER
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-crypto@vger.kernel.org

View File

@ -6,6 +6,7 @@
#define ASM_VENDOR_LIST_H
#define ANDES_VENDOR_ID 0x31e
#define MICROCHIP_VENDOR_ID 0x029
#define SIFIVE_VENDOR_ID 0x489
#define THEAD_VENDOR_ID 0x5b7

View File

@ -43,6 +43,7 @@ enum ipi_message_type {
unsigned long __cpuid_to_hartid_map[NR_CPUS] __ro_after_init = {
[0 ... NR_CPUS-1] = INVALID_HARTID
};
EXPORT_SYMBOL_GPL(__cpuid_to_hartid_map);
void __init smp_setup_processor_id(void)
{

View File

@ -36,6 +36,17 @@ config ARM_MHU_V3
that provides different means of transports: supported extensions
will be discovered and possibly managed at probe-time.
config EXYNOS_MBOX
tristate "Exynos Mailbox"
depends on ARCH_EXYNOS || COMPILE_TEST
help
Say Y here if you want to build the Samsung Exynos Mailbox controller
driver. The controller has 16 flag bits for hardware interrupt
generation and a shared register for passing mailbox messages.
When the controller is used by the ACPM interface the shared register
is ignored and the mailbox controller acts as a doorbell that raises
the interrupt to the ACPM firmware.
config IMX_MBOX
tristate "i.MX Mailbox"
depends on ARCH_MXC || COMPILE_TEST
@ -178,6 +189,19 @@ config POLARFIRE_SOC_MAILBOX
If unsure, say N.
config MCHP_SBI_IPC_MBOX
tristate "Microchip Inter-processor Communication (IPC) SBI driver"
depends on RISCV_SBI || COMPILE_TEST
depends on ARCH_MICROCHIP
help
Mailbox implementation for Microchip devices with an
Inter-process communication (IPC) controller.
To compile this driver as a module, choose M here. the
module will be called mailbox-mchp-ipc-sbi.
If unsure, say N.
config QCOM_APCS_IPC
tristate "Qualcomm APCS IPC driver"
depends on ARCH_QCOM || COMPILE_TEST

View File

@ -11,6 +11,8 @@ obj-$(CONFIG_ARM_MHU_V2) += arm_mhuv2.o
obj-$(CONFIG_ARM_MHU_V3) += arm_mhuv3.o
obj-$(CONFIG_EXYNOS_MBOX) += exynos-mailbox.o
obj-$(CONFIG_IMX_MBOX) += imx-mailbox.o
obj-$(CONFIG_ARMADA_37XX_RWTM_MBOX) += armada-37xx-rwtm-mailbox.o
@ -45,6 +47,8 @@ obj-$(CONFIG_BCM_FLEXRM_MBOX) += bcm-flexrm-mailbox.o
obj-$(CONFIG_POLARFIRE_SOC_MAILBOX) += mailbox-mpfs.o
obj-$(CONFIG_MCHP_SBI_IPC_MBOX) += mailbox-mchp-ipc-sbi.o
obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o
obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o

View File

@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2020 Samsung Electronics Co., Ltd.
* Copyright 2020 Google LLC.
* Copyright 2024 Linaro Ltd.
*/
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/exynos-message.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define EXYNOS_MBOX_MCUCTRL 0x0 /* Mailbox Control Register */
#define EXYNOS_MBOX_INTCR0 0x24 /* Interrupt Clear Register 0 */
#define EXYNOS_MBOX_INTMR0 0x28 /* Interrupt Mask Register 0 */
#define EXYNOS_MBOX_INTSR0 0x2c /* Interrupt Status Register 0 */
#define EXYNOS_MBOX_INTMSR0 0x30 /* Interrupt Mask Status Register 0 */
#define EXYNOS_MBOX_INTGR1 0x40 /* Interrupt Generation Register 1 */
#define EXYNOS_MBOX_INTMR1 0x48 /* Interrupt Mask Register 1 */
#define EXYNOS_MBOX_INTSR1 0x4c /* Interrupt Status Register 1 */
#define EXYNOS_MBOX_INTMSR1 0x50 /* Interrupt Mask Status Register 1 */
#define EXYNOS_MBOX_INTMR0_MASK GENMASK(15, 0)
#define EXYNOS_MBOX_INTGR1_MASK GENMASK(15, 0)
#define EXYNOS_MBOX_CHAN_COUNT HWEIGHT32(EXYNOS_MBOX_INTGR1_MASK)
/**
* struct exynos_mbox - driver's private data.
* @regs: mailbox registers base address.
* @mbox: pointer to the mailbox controller.
* @pclk: pointer to the mailbox peripheral clock.
*/
struct exynos_mbox {
void __iomem *regs;
struct mbox_controller *mbox;
struct clk *pclk;
};
static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
{
struct device *dev = chan->mbox->dev;
struct exynos_mbox *exynos_mbox = dev_get_drvdata(dev);
struct exynos_mbox_msg *msg = data;
if (msg->chan_id >= exynos_mbox->mbox->num_chans) {
dev_err(dev, "Invalid channel ID %d\n", msg->chan_id);
return -EINVAL;
}
if (msg->chan_type != EXYNOS_MBOX_CHAN_TYPE_DOORBELL) {
dev_err(dev, "Unsupported channel type [%d]\n", msg->chan_type);
return -EINVAL;
};
writel(BIT(msg->chan_id), exynos_mbox->regs + EXYNOS_MBOX_INTGR1);
return 0;
}
static const struct mbox_chan_ops exynos_mbox_chan_ops = {
.send_data = exynos_mbox_send_data,
};
static struct mbox_chan *exynos_mbox_of_xlate(struct mbox_controller *mbox,
const struct of_phandle_args *sp)
{
int i;
if (sp->args_count != 0)
return ERR_PTR(-EINVAL);
/*
* Return the first available channel. When we don't pass the
* channel ID from device tree, each channel populated by the driver is
* just a software construct or a virtual channel. We use 'void *data'
* in send_data() to pass the channel identifiers.
*/
for (i = 0; i < mbox->num_chans; i++)
if (mbox->chans[i].cl == NULL)
return &mbox->chans[i];
return ERR_PTR(-EINVAL);
}
static const struct of_device_id exynos_mbox_match[] = {
{ .compatible = "google,gs101-mbox" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_mbox_match);
static int exynos_mbox_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos_mbox *exynos_mbox;
struct mbox_controller *mbox;
struct mbox_chan *chans;
int i;
exynos_mbox = devm_kzalloc(dev, sizeof(*exynos_mbox), GFP_KERNEL);
if (!exynos_mbox)
return -ENOMEM;
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
if (!mbox)
return -ENOMEM;
chans = devm_kcalloc(dev, EXYNOS_MBOX_CHAN_COUNT, sizeof(*chans),
GFP_KERNEL);
if (!chans)
return -ENOMEM;
exynos_mbox->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(exynos_mbox->regs))
return PTR_ERR(exynos_mbox->regs);
exynos_mbox->pclk = devm_clk_get_enabled(dev, "pclk");
if (IS_ERR(exynos_mbox->pclk))
return dev_err_probe(dev, PTR_ERR(exynos_mbox->pclk),
"Failed to enable clock.\n");
mbox->num_chans = EXYNOS_MBOX_CHAN_COUNT;
mbox->chans = chans;
mbox->dev = dev;
mbox->ops = &exynos_mbox_chan_ops;
mbox->of_xlate = exynos_mbox_of_xlate;
for (i = 0; i < EXYNOS_MBOX_CHAN_COUNT; i++)
chans[i].mbox = mbox;
exynos_mbox->mbox = mbox;
platform_set_drvdata(pdev, exynos_mbox);
/* Mask out all interrupts. We support just polling channels for now. */
writel(EXYNOS_MBOX_INTMR0_MASK, exynos_mbox->regs + EXYNOS_MBOX_INTMR0);
return devm_mbox_controller_register(dev, mbox);
}
static struct platform_driver exynos_mbox_driver = {
.probe = exynos_mbox_probe,
.driver = {
.name = "exynos-acpm-mbox",
.of_match_table = exynos_mbox_match,
},
};
module_platform_driver(exynos_mbox_driver);
MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>");
MODULE_DESCRIPTION("Samsung Exynos mailbox driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,504 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Microchip Inter-Processor communication (IPC) driver
*
* Copyright (c) 2021 - 2024 Microchip Technology Inc. All rights reserved.
*
* Author: Valentina Fernandez <valentina.fernandezalanis@microchip.com>
*
*/
#include <linux/io.h>
#include <linux/err.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of_device.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/mailbox/mchp-ipc.h>
#include <asm/sbi.h>
#include <asm/vendorid_list.h>
#define IRQ_STATUS_BITS 12
#define NUM_CHANS_PER_CLUSTER 5
#define IPC_DMA_BIT_MASK 32
#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \
MICROCHIP_VENDOR_ID)
enum {
SBI_EXT_IPC_PROBE = 0x100,
SBI_EXT_IPC_CH_INIT,
SBI_EXT_IPC_SEND,
SBI_EXT_IPC_RECEIVE,
SBI_EXT_IPC_STATUS,
};
enum ipc_hw {
MIV_IHC,
};
/**
* struct mchp_ipc_mbox_info - IPC probe message format
*
* @hw_type: IPC implementation available in the hardware
* @num_channels: number of IPC channels available in the hardware
*
* Used to retrieve information on the IPC implementation
* using the SBI_EXT_IPC_PROBE SBI function id.
*/
struct mchp_ipc_mbox_info {
enum ipc_hw hw_type;
u8 num_channels;
};
/**
* struct mchp_ipc_init - IPC channel init message format
*
* @max_msg_size: maxmimum message size in bytes of a given channel
*
* struct used by the SBI_EXT_IPC_CH_INIT SBI function id to get
* the max message size in bytes of the initialized channel.
*/
struct mchp_ipc_init {
u16 max_msg_size;
};
/**
* struct mchp_ipc_status - IPC status message format
*
* @status: interrupt status for all channels associated to a cluster
* @cluster: specifies the cluster instance that originated an irq
*
* struct used by the SBI_EXT_IPC_STATUS SBI function id to get
* the message present and message clear interrupt status for all the
* channels associated to a cluster.
*/
struct mchp_ipc_status {
u32 status;
u8 cluster;
};
/**
* struct mchp_ipc_sbi_msg - IPC SBI payload message
*
* @buf_addr: physical address where the received data should be copied to
* @size: maximum size(in bytes) that can be stored in the buffer pointed to by `buf`
* @irq_type: mask representing the irq types that triggered an irq
*
* struct used by the SBI_EXT_IPC_SEND/SBI_EXT_IPC_RECEIVE SBI function
* ids to send/receive a message from an associated processor using
* the IPC.
*/
struct mchp_ipc_sbi_msg {
u64 buf_addr;
u16 size;
u8 irq_type;
};
struct mchp_ipc_cluster_cfg {
void *buf_base;
phys_addr_t buf_base_addr;
int irq;
};
struct mchp_ipc_sbi_mbox {
struct device *dev;
struct mbox_chan *chans;
struct mchp_ipc_cluster_cfg *cluster_cfg;
void *buf_base;
unsigned long buf_base_addr;
struct mbox_controller controller;
enum ipc_hw hw_type;
};
static int mchp_ipc_sbi_chan_send(u32 command, u32 channel, unsigned long address)
{
struct sbiret ret;
ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, channel,
address, 0, 0, 0, 0);
if (ret.error)
return sbi_err_map_linux_errno(ret.error);
else
return ret.value;
}
static int mchp_ipc_sbi_send(u32 command, unsigned long address)
{
struct sbiret ret;
ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, address,
0, 0, 0, 0, 0);
if (ret.error)
return sbi_err_map_linux_errno(ret.error);
else
return ret.value;
}
static struct mchp_ipc_sbi_mbox *to_mchp_ipc_mbox(struct mbox_controller *mbox)
{
return container_of(mbox, struct mchp_ipc_sbi_mbox, controller);
}
static inline void mchp_ipc_prepare_receive_req(struct mbox_chan *chan)
{
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
struct mchp_ipc_sbi_msg request;
request.buf_addr = chan_info->msg_buf_rx_addr;
request.size = chan_info->max_msg_size;
memcpy(chan_info->buf_base_rx, &request, sizeof(struct mchp_ipc_sbi_msg));
}
static inline void mchp_ipc_process_received_data(struct mbox_chan *chan,
struct mchp_ipc_msg *ipc_msg)
{
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
struct mchp_ipc_sbi_msg sbi_msg;
memcpy(&sbi_msg, chan_info->buf_base_rx, sizeof(struct mchp_ipc_sbi_msg));
ipc_msg->buf = (u32 *)chan_info->msg_buf_rx;
ipc_msg->size = sbi_msg.size;
}
static irqreturn_t mchp_ipc_cluster_aggr_isr(int irq, void *data)
{
struct mbox_chan *chan;
struct mchp_ipc_sbi_chan *chan_info;
struct mchp_ipc_sbi_mbox *ipc = (struct mchp_ipc_sbi_mbox *)data;
struct mchp_ipc_msg ipc_msg;
struct mchp_ipc_status status_msg;
int ret;
unsigned long hartid;
u32 i, chan_index, chan_id;
/* Find out the hart that originated the irq */
for_each_online_cpu(i) {
hartid = cpuid_to_hartid_map(i);
if (irq == ipc->cluster_cfg[hartid].irq)
break;
}
status_msg.cluster = hartid;
memcpy(ipc->cluster_cfg[hartid].buf_base, &status_msg, sizeof(struct mchp_ipc_status));
ret = mchp_ipc_sbi_send(SBI_EXT_IPC_STATUS, ipc->cluster_cfg[hartid].buf_base_addr);
if (ret < 0) {
dev_err_ratelimited(ipc->dev, "could not get IHC irq status ret=%d\n", ret);
return IRQ_HANDLED;
}
memcpy(&status_msg, ipc->cluster_cfg[hartid].buf_base, sizeof(struct mchp_ipc_status));
/*
* Iterate over each bit set in the IHC interrupt status register (IRQ_STATUS) to identify
* the channel(s) that have a message to be processed/acknowledged.
* The bits are organized in alternating format, where each pair of bits represents
* the status of the message present and message clear interrupts for each cluster/hart
* (from hart 0 to hart 5). Each cluster can have up to 5 fixed channels associated.
*/
for_each_set_bit(i, (unsigned long *)&status_msg.status, IRQ_STATUS_BITS) {
/* Find out the destination hart that triggered the interrupt */
chan_index = i / 2;
/*
* The IP has no loopback channels, so we need to decrement the index when
* the target hart has a greater index than our own
*/
if (chan_index >= status_msg.cluster)
chan_index--;
/*
* Calculate the channel id given the hart and channel index. Channel IDs
* are unique across all clusters of an IPC, and iterate contiguously
* across all clusters.
*/
chan_id = status_msg.cluster * (NUM_CHANS_PER_CLUSTER + chan_index);
chan = &ipc->chans[chan_id];
chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
if (i % 2 == 0) {
mchp_ipc_prepare_receive_req(chan);
ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id,
chan_info->buf_base_rx_addr);
if (ret < 0)
continue;
mchp_ipc_process_received_data(chan, &ipc_msg);
mbox_chan_received_data(&ipc->chans[chan_id], (void *)&ipc_msg);
} else {
ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id,
chan_info->buf_base_rx_addr);
mbox_chan_txdone(&ipc->chans[chan_id], ret);
}
}
return IRQ_HANDLED;
}
static int mchp_ipc_send_data(struct mbox_chan *chan, void *data)
{
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
const struct mchp_ipc_msg *msg = data;
struct mchp_ipc_sbi_msg sbi_payload;
memcpy(chan_info->msg_buf_tx, msg->buf, msg->size);
sbi_payload.buf_addr = chan_info->msg_buf_tx_addr;
sbi_payload.size = msg->size;
memcpy(chan_info->buf_base_tx, &sbi_payload, sizeof(sbi_payload));
return mchp_ipc_sbi_chan_send(SBI_EXT_IPC_SEND, chan_info->id, chan_info->buf_base_tx_addr);
}
static int mchp_ipc_startup(struct mbox_chan *chan)
{
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
struct mchp_ipc_sbi_mbox *ipc = to_mchp_ipc_mbox(chan->mbox);
struct mchp_ipc_init ch_init_msg;
int ret;
/*
* The TX base buffer is used to transmit two types of messages:
* - struct mchp_ipc_init to initialize the channel
* - struct mchp_ipc_sbi_msg to transmit user data/payload
* Ensure the TX buffer size is large enough to accommodate either message type.
*/
size_t max_size = max(sizeof(struct mchp_ipc_init), sizeof(struct mchp_ipc_sbi_msg));
chan_info->buf_base_tx = kmalloc(max_size, GFP_KERNEL);
if (!chan_info->buf_base_tx) {
ret = -ENOMEM;
goto fail;
}
chan_info->buf_base_tx_addr = __pa(chan_info->buf_base_tx);
chan_info->buf_base_rx = kmalloc(max_size, GFP_KERNEL);
if (!chan_info->buf_base_rx) {
ret = -ENOMEM;
goto fail_free_buf_base_tx;
}
chan_info->buf_base_rx_addr = __pa(chan_info->buf_base_rx);
ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_CH_INIT, chan_info->id,
chan_info->buf_base_tx_addr);
if (ret < 0) {
dev_err(ipc->dev, "channel %u init failed\n", chan_info->id);
goto fail_free_buf_base_rx;
}
memcpy(&ch_init_msg, chan_info->buf_base_tx, sizeof(struct mchp_ipc_init));
chan_info->max_msg_size = ch_init_msg.max_msg_size;
chan_info->msg_buf_tx = kmalloc(chan_info->max_msg_size, GFP_KERNEL);
if (!chan_info->msg_buf_tx) {
ret = -ENOMEM;
goto fail_free_buf_base_rx;
}
chan_info->msg_buf_tx_addr = __pa(chan_info->msg_buf_tx);
chan_info->msg_buf_rx = kmalloc(chan_info->max_msg_size, GFP_KERNEL);
if (!chan_info->msg_buf_rx) {
ret = -ENOMEM;
goto fail_free_buf_msg_tx;
}
chan_info->msg_buf_rx_addr = __pa(chan_info->msg_buf_rx);
switch (ipc->hw_type) {
case MIV_IHC:
return 0;
default:
goto fail_free_buf_msg_rx;
}
if (ret) {
dev_err(ipc->dev, "failed to register interrupt(s)\n");
goto fail_free_buf_msg_rx;
}
return ret;
fail_free_buf_msg_rx:
kfree(chan_info->msg_buf_rx);
fail_free_buf_msg_tx:
kfree(chan_info->msg_buf_tx);
fail_free_buf_base_rx:
kfree(chan_info->buf_base_rx);
fail_free_buf_base_tx:
kfree(chan_info->buf_base_tx);
fail:
return ret;
}
static void mchp_ipc_shutdown(struct mbox_chan *chan)
{
struct mchp_ipc_sbi_chan *chan_info = (struct mchp_ipc_sbi_chan *)chan->con_priv;
kfree(chan_info->buf_base_tx);
kfree(chan_info->buf_base_rx);
kfree(chan_info->msg_buf_tx);
kfree(chan_info->msg_buf_rx);
}
static const struct mbox_chan_ops mchp_ipc_ops = {
.startup = mchp_ipc_startup,
.send_data = mchp_ipc_send_data,
.shutdown = mchp_ipc_shutdown,
};
static struct mbox_chan *mchp_ipc_mbox_xlate(struct mbox_controller *controller,
const struct of_phandle_args *spec)
{
struct mchp_ipc_sbi_mbox *ipc = to_mchp_ipc_mbox(controller);
unsigned int chan_id = spec->args[0];
if (chan_id >= ipc->controller.num_chans) {
dev_err(ipc->dev, "invalid channel id %d\n", chan_id);
return ERR_PTR(-EINVAL);
}
return &ipc->chans[chan_id];
}
static int mchp_ipc_get_cluster_aggr_irq(struct mchp_ipc_sbi_mbox *ipc)
{
struct platform_device *pdev = to_platform_device(ipc->dev);
char *irq_name;
int cpuid, ret;
unsigned long hartid;
bool irq_found = false;
for_each_online_cpu(cpuid) {
hartid = cpuid_to_hartid_map(cpuid);
irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid);
ret = platform_get_irq_byname_optional(pdev, irq_name);
if (ret <= 0)
continue;
ipc->cluster_cfg[hartid].irq = ret;
ret = devm_request_irq(ipc->dev, ipc->cluster_cfg[hartid].irq,
mchp_ipc_cluster_aggr_isr, IRQF_SHARED,
"miv-ihc-irq", ipc);
if (ret)
return ret;
ipc->cluster_cfg[hartid].buf_base = devm_kmalloc(ipc->dev,
sizeof(struct mchp_ipc_status),
GFP_KERNEL);
if (!ipc->cluster_cfg[hartid].buf_base)
return -ENOMEM;
ipc->cluster_cfg[hartid].buf_base_addr = __pa(ipc->cluster_cfg[hartid].buf_base);
irq_found = true;
}
return irq_found;
}
static int mchp_ipc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mchp_ipc_mbox_info ipc_info;
struct mchp_ipc_sbi_mbox *ipc;
struct mchp_ipc_sbi_chan *priv;
bool irq_avail = false;
int ret;
u32 chan_id;
ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY);
if (ret <= 0)
return dev_err_probe(dev, ret, "Microchip SBI extension not detected\n");
ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL);
if (!ipc)
return -ENOMEM;
platform_set_drvdata(pdev, ipc);
ipc->buf_base = devm_kmalloc(dev, sizeof(struct mchp_ipc_mbox_info), GFP_KERNEL);
if (!ipc->buf_base)
return -ENOMEM;
ipc->buf_base_addr = __pa(ipc->buf_base);
ret = mchp_ipc_sbi_send(SBI_EXT_IPC_PROBE, ipc->buf_base_addr);
if (ret < 0)
return dev_err_probe(dev, ret, "could not probe IPC SBI service\n");
memcpy(&ipc_info, ipc->buf_base, sizeof(struct mchp_ipc_mbox_info));
ipc->controller.num_chans = ipc_info.num_channels;
ipc->hw_type = ipc_info.hw_type;
ipc->chans = devm_kcalloc(dev, ipc->controller.num_chans, sizeof(*ipc->chans), GFP_KERNEL);
if (!ipc->chans)
return -ENOMEM;
ipc->dev = dev;
ipc->controller.txdone_irq = true;
ipc->controller.dev = ipc->dev;
ipc->controller.ops = &mchp_ipc_ops;
ipc->controller.chans = ipc->chans;
ipc->controller.of_xlate = mchp_ipc_mbox_xlate;
for (chan_id = 0; chan_id < ipc->controller.num_chans; chan_id++) {
priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
ipc->chans[chan_id].con_priv = priv;
priv->id = chan_id;
}
if (ipc->hw_type == MIV_IHC) {
ipc->cluster_cfg = devm_kcalloc(dev, num_online_cpus(),
sizeof(struct mchp_ipc_cluster_cfg),
GFP_KERNEL);
if (!ipc->cluster_cfg)
return -ENOMEM;
if (mchp_ipc_get_cluster_aggr_irq(ipc))
irq_avail = true;
}
if (!irq_avail)
return dev_err_probe(dev, -ENODEV, "missing interrupt property\n");
ret = devm_mbox_controller_register(dev, &ipc->controller);
if (ret)
return dev_err_probe(dev, ret,
"Inter-Processor communication (IPC) registration failed\n");
return 0;
}
static const struct of_device_id mchp_ipc_of_match[] = {
{.compatible = "microchip,sbi-ipc", },
{}
};
MODULE_DEVICE_TABLE(of, mchp_ipc_of_match);
static struct platform_driver mchp_ipc_driver = {
.driver = {
.name = "microchip_ipc",
.of_match_table = mchp_ipc_of_match,
},
.probe = mchp_ipc_probe,
};
module_platform_driver(mchp_ipc_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>");
MODULE_DESCRIPTION("Microchip Inter-Processor Communication (IPC) driver");

View File

@ -251,7 +251,7 @@ static inline int mpfs_mbox_syscon_probe(struct mpfs_mbox *mbox, struct platform
return PTR_ERR(mbox->sysreg_scb);
mbox->mbox_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mbox->ctrl_base))
if (IS_ERR(mbox->mbox_base))
return PTR_ERR(mbox->mbox_base);
return 0;

View File

@ -41,7 +41,7 @@
#ifdef CONFIG_PM_SLEEP
/* store MBOX context across system-wide suspend/resume transitions */
struct th1520_mbox_context {
u32 intr_mask[TH_1520_MBOX_CHANS - 1];
u32 intr_mask[TH_1520_MBOX_CHANS];
};
#endif
@ -387,8 +387,10 @@ static void __iomem *th1520_map_mmio(struct platform_device *pdev,
mapped = devm_ioremap(&pdev->dev, res->start + offset,
resource_size(res) - offset);
if (IS_ERR(mapped))
if (!mapped) {
dev_err(&pdev->dev, "Failed to map resource: %s\n", res_name);
return ERR_PTR(-ENOMEM);
}
return mapped;
}

View File

@ -157,6 +157,7 @@ static const struct of_device_id qcom_apcs_ipc_of_match[] = {
{ .compatible = "qcom,sm6125-apcs-hmss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,sm6115-apcs-hmss-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,ipq5332-apcs-apps-global", .data = &ipq6018_apcs_data },
{ .compatible = "qcom,ipq5424-apcs-apps-global", .data = &msm8994_apcs_data },
{ .compatible = "qcom,ipq8074-apcs-apps-global", .data = &ipq6018_apcs_data },
{ .compatible = "qcom,sc7180-apss-shared", .data = &apps_shared_apcs_data },
{ .compatible = "qcom,sc8180x-apss-shared", .data = &apps_shared_apcs_data },

View File

@ -14,6 +14,7 @@
#include <dt-bindings/mailbox/qcom-ipcc.h>
/* IPCC Register offsets */
#define IPCC_REG_CONFIG 0x08
#define IPCC_REG_SEND_ID 0x0c
#define IPCC_REG_RECV_ID 0x10
#define IPCC_REG_RECV_SIGNAL_ENABLE 0x14
@ -21,6 +22,7 @@
#define IPCC_REG_RECV_SIGNAL_CLEAR 0x1c
#define IPCC_REG_CLIENT_CLEAR 0x38
#define IPCC_CLEAR_ON_RECV_RD BIT(0)
#define IPCC_SIGNAL_ID_MASK GENMASK(15, 0)
#define IPCC_CLIENT_ID_MASK GENMASK(31, 16)
@ -274,6 +276,7 @@ static int qcom_ipcc_pm_resume(struct device *dev)
static int qcom_ipcc_probe(struct platform_device *pdev)
{
struct qcom_ipcc *ipcc;
u32 config_value;
static int id;
char *name;
int ret;
@ -288,6 +291,19 @@ static int qcom_ipcc_probe(struct platform_device *pdev)
if (IS_ERR(ipcc->base))
return PTR_ERR(ipcc->base);
/*
* It is possible that boot firmware is using the same IPCC instance
* as of the HLOS and it has kept CLEAR_ON_RECV_RD set which basically
* means Interrupt pending registers are cleared when RECV_ID is read.
* The register automatically updates to the next pending interrupt/client
* status based on priority.
*/
config_value = readl(ipcc->base + IPCC_REG_CONFIG);
if (config_value & IPCC_CLEAR_ON_RECV_RD) {
config_value &= ~(IPCC_CLEAR_ON_RECV_RD);
writel(config_value, ipcc->base + IPCC_REG_CONFIG);
}
ipcc->irq = platform_get_irq(pdev, 0);
if (ipcc->irq < 0)
return ipcc->irq;

View File

@ -388,7 +388,6 @@ static void tegra_hsp_sm_recv32(struct tegra_hsp_channel *channel)
value = tegra_hsp_channel_readl(channel, HSP_SM_SHRD_MBOX);
value &= ~HSP_SM_SHRD_MBOX_FULL;
msg = (void *)(unsigned long)value;
mbox_chan_received_data(channel->chan, msg);
/*
* Need to clear all bits here since some producers, such as TCU, depend
@ -398,6 +397,8 @@ static void tegra_hsp_sm_recv32(struct tegra_hsp_channel *channel)
* explicitly, so we have to make sure we cover all possible cases.
*/
tegra_hsp_channel_writel(channel, 0x0, HSP_SM_SHRD_MBOX);
mbox_chan_received_data(channel->chan, msg);
}
static const struct tegra_hsp_sm_ops tegra_hsp_sm_32bit_ops = {
@ -433,7 +434,6 @@ static void tegra_hsp_sm_recv128(struct tegra_hsp_channel *channel)
value[3] = tegra_hsp_channel_readl(channel, HSP_SHRD_MBOX_TYPE1_DATA3);
msg = (void *)(unsigned long)value;
mbox_chan_received_data(channel->chan, msg);
/*
* Clear data registers and tag.
@ -443,6 +443,8 @@ static void tegra_hsp_sm_recv128(struct tegra_hsp_channel *channel)
tegra_hsp_channel_writel(channel, 0x0, HSP_SHRD_MBOX_TYPE1_DATA2);
tegra_hsp_channel_writel(channel, 0x0, HSP_SHRD_MBOX_TYPE1_DATA3);
tegra_hsp_channel_writel(channel, 0x0, HSP_SHRD_MBOX_TYPE1_TAG);
mbox_chan_received_data(channel->chan, msg);
}
static const struct tegra_hsp_sm_ops tegra_hsp_sm_128bit_ops = {

View File

@ -905,7 +905,7 @@ static int zynqmp_ipi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *nc, *np = pdev->dev.of_node;
struct zynqmp_ipi_pdata __percpu *pdata;
struct zynqmp_ipi_pdata *pdata;
struct of_phandle_args out_irq;
struct zynqmp_ipi_mbox *mbox;
int num_mboxes, ret = -EINVAL;

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Exynos mailbox message.
*
* Copyright 2024 Linaro Ltd.
*/
#ifndef _LINUX_EXYNOS_MESSAGE_H_
#define _LINUX_EXYNOS_MESSAGE_H_
#define EXYNOS_MBOX_CHAN_TYPE_DOORBELL 0
#define EXYNOS_MBOX_CHAN_TYPE_DATA 1
struct exynos_mbox_msg {
unsigned int chan_id;
unsigned int chan_type;
};
#endif /* _LINUX_EXYNOS_MESSAGE_H_ */

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
*Copyright (c) 2024 Microchip Technology Inc. All rights reserved.
*/
#ifndef _LINUX_MCHP_IPC_H_
#define _LINUX_MCHP_IPC_H_
#include <linux/mailbox_controller.h>
#include <linux/types.h>
struct mchp_ipc_msg {
u32 *buf;
u16 size;
};
struct mchp_ipc_sbi_chan {
void *buf_base_tx;
void *buf_base_rx;
void *msg_buf_tx;
void *msg_buf_rx;
phys_addr_t buf_base_tx_addr;
phys_addr_t buf_base_rx_addr;
phys_addr_t msg_buf_tx_addr;
phys_addr_t msg_buf_rx_addr;
int chan_aggregated_irq;
int mp_irq;
int mc_irq;
u32 id;
u32 max_msg_size;
};
#endif /* _LINUX_MCHP_IPC_H_ */