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