diff --git a/drivers/misc/mods/Makefile b/drivers/misc/mods/Makefile
index 94a7d142..4d9132a0 100644
--- a/drivers/misc/mods/Makefile
+++ b/drivers/misc/mods/Makefile
@@ -1,9 +1,40 @@
# SPDX-License-Identifier: GPL-2.0
-# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
-# NOTE: Do not change or add anything in this makefile.
-# The source code and makefile rules are copied from the
-# kernel/nvidia/drivers/misc/mods. This file is
-# just place-holder for empty makefile to avoid any build
-# issue when copy is not done from command line and building
-# the tree independent of source copy.
+ccflags-y += -I$(srctree.nvidia)/include
+ccflags-y += -DMODS_HAS_TEGRA
+
+ifeq ($(CONFIG_TEGRA_OOT_MODULE),m)
+CONFIG_MODS := m
+endif
+
+obj-$(CONFIG_MODS) := mods.o
+
+mods-y := mods_irq.o
+mods-y += mods_krnl.o
+mods-y += mods_mem.o
+
+mods-$(CONFIG_ACPI) += mods_acpi.o
+mods-$(CONFIG_TEGRA_NVADSP) += mods_adsp.o
+mods-$(CONFIG_ARM_FFA_TRANSPORT) += mods_arm_ffa.o
+mods-$(CONFIG_TEGRA_IVC) += mods_bpmpipc.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
+mods-$(CONFIG_PCI) += mods_pci.o
+mods-$(CONFIG_ARCH_TEGRA_19x_SOC) += mods_ras.o
+mods-$(CONFIG_ARCH_TEGRA) += mods_smmu_drv.o
+mods-$(CONFIG_TEGRA_DC) += mods_tegradc.o
+mods-$(CONFIG_TRUSTY) += mods_tz.o
+
+mods-objs := mods.dtb.o
+
+ifneq ($(CONFIG_TEGRA_OOT_MODULE),m)
+ccflags-y += -DMODS_HAS_DMABUF
+ccflags-y += -DMODS_HAS_PROD
+mods-$(CONFIG_DMA_SHARED_BUFFER) += mods_dmabuf.o
+mods-$(CONFIG_ARCH_TEGRA) += mods_tegraprod.o
+endif
diff --git a/drivers/misc/mods/mods.dts b/drivers/misc/mods/mods.dts
new file mode 100644
index 00000000..673eeaf1
--- /dev/null
+++ b/drivers/misc/mods/mods.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2015-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 .
+ */
+
+/dts-v1/;
+
+/ {
+ mods {
+ compatible = "nvidia,tegra-mods";
+ status = "okay";
+ };
+};
diff --git a/drivers/misc/mods/mods.h b/drivers/misc/mods/mods.h
new file mode 100644
index 00000000..adb43beb
--- /dev/null
+++ b/drivers/misc/mods/mods.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2020-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
diff --git a/drivers/misc/mods/mods_acpi.c b/drivers/misc/mods/mods_acpi.c
new file mode 100644
index 00000000..8bb1b664
--- /dev/null
+++ b/drivers/misc/mods/mods_acpi.c
@@ -0,0 +1,726 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2008-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
+
+static acpi_status mods_acpi_find_acpi_handler(acpi_handle,
+ u32,
+ void *,
+ void **);
+
+/*********************
+ * PRIVATE FUNCTIONS *
+ *********************/
+
+struct acpi_dev_children_in_data {
+ u32 expected_acpi_ids_buf[4];
+ u32 num_expected_acpi_ids;
+ u32 acpi_dev_mask;
+ acpi_handle dev_handle;
+};
+
+typedef int (*dev_children_fptr)(struct mods_client *,
+ u32,
+ acpi_handle,
+ void *);
+
+static int acpi_store_dev_children(struct mods_client *client,
+ u32 curr_acpi_id,
+ acpi_handle dev_handle,
+ void *out_data)
+{
+ struct MODS_GET_ACPI_DEV_CHILDREN *acpi_out_data
+ = (struct MODS_GET_ACPI_DEV_CHILDREN *)out_data;
+
+ if (acpi_out_data->num_children >= ACPI_MAX_DEV_CHILDREN) {
+ cl_error("ACPI: output buffer too small to store all children\n");
+ return -ENOBUFS;
+ }
+
+ acpi_out_data->children[acpi_out_data->num_children] = curr_acpi_id;
+ ++acpi_out_data->num_children;
+
+ return OK;
+}
+
+static int acpi_compare_dev_id(struct mods_client *client,
+ u32 curr_acpi_id,
+ acpi_handle dev_handle,
+ void *out_data)
+{
+ u32 i;
+ struct acpi_dev_children_in_data *dev_child_data
+ = (struct acpi_dev_children_in_data *)out_data;
+
+ for (i = 0; i < dev_child_data->num_expected_acpi_ids; ++i) {
+ if ((dev_child_data->expected_acpi_ids_buf[i] & dev_child_data->acpi_dev_mask)
+ == (curr_acpi_id & dev_child_data->acpi_dev_mask)) {
+ dev_child_data->dev_handle = dev_handle;
+ /*
+ * returning unique error code to signal caller that value is found
+ */
+ return -EALREADY;
+ }
+ }
+ return -ENODEV;
+}
+
+struct acpi_dev_check_context {
+ struct mods_client *client;
+ dev_children_fptr fptr;
+ void *out_data;
+};
+
+static int acpi_dev_check_one(struct acpi_device *adev, void *data)
+{
+ int err = OK;
+ unsigned long long device_id = 0;
+ acpi_status status;
+ struct acpi_dev_check_context *adwc = data;
+
+ status = acpi_evaluate_integer(adev->handle,
+ "_ADR",
+ NULL,
+ &device_id);
+ if (ACPI_FAILURE(status))
+ /* Couldnt query device_id for this device */
+ return OK;
+
+ err = adwc->fptr(adwc->client, device_id, adev->handle, adwc->out_data);
+ return ((err == -EALREADY) ? OK : err);
+}
+
+#if KERNEL_VERSION(6, 0, 0) > MODS_KERNEL_VERSION
+static int acpi_dev_each_child_node(struct acpi_device *adev,
+ int (*fptr)(struct acpi_device *, void *),
+ void *data)
+{
+ struct list_head *node = NULL;
+ struct list_head *next = NULL;
+
+ list_for_each_safe(node, next, &adev->children) {
+ struct acpi_device *dev =
+ list_entry(node, struct acpi_device, node);
+
+ fptr(dev, data);
+ }
+ return OK;
+}
+#else
+#define acpi_dev_each_child_node acpi_dev_for_each_child
+#endif
+
+static int acpi_get_dev_children(struct mods_client *client,
+ dev_children_fptr fptr,
+ acpi_handle dev_handle,
+ void *out_data)
+{
+ int err = OK;
+ struct acpi_device *device = NULL;
+ struct acpi_dev_check_context adcc = {
+ .client = client,
+ .fptr = fptr,
+ .out_data = out_data,
+ };
+
+ LOG_ENT();
+
+#ifdef MODS_HAS_ACPI_FETCH
+ device = acpi_fetch_acpi_dev(dev_handle);
+ err = device ? 0 : -EINVAL;
+#else
+ err = acpi_bus_get_device(dev_handle, &device);
+#endif
+ if (unlikely(err))
+ cl_error("ACPI: device for fetching device children not found\n");
+ else
+ err = acpi_dev_each_child_node(device, acpi_dev_check_one, &adcc);
+
+ LOG_EXT();
+ return err;
+}
+
+
+/* store handle if found. */
+static void mods_acpi_handle_init(struct mods_client *client,
+ char *method_name,
+ acpi_handle *handler)
+{
+ MODS_ACPI_WALK_NAMESPACE(ACPI_TYPE_ANY,
+ ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ mods_acpi_find_acpi_handler,
+ method_name,
+ handler);
+
+ if (!(*handler)) {
+ cl_debug(DEBUG_ACPI,
+ "ACPI method %s not found\n",
+ method_name);
+ }
+}
+
+static acpi_status mods_acpi_find_acpi_handler(
+ acpi_handle handle,
+ u32 nest_level,
+ void *dummy1,
+ void **dummy2
+)
+{
+ acpi_handle acpi_method_handler_temp;
+
+ if (!acpi_get_handle(handle, dummy1, &acpi_method_handler_temp))
+ *dummy2 = acpi_method_handler_temp;
+
+ return OK;
+}
+
+static int mods_extract_acpi_object(struct mods_client *client,
+ char *method,
+ union acpi_object *obj,
+ u8 **buf,
+ u8 *buf_end)
+{
+ int err = OK;
+
+ switch (obj->type) {
+
+ case ACPI_TYPE_BUFFER:
+ if (obj->buffer.length == 0) {
+ cl_error(
+ "empty ACPI output buffer from ACPI method %s\n",
+ method);
+ err = -EINVAL;
+ } else if (obj->buffer.length <= buf_end-*buf) {
+ u32 size = obj->buffer.length;
+
+ memcpy(*buf, obj->buffer.pointer, size);
+ *buf += size;
+ } else {
+ cl_error("output buffer too small for ACPI method %s\n",
+ method);
+ err = -EINVAL;
+ }
+ break;
+
+ case ACPI_TYPE_INTEGER:
+ if (buf_end - *buf >= 4) {
+ if (obj->integer.value > 0xFFFFFFFFU) {
+ cl_error(
+ "integer value from ACPI method %s out of range\n",
+ method);
+ err = -EINVAL;
+ } else {
+ memcpy(*buf, &obj->integer.value, 4);
+ *buf += 4;
+ }
+ } else {
+ cl_error("output buffer too small for ACPI method %s\n",
+ method);
+ err = -EINVAL;
+ }
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ if (obj->package.count == 0) {
+ cl_error(
+ "empty ACPI output package from ACPI method %s\n",
+ method);
+ err = -EINVAL;
+ } else {
+ union acpi_object *elements = obj->package.elements;
+ u32 size = 0;
+ u32 i;
+
+ for (i = 0; i < obj->package.count; i++) {
+ u8 *old_buf = *buf;
+ u32 new_size;
+
+ err = mods_extract_acpi_object(client,
+ method,
+ &elements[i],
+ buf,
+ buf_end);
+ if (err)
+ break;
+
+ new_size = *buf - old_buf;
+
+ if (size == 0) {
+ size = new_size;
+ } else if (size != new_size) {
+ cl_error(
+ "ambiguous package element size from ACPI method %s\n",
+ method);
+ err = -EINVAL;
+ }
+ }
+ }
+ break;
+
+ case ACPI_TYPE_LOCAL_REFERENCE:
+ if (obj->reference.actual_type == ACPI_TYPE_POWER) {
+ memcpy(*buf, &obj->reference.handle,
+ sizeof(obj->reference.handle));
+ *buf += sizeof(obj->reference.handle);
+ } else {
+ cl_error("Unsupported ACPI reference type\n");
+ err = -EINVAL;
+ }
+ break;
+
+ default:
+ cl_error("unsupported ACPI output type 0x%02x from method %s\n",
+ (unsigned int)obj->type,
+ method);
+ err = -EINVAL;
+ break;
+
+ }
+ return err;
+}
+
+static int mods_eval_acpi_method(struct mods_client *client,
+ struct MODS_EVAL_ACPI_METHOD *p,
+ struct mods_pci_dev_2 *pdevice,
+ u32 acpi_id)
+{
+ int err = OK;
+ int i;
+ acpi_status status;
+ struct acpi_object_list input;
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *acpi_method = NULL;
+ union acpi_object acpi_params[ACPI_MAX_ARGUMENT_NUMBER];
+ acpi_handle acpi_method_handler = NULL;
+ struct pci_dev *dev = NULL;
+
+ LOG_ENT();
+
+ if (p->argument_count >= ACPI_MAX_ARGUMENT_NUMBER) {
+ cl_error("invalid argument count for ACPI call\n");
+ LOG_EXT();
+ return -EINVAL;
+ }
+
+ if (pdevice) {
+ cl_debug(DEBUG_ACPI,
+ "ACPI %s for dev %04x:%02x:%02x.%x\n",
+ p->method_name,
+ pdevice->domain,
+ pdevice->bus,
+ pdevice->device,
+ pdevice->function);
+
+ err = mods_find_pci_dev(client, pdevice, &dev);
+ if (unlikely(err)) {
+ if (err == -ENODEV)
+ cl_error(
+ "ACPI: dev %04x:%02x:%02x.%x not found\n",
+ pdevice->domain,
+ pdevice->bus,
+ pdevice->device,
+ pdevice->function);
+ LOG_EXT();
+ return err;
+ }
+ acpi_method_handler = MODS_ACPI_HANDLE(&dev->dev);
+ } else {
+ cl_debug(DEBUG_ACPI, "ACPI %s\n", p->method_name);
+ mods_acpi_handle_init(client,
+ p->method_name,
+ &acpi_method_handler);
+ }
+
+ if (acpi_id != ACPI_MODS_IGNORE_ACPI_ID) {
+ struct acpi_dev_children_in_data in_data = { {}, 0, 0, NULL};
+
+ in_data.expected_acpi_ids_buf[0] = acpi_id;
+ in_data.num_expected_acpi_ids = 1;
+ in_data.acpi_dev_mask = 0xffffffff;
+
+ err = acpi_get_dev_children(client, acpi_compare_dev_id,
+ acpi_method_handler, (void *)&in_data);
+ if (err) {
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+ acpi_method_handler = in_data.dev_handle;
+ }
+
+ if (!acpi_method_handler) {
+ cl_debug(DEBUG_ACPI,
+ "ACPI: handle for %s not found\n",
+ p->method_name);
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+ cl_debug(DEBUG_ACPI,
+ "ACPI: found %s (id = 0x%x) on dev %04x:%02x:%02x.%x\n",
+ p->method_name,
+ (unsigned int)acpi_id,
+ pdevice ? pdevice->domain : 0U,
+ pdevice ? pdevice->bus : 0U,
+ pdevice ? pdevice->device : 0U,
+ pdevice ? pdevice->function : 0U);
+
+ input.count = p->argument_count;
+ input.pointer = acpi_params;
+
+ for (i = 0; i < p->argument_count; i++) {
+ switch (p->argument[i].type) {
+ case ACPI_MODS_TYPE_INTEGER: {
+ acpi_params[i].integer.type = ACPI_TYPE_INTEGER;
+ acpi_params[i].integer.value
+ = p->argument[i].integer.value;
+ break;
+ }
+ case ACPI_MODS_TYPE_BUFFER: {
+ acpi_params[i].buffer.type = ACPI_TYPE_BUFFER;
+ acpi_params[i].buffer.length
+ = p->argument[i].buffer.length;
+ acpi_params[i].buffer.pointer
+ = p->in_buffer + p->argument[i].buffer.offset;
+ break;
+ }
+ case ACPI_MODS_TYPE_METHOD: {
+ memcpy(&acpi_method_handler,
+ &p->argument[i].method.handle,
+ sizeof(acpi_method_handler));
+
+ if (!acpi_method_handler) {
+ cl_error("ACPI: Invalid reference handle 0\n");
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+
+ if (i != p->argument_count - 1) {
+ cl_error("ACPI: Invalid argument count\n");
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+
+ --input.count;
+ break;
+ }
+ default: {
+ cl_error("unsupported ACPI argument type\n");
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+ }
+ }
+
+ status = acpi_evaluate_object(acpi_method_handler,
+ pdevice ? p->method_name : NULL,
+ &input,
+ &output);
+
+ if (ACPI_FAILURE(status)) {
+ cl_info("ACPI method %s failed\n", p->method_name);
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+
+ acpi_method = output.pointer;
+ if (!acpi_method) {
+ cl_error("missing output from ACPI method %s\n",
+ p->method_name);
+ err = -EINVAL;
+ } else {
+ u8 *buf = p->out_buffer;
+
+ err = mods_extract_acpi_object(client,
+ p->method_name,
+ acpi_method,
+ &buf,
+ buf+sizeof(p->out_buffer));
+ p->out_data_size = err ? 0 : (buf - p->out_buffer);
+ }
+
+ kfree(output.pointer);
+ pci_dev_put(dev);
+ LOG_EXT();
+ return err;
+}
+
+static int mods_acpi_get_ddc(struct mods_client *client,
+ struct MODS_ACPI_GET_DDC_2 *p,
+ struct mods_pci_dev_2 *pci_device)
+{
+ int err;
+ acpi_status status;
+ union acpi_object *ddc;
+ union acpi_object ddc_arg0 = { ACPI_TYPE_INTEGER };
+ struct acpi_object_list input = { 1, &ddc_arg0 };
+ u32 i;
+ acpi_handle dev_handle = NULL;
+ acpi_handle lcd_dev_handle = NULL;
+ struct pci_dev *dev = NULL;
+ bool data_found = false;
+ struct acpi_dev_children_in_data in_data = { {}, 0, 0, NULL };
+
+ LOG_ENT();
+
+ cl_debug(DEBUG_ACPI,
+ "ACPI _DDC (EDID) for dev %04x:%02x:%02x.%x\n",
+ pci_device->domain,
+ pci_device->bus,
+ pci_device->device,
+ pci_device->function);
+
+ err = mods_find_pci_dev(client, pci_device, &dev);
+ if (unlikely(err)) {
+ if (err == -ENODEV)
+ cl_error("ACPI: dev %04x:%02x:%02x.%x not found\n",
+ pci_device->domain,
+ pci_device->bus,
+ pci_device->device,
+ pci_device->function);
+ LOG_EXT();
+ return err;
+ }
+
+ dev_handle = MODS_ACPI_HANDLE(&dev->dev);
+ if (!dev_handle) {
+ cl_debug(DEBUG_ACPI, "ACPI: handle for _DDC not found\n");
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+
+ /*
+ * List of supported display's (panels)
+ * Reference: https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf
+ * Section: Table B-390 Video Output Device Attributes
+ */
+ in_data.expected_acpi_ids_buf[0] = 0x0110;
+ in_data.expected_acpi_ids_buf[1] = 0x0118;
+ in_data.expected_acpi_ids_buf[2] = 0x0400;
+ in_data.expected_acpi_ids_buf[3] = 0xa450;
+ in_data.num_expected_acpi_ids = 4;
+ in_data.acpi_dev_mask = 0xffff;
+
+ err = acpi_get_dev_children(client, acpi_compare_dev_id,
+ dev_handle, (void *)&in_data);
+ if (err) {
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+ lcd_dev_handle = in_data.dev_handle;
+
+ if (lcd_dev_handle == NULL) {
+ cl_error("ACPI: LCD not found for dev %04x:%02x:%02x.%x\n",
+ p->device.domain,
+ p->device.bus,
+ p->device.device,
+ p->device.function);
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+
+ cl_debug(DEBUG_ACPI,
+ "ACPI: Found LCD on dev %04x:%02x:%02x.%x\n",
+ p->device.domain,
+ p->device.bus,
+ p->device.device,
+ p->device.function);
+
+ /*
+ * As per ACPI Spec 3.0:
+ * ARG0 is size of EDID buffer in 128-byte blocks.
+ */
+ err = -EINVAL;
+ for (i = 4; i >= 1; --i) {
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ ddc_arg0.integer.value = i;
+ status = acpi_evaluate_object(lcd_dev_handle,
+ "_DDC",
+ &input,
+ &output);
+ if (ACPI_SUCCESS(status)) {
+ ddc = output.pointer;
+ if (ddc && (ddc->type != ACPI_TYPE_BUFFER))
+ continue;
+
+ if (!ddc || ddc->buffer.length == 0) {
+ cl_error("unsupported ACPI output type\n");
+ } else if (ddc->buffer.length > sizeof(p->out_buffer)) {
+ cl_error(
+ "output buffer too small for ACPI method _DDC (EDID)\n");
+ } else {
+ p->out_data_size = ddc->buffer.length;
+ memcpy(p->out_buffer,
+ ddc->buffer.pointer,
+ p->out_data_size);
+ err = OK;
+ data_found = true;
+ }
+
+ kfree(ddc);
+
+ break;
+ }
+ }
+
+ if (!data_found)
+ cl_error("ACPI method _DDC (EDID) failed\n");
+
+ pci_dev_put(dev);
+ LOG_EXT();
+ return err;
+}
+
+/*************************
+ * ESCAPE CALL FUNCTIONS *
+ *************************/
+
+int esc_mods_eval_acpi_method(struct mods_client *client,
+ struct MODS_EVAL_ACPI_METHOD *p)
+{
+ return mods_eval_acpi_method(client, p, NULL, ACPI_MODS_IGNORE_ACPI_ID);
+}
+
+int esc_mods_eval_dev_acpi_method_3(struct mods_client *client,
+ struct MODS_EVAL_DEV_ACPI_METHOD_3 *p)
+{
+ return mods_eval_acpi_method(client,
+ &p->method,
+ &p->device,
+ p->acpi_id);
+}
+
+int esc_mods_eval_dev_acpi_method_2(struct mods_client *client,
+ struct MODS_EVAL_DEV_ACPI_METHOD_2 *p)
+{
+ return mods_eval_acpi_method(client,
+ &p->method,
+ &p->device,
+ ACPI_MODS_IGNORE_ACPI_ID);
+}
+
+int esc_mods_eval_dev_acpi_method(struct mods_client *client,
+ struct MODS_EVAL_DEV_ACPI_METHOD *p)
+{
+ struct mods_pci_dev_2 device = {0};
+
+ device.domain = 0;
+ device.bus = p->device.bus;
+ device.device = p->device.device;
+ device.function = p->device.function;
+ return mods_eval_acpi_method(client, &p->method, &device,
+ ACPI_MODS_IGNORE_ACPI_ID);
+}
+
+int esc_mods_acpi_get_ddc_2(struct mods_client *client,
+ struct MODS_ACPI_GET_DDC_2 *p)
+{
+ return mods_acpi_get_ddc(client, p, &p->device);
+}
+
+int esc_mods_acpi_get_ddc(struct mods_client *client,
+ struct MODS_ACPI_GET_DDC *p)
+{
+ struct MODS_ACPI_GET_DDC_2 *pp = (struct MODS_ACPI_GET_DDC_2 *) p;
+ struct mods_pci_dev_2 device = {0};
+
+ device.domain = 0;
+ device.bus = p->device.bus;
+ device.device = p->device.device;
+ device.function = p->device.function;
+
+ return mods_acpi_get_ddc(client, pp, &device);
+}
+
+int esc_mods_get_acpi_dev_children(struct mods_client *client,
+ struct MODS_GET_ACPI_DEV_CHILDREN *p)
+{
+ int err;
+ struct pci_dev *dev = NULL;
+ acpi_handle dev_handle = NULL;
+
+ LOG_ENT();
+
+ cl_debug(DEBUG_ACPI,
+ "ACPI: failed to get children for dev %04x:%02x:%02x.%x\n",
+ p->device.domain,
+ p->device.bus,
+ p->device.device,
+ p->device.function);
+
+ err = mods_find_pci_dev(client, &p->device, &dev);
+ if (unlikely(err)) {
+ if (err == -ENODEV)
+ cl_error("ACPI: dev %04x:%02x:%02x.%x not found\n",
+ p->device.domain,
+ p->device.bus,
+ p->device.device,
+ p->device.function);
+ LOG_EXT();
+ return err;
+ }
+
+ dev_handle = MODS_ACPI_HANDLE(&dev->dev);
+ if (!dev_handle) {
+ cl_error("ACPI: handle for fetching device children not found\n");
+ pci_dev_put(dev);
+ LOG_EXT();
+ return -EINVAL;
+ }
+
+ p->num_children = 0;
+ err = acpi_get_dev_children(client, acpi_store_dev_children,
+ dev_handle, (void *)p);
+
+ if (err) {
+ cl_error("ACPI: failed to get children for dev %04x:%02x:%02x.%x\n",
+ p->device.domain,
+ p->device.bus,
+ p->device.device,
+ p->device.function);
+ }
+
+ pci_dev_put(dev);
+ LOG_EXT();
+ return err;
+}
+
+#ifdef MODS_HAS_PXM_TO_NODE
+int esc_mods_proximity_to_numa_node(struct mods_client *client,
+ struct MODS_PROXIMITY_TO_NUMA_NODE *p)
+{
+ p->numa_node = acpi_map_pxm_to_node(p->proximity);
+ return OK;
+}
+#endif
diff --git a/drivers/misc/mods/mods_adsp.c b/drivers/misc/mods/mods_adsp.c
new file mode 100644
index 00000000..2e78d504
--- /dev/null
+++ b/drivers/misc/mods/mods_adsp.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2014-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
+#include "mods_internal.h"
+#include
+
+int esc_mods_adsp_load(struct mods_client *client)
+{
+ return nvadsp_os_load();
+}
+
+int esc_mods_adsp_start(struct mods_client *client)
+{
+ return nvadsp_os_start();
+}
+
+int esc_mods_adsp_stop(struct mods_client *client)
+{
+ return nvadsp_os_suspend();
+}
+
+int esc_mods_adsp_run_app(struct mods_client *client,
+ struct MODS_ADSP_RUN_APP_INFO *p)
+{
+ int rc = -1;
+ int max_retry = 3;
+ int rcount = 0;
+ nvadsp_app_handle_t handle;
+ nvadsp_app_info_t *p_app_info;
+ nvadsp_app_args_t app_args;
+
+ handle = nvadsp_app_load(p->app_name, p->app_file_name);
+ if (!handle) {
+ cl_error("load adsp app fail");
+ return -1;
+ }
+
+ if (p->argc > 0 && p->argc <= MODS_ADSP_APP_MAX_PARAM) {
+ app_args.argc = p->argc;
+ memcpy(app_args.argv, p->argv, p->argc * sizeof(__u32));
+ p_app_info = nvadsp_app_init(handle, &app_args);
+ } else
+ p_app_info = nvadsp_app_init(handle, NULL);
+
+ if (!p_app_info) {
+ cl_error("init adsp app fail");
+ nvadsp_app_unload(handle);
+ return -1;
+ }
+
+ rc = nvadsp_app_start(p_app_info);
+ if (rc) {
+ cl_error("start adsp app fail");
+ goto failed;
+ }
+
+ while (rcount++ < max_retry) {
+ rc = wait_for_nvadsp_app_complete_timeout(p_app_info,
+ msecs_to_jiffies(p->timeout));
+ if (rc == -ERESTARTSYS)
+ continue;
+ else if (rc == 0) {
+ cl_error("app timeout(%d)", p->timeout);
+ rc = -1;
+ } else if (rc < 0) {
+ cl_error("run app failed, err=%d\n", rc);
+ rc = -1;
+ } else
+ rc = 0;
+ break;
+ }
+
+failed:
+ nvadsp_app_deinit(p_app_info);
+ nvadsp_app_unload(handle);
+
+ return rc;
+}
diff --git a/drivers/misc/mods/mods_arm_ffa.c b/drivers/misc/mods/mods_arm_ffa.c
new file mode 100644
index 00000000..64178c6b
--- /dev/null
+++ b/drivers/misc/mods/mods_arm_ffa.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#include "mods_internal.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static const struct ffa_device_id mods_ffa_device_id[] = {
+ { UUID_INIT(0x1f4bfeb9, 0x0f48, 0xdd1e,
+ 0x11, 0x9c, 0x2c, 0x86, 0xc9, 0x14, 0x03, 0x22) },
+ {}
+};
+
+struct mods_ffa_ctx {
+ struct ffa_device *ffa_dev;
+#if KERNEL_VERSION(6, 2, 0) <= MODS_KERNEL_VERSION
+ const struct ffa_msg_ops *ffa_ops;
+#else
+ const struct ffa_dev_ops *ffa_ops;
+#endif
+};
+
+static struct mods_ffa_ctx mods_ffa_info;
+
+static int ffa_probe(struct ffa_device *ffa_dev)
+{
+ int ret = 0;
+
+#if KERNEL_VERSION(6, 2, 0) <= MODS_KERNEL_VERSION
+ const struct ffa_msg_ops *ffa_ops = NULL;
+
+ if (ffa_dev->ops)
+ ffa_ops = ffa_dev->ops->msg_ops;
+#else
+ const struct ffa_dev_ops *ffa_ops;
+
+ ffa_ops = ffa_dev_ops_get(ffa_dev);
+#endif
+ if (!ffa_ops) {
+ mods_error_printk("failed \"method\" init: ffa\n");
+ return -ENOENT;
+ }
+ mods_ffa_info.ffa_dev = ffa_dev;
+ mods_ffa_info.ffa_ops = ffa_ops;
+
+ mods_debug_printk(DEBUG_TEGRADMA, "mods ffa driver registered\n");
+
+ return ret;
+}
+
+static void ffa_remove(struct ffa_device *ffa_dev)
+{
+ mods_ffa_info.ffa_dev = NULL;
+ mods_ffa_info.ffa_ops = NULL;
+}
+
+static struct ffa_driver mods_ffa_driver = {
+ .name = "mods_arm_ffa",
+ .probe = ffa_probe,
+ .remove = ffa_remove,
+ .id_table = mods_ffa_device_id,
+};
+
+int mods_ffa_abi_register(void)
+{
+ mods_debug_printk(DEBUG_TEGRADMA, "registering MODS FFA driver\n");
+ return ffa_register(&mods_ffa_driver);
+}
+
+void mods_ffa_abi_unregister(void)
+{
+ ffa_unregister(&mods_ffa_driver);
+}
+
+int esc_mods_arm_ffa_cmd(struct mods_client *client,
+ struct MODS_FFA_PARAMS *p)
+{
+ int err = -EINVAL;
+ struct ffa_send_direct_data data = { 0 };
+
+ // Fill the reg TX command parameters
+ data.data0 = p->cmd;
+ // 64 bit of the physical address
+ data.data1 = p->indata[0];
+ // 32 bit of the reg value
+ data.data2 = p->indata[1];
+
+ if (!mods_ffa_info.ffa_ops) {
+ cl_error("mods ffa cmd error, device not found\n");
+ return -ENODEV;
+ }
+
+ switch (p->cmd) {
+ case MODS_FFA_CMD_READ_REG:
+ // Read command
+ cl_debug(DEBUG_TEGRADMA, "sending data to SP :read cmd 0x%llx, addr:0x%llx\n",
+ (unsigned long long)data.data0,
+ (unsigned long long)data.data1);
+ break;
+ case MODS_FFA_CMD_WRITE_REG:
+ // Write command
+ cl_debug(DEBUG_TEGRADMA, "sending data to SP :write cmd 0x%llx,addr:0x%llx,write_val:0x%llx\n",
+ (unsigned long long)data.data0,
+ (unsigned long long)data.data1,
+ (unsigned long long)data.data2);
+ break;
+ case MODS_FFA_CMD_READ_VER:
+ cl_debug(DEBUG_TEGRADMA, "sending cmd MODS_FFA_CMD_READ_VER to SP\n");
+ break;
+ case MODS_FFA_CMD_SE_TESTS:
+ cl_debug(DEBUG_TEGRADMA, "sending SE_TESTS data to SP :read cmd 0x%llx, alg|engineId:0x%llx\n",
+ (unsigned long long)data.data0,
+ (unsigned long long)data.data1);
+ break;
+ case MODS_FFA_CMD_SE_KEY_MOVER:
+ cl_debug(DEBUG_TEGRADMA, "sending SE_KEY_MOVER data to SP :read cmd 0x%llx, data:0x%llx\n",
+ (unsigned long long)data.data0,
+ (unsigned long long)data.data1);
+ break;
+ case MODS_FFA_CMD_HSS_TEST:
+ cl_debug(DEBUG_TEGRADMA, "sending cmd MODS_FFA_CMD_HSS_TEST to SP\n");
+ break;
+ case MODS_FFA_CMD_C2C_TEST:
+ cl_debug(DEBUG_TEGRADMA, "sending cmd MODS_FFA_CMD_C2C_TEST to SP\n");
+ break;
+ default:
+ cl_error("Unexpected command from SP 0x%llx\n", (unsigned long long)p->cmd);
+ return err;
+ }
+
+ err = mods_ffa_info.ffa_ops->sync_send_receive(mods_ffa_info.ffa_dev, &data);
+
+ switch (p->cmd) {
+ case MODS_FFA_CMD_READ_REG:
+ // Read command
+ cl_debug(DEBUG_TEGRADMA, "received read reg status from SP status:%d,read_val:0x%llx\n",
+ err, (unsigned long long)data.data1);
+ p->outdata[0] = data.data1;
+ break;
+ case MODS_FFA_CMD_WRITE_REG:
+ // write command
+ cl_debug(DEBUG_TEGRADMA, "received write reg status from SP status: %d\n",
+ err);
+ break;
+ case MODS_FFA_CMD_READ_VER:
+ cl_debug(DEBUG_TEGRADMA, "received version from SP : 0x%llx\n",
+ (unsigned long long)data.data1);
+ p->outdata[0] = data.data1;
+ break;
+ case MODS_FFA_CMD_HSS_TEST:
+ cl_debug(DEBUG_TEGRADMA, "received response from SP for CMD_HSS_TEST: 0x%llx\n",
+ (unsigned long long)data.data1);
+ p->outdata[0] = data.data1;
+ break;
+ case MODS_FFA_CMD_C2C_TEST:
+ cl_debug(DEBUG_TEGRADMA, "received response from SP for CMD_C2C_TEST: 0x%llx\n",
+ (unsigned long long)data.data1);
+ p->outdata[0] = data.data1;
+ break;
+ }
+
+ if (err) {
+ cl_error("unexpected error from SP: %d\n", err);
+ return err;
+ }
+ // data.data0 always holds the error code of the ffa cmd
+ if (data.data0) {
+ cl_error("error response from SP: %ld\n", (long)data.data0);
+ return -EFAULT;
+ }
+ return OK;
+}
diff --git a/drivers/misc/mods/mods_bpmpipc.c b/drivers/misc/mods/mods_bpmpipc.c
new file mode 100644
index 00000000..f4a3b7a0
--- /dev/null
+++ b/drivers/misc/mods/mods_bpmpipc.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2022-2023, 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
+
+#if (KERNEL_VERSION(6, 2, 0) <= MODS_KERNEL_VERSION)
+#include
+#endif
+
+#define IVC_CHANNEL_SIZE 256
+#define MRQ_MSG_SIZE 128
+#define BPMP_MAIL_DO_ACK (1U << 0U)
+#define BPMP_IVC_TIMEOUT 120000 /* really large timeout to support simulation platforms */
+
+static DEFINE_MUTEX(mods_bpmpipc_lock);
+
+static const u32 MODS_CMD_UPHY_LANE_EOM_SCAN = 9;
+
+struct mods_cmd_uphy_lane_eom_scan_request {
+ u32 brick;
+ u32 lane;
+ u32 pcie_gen5;
+};
+
+struct mods_cmd_uphy_lane_eom_scan_response {
+ u32 data;
+};
+
+struct mods_mrq_uphy_request {
+ u16 lane;
+ u16 cmd;
+ struct mods_cmd_uphy_lane_eom_scan_request lane_eom_scan;
+};
+
+struct mods_mrq_uphy_response {
+ struct mods_cmd_uphy_lane_eom_scan_response eom_status;
+};
+
+struct bpmp_ipc_ch {
+ bool is_init;
+ struct tegra_ivc ivc;
+ void __iomem *db_base;
+ void __iomem *req_base;
+ void __iomem *resp_base;
+ phys_addr_t db_phys_addr;
+ phys_addr_t req_phys_addr;
+ phys_addr_t resp_phys_addr;
+};
+
+static struct bpmp_ipc_ch mods_bpmp_ch = {.is_init = false};
+
+static void bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)
+{
+ struct bpmp_ipc_ch *bpmp_ipc_ch = (struct bpmp_ipc_ch *)data;
+
+ __raw_writel(1, bpmp_ipc_ch->db_base);
+}
+
+static int bpmp_ipc_send(struct mods_client *client,
+ struct tegra_ivc *ivc,
+ const void *data,
+ size_t sz)
+{
+#if (KERNEL_VERSION(6, 2, 0) <= MODS_KERNEL_VERSION)
+ int err;
+ struct iosys_map ob;
+
+ err = tegra_ivc_write_get_next_frame(ivc, &ob);
+ if (err) {
+ cl_error("failed to get next tegra-ivc output frame!\n");
+ iosys_map_clear(&ob);
+ return err;
+ }
+ iosys_map_memcpy_to(&ob, 0, data, sz);
+#else
+ void *frame;
+
+ frame = tegra_ivc_write_get_next_frame(ivc);
+ if (IS_ERR(frame)) {
+ cl_error("failed to get next tegra-ivc output frame!\n");
+ return PTR_ERR(frame);
+ }
+
+ memcpy_toio(frame, data, sz);
+#endif
+
+ return tegra_ivc_write_advance(ivc);
+}
+
+static int bpmp_ipc_recv(struct mods_client *client,
+ struct tegra_ivc *ivc,
+ void *data,
+ size_t sz,
+ u32 timeout_ms)
+{
+ int err;
+#if (KERNEL_VERSION(6, 2, 0) <= MODS_KERNEL_VERSION)
+ struct iosys_map ib;
+#else
+ const void *frame;
+#endif
+ ktime_t end;
+
+ end = ktime_add_ms(ktime_get(), timeout_ms);
+
+#if (KERNEL_VERSION(6, 2, 0) <= MODS_KERNEL_VERSION)
+ do {
+ err = tegra_ivc_read_get_next_frame(ivc, &ib);
+ if (!err)
+ break;
+ } while (ktime_before(ktime_get(), end));
+ if (err) {
+ iosys_map_clear(&ib);
+ err = tegra_ivc_read_get_next_frame(ivc, &ib);
+ if (err) {
+ cl_error("get next tegra-ivc input frame timeout\n");
+ iosys_map_clear(&ib);
+ return err;
+ }
+ }
+ iosys_map_memcpy_from(data, &ib, 0, sz);
+#else
+ do {
+ frame = tegra_ivc_read_get_next_frame(ivc);
+ if (!IS_ERR(frame))
+ break;
+ } while (ktime_before(ktime_get(), end));
+
+ if (IS_ERR(frame)) {
+ frame = tegra_ivc_read_get_next_frame(ivc);
+
+ if (IS_ERR(frame)) {
+ cl_error("get next tegra-ivc input frame timeout\n");
+ return -ETIMEDOUT;
+ }
+ }
+ memcpy_fromio(data, frame, sz);
+#endif
+
+ err = tegra_ivc_read_advance(ivc);
+ if (err < 0)
+ cl_error("tegra_ivc read failed: %d\n", err);
+
+ return err;
+}
+
+static int bpmp_transfer(struct mods_client *client,
+ struct tegra_bpmp_message *msg)
+{
+ int err;
+ struct tegra_bpmp_mb_data req;
+ struct tegra_bpmp_mb_data resp;
+
+ req.code = msg->mrq;
+ req.flags = BPMP_MAIL_DO_ACK;
+ memcpy(req.data, msg->tx.data, msg->tx.size);
+ err = bpmp_ipc_send(client, &mods_bpmp_ch.ivc, &req, sizeof(req));
+
+ if (err == 0) {
+ err = bpmp_ipc_recv(client, &mods_bpmp_ch.ivc,
+ &resp,
+ sizeof(resp),
+ BPMP_IVC_TIMEOUT);
+ }
+
+ if (err == 0) {
+ memcpy(msg->rx.data, resp.data, msg->rx.size);
+ msg->rx.ret = resp.code;
+ }
+
+ return err;
+}
+
+static int mrq_uphy_lane_eom_scan(struct mods_client *client,
+ u32 brick,
+ u32 lane,
+ u32 pcie_gen5,
+ u32 *data)
+{
+ int err;
+ struct mods_mrq_uphy_request req = {
+ .cmd = cpu_to_le32(MODS_CMD_UPHY_LANE_EOM_SCAN)
+ };
+ struct mods_mrq_uphy_response resp;
+ struct tegra_bpmp_message msg = {
+ .mrq = MRQ_UPHY,
+ .tx = {
+ .data = &req,
+ .size = sizeof(req),
+ },
+ .rx = {
+ .data = &resp,
+ .size = sizeof(resp),
+ },
+ };
+
+ req.lane_eom_scan.brick = brick;
+ req.lane_eom_scan.lane = lane;
+ req.lane_eom_scan.pcie_gen5 = pcie_gen5;
+
+ err = bpmp_transfer(client, &msg);
+
+ if (err < 0) {
+ return err;
+ } else if (msg.rx.ret < 0) {
+ err = -EINVAL;
+ return err;
+ }
+
+ *data = resp.eom_status.data;
+ return err;
+}
+
+static int bpmp_ioremap(struct mods_client *client,
+ struct bpmp_ipc_ch *bpmp_ipc_ch,
+ u64 db_phys_addr,
+ u64 req_phys_addr,
+ u64 resp_phys_addr)
+{
+ bpmp_ipc_ch->db_phys_addr = db_phys_addr;
+ bpmp_ipc_ch->req_phys_addr = req_phys_addr;
+ bpmp_ipc_ch->resp_phys_addr = resp_phys_addr;
+
+ bpmp_ipc_ch->db_base = ioremap(bpmp_ipc_ch->db_phys_addr, 64);
+ if (!bpmp_ipc_ch->db_base) {
+ cl_error("failed to remap aperture: 0x%llx\n",
+ (unsigned long long)bpmp_ipc_ch->db_phys_addr);
+ return -ENOMEM;
+ }
+ bpmp_ipc_ch->req_base = ioremap(bpmp_ipc_ch->req_phys_addr, IVC_CHANNEL_SIZE);
+ if (!bpmp_ipc_ch->req_base) {
+ iounmap(bpmp_ipc_ch->db_base);
+ cl_error("failed to remap aperture: 0x%llx\n",
+ (unsigned long long)bpmp_ipc_ch->req_phys_addr);
+ return -ENOMEM;
+ }
+ bpmp_ipc_ch->resp_base = ioremap(bpmp_ipc_ch->resp_phys_addr, IVC_CHANNEL_SIZE);
+ if (!bpmp_ipc_ch->resp_base) {
+ iounmap(bpmp_ipc_ch->db_base);
+ iounmap(bpmp_ipc_ch->req_base);
+ cl_error("failed to remap aperture: 0x%llx\n",
+ (unsigned long long)bpmp_ipc_ch->resp_phys_addr);
+ return -ENOMEM;
+ }
+
+ return OK;
+}
+
+static void bpmp_iounmap(struct bpmp_ipc_ch *bpmp_ipc_ch)
+{
+ iounmap(bpmp_ipc_ch->db_base);
+ iounmap(bpmp_ipc_ch->req_base);
+ iounmap(bpmp_ipc_ch->resp_base);
+
+ bpmp_ipc_ch->db_phys_addr = 0;
+ bpmp_ipc_ch->req_phys_addr = 0;
+ bpmp_ipc_ch->resp_phys_addr = 0;
+}
+
+static int bpmp_ipc_channel_init(struct mods_client *client,
+ struct bpmp_ipc_ch *bpmp_ipc_ch)
+{
+ int err;
+ ktime_t end;
+
+ err = tegra_ivc_init(&bpmp_ipc_ch->ivc, NULL,
+ bpmp_ipc_ch->resp_base, 0,
+ bpmp_ipc_ch->req_base, 0,
+ 1, MRQ_MSG_SIZE,
+ bpmp_ivc_notify, bpmp_ipc_ch);
+
+
+ if (err != 0) {
+ cl_error("tegra-ivc init failed: %d\n", err);
+ return err;
+ }
+
+ tegra_ivc_reset(&bpmp_ipc_ch->ivc);
+
+ end = ktime_add_us(ktime_get(), 2000 * 1000);
+
+ while (tegra_ivc_notified(&bpmp_ipc_ch->ivc) != 0) {
+ usleep_range(100, 200);
+ if (ktime_after(ktime_get(), end)) {
+ cl_error("initialize IVC connection timeout\n");
+ err = -ETIMEDOUT;
+ break;
+ }
+ }
+
+ bpmp_ipc_ch->is_init = true;
+
+ return err;
+}
+
+static void bpmp_ipc_channel_uninit(struct bpmp_ipc_ch *bpmp_ipc_ch)
+{
+ tegra_ivc_cleanup(&bpmp_ipc_ch->ivc);
+}
+
+int mods_bpmpipc_init(struct mods_client *client,
+ u64 db_phys_addr,
+ u64 req_phys_addr,
+ u64 resp_phys_addr)
+{
+ int err = OK;
+
+ if (mods_bpmp_ch.is_init) {
+ if (mods_bpmp_ch.db_phys_addr == db_phys_addr &&
+ mods_bpmp_ch.req_phys_addr == req_phys_addr &&
+ mods_bpmp_ch.resp_phys_addr == resp_phys_addr)
+ return OK;
+ mods_bpmpipc_cleanup();
+ }
+
+ err = bpmp_ioremap(client,
+ &mods_bpmp_ch,
+ db_phys_addr,
+ req_phys_addr,
+ resp_phys_addr);
+ if (err != OK)
+ return err;
+
+ err = bpmp_ipc_channel_init(client, &mods_bpmp_ch);
+ if (err != OK) {
+ bpmp_iounmap(&mods_bpmp_ch);
+ return err;
+ }
+
+ mods_bpmp_ch.is_init = true;
+ mods_debug_printk(DEBUG_TEGRADMA, "bpmp ipc init done\n");
+
+ return err;
+}
+
+void mods_bpmpipc_cleanup(void)
+{
+ if (!mods_bpmp_ch.is_init)
+ return;
+
+ bpmp_ipc_channel_uninit(&mods_bpmp_ch);
+ bpmp_iounmap(&mods_bpmp_ch);
+ mods_bpmp_ch.is_init = false;
+}
+
+int esc_mods_bpmp_uphy_lane_eom_scan(struct mods_client *client,
+ struct MODS_BPMP_UPHY_LANE_EOM_SCAN_PARAMS *p)
+{
+ int err = OK;
+
+ mutex_lock(&mods_bpmpipc_lock);
+
+ err = mods_bpmpipc_init(client,
+ p->db_phys_addr,
+ p->req_phys_addr,
+ p->resp_phys_addr);
+ if (err != OK)
+ goto error;
+
+ err = mrq_uphy_lane_eom_scan(client,
+ p->brick,
+ p->lane,
+ p->pcie_gen5,
+ &p->data);
+
+ if (err != OK)
+ cl_error("mrq uphy lane eom scan failed with brick(%u), lane(%u), pcie_gen5(%u)\n",
+ p->brick, p->lane, p->pcie_gen5);
+
+error:
+ mutex_unlock(&mods_bpmpipc_lock);
+ return err;
+}
diff --git a/drivers/misc/mods/mods_clock.c b/drivers/misc/mods/mods_clock.c
new file mode 100644
index 00000000..86477184
--- /dev/null
+++ b/drivers/misc/mods/mods_clock.c
@@ -0,0 +1,737 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2011-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
+#include
+
+#define ARBITRARY_MAX_CLK_FREQ 3500000000
+
+static struct list_head mods_clock_handles;
+static spinlock_t mods_clock_lock;
+static u32 last_handle;
+
+struct clock_entry {
+ struct clk *pclk;
+ u32 handle;
+ struct list_head list;
+};
+
+static LIST_HEAD(reset_handles);
+
+struct reset_data {
+ char name[MAX_DT_SIZE];
+ struct reset_control *rst;
+};
+
+struct reset_entry {
+ struct list_head list;
+ struct reset_data rst_data;
+ u32 handle;
+};
+
+static struct device_node *find_clocks_node(const char *name)
+{
+ const char *node_name = "mods-simple-bus";
+ struct device_node *pp = NULL, *np = NULL;
+
+ pp = of_find_node_by_name(NULL, node_name);
+
+ if (!pp) {
+ mods_warning_printk("'mods-simple-bus' node not found in device tree\n");
+ return pp;
+ }
+
+ np = of_get_child_by_name(pp, name);
+ return np;
+}
+
+void mods_init_clock_api(void)
+{
+ const char *okay_value = "okay";
+ struct device_node *mods_np = NULL;
+ struct property *pp = NULL;
+ int size_value = 0;
+
+ mods_np = find_clocks_node("mods-clocks");
+ if (!mods_np) {
+ mods_warning_printk("'mods-clocks' node not found in device tree\n");
+ goto err;
+ }
+
+ pp = of_find_property(mods_np, "status", NULL);
+ if (IS_ERR(pp)) {
+ mods_error_printk("'status' prop not found in 'mods-clocks' node.");
+ goto err;
+ }
+
+ /* if status is 'okay', then skip updating property */
+ if (of_device_is_available(mods_np))
+ goto err;
+
+ size_value = strlen(okay_value) + 1;
+ pp->value = kmalloc(size_value, GFP_KERNEL);
+ if (unlikely(!pp->value)) {
+ pp->length = 0;
+ goto err;
+ }
+ strncpy(pp->value, okay_value, size_value);
+ pp->length = size_value;
+
+err:
+ of_node_put(mods_np);
+
+ spin_lock_init(&mods_clock_lock);
+ INIT_LIST_HEAD(&mods_clock_handles);
+ last_handle = 0;
+}
+
+void mods_shutdown_clock_api(void)
+{
+ struct list_head *head = &mods_clock_handles;
+ struct list_head *reset_head = &reset_handles;
+ struct reset_entry *entry = NULL;
+ struct list_head *iter = NULL;
+ struct list_head *tmp = NULL;
+
+ spin_lock(&mods_clock_lock);
+
+ list_for_each_safe(iter, tmp, head) {
+ struct clock_entry *entry
+ = list_entry(iter, struct clock_entry, list);
+ list_del(iter);
+ kfree(entry);
+ }
+
+ list_for_each_safe(iter, tmp, reset_head) {
+ entry = list_entry(iter, struct reset_entry, list);
+ list_del(iter);
+ kfree(entry);
+ }
+
+ spin_unlock(&mods_clock_lock);
+}
+
+static u32 mods_get_clock_handle(struct clk *pclk)
+{
+ struct list_head *head = &mods_clock_handles;
+ struct list_head *iter;
+ struct clock_entry *entry = NULL;
+ u32 handle = 0;
+
+ spin_lock(&mods_clock_lock);
+
+ list_for_each(iter, head) {
+ struct clock_entry *cur
+ = list_entry(iter, struct clock_entry, list);
+ if (cur->pclk == pclk) {
+ entry = cur;
+ handle = cur->handle;
+ break;
+ }
+ }
+
+ if (!entry) {
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!unlikely(!entry)) {
+ entry->pclk = pclk;
+ entry->handle = ++last_handle;
+ handle = entry->handle;
+ list_add(&entry->list, &mods_clock_handles);
+ }
+ }
+
+ spin_unlock(&mods_clock_lock);
+
+ return handle;
+}
+
+static struct clk *mods_get_clock(u32 handle)
+{
+ struct list_head *head = &mods_clock_handles;
+ struct list_head *iter = NULL;
+ struct clk *pclk = NULL;
+
+ spin_lock(&mods_clock_lock);
+
+ list_for_each(iter, head) {
+ struct clock_entry *entry
+ = list_entry(iter, struct clock_entry, list);
+ if (entry->handle == handle) {
+ pclk = entry->pclk;
+ break;
+ }
+ }
+
+ spin_unlock(&mods_clock_lock);
+
+ return pclk;
+}
+
+static struct reset_data find_reset_data(u32 handle)
+{
+ struct list_head *entry = NULL;
+ struct reset_entry *rst_entry = NULL;
+ struct reset_data reset_data = {"", NULL};
+
+ spin_lock(&mods_clock_lock);
+
+ list_for_each(entry, &reset_handles) {
+ rst_entry = list_entry(entry, struct reset_entry, list);
+ if (handle == rst_entry->handle) {
+ reset_data = rst_entry->rst_data;
+ break;
+ }
+ }
+
+ spin_unlock(&mods_clock_lock);
+
+ return reset_data;
+}
+
+static int get_reset_handle(struct reset_data reset_data)
+{
+ int handle = -1;
+ struct list_head *entry = NULL;
+ struct reset_entry *rst_entry = NULL;
+
+ spin_lock(&mods_clock_lock);
+
+ /* If entry has no rst structure, we are past last cached entry */
+ list_for_each(entry, &reset_handles) {
+ rst_entry = list_entry(entry, struct reset_entry, list);
+ handle = rst_entry->handle;
+ if (strcmp(rst_entry->rst_data.name,
+ reset_data.name) == 0) {
+ goto failed;
+ }
+ }
+
+ /* If reset not already in array, then we must add it */
+ rst_entry = kzalloc(sizeof(struct reset_entry), GFP_ATOMIC);
+ if (unlikely(!rst_entry)) {
+ handle = -1;
+ goto failed;
+ }
+ rst_entry->handle = ++handle;
+ rst_entry->rst_data = reset_data;
+ INIT_LIST_HEAD(&rst_entry->list);
+ list_add_tail(&rst_entry->list, &reset_handles);
+
+failed:
+ spin_unlock(&mods_clock_lock);
+ return handle;
+}
+
+int esc_mods_get_clock_handle(struct mods_client *client,
+ struct MODS_GET_CLOCK_HANDLE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ struct device_node *mods_np = NULL;
+ struct property *pp = NULL;
+
+ LOG_ENT();
+
+ mods_np = find_clocks_node("mods-clocks");
+ if (!mods_np || !of_device_is_available(mods_np)) {
+ cl_warn("'mods-clocks' node not found in device tree\n");
+ goto err;
+ }
+ pp = of_find_property(mods_np, "clock-names", NULL);
+ if (IS_ERR(pp)) {
+ cl_error(
+ "No 'clock-names' prop in 'mods-clocks' node for dev %s\n",
+ p->controller_name);
+ goto err;
+ }
+
+ pclk = of_clk_get_by_name(mods_np, p->controller_name);
+
+ if (IS_ERR(pclk))
+ cl_error("clk (%s) not found\n", p->controller_name);
+ else {
+ p->clock_handle = mods_get_clock_handle(pclk);
+ ret = OK;
+ }
+err:
+ of_node_put(mods_np);
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_get_rst_handle(struct mods_client *client,
+ struct MODS_GET_RESET_HANDLE *p)
+{
+ struct reset_data reset_data = {{0}, NULL};
+ struct reset_control *p_reset_ctrl = NULL;
+ int ret = -EINVAL;
+
+ struct device_node *mods_np = NULL;
+ struct property *pp = NULL;
+
+ LOG_ENT();
+
+ mods_np = find_clocks_node("mods-clocks");
+ if (!mods_np || !of_device_is_available(mods_np)) {
+ cl_warn("'mods-clocks' node not found in device tree\n");
+ goto err;
+ }
+ pp = of_find_property(mods_np, "reset-names", NULL);
+ if (IS_ERR(pp)) {
+ cl_error(
+ "No 'reset-names' prop in 'mods-clocks' node for dev %s\n",
+ p->reset_name);
+ goto err;
+ }
+
+ p_reset_ctrl = of_reset_control_get(mods_np, p->reset_name);
+
+ if (IS_ERR(p_reset_ctrl))
+ cl_error("reset (%s) not found\n", p->reset_name);
+ else {
+ strncpy(reset_data.name, p->reset_name,
+ sizeof(reset_data.name) - 1);
+ if (reset_data.name[sizeof(reset_data.name) - 1] != '\0') {
+ cl_error(
+ "reset name %sis too large to store in reset array\n",
+ reset_data.name);
+ goto err;
+ }
+ reset_data.rst = p_reset_ctrl;
+ p->reset_handle = get_reset_handle(reset_data);
+ if (p->reset_handle == -1) {
+ cl_error("no valid reset handle was acquired");
+ goto err;
+ }
+ ret = OK;
+ }
+err:
+ of_node_put(mods_np);
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_set_clock_rate(struct mods_client *client,
+ struct MODS_CLOCK_RATE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n",
+ p->clock_handle);
+ } else {
+ ret = clk_set_rate(pclk, p->clock_rate_hz);
+ if (ret) {
+ cl_error(
+ "unable to set rate %lluHz on clock 0x%x\n",
+ p->clock_rate_hz, p->clock_handle);
+ } else {
+ cl_debug(DEBUG_CLOCK,
+ "successfuly set rate %lluHz on clock 0x%x\n",
+ p->clock_rate_hz, p->clock_handle);
+ }
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_get_clock_rate(struct mods_client *client,
+ struct MODS_CLOCK_RATE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n",
+ p->clock_handle);
+ } else {
+ p->clock_rate_hz = clk_get_rate(pclk);
+ cl_debug(DEBUG_CLOCK, "clock 0x%x has rate %lluHz\n",
+ p->clock_handle, p->clock_rate_hz);
+ ret = OK;
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_get_clock_max_rate(struct mods_client *client,
+ struct MODS_CLOCK_RATE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+ long rate = clk_round_rate(pclk, ARBITRARY_MAX_CLK_FREQ);
+
+ p->clock_rate_hz = rate < 0 ? ARBITRARY_MAX_CLK_FREQ
+ : (unsigned long)rate;
+ cl_debug(DEBUG_CLOCK,
+ "clock 0x%x has max rate %lluHz\n",
+ p->clock_handle, p->clock_rate_hz);
+ ret = OK;
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_set_clock_max_rate(struct mods_client *client,
+ struct MODS_CLOCK_RATE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+#if defined(CONFIG_TEGRA_CLOCK_DEBUG_FUNC)
+ ret = tegra_clk_set_max(pclk, p->clock_rate_hz);
+ if (ret) {
+ cl_error(
+ "unable to override max clock rate %lluHz on clock 0x%x\n",
+ p->clock_rate_hz, p->clock_handle);
+ } else {
+ cl_debug(DEBUG_CLOCK,
+ "successfuly set max rate %lluHz on clock 0x%x\n",
+ p->clock_rate_hz, p->clock_handle);
+ }
+#else
+ cl_error("unable to override max clock rate\n");
+ cl_error(
+ "reconfigure kernel with CONFIG_TEGRA_CLOCK_DEBUG_FUNC=y\n");
+ ret = -EINVAL;
+#endif
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_set_clock_parent(struct mods_client *client,
+ struct MODS_CLOCK_PARENT *p)
+{
+ struct clk *pclk = NULL;
+ struct clk *pparent = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+ pparent = mods_get_clock(p->clock_parent_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else if (!pparent) {
+ cl_error("unrecognized parent clock handle: 0x%x\n",
+ p->clock_parent_handle);
+ } else {
+ ret = clk_set_parent(pclk, pparent);
+ if (ret) {
+ cl_error(
+ "unable to make clock 0x%x parent of clock 0x%x\n",
+ p->clock_parent_handle, p->clock_handle);
+ } else {
+ cl_debug(DEBUG_CLOCK,
+ "successfuly made clock 0x%x parent of clock 0x%x\n",
+ p->clock_parent_handle, p->clock_handle);
+ }
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_get_clock_parent(struct mods_client *client,
+ struct MODS_CLOCK_PARENT *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+ struct clk *pparent = clk_get_parent(pclk);
+
+ p->clock_parent_handle = mods_get_clock_handle(pparent);
+ cl_debug(DEBUG_CLOCK,
+ "clock 0x%x is parent of clock 0x%x\n",
+ p->clock_parent_handle, p->clock_handle);
+ ret = OK;
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_enable_clock(struct mods_client *client,
+ struct MODS_CLOCK_HANDLE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+ ret = clk_prepare(pclk);
+ if (ret) {
+ cl_error(
+ "unable to prepare clock 0x%x before enabling\n",
+ p->clock_handle);
+ }
+ ret = clk_enable(pclk);
+ if (ret) {
+ cl_error("failed to enable clock 0x%x\n",
+ p->clock_handle);
+ } else {
+ cl_debug(DEBUG_CLOCK, "clock 0x%x enabled\n",
+ p->clock_handle);
+ }
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_disable_clock(struct mods_client *client,
+ struct MODS_CLOCK_HANDLE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+ clk_disable(pclk);
+ clk_unprepare(pclk);
+ cl_debug(DEBUG_CLOCK, "clock 0x%x disabled\n",
+ p->clock_handle);
+ ret = OK;
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_is_clock_enabled(struct mods_client *client,
+ struct MODS_CLOCK_ENABLED *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+ p->enable_count = (u32)__clk_is_enabled(pclk);
+ cl_debug(DEBUG_CLOCK, "clock 0x%x enable count is %u\n",
+ p->clock_handle, p->enable_count);
+ ret = OK;
+ }
+
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_reset_assert(struct mods_client *client,
+ struct MODS_RESET_HANDLE *p)
+{
+ int err = -EINVAL;
+ const struct reset_data reset_data = find_reset_data(p->handle);
+ struct device_node *mods_np = NULL;
+
+ LOG_ENT();
+ mods_np = find_clocks_node("mods-clocks");
+ if (!mods_np || !of_device_is_available(mods_np)) {
+ cl_warn("'mods-clocks' node not found in DTB\n");
+ goto error;
+ }
+
+ if (!reset_data.rst) {
+ cl_error("No reset corresponding to requested handle!\n");
+ goto error;
+ }
+
+ if (p->assert)
+ err = reset_control_assert(reset_data.rst);
+ else
+ err = reset_control_deassert(reset_data.rst);
+ if (err) {
+ cl_error("failed to %s reset on '%s'\n",
+ (p->assert ? "asserted" : "deasserted"),
+ reset_data.name);
+ } else {
+ cl_debug(DEBUG_CLOCK, "%s reset on '%s'",
+ (p->assert ? "asserted" : "desasserted"),
+ reset_data.name);
+ }
+error:
+ of_node_put(mods_np);
+
+ LOG_EXT();
+ return err;
+}
+
+int esc_mods_clock_reset_assert(struct mods_client *client,
+ struct MODS_CLOCK_HANDLE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+ const char *clk_name = NULL;
+ struct reset_control *prst = NULL;
+ struct device_node *mods_np = NULL;
+ struct property *pp = NULL;
+
+ mods_np = find_clocks_node("mods-clocks");
+ if (!mods_np || !of_device_is_available(mods_np)) {
+ cl_warn("'mods-clocks' node not found in DTB\n");
+ goto err;
+ }
+ pp = of_find_property(mods_np, "reset-names", NULL);
+ if (IS_ERR(pp)) {
+ cl_error(
+ "No 'reset-names' prop in 'mods-clocks' node for dev %s\n",
+ __clk_get_name(pclk));
+ goto err;
+ }
+
+ clk_name = __clk_get_name(pclk);
+
+ prst = of_reset_control_get(mods_np, clk_name);
+ if (IS_ERR(prst)) {
+ cl_error("reset device %s not found\n", clk_name);
+ goto err;
+ }
+ ret = reset_control_assert(prst);
+ if (ret) {
+ cl_error("failed to assert reset on '%s'\n", clk_name);
+ } else {
+ cl_debug(DEBUG_CLOCK, "asserted reset on '%s'",
+ clk_name);
+ }
+
+err:
+ of_node_put(mods_np);
+ }
+ LOG_EXT();
+ return ret;
+}
+
+int esc_mods_clock_reset_deassert(struct mods_client *client,
+ struct MODS_CLOCK_HANDLE *p)
+{
+ struct clk *pclk = NULL;
+ int ret = -EINVAL;
+
+ LOG_ENT();
+
+ pclk = mods_get_clock(p->clock_handle);
+
+ if (!pclk) {
+ cl_error("unrecognized clock handle: 0x%x\n", p->clock_handle);
+ } else {
+ const char *clk_name = NULL;
+ struct reset_control *prst = NULL;
+ struct device_node *mods_np = NULL;
+ struct property *pp = NULL;
+
+ mods_np = find_clocks_node("mods-clocks");
+ if (!mods_np || !of_device_is_available(mods_np)) {
+ cl_warn("'mods-clocks' node not found in DTB\n");
+ goto err;
+ }
+ pp = of_find_property(mods_np, "reset-names", NULL);
+ if (IS_ERR(pp)) {
+ cl_error(
+ "No 'reset-names' prop in 'mods-clocks' node for dev %s\n",
+ __clk_get_name(pclk));
+ goto err;
+ }
+
+ clk_name = __clk_get_name(pclk);
+
+ prst = of_reset_control_get(mods_np, clk_name);
+ if (IS_ERR(prst)) {
+ cl_error("reset device %s not found\n", clk_name);
+ goto err;
+ }
+ ret = reset_control_deassert(prst);
+ if (ret) {
+ cl_error("failed to assert reset on '%s'\n", clk_name);
+ } else {
+ cl_debug(DEBUG_CLOCK, "deasserted reset on '%s'",
+ clk_name);
+ }
+
+err:
+ of_node_put(mods_np);
+ }
+
+ LOG_EXT();
+ return ret;
+}
diff --git a/drivers/misc/mods/mods_config.h b/drivers/misc/mods/mods_config.h
new file mode 100644
index 00000000..b4d9615c
--- /dev/null
+++ b/drivers/misc/mods/mods_config.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2008-2023, 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 .
+ */
+
+#ifndef _MODS_CONFIG_H_
+#define _MODS_CONFIG_H_
+
+#define MODS_KERNEL_VERSION LINUX_VERSION_CODE
+
+#if KERNEL_VERSION(2, 6, 30) <= MODS_KERNEL_VERSION && \
+ KERNEL_VERSION(4, 16, 0) > MODS_KERNEL_VERSION && \
+ defined(CONFIG_X86)
+# define MODS_HAS_DMA_OPS 1
+#endif
+
+#if KERNEL_VERSION(2, 6, 30) > MODS_KERNEL_VERSION
+# define MODS_HASNT_PCI_RESCAN_BUS 1
+#endif
+
+#if KERNEL_VERSION(2, 6, 31) <= MODS_KERNEL_VERSION
+# define MODS_HAS_IORESOURCE_MEM_64 1
+#endif
+
+#if KERNEL_VERSION(2, 6, 33) <= MODS_KERNEL_VERSION
+# define MODS_HAS_NEW_ACPI_WALK 1
+#else
+# define MODS_HASNT_NUMA_NO_NODE 1
+#endif
+
+#if KERNEL_VERSION(2, 6, 34) <= MODS_KERNEL_VERSION
+# define MODS_HAS_SET_COHERENT_MASK 1
+#endif
+
+#if KERNEL_VERSION(2, 6, 38) <= MODS_KERNEL_VERSION
+# if defined(CONFIG_X86)
+# define MODS_HAS_CONSOLE_LOCK 1
+# endif
+#endif
+
+#if KERNEL_VERSION(3, 4, 0) > MODS_KERNEL_VERSION
+# define MODS_HASNT_PCI_BUS_REMOVE_DEV 1
+#endif
+
+#if KERNEL_VERSION(3, 8, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_NEW_ACPI_HANDLE 1
+# define MODS_HAS_SRIOV 1
+#endif
+
+#if KERNEL_VERSION(3, 14, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_MSIX_RANGE 1
+#else
+# define MODS_HASNT_PCI_LOCK_RESCAN_REMOVE 1
+#endif
+
+#if KERNEL_VERSION(3, 16, 0) <= MODS_KERNEL_VERSION && \
+ defined(CONFIG_VT_HW_CONSOLE_BINDING)
+# define MODS_HAS_CONSOLE_BINDING 1
+#endif
+
+#if KERNEL_VERSION(3, 19, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_DEV_PROPS 1
+#endif
+
+#if defined(CONFIG_PPC64) && KERNEL_VERSION(4, 5, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_PNV_PCI_GET_NPU_DEV 1
+#endif
+
+#if KERNEL_VERSION(4, 12, 0) <= MODS_KERNEL_VERSION && \
+ KERNEL_VERSION(4, 13, 0) > MODS_KERNEL_VERSION && \
+ defined(CONFIG_X86)
+# define MODS_HAS_ASM_SET_MEMORY_HEADER 1
+#endif
+
+#if KERNEL_VERSION(4, 13, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_SET_MEMORY_HEADER 1
+#endif
+
+#if KERNEL_VERSION(4, 14, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_KERNEL_WRITE
+#endif
+
+#if KERNEL_VERSION(4, 16, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_POLL_T 1
+#endif
+
+#if defined(CONFIG_ACPI_NUMA) && KERNEL_VERSION(5, 1, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_PXM_TO_NODE 1
+#endif
+
+#if KERNEL_VERSION(5, 17, 0) <= MODS_KERNEL_VERSION
+# define MODS_HAS_ACPI_FETCH 1
+#endif
+
+#if defined(MODS_HAS_TEGRA) && KERNEL_VERSION(5, 1, 0) <= MODS_KERNEL_VERSION
+# define MODS_ENABLE_BPMP_MRQ_API 1
+#endif
+
+#if KERNEL_VERSION(6, 1, 0) > MODS_KERNEL_VERSION
+# define MODS_HAS_FB_SET_SUSPEND 1
+#endif
+
+#endif /* _MODS_CONFIG_H_ */
diff --git a/drivers/misc/mods/mods_debugfs.c b/drivers/misc/mods/mods_debugfs.c
new file mode 100644
index 00000000..27a9fffe
--- /dev/null
+++ b/drivers/misc/mods/mods_debugfs.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file is part of NVIDIA MODS kernel driver.
+ *
+ * Copyright (c) 2014-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
+#include
+
+static struct dentry *mods_debugfs_dir;
+
+#ifdef CONFIG_ARCH_TEGRA_19x_SOC
+#include "mods_ras.h"
+#endif
+
+#if defined(MODS_HAS_TEGRA) && defined(CONFIG_TEGRA_KFUSE)
+#include
+#endif
+
+#if defined(MODS_HAS_TEGRA) && defined(CONFIG_TEGRA_DC)
+#include