diff --git a/drivers/misc/mods/mods.h b/drivers/misc/mods/mods.h index 897718d9..24b96fbe 100644 --- a/drivers/misc/mods/mods.h +++ b/drivers/misc/mods/mods.h @@ -2,7 +2,7 @@ /* * mods.h - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2020, 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, @@ -643,6 +643,22 @@ struct MODS_DT_INFO { __u32 index; }; +#define MAX_GPIO_NAME_SIZE 256 +struct MODS_GPIO_INFO { + /* OUTPUT */ + /* Irq number to be mapped to gpio and returned to user */ + __u32 irq; + /* IN */ + /* Name of Gpio to be mapped */ + char name[MAX_GPIO_NAME_SIZE]; + /* IN */ + /* DT Name of device that gpio belongs to */ + char dt_name[MAX_DT_SIZE]; + /* IN */ + /* Name of device that gpio belongs to */ + char full_name[MAX_DT_SIZE]; +}; + #define MODS_MASK_TYPE_IRQ_DISABLE 0 #define MODS_MASK_TYPE_IRQ_DISABLE64 1 @@ -1150,6 +1166,7 @@ struct MODS_SYSFS_NODE { }; #define MODS_IRQ_TYPE_FROM_FLAGS(flags) ((flags)&0xf) +#define MODS_IRQ_FLAG_FROM_FLAGS(flags) (((flags)&0xfff0)>>4) /* MODS_ESC_SET_NUM_VF */ struct MODS_SET_NUM_VF { @@ -1470,5 +1487,8 @@ struct MODS_MSR { _IOWR(MODS_IOC_MAGIC, 126, struct MODS_DEVICE_NUMA_INFO_3) #define MODS_ESC_PCI_BUS_RESCAN \ _IOW(MODS_IOC_MAGIC, 127, struct MODS_PCI_BUS_RESCAN) +#define MODS_ESC_MAP_GPIO \ + _IOWR(MODS_IOC_MAGIC, 128, \ + struct MODS_GPIO_INFO) #endif /* _MODS_H_ */ diff --git a/drivers/misc/mods/mods_internal.h b/drivers/misc/mods/mods_internal.h index bf8fb388..433ccc6b 100644 --- a/drivers/misc/mods/mods_internal.h +++ b/drivers/misc/mods/mods_internal.h @@ -2,7 +2,7 @@ /* * mods_internal.h - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2020, 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, @@ -505,8 +505,10 @@ int esc_mods_pci_set_dma_mask(struct mods_client *client, struct MODS_PCI_DMA_MASK *dma_mask); #endif /* irq */ -#if defined(MODS_TEGRA) && defined(CONFIG_OF_IRQ) && defined(CONFIG_OF) +#if defined(MODS_TEGRA) && defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) int esc_mods_map_irq(struct mods_client *client, struct MODS_DT_INFO *p); +int esc_mods_map_irq_to_gpio(struct mods_client *client, + struct MODS_GPIO_INFO *p); #endif int esc_mods_register_irq(struct mods_client *client, struct MODS_REGISTER_IRQ *p); diff --git a/drivers/misc/mods/mods_irq.c b/drivers/misc/mods/mods_irq.c index de313278..08471bec 100644 --- a/drivers/misc/mods/mods_irq.c +++ b/drivers/misc/mods/mods_irq.c @@ -2,7 +2,7 @@ /* * mods_irq.c - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2020, 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, @@ -24,12 +24,14 @@ #include #include #include -#if defined(MODS_TEGRA) && defined(CONFIG_OF_IRQ) && defined(CONFIG_OF) +#if defined(MODS_TEGRA) && defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) #include #include #include #include #include +#include +#include #endif #define PCI_VENDOR_ID_NVIDIA 0x10de @@ -271,9 +273,9 @@ static int rec_irq_done(struct dev_irq_map *t, } else #endif mods_debug_printk(DEBUG_ISR_DETAILED, - "CPU IRQ 0x%x, time=%uus\n", - t->apic_irq, - irq_time); + "CPU IRQ 0x%x, time=%uus\n", + t->apic_irq, + irq_time); return true; } @@ -332,6 +334,9 @@ static int mods_lookup_cpu_irq(u8 client_id, unsigned int irq) struct dev_irq_map *t = NULL; struct dev_irq_map *next = NULL; + if (!test_bit(client_idx - 1, &mp.client_flags)) + continue; + list_for_each_entry_safe(t, next, &client_from_id(client_idx)->irq_list, @@ -346,7 +351,7 @@ static int mods_lookup_cpu_irq(u8 client_id, unsigned int irq) } /* Break out of the outer loop */ - client_idx = MODS_MAX_CLIENTS - 1; + client_idx = MODS_MAX_CLIENTS; break; } } @@ -411,11 +416,37 @@ static int add_irq_map(struct mods_client *client, u32 irq, u32 entry) { - u32 irq_type = MODS_IRQ_TYPE_FROM_FLAGS(p->irq_flags); - struct dev_irq_map *newmap = NULL; + u32 irq_type = MODS_IRQ_TYPE_FROM_FLAGS(p->irq_flags); + struct dev_irq_map *newmap = NULL; + u64 irq_flags = MODS_IRQ_FLAG_FROM_FLAGS(p->irq_flags); + u64 valid_mask = IRQF_TRIGGER_NONE; LOG_ENT(); + /* Get the flags based on the interrupt type*/ + switch (irq_type) { + case MODS_IRQ_TYPE_INT: + irq_flags = IRQF_SHARED; + break; + + case MODS_IRQ_TYPE_CPU: + valid_mask = IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_SHARED; + + /* Either use a valid flag bit or no flags */ + if (irq_flags & ~valid_mask) { + mods_error_printk("invalid Device Interrupt flag %llx\n", + (long long) irq_flags); + return -EINVAL; + } + break; + + default: + irq_flags = IRQF_TRIGGER_NONE; + break; + } + /* Allocate memory for the new entry */ newmap = kzalloc(sizeof(*newmap), GFP_KERNEL | __GFP_NORETRY); if (unlikely(!newmap)) { @@ -437,10 +468,12 @@ static int add_irq_map(struct mods_client *client, if (request_irq( irq, &mods_irq_handle, - (irq_type == MODS_IRQ_TYPE_INT) ? IRQF_SHARED : 0, + irq_flags, "nvidia mods", newmap)) { - mods_error_printk("unable to enable IRQ 0x%x\n", irq); + mods_error_printk("unable to enable IRQ 0x%x with flags %llx\n", + irq, + (long long) irq_flags); kfree(newmap); atomic_dec(&client->num_allocs); LOG_EXT(); @@ -475,7 +508,9 @@ static int add_irq_map(struct mods_client *client, /* Print out successful registration string */ if (irq_type == MODS_IRQ_TYPE_CPU) - mods_debug_printk(DEBUG_ISR, "registered CPU IRQ 0x%x\n", irq); + mods_debug_printk(DEBUG_ISR, "registered CPU IRQ 0x%x with flags %llx\n", + irq, + (long long) irq_flags); #ifdef CONFIG_PCI else if ((irq_type == MODS_IRQ_TYPE_INT) || (irq_type == MODS_IRQ_TYPE_MSI) || @@ -1292,7 +1327,7 @@ int esc_mods_query_irq_3(struct mods_client *client, p->irq_list[i].dev.function = PCI_FUNC(dev->devfn); } else { p->irq_list[i].dev.domain = 0; - p->irq_list[i].dev.bus = 0; + p->irq_list[i].dev.bus = q->data[index].irq; p->irq_list[i].dev.device = 0xFFU; p->irq_list[i].dev.function = 0xFFU; } @@ -1428,7 +1463,7 @@ int esc_mods_irq_handled(struct mods_client *client, int esc_mods_map_irq(struct mods_client *client, struct MODS_DT_INFO *p) { - int err; + int err = 0; /* the physical irq */ int hwirq; /* platform device handle */ @@ -1438,18 +1473,31 @@ int esc_mods_map_irq(struct mods_client *client, /* Search for the node by device tree name */ struct device_node *np = of_find_node_by_name(NULL, p->dt_name); + if (!np) { + mods_error_printk("node %s is not valid\n", p->full_name); + err = -EINVAL; + goto error; + } + /* Can be multiple nodes that share the same dt name, */ /* make sure you get the correct node matched by the device's full */ /* name in device tree (i.e. watchdog@30c0000 as opposed */ /* to watchdog) */ - while (of_node_cmp(np->full_name, p->full_name) != 0) + while (of_node_cmp(np->full_name, p->full_name)) { np = of_find_node_by_name(np, p->dt_name); + if (!np) { + mods_error_printk("node %s is not valid\n", + p->full_name); + err = -EINVAL; + goto error; + } + } p->irq = irq_of_parse_and_map(np, p->index); err = of_irq_parse_one(np, p->index, &oirq); if (err) { - mods_error_printk("Could not parse IRQ\n"); - return err; + mods_error_printk("could not parse IRQ\n"); + goto error; } hwirq = oirq.args[1]; @@ -1470,7 +1518,61 @@ int esc_mods_map_irq(struct mods_client *client, TOP_TKE_TKEIE(hwirq)); } +error: + of_node_put(np); /* enable the interrupt */ - return OK; + return err; +} + +int esc_mods_map_irq_to_gpio(struct mods_client *client, + struct MODS_GPIO_INFO *p) +{ + //TODO: Make sure you are allocating gpio properly + int gpio_handle; + int irq; + int err = 0; + + struct device_node *np = of_find_node_by_name(NULL, p->dt_name); + + if (!np) { + mods_error_printk("node %s is not valid\n", p->full_name); + err = -EINVAL; + goto error; + } + + while (of_node_cmp(np->full_name, p->full_name)) { + np = of_find_node_by_name(np, p->dt_name); + if (!np) { + mods_error_printk("node %s is not valid\n", + p->full_name); + err = -EINVAL; + goto error; + } + } + + gpio_handle = of_get_named_gpio(np, p->name, 0); + if (!gpio_is_valid(gpio_handle)) { + mods_error_printk("gpio %s is missing\n", p->name); + err = gpio_handle; + goto error; + } + + err = gpio_direction_input(gpio_handle); + if (err < 0) { + mods_error_printk("pex_rst_gpio input direction change failed\n"); + goto error; + } + + irq = gpio_to_irq(gpio_handle); + if (irq < 0) { + mods_error_printk("Unable to get irq for pex_rst_gpio\n"); + err = -EINVAL; + goto error; + } + p->irq = irq; + +error: + of_node_put(np); + return err; } #endif diff --git a/drivers/misc/mods/mods_krnl.c b/drivers/misc/mods/mods_krnl.c index ac258bad..eae3096e 100644 --- a/drivers/misc/mods/mods_krnl.c +++ b/drivers/misc/mods/mods_krnl.c @@ -2,7 +2,7 @@ /* * mods_krnl.c - This file is part of NVIDIA MODS kernel driver. * - * Copyright (c) 2008-2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2008-2020, 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, @@ -1829,13 +1829,18 @@ static long mods_krnl_ioctl(struct file *fp, err = -EINVAL; break; +#if defined(MODS_TEGRA) && defined(CONFIG_OF) && defined(CONFIG_OF_IRQ) case MODS_ESC_MAP_INTERRUPT: -#if defined(MODS_TEGRA) && defined(CONFIG_OF_IRQ) && defined(CONFIG_OF) MODS_IOCTL(MODS_ESC_MAP_INTERRUPT, esc_mods_map_irq, MODS_DT_INFO); -#endif break; + case MODS_ESC_MAP_GPIO: + MODS_IOCTL(MODS_ESC_MAP_GPIO, + esc_mods_map_irq_to_gpio, MODS_GPIO_INFO); + break; +#endif + case MODS_ESC_REGISTER_IRQ: MODS_IOCTL_NORETVAL(MODS_ESC_REGISTER_IRQ, esc_mods_register_irq, MODS_REGISTER_IRQ);