summaryrefslogtreecommitdiff
path: root/drivers/mailbox
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mailbox')
-rw-r--r--drivers/mailbox/Kconfig24
-rw-r--r--drivers/mailbox/Makefile4
-rw-r--r--drivers/mailbox/arm_mhu.c2
-rw-r--r--drivers/mailbox/arm_mhu_db.c2
-rw-r--r--drivers/mailbox/arm_mhuv2.c2
-rw-r--r--drivers/mailbox/exynos-mailbox.c157
-rw-r--r--drivers/mailbox/imx-mailbox.c21
-rw-r--r--drivers/mailbox/mailbox-altera.c2
-rw-r--r--drivers/mailbox/mailbox-mchp-ipc-sbi.c504
-rw-r--r--drivers/mailbox/mailbox.c29
-rw-r--r--drivers/mailbox/mailbox.h2
-rw-r--r--drivers/mailbox/mtk-cmdq-mailbox.c51
-rw-r--r--drivers/mailbox/pcc.c113
-rw-r--r--drivers/mailbox/pl320-ipc.c14
-rw-r--r--drivers/mailbox/qcom-apcs-ipc-mailbox.c1
-rw-r--r--drivers/mailbox/qcom-ipcc.c16
-rw-r--r--drivers/mailbox/tegra-hsp.c72
17 files changed, 878 insertions, 138 deletions
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 8ecba7fb999e..e8445cda7c61 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -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
+ depends on ARCH_MICROCHIP || COMPILE_TEST
+ 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
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 5f4f5b0ce2cc..9a1542b55539 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -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
diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c
index 537f7bfb7b06..0950b7bce184 100644
--- a/drivers/mailbox/arm_mhu.c
+++ b/drivers/mailbox/arm_mhu.c
@@ -153,7 +153,7 @@ static int mhu_probe(struct amba_device *adev, const struct amba_id *id)
return 0;
}
-static struct amba_id mhu_ids[] = {
+static const struct amba_id mhu_ids[] = {
{
.id = 0x1bb098,
.mask = 0xffffff,
diff --git a/drivers/mailbox/arm_mhu_db.c b/drivers/mailbox/arm_mhu_db.c
index 27a510d46908..9e937b09c5fb 100644
--- a/drivers/mailbox/arm_mhu_db.c
+++ b/drivers/mailbox/arm_mhu_db.c
@@ -328,7 +328,7 @@ static int mhu_db_probe(struct amba_device *adev, const struct amba_id *id)
return 0;
}
-static struct amba_id mhu_ids[] = {
+static const struct amba_id mhu_ids[] = {
{
.id = 0x1bb098,
.mask = 0xffffff,
diff --git a/drivers/mailbox/arm_mhuv2.c b/drivers/mailbox/arm_mhuv2.c
index cff7c343ee08..f035284944c0 100644
--- a/drivers/mailbox/arm_mhuv2.c
+++ b/drivers/mailbox/arm_mhuv2.c
@@ -1107,7 +1107,7 @@ static void mhuv2_remove(struct amba_device *adev)
writel_relaxed(0x0, &mhu->send->access_request);
}
-static struct amba_id mhuv2_ids[] = {
+static const struct amba_id mhuv2_ids[] = {
{
/* 2.0 */
.id = 0xbb0d1,
diff --git a/drivers/mailbox/exynos-mailbox.c b/drivers/mailbox/exynos-mailbox.c
new file mode 100644
index 000000000000..2320649bf60c
--- /dev/null
+++ b/drivers/mailbox/exynos-mailbox.c
@@ -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");
diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
index 6ef8338add0d..6778afc64a04 100644
--- a/drivers/mailbox/imx-mailbox.c
+++ b/drivers/mailbox/imx-mailbox.c
@@ -226,7 +226,7 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv,
{
u32 *arg = data;
u32 val;
- int ret;
+ int ret, count;
switch (cp->type) {
case IMX_MU_TYPE_TX:
@@ -240,11 +240,20 @@ static int imx_mu_generic_tx(struct imx_mu_priv *priv,
case IMX_MU_TYPE_TXDB_V2:
imx_mu_write(priv, IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx),
priv->dcfg->xCR[IMX_MU_GCR]);
- ret = readl_poll_timeout(priv->base + priv->dcfg->xCR[IMX_MU_GCR], val,
- !(val & IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx)),
- 0, 1000);
- if (ret)
- dev_warn_ratelimited(priv->dev, "channel type: %d failure\n", cp->type);
+ ret = -ETIMEDOUT;
+ count = 0;
+ while (ret && (count < 10)) {
+ ret =
+ readl_poll_timeout(priv->base + priv->dcfg->xCR[IMX_MU_GCR], val,
+ !(val & IMX_MU_xCR_GIRn(priv->dcfg->type, cp->idx)),
+ 0, 10000);
+
+ if (ret) {
+ dev_warn_ratelimited(priv->dev,
+ "channel type: %d timeout, %d times, retry\n",
+ cp->type, ++count);
+ }
+ }
break;
default:
dev_warn_ratelimited(priv->dev, "Send data on wrong channel type: %d\n", cp->type);
diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c
index afb320e9d69c..748128661892 100644
--- a/drivers/mailbox/mailbox-altera.c
+++ b/drivers/mailbox/mailbox-altera.c
@@ -270,7 +270,7 @@ static void altera_mbox_shutdown(struct mbox_chan *chan)
writel_relaxed(~0, mbox->mbox_base + MAILBOX_INTMASK_REG);
free_irq(mbox->irq, chan);
} else if (!mbox->is_sender) {
- del_timer_sync(&mbox->rxpoll_timer);
+ timer_delete_sync(&mbox->rxpoll_timer);
}
}
diff --git a/drivers/mailbox/mailbox-mchp-ipc-sbi.c b/drivers/mailbox/mailbox-mchp-ipc-sbi.c
new file mode 100644
index 000000000000..a6e52009a424
--- /dev/null
+++ b/drivers/mailbox/mailbox-mchp-ipc-sbi.c
@@ -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");
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index d3d26a2c9895..aea0e690b63e 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -6,18 +6,15 @@
* Author: Jassi Brar <jassisinghbrar@gmail.com>
*/
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-#include <linux/module.h>
#include <linux/device.h>
-#include <linux/bitops.h>
+#include <linux/err.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/spinlock.h>
#include "mailbox.h"
@@ -413,15 +410,15 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index)
return ERR_PTR(-ENODEV);
}
- mutex_lock(&con_mutex);
-
- if (of_parse_phandle_with_args(dev->of_node, "mboxes",
- "#mbox-cells", index, &spec)) {
+ ret = of_parse_phandle_with_args(dev->of_node, "mboxes", "#mbox-cells",
+ index, &spec);
+ if (ret) {
dev_dbg(dev, "%s: can't parse \"mboxes\" property\n", __func__);
- mutex_unlock(&con_mutex);
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(ret);
}
+ mutex_lock(&con_mutex);
+
chan = ERR_PTR(-EPROBE_DEFER);
list_for_each_entry(mbox, &mbox_cons, node)
if (mbox->dev->of_node == spec.np) {
@@ -489,8 +486,8 @@ void mbox_free_channel(struct mbox_chan *chan)
if (chan->txdone_method == TXDONE_BY_ACK)
chan->txdone_method = TXDONE_BY_POLL;
- module_put(chan->mbox->dev->driver->owner);
spin_unlock_irqrestore(&chan->lock, flags);
+ module_put(chan->mbox->dev->driver->owner);
}
EXPORT_SYMBOL_GPL(mbox_free_channel);
@@ -534,9 +531,7 @@ int mbox_controller_register(struct mbox_controller *mbox)
return -EINVAL;
}
- hrtimer_init(&mbox->poll_hrt, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- mbox->poll_hrt.function = txdone_hrtimer;
+ hrtimer_setup(&mbox->poll_hrt, txdone_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
spin_lock_init(&mbox->poll_hrt_lock);
}
diff --git a/drivers/mailbox/mailbox.h b/drivers/mailbox/mailbox.h
index 046d6d258b32..e1ec4efab693 100644
--- a/drivers/mailbox/mailbox.h
+++ b/drivers/mailbox/mailbox.h
@@ -3,6 +3,8 @@
#ifndef __MAILBOX_H
#define __MAILBOX_H
+#include <linux/bits.h>
+
#define TXDONE_BY_IRQ BIT(0) /* controller has remote RTR irq */
#define TXDONE_BY_POLL BIT(1) /* controller can read status of last TX */
#define TXDONE_BY_ACK BIT(2) /* S/W ACK received by Client ticks the TX */
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
index d186865b8dce..ab4e8d1954a1 100644
--- a/drivers/mailbox/mtk-cmdq-mailbox.c
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -92,18 +92,6 @@ struct gce_plat {
u32 gce_num;
};
-static void cmdq_sw_ddr_enable(struct cmdq *cmdq, bool enable)
-{
- WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));
-
- if (enable)
- writel(GCE_DDR_EN | GCE_CTRL_BY_SW, cmdq->base + GCE_GCTL_VALUE);
- else
- writel(GCE_CTRL_BY_SW, cmdq->base + GCE_GCTL_VALUE);
-
- clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);
-}
-
u8 cmdq_get_shift_pa(struct mbox_chan *chan)
{
struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
@@ -112,6 +100,19 @@ u8 cmdq_get_shift_pa(struct mbox_chan *chan)
}
EXPORT_SYMBOL(cmdq_get_shift_pa);
+static void cmdq_gctl_value_toggle(struct cmdq *cmdq, bool ddr_enable)
+{
+ u32 val = cmdq->pdata->control_by_sw ? GCE_CTRL_BY_SW : 0;
+
+ if (!cmdq->pdata->control_by_sw && !cmdq->pdata->sw_ddr_en)
+ return;
+
+ if (cmdq->pdata->sw_ddr_en && ddr_enable)
+ val |= GCE_DDR_EN;
+
+ writel(val, cmdq->base + GCE_GCTL_VALUE);
+}
+
static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
{
u32 status;
@@ -140,16 +141,10 @@ static void cmdq_thread_resume(struct cmdq_thread *thread)
static void cmdq_init(struct cmdq *cmdq)
{
int i;
- u32 gctl_regval = 0;
WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));
- if (cmdq->pdata->control_by_sw)
- gctl_regval = GCE_CTRL_BY_SW;
- if (cmdq->pdata->sw_ddr_en)
- gctl_regval |= GCE_DDR_EN;
- if (gctl_regval)
- writel(gctl_regval, cmdq->base + GCE_GCTL_VALUE);
+ cmdq_gctl_value_toggle(cmdq, true);
writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
for (i = 0; i <= CMDQ_MAX_EVENT; i++)
@@ -315,14 +310,21 @@ static irqreturn_t cmdq_irq_handler(int irq, void *dev)
static int cmdq_runtime_resume(struct device *dev)
{
struct cmdq *cmdq = dev_get_drvdata(dev);
+ int ret;
- return clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks);
+ ret = clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks);
+ if (ret)
+ return ret;
+
+ cmdq_gctl_value_toggle(cmdq, true);
+ return 0;
}
static int cmdq_runtime_suspend(struct device *dev)
{
struct cmdq *cmdq = dev_get_drvdata(dev);
+ cmdq_gctl_value_toggle(cmdq, false);
clk_bulk_disable(cmdq->pdata->gce_num, cmdq->clocks);
return 0;
}
@@ -347,9 +349,6 @@ static int cmdq_suspend(struct device *dev)
if (task_running)
dev_warn(dev, "exist running task(s) in suspend\n");
- if (cmdq->pdata->sw_ddr_en)
- cmdq_sw_ddr_enable(cmdq, false);
-
return pm_runtime_force_suspend(dev);
}
@@ -360,9 +359,6 @@ static int cmdq_resume(struct device *dev)
WARN_ON(pm_runtime_force_resume(dev));
cmdq->suspended = false;
- if (cmdq->pdata->sw_ddr_en)
- cmdq_sw_ddr_enable(cmdq, true);
-
return 0;
}
@@ -370,9 +366,6 @@ static void cmdq_remove(struct platform_device *pdev)
{
struct cmdq *cmdq = platform_get_drvdata(pdev);
- if (cmdq->pdata->sw_ddr_en)
- cmdq_sw_ddr_enable(cmdq, false);
-
if (!IS_ENABLED(CONFIG_PM))
cmdq_runtime_suspend(&pdev->dev);
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 82102a4c5d68..f6714c233f5a 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -117,8 +117,6 @@ struct pcc_chan_info {
static struct pcc_chan_info *chan_info;
static int pcc_chan_count;
-static int pcc_send_data(struct mbox_chan *chan, void *data);
-
/*
* PCC can be used with perf critical drivers such as CPPC
* So it makes sense to locally cache the virtual address and
@@ -245,13 +243,13 @@ static bool pcc_mbox_cmd_complete_check(struct pcc_chan_info *pchan)
u64 val;
int ret;
+ if (!pchan->cmd_complete.gas)
+ return true;
+
ret = pcc_chan_reg_read(&pchan->cmd_complete, &val);
if (ret)
return false;
- if (!pchan->cmd_complete.gas)
- return true;
-
/*
* Judge if the channel respond the interrupt based on the value of
* command complete.
@@ -269,33 +267,43 @@ static bool pcc_mbox_cmd_complete_check(struct pcc_chan_info *pchan)
return !!val;
}
-static void check_and_ack(struct pcc_chan_info *pchan, struct mbox_chan *chan)
+static int pcc_mbox_error_check_and_clear(struct pcc_chan_info *pchan)
+{
+ u64 val;
+ int ret;
+
+ ret = pcc_chan_reg_read(&pchan->error, &val);
+ if (ret)
+ return ret;
+
+ val &= pchan->error.status_mask;
+ if (val) {
+ val &= ~pchan->error.status_mask;
+ pcc_chan_reg_write(&pchan->error, val);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void pcc_chan_acknowledge(struct pcc_chan_info *pchan)
{
- struct acpi_pcct_ext_pcc_shared_memory pcc_hdr;
+ struct acpi_pcct_ext_pcc_shared_memory __iomem *pcc_hdr;
if (pchan->type != ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE)
return;
- /* If the memory region has not been mapped, we cannot
- * determine if we need to send the message, but we still
- * need to set the cmd_update flag before returning.
- */
- if (pchan->chan.shmem == NULL) {
- pcc_chan_reg_read_modify_write(&pchan->cmd_update);
- return;
- }
- memcpy_fromio(&pcc_hdr, pchan->chan.shmem,
- sizeof(struct acpi_pcct_ext_pcc_shared_memory));
+
+ pcc_chan_reg_read_modify_write(&pchan->cmd_update);
+
+ pcc_hdr = pchan->chan.shmem;
+
/*
- * The PCC slave subspace channel needs to set the command complete bit
- * after processing message. If the PCC_ACK_FLAG is set, it should also
- * ring the doorbell.
- *
- * The PCC master subspace channel clears chan_in_use to free channel.
+ * The PCC slave subspace channel needs to set the command
+ * complete bit after processing message. If the PCC_ACK_FLAG
+ * is set, it should also ring the doorbell.
*/
- if (le32_to_cpup(&pcc_hdr.flags) & PCC_ACK_FLAG_MASK)
- pcc_send_data(chan, NULL);
- else
- pcc_chan_reg_read_modify_write(&pchan->cmd_update);
+ if (ioread32(&pcc_hdr->flags) & PCC_CMD_COMPLETION_NOTIFY)
+ pcc_chan_reg_read_modify_write(&pchan->db);
}
/**
@@ -309,10 +317,12 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
{
struct pcc_chan_info *pchan;
struct mbox_chan *chan = p;
- u64 val;
- int ret;
pchan = chan->con_priv;
+
+ if (pcc_chan_reg_read_modify_write(&pchan->plat_irq_ack))
+ return IRQ_NONE;
+
if (pchan->type == ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE &&
!pchan->chan_in_use)
return IRQ_NONE;
@@ -320,23 +330,19 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
if (!pcc_mbox_cmd_complete_check(pchan))
return IRQ_NONE;
- ret = pcc_chan_reg_read(&pchan->error, &val);
- if (ret)
- return IRQ_NONE;
- val &= pchan->error.status_mask;
- if (val) {
- val &= ~pchan->error.status_mask;
- pcc_chan_reg_write(&pchan->error, val);
- return IRQ_NONE;
- }
-
- if (pcc_chan_reg_read_modify_write(&pchan->plat_irq_ack))
+ if (pcc_mbox_error_check_and_clear(pchan))
return IRQ_NONE;
+ /*
+ * Clear this flag after updating interrupt ack register and just
+ * before mbox_chan_received_data() which might call pcc_send_data()
+ * where the flag is set again to start new transfer. This is
+ * required to avoid any possible race in updatation of this flag.
+ */
+ pchan->chan_in_use = false;
mbox_chan_received_data(chan, NULL);
- check_and_ack(pchan, chan);
- pchan->chan_in_use = false;
+ pcc_chan_acknowledge(pchan);
return IRQ_HANDLED;
}
@@ -356,6 +362,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)
struct pcc_mbox_chan *
pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
{
+ struct pcc_mbox_chan *pcc_mchan;
struct pcc_chan_info *pchan;
struct mbox_chan *chan;
int rc;
@@ -374,7 +381,14 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)
if (rc)
return ERR_PTR(rc);
- return &pchan->chan;
+ 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;
+
+ mbox_free_channel(chan);
+ return ERR_PTR(-ENXIO);
}
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
@@ -403,21 +417,6 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan)
}
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
-int pcc_mbox_ioremap(struct mbox_chan *chan)
-{
- struct pcc_chan_info *pchan_info;
- struct pcc_mbox_chan *pcc_mbox_chan;
-
- if (!chan || !chan->cl)
- return -1;
- pchan_info = chan->con_priv;
- pcc_mbox_chan = &pchan_info->chan;
- pcc_mbox_chan->shmem = ioremap(pcc_mbox_chan->shmem_base_addr,
- pcc_mbox_chan->shmem_size);
- return 0;
-}
-EXPORT_SYMBOL_GPL(pcc_mbox_ioremap);
-
/**
* pcc_send_data - Called from Mailbox Controller code. Used
* here only to ring the channel doorbell. The PCC client
diff --git a/drivers/mailbox/pl320-ipc.c b/drivers/mailbox/pl320-ipc.c
index fbcf07930390..606f26a2a6fd 100644
--- a/drivers/mailbox/pl320-ipc.c
+++ b/drivers/mailbox/pl320-ipc.c
@@ -45,18 +45,6 @@ static DEFINE_MUTEX(ipc_m1_lock);
static DECLARE_COMPLETION(ipc_completion);
static ATOMIC_NOTIFIER_HEAD(ipc_notifier);
-static inline void set_destination(int source, int mbox)
-{
- writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDSET(mbox));
- writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMSET(mbox));
-}
-
-static inline void clear_destination(int source, int mbox)
-{
- writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxDCLEAR(mbox));
- writel_relaxed(CHAN_MASK(source), ipc_base + IPCMxMCLEAR(mbox));
-}
-
static void __ipc_send(int mbox, u32 *data)
{
int i;
@@ -164,7 +152,7 @@ err:
return ret;
}
-static struct amba_id pl320_ids[] = {
+static const struct amba_id pl320_ids[] = {
{
.id = 0x00041320,
.mask = 0x000fffff,
diff --git a/drivers/mailbox/qcom-apcs-ipc-mailbox.c b/drivers/mailbox/qcom-apcs-ipc-mailbox.c
index f0d1fc0fb9ff..11c41e935a36 100644
--- a/drivers/mailbox/qcom-apcs-ipc-mailbox.c
+++ b/drivers/mailbox/qcom-apcs-ipc-mailbox.c
@@ -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 },
diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c
index 14c7907c6632..0b17a38ea6bf 100644
--- a/drivers/mailbox/qcom-ipcc.c
+++ b/drivers/mailbox/qcom-ipcc.c
@@ -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;
diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
index c1981f091bd1..ed9a0bb2bcd8 100644
--- a/drivers/mailbox/tegra-hsp.c
+++ b/drivers/mailbox/tegra-hsp.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2023, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2016-2025, NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/delay.h>
@@ -28,12 +28,6 @@
#define HSP_INT_FULL_MASK 0xff
#define HSP_INT_DIMENSIONING 0x380
-#define HSP_nSM_SHIFT 0
-#define HSP_nSS_SHIFT 4
-#define HSP_nAS_SHIFT 8
-#define HSP_nDB_SHIFT 12
-#define HSP_nSI_SHIFT 16
-#define HSP_nINT_MASK 0xf
#define HSP_DB_TRIGGER 0x0
#define HSP_DB_ENABLE 0x4
@@ -97,6 +91,20 @@ struct tegra_hsp_soc {
bool has_per_mb_ie;
bool has_128_bit_mb;
unsigned int reg_stride;
+
+ /* Shifts for dimensioning register. */
+ unsigned int si_shift;
+ unsigned int db_shift;
+ unsigned int as_shift;
+ unsigned int ss_shift;
+ unsigned int sm_shift;
+
+ /* Masks for dimensioning register. */
+ unsigned int si_mask;
+ unsigned int db_mask;
+ unsigned int as_mask;
+ unsigned int ss_mask;
+ unsigned int sm_mask;
};
struct tegra_hsp {
@@ -747,11 +755,11 @@ static int tegra_hsp_probe(struct platform_device *pdev)
return PTR_ERR(hsp->regs);
value = tegra_hsp_readl(hsp, HSP_INT_DIMENSIONING);
- hsp->num_sm = (value >> HSP_nSM_SHIFT) & HSP_nINT_MASK;
- hsp->num_ss = (value >> HSP_nSS_SHIFT) & HSP_nINT_MASK;
- hsp->num_as = (value >> HSP_nAS_SHIFT) & HSP_nINT_MASK;
- hsp->num_db = (value >> HSP_nDB_SHIFT) & HSP_nINT_MASK;
- hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
+ hsp->num_sm = (value >> hsp->soc->sm_shift) & hsp->soc->sm_mask;
+ hsp->num_ss = (value >> hsp->soc->ss_shift) & hsp->soc->ss_mask;
+ hsp->num_as = (value >> hsp->soc->as_shift) & hsp->soc->as_mask;
+ hsp->num_db = (value >> hsp->soc->db_shift) & hsp->soc->db_mask;
+ hsp->num_si = (value >> hsp->soc->si_shift) & hsp->soc->si_mask;
err = platform_get_irq_byname_optional(pdev, "doorbell");
if (err >= 0)
@@ -915,6 +923,16 @@ static const struct tegra_hsp_soc tegra186_hsp_soc = {
.has_per_mb_ie = false,
.has_128_bit_mb = false,
.reg_stride = 0x100,
+ .si_shift = 16,
+ .db_shift = 12,
+ .as_shift = 8,
+ .ss_shift = 4,
+ .sm_shift = 0,
+ .si_mask = 0xf,
+ .db_mask = 0xf,
+ .as_mask = 0xf,
+ .ss_mask = 0xf,
+ .sm_mask = 0xf,
};
static const struct tegra_hsp_soc tegra194_hsp_soc = {
@@ -922,6 +940,16 @@ static const struct tegra_hsp_soc tegra194_hsp_soc = {
.has_per_mb_ie = true,
.has_128_bit_mb = false,
.reg_stride = 0x100,
+ .si_shift = 16,
+ .db_shift = 12,
+ .as_shift = 8,
+ .ss_shift = 4,
+ .sm_shift = 0,
+ .si_mask = 0xf,
+ .db_mask = 0xf,
+ .as_mask = 0xf,
+ .ss_mask = 0xf,
+ .sm_mask = 0xf,
};
static const struct tegra_hsp_soc tegra234_hsp_soc = {
@@ -929,6 +957,16 @@ static const struct tegra_hsp_soc tegra234_hsp_soc = {
.has_per_mb_ie = false,
.has_128_bit_mb = true,
.reg_stride = 0x100,
+ .si_shift = 16,
+ .db_shift = 12,
+ .as_shift = 8,
+ .ss_shift = 4,
+ .sm_shift = 0,
+ .si_mask = 0xf,
+ .db_mask = 0xf,
+ .as_mask = 0xf,
+ .ss_mask = 0xf,
+ .sm_mask = 0xf,
};
static const struct tegra_hsp_soc tegra264_hsp_soc = {
@@ -936,6 +974,16 @@ static const struct tegra_hsp_soc tegra264_hsp_soc = {
.has_per_mb_ie = false,
.has_128_bit_mb = true,
.reg_stride = 0x1000,
+ .si_shift = 17,
+ .db_shift = 12,
+ .as_shift = 8,
+ .ss_shift = 4,
+ .sm_shift = 0,
+ .si_mask = 0x1f,
+ .db_mask = 0x1f,
+ .as_mask = 0xf,
+ .ss_mask = 0xf,
+ .sm_mask = 0xf,
};
static const struct of_device_id tegra_hsp_match[] = {