misc: mods: update MODS kernel driver

Bug 3461002

Change-Id: I9f577323cd8034a4464953031398c39a409ad69c
Signed-off-by: Chris Dragan <kdragan@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2658852
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
GVS: Gerrit_Virtual_Submit
This commit is contained in:
Chris Dragan
2022-01-24 07:31:46 -08:00
committed by Laxman Dewangan
parent aa915e1412
commit 13ac7c137c
7 changed files with 285 additions and 142 deletions

View File

@@ -2,7 +2,7 @@
/*
* mods_acpi.c - This file is part of NVIDIA MODS kernel driver.
*
* Copyright (c) 2008-2020, NVIDIA CORPORATION. All rights reserved.
* 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,
@@ -34,6 +34,105 @@ static acpi_status mods_acpi_find_acpi_handler(acpi_handle,
* 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;
}
static int acpi_get_dev_children(struct mods_client *client,
dev_children_fptr fptr,
acpi_handle dev_handle,
void *out_data)
{
int err = OK;
acpi_status status;
struct acpi_device *device = NULL;
struct list_head *node = NULL;
struct list_head *next = NULL;
LOG_ENT();
status = acpi_bus_get_device(dev_handle, &device);
if (ACPI_FAILURE(status) || !device) {
cl_error("ACPI: device for fetching device children not found\n");
LOG_EXT();
return -EINVAL;
}
list_for_each_safe(node, next, &device->children) {
unsigned long long device_id = 0;
struct acpi_device *dev =
list_entry(node, struct acpi_device, node);
status = acpi_evaluate_integer(dev->handle,
"_ADR",
NULL,
&device_id);
if (ACPI_FAILURE(status))
/* Couldnt query device_id for this device */
continue;
err = fptr(client, device_id, dev->handle, out_data);
if (err == -EALREADY) {
err = OK;
break;
}
}
LOG_EXT();
return err;
}
/* store handle if found. */
static void mods_acpi_handle_init(struct mods_client *client,
char *method_name,
@@ -187,9 +286,6 @@ static int mods_eval_acpi_method(struct mods_client *client,
union acpi_object acpi_params[ACPI_MAX_ARGUMENT_NUMBER];
acpi_handle acpi_method_handler = NULL;
struct pci_dev *dev = NULL;
struct acpi_device *acpi_dev = NULL;
struct list_head *node = NULL;
struct list_head *next = NULL;
LOG_ENT();
@@ -229,46 +325,20 @@ static int mods_eval_acpi_method(struct mods_client *client,
}
if (acpi_id != ACPI_MODS_IGNORE_ACPI_ID) {
status = acpi_bus_get_device(acpi_method_handler, &acpi_dev);
if (ACPI_FAILURE(status) || !acpi_dev) {
cl_error("ACPI: device for %s not found\n",
p->method_name);
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 = NULL;
list_for_each_safe(node, next, &acpi_dev->children) {
unsigned long long device_id = 0;
struct acpi_device *acpi_dev =
list_entry(node, struct acpi_device, node);
if (!acpi_dev)
continue;
status = acpi_evaluate_integer(acpi_dev->handle,
"_ADR",
NULL,
&device_id);
if (ACPI_FAILURE(status))
/* Couldn't query device_id for this device */
continue;
if (device_id == acpi_id) {
acpi_method_handler = acpi_dev->handle;
cl_debug(DEBUG_ACPI,
"ACPI: found %s (id = 0x%x) on dev %04x:%02x:%02x.%x\n",
p->method_name,
(unsigned int)device_id,
pdevice->domain,
pdevice->bus,
pdevice->device,
pdevice->function);
break;
}
}
acpi_method_handler = in_data.dev_handle;
}
if (!acpi_method_handler) {
@@ -279,6 +349,14 @@ static int mods_eval_acpi_method(struct mods_client *client,
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;
@@ -368,19 +446,17 @@ 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;
struct acpi_device *device = NULL;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *ddc;
union acpi_object ddc_arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list input = { 1, &ddc_arg0 };
struct list_head *node;
struct list_head *next;
u32 i;
acpi_handle dev_handle = NULL;
acpi_handle lcd_dev_handle = NULL;
struct pci_dev *dev = NULL;
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();
@@ -411,49 +487,26 @@ static int mods_acpi_get_ddc(struct mods_client *client,
return -EINVAL;
}
status = acpi_bus_get_device(dev_handle, &device);
if (ACPI_FAILURE(status) || !device) {
cl_error("ACPI: device for _DDC not found\n");
/*
* 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;
}
list_for_each_safe(node, next, &device->children) {
unsigned long long device_id = 0;
struct acpi_device *dev =
list_entry(node, struct acpi_device, node);
if (!dev)
continue;
status = acpi_evaluate_integer(dev->handle,
"_ADR",
NULL,
&device_id);
if (ACPI_FAILURE(status))
/* Couldnt query device_id for this device */
continue;
device_id = (device_id & 0xffff);
if ((device_id == 0x0110) || /* Only for an LCD*/
(device_id == 0x0118) ||
(device_id == 0x0400)) {
lcd_dev_handle = dev->handle;
cl_debug(DEBUG_ACPI,
"ACPI: Found LCD 0x%llx on dev %04x:%02x:%02x.%x\n",
device_id,
p->device.domain,
p->device.bus,
p->device.device,
p->device.function);
break;
}
}
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",
@@ -466,49 +519,54 @@ static int mods_acpi_get_ddc(struct mods_client *client,
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 = 0x1 for 128 bytes EDID buffer
* ARG0 = 0x2 for 256 bytes EDID buffer
* ARG0 is size of EDID buffer in 128-byte blocks.
*/
for (i = 1; i <= 2; i++) {
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))
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 (ACPI_FAILURE(status)) {
cl_error("ACPI method _DDC (EDID) failed\n");
pci_dev_put(dev);
LOG_EXT();
return -EINVAL;
}
ddc = output.pointer;
if (ddc && (ddc->type == ACPI_TYPE_BUFFER)
&& (ddc->buffer.length > 0)) {
if (ddc->buffer.length <= sizeof(p->out_buffer)) {
p->out_data_size = ddc->buffer.length;
memcpy(p->out_buffer,
ddc->buffer.pointer,
p->out_data_size);
err = OK;
} else {
cl_error(
"output buffer too small for ACPI method _DDC (EDID)\n");
err = -EINVAL;
}
} else {
cl_error("unsupported ACPI output type\n");
err = -EINVAL;
}
kfree(output.pointer);
if (!data_found)
cl_error("ACPI method _DDC (EDID) failed\n");
pci_dev_put(dev);
LOG_EXT();
return err;
@@ -574,3 +632,56 @@ int esc_mods_acpi_get_ddc(struct mods_client *client,
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;
}

View File

@@ -2,7 +2,7 @@
/*
* mods_config.h - This file is part of NVIDIA MODS kernel driver.
*
* Copyright (c) 2008-2021, NVIDIA CORPORATION. All rights reserved.
* 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,
@@ -100,4 +100,8 @@
# define MODS_HAS_TEGRA 1
#endif
#if defined(MODS_HAS_TEGRA) && KERNEL_VERSION(5, 1, 0) <= MODS_KERNEL_VERSION
# define MODS_ENABLE_BPMP_MRQ_API 1
#endif
#endif /* _MODS_CONFIG_H_ */

View File

@@ -2,7 +2,7 @@
/*
* mods_internal.h - This file is part of NVIDIA MODS kernel driver.
*
* Copyright (c) 2008-2022, NVIDIA CORPORATION. All rights reserved.
* 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,
@@ -38,8 +38,7 @@
#include <linux/set_memory.h>
#endif
#if KERNEL_VERSION(5, 1, 0) <= LINUX_VERSION_CODE
#define MODS_ENABLE_BPMP_MRQ_API
#ifdef MODS_ENABLE_BPMP_MRQ_API
#include <soc/tegra/bpmp.h>
#endif
@@ -515,6 +514,8 @@ int esc_mods_acpi_get_ddc(struct mods_client *client,
struct MODS_ACPI_GET_DDC *p);
int esc_mods_acpi_get_ddc_2(struct mods_client *client,
struct MODS_ACPI_GET_DDC_2 *p);
int esc_mods_get_acpi_dev_children(struct mods_client *client,
struct MODS_GET_ACPI_DEV_CHILDREN *p);
#endif
/* pci */
#ifdef CONFIG_PCI

View File

@@ -2,7 +2,7 @@
/*
* mods_irq.c - This file is part of NVIDIA MODS kernel driver.
*
* Copyright (c) 2008-2021, NVIDIA CORPORATION. All rights reserved.
* 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,
@@ -534,10 +534,10 @@ static int add_irq_map(struct mods_client *client,
(irq_type == MODS_IRQ_TYPE_MSIX)) {
cl_debug(DEBUG_ISR,
"dev %04x:%02x:%02x.%x registered %s IRQ 0x%x\n",
pci_domain_nr(dev->bus),
dev->bus->number,
PCI_SLOT(dev->devfn),
PCI_FUNC(dev->devfn),
dev ? pci_domain_nr(dev->bus) : 0U,
dev ? dev->bus->number : 0U,
dev ? PCI_SLOT(dev->devfn) : 0U,
dev ? PCI_FUNC(dev->devfn) : 0U,
mods_irq_type_name(irq_type),
irq);
}

View File

@@ -100,6 +100,14 @@ static const struct pci_device_id mods_pci_table[] = {
.class = (PCI_CLASS_DISPLAY_3D << 8),
.class_mask = ~0
},
{
.vendor = PCI_VENDOR_ID_NVIDIA,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.class = (PCI_CLASS_BRIDGE_OTHER << 8),
.class_mask = ~0
},
{ 0 }
};
@@ -1697,6 +1705,7 @@ static int esc_mods_write_sysfs_node(struct mods_client *client,
memcpy(pdata->path, "/sys/", 5);
pdata->path[sizeof(pdata->path) - 1] = 0;
memset(&task, 0, sizeof(task));
task.path = pdata->path;
task.data = pdata->contents;
task.data_size = pdata->size;
@@ -2244,6 +2253,10 @@ static long mods_krnl_ioctl(struct file *fp,
esc_mods_acpi_get_ddc_2, MODS_ACPI_GET_DDC_2);
break;
case MODS_ESC_GET_ACPI_DEV_CHILDREN:
MODS_IOCTL(MODS_ESC_GET_ACPI_DEV_CHILDREN,
esc_mods_get_acpi_dev_children, MODS_GET_ACPI_DEV_CHILDREN);
break;
#else
case MODS_ESC_EVAL_ACPI_METHOD:
/* fallthrough */
@@ -2256,6 +2269,8 @@ static long mods_krnl_ioctl(struct file *fp,
case MODS_ESC_ACPI_GET_DDC:
/* fallthrough */
case MODS_ESC_ACPI_GET_DDC_2:
/* fallthrough */
case MODS_ESC_GET_ACPI_DEV_CHILDREN:
/* Silent failure to avoid clogging kernel log */
err = -EINVAL;
break;

View File

@@ -2,7 +2,7 @@
/*
* mods_mem.c - This file is part of NVIDIA MODS kernel driver.
*
* Copyright (c) 2008-2021, NVIDIA CORPORATION. All rights reserved.
* 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,
@@ -27,7 +27,7 @@
#include <linux/of.h>
#endif
#ifdef CONFIG_ARCH64
#ifdef CONFIG_ARM64
#include <linux/cache.h>
#endif
@@ -343,7 +343,7 @@ static int mods_create_default_dma_map(struct mods_client *client,
return err;
}
#endif
#endif /* CONFIG_PCI */
/* Find the dma mapping chunk for the specified memory. */
static struct MODS_DMA_MAP *find_dma_map(struct MODS_MEM_INFO *p_mem_info,
@@ -2051,7 +2051,7 @@ int esc_mods_dma_unmap_memory(struct mods_client *client,
LOG_EXT();
return err;
}
#endif
#endif /* CONFIG_PCI */
#ifdef MODS_HAS_TEGRA
/* map dma buffer by iommu */
@@ -2141,15 +2141,15 @@ int esc_mods_iommu_dma_map_memory(struct mods_client *client,
/* Check if IOVAs are contiguous */
iova_offset = 0;
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
if (sg_dma_address(sg) != (~(dma_addr_t)0) &&
sg_dma_address(sg) != (iova + iova_offset)) {
iova_offset = iova_offset + sg->offset;
if (sg_dma_address(sg) != (iova + iova_offset)
|| sg_dma_len(sg) != sg->length) {
cl_error("sg not contiguous:dma 0x%llx, iova 0x%llx\n",
sg_dma_address(sg),
(u64)(iova + iova_offset));
err = -EINVAL;
break;
}
iova_offset += sg->length;
}
if (err) {
dma_unmap_sg_attrs(smmu_pdev->dev, sgt->sgl, sgt->nents,
@@ -2236,7 +2236,7 @@ failed:
LOG_EXT();
return err;
}
#endif
#endif /* MODS_HAS_TEGRA */
#ifdef CONFIG_ARM64
static void clear_contiguous_cache(struct mods_client *client,
@@ -2422,7 +2422,7 @@ int esc_mods_flush_cpu_cache_range(struct mods_client *client,
LOG_EXT();
return OK;
}
#endif
#endif /* CONFIG_ARM64 */
static int mods_post_alloc(struct mods_client *client,
struct MODS_PHYS_CHUNK *chunk,

View File

@@ -25,7 +25,7 @@
/* Driver version */
#define MODS_DRIVER_VERSION_MAJOR 4
#define MODS_DRIVER_VERSION_MINOR 2
#define MODS_DRIVER_VERSION_MINOR 8
#define MODS_DRIVER_VERSION ((MODS_DRIVER_VERSION_MAJOR << 8) | \
((MODS_DRIVER_VERSION_MINOR / 10) << 4) | \
(MODS_DRIVER_VERSION_MINOR % 10))
@@ -1056,6 +1056,7 @@ union ACPI_ARGUMENT {
#define ACPI_MODS_TYPE_METHOD 3
#define ACPI_MAX_BUFFER_LENGTH 4096
#define ACPI_MAX_DEV_CHILDREN 16
#define ACPI_MAX_METHOD_LENGTH 12
#define ACPI_MAX_ARGUMENT_NUMBER 12
@@ -1151,6 +1152,16 @@ struct MODS_ACPI_GET_DDC {
struct mods_pci_dev device;
};
/* Used by MODS_ESC_GET_ACPI_DEV_CHILDREN ioctl */
struct MODS_GET_ACPI_DEV_CHILDREN {
/* OUT */
__u32 num_children;
__u32 children[ACPI_MAX_DEV_CHILDREN];
/* IN */
struct mods_pci_dev_2 device;
};
/* Used by ioctls:
* - MODS_ESC_GET_API_VERSION
* - MODS_ESC_GET_KERNEL_VERSION (deprecated)
@@ -2009,6 +2020,7 @@ struct MODS_TEGRA_OIST_STATUS {
#define MODS_ESC_MODS_GET_DRIVER_STATS MODSIO(R, 135, MODS_GET_DRIVER_STATS)
#define MODS_ESC_BPMP_SET_PCIE_STATE MODSIO(W, 136, MODS_SET_PCIE_STATE)
#define MODS_ESC_BPMP_INIT_PCIE_EP_PLL MODSIO(W, 137, MODS_INIT_PCIE_EP_PLL)
#define MODS_ESC_GET_ACPI_DEV_CHILDREN MODSIO(WR, 138, MODS_GET_ACPI_DEV_CHILDREN)
#define MODS_ESC_SEND_TZ_MSG MODSIO(WR, 139, MODS_TZ_PARAMS)
#define MODS_ESC_OIST_STATUS MODSIO(WR, 140, MODS_TEGRA_OIST_STATUS)