diff --git a/drivers/misc/mods/Makefile b/drivers/misc/mods/Makefile
index 0195ea4a..9e61b417 100644
--- a/drivers/misc/mods/Makefile
+++ b/drivers/misc/mods/Makefile
@@ -18,6 +18,7 @@ mods-$(CONFIG_TEGRA_NVADSP) += mods_adsp.o
mods-$(CONFIG_COMMON_CLK) += mods_clock.o
mods-$(CONFIG_DEBUG_FS) += mods_debugfs.o
mods-$(CONFIG_DMA_ENGINE) += mods_dma.o
+mods-$(CONFIG_ARCH_TEGRA) += mods_ipi.o
mods-$(CONFIG_NET) += mods_netdevice.o
mods-$(CONFIG_ARCH_TEGRA) += mods_oist.o
mods-$(CONFIG_OPTEE) += mods_optee.o
diff --git a/drivers/misc/mods/mods_internal.h b/drivers/misc/mods/mods_internal.h
index e2c012ea..f9252dba 100644
--- a/drivers/misc/mods/mods_internal.h
+++ b/drivers/misc/mods/mods_internal.h
@@ -739,4 +739,8 @@ int smmu_driver_init(void);
void smmu_driver_exit(void);
#endif
+#if defined(MODS_HAS_TEGRA)
+int esc_mods_send_ipi(struct mods_client *client, struct MODS_SEND_IPI *p);
+#endif
+
#endif /* _MODS_INTERNAL_H_ */
diff --git a/drivers/misc/mods/mods_ipi.c b/drivers/misc/mods/mods_ipi.c
new file mode 100644
index 00000000..b4c89f54
--- /dev/null
+++ b/drivers/misc/mods/mods_ipi.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
+ *
+ * NVIDIA MODS kernel driver is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * NVIDIA MODS kernel driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with NVIDIA MODS kernel driver.
+ * If not, see .
+ */
+
+#include "mods_internal.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_IPI_WFI_LOOPS 5
+#define MAX_IPI_WFE_LOOPS 10
+
+static void ipi_wfi_cpu(void *p)
+{
+ struct MODS_SEND_IPI *p_ipi = (struct MODS_SEND_IPI *)p;
+ u32 i;
+
+ for (i = 0; i < p_ipi->num_loops; i++)
+ wfi();
+}
+
+static void ipi_wfe_cpu(void *p)
+{
+ struct MODS_SEND_IPI *p_ipi = (struct MODS_SEND_IPI *)p;
+ u32 i;
+
+ for (i = 0; i < p_ipi->num_loops; i++)
+ wfe();
+}
+
+int esc_mods_send_ipi(struct mods_client *client, struct MODS_SEND_IPI *p)
+{
+ LOG_ENT();
+
+ switch (p->ipi_type) {
+ case MODS_IPI_KICK: {
+ kick_all_cpus_sync();
+ break;
+ }
+ case MODS_IPI_WFI: {
+ if (p->num_loops > MAX_IPI_WFI_LOOPS) {
+ cl_error("num_loops %u exceed maximum limit %d for MODS_IPI_WFI\n",
+ p->num_loops,
+ MAX_IPI_WFI_LOOPS);
+ LOG_EXT();
+ return -EINVAL;
+ }
+ preempt_disable();
+ smp_call_function_many(cpu_online_mask, ipi_wfi_cpu, (void *)p, 1);
+ preempt_enable();
+ break;
+ }
+ case MODS_IPI_WFE: {
+ if (p->num_loops > MAX_IPI_WFE_LOOPS) {
+ cl_error("num_loops %u exceed maximum limit %d for MODS_IPI_WFE\n",
+ p->num_loops,
+ MAX_IPI_WFE_LOOPS);
+ LOG_EXT();
+ return -EINVAL;
+ }
+ preempt_disable();
+ smp_call_function_many(cpu_online_mask, ipi_wfe_cpu, (void *)p, 1);
+ preempt_enable();
+ break;
+ }
+ default: {
+ cl_error("unsupported MODS_IPI_TYPE\n");
+ LOG_EXT();
+ return -EINVAL;
+ }
+ }
+
+ LOG_EXT();
+ return 0;
+}
diff --git a/drivers/misc/mods/mods_krnl.c b/drivers/misc/mods/mods_krnl.c
index 33e477ec..8b3485d4 100644
--- a/drivers/misc/mods/mods_krnl.c
+++ b/drivers/misc/mods/mods_krnl.c
@@ -2604,6 +2604,11 @@ static long mods_krnl_ioctl(struct file *fp,
MODS_IOCTL(MODS_ESC_OIST_STATUS,
esc_mods_oist_status, MODS_TEGRA_OIST_STATUS);
break;
+
+ case MODS_ESC_MODS_SEND_IPI:
+ MODS_IOCTL(MODS_ESC_MODS_SEND_IPI,
+ esc_mods_send_ipi, MODS_SEND_IPI);
+ break;
#endif
case MODS_ESC_ACQUIRE_ACCESS_TOKEN:
diff --git a/include/uapi/misc/mods.h b/include/uapi/misc/mods.h
index ec4c01e5..bcd5e26f 100644
--- a/include/uapi/misc/mods.h
+++ b/include/uapi/misc/mods.h
@@ -1858,6 +1858,23 @@ struct MODS_TEGRA_OIST_STATUS {
__u64 smc_status;
};
+enum MODS_IPI_TYPE {
+ MODS_IPI_KICK,
+ MODS_IPI_WFI,
+ MODS_IPI_WFE
+};
+
+/* Used by MODS_ESC_MODS_SEND_IPI ioctl.
+ *
+ * Available only on Tegra.
+ */
+struct MODS_SEND_IPI {
+ /* IN */
+ __u32 ipi_type;
+ /* IN */
+ __u32 num_loops;
+};
+
#define MODS_IOMMU_MAP_CONTIGUOUS 1
#define MODS_MAX_PROP_NAME_LEN 64
@@ -2097,5 +2114,6 @@ struct MODS_PROXIMITY_TO_NUMA_NODE {
#define MODS_ESC_INVOKE_OPTEE_TA MODSIO(WR, 141, MODS_OPTEE_PARAMS)
#define MODS_ESC_READ_DEV_PROPERTY MODSIO(WR, 142, MODS_READ_DEV_PROPERTY)
#define MODS_ESC_PROXIMITY_TO_NUMA_NODE MODSIO(WR, 143, MODS_PROXIMITY_TO_NUMA_NODE)
+#define MODS_ESC_MODS_SEND_IPI MODSIO(W, 144, MODS_SEND_IPI)
#endif /* _UAPI_MODS_H_ */