drivers: Add GTE support

The CL adds GTE driver support in OOT. The driver is copied from nvidia
directory in dev-main with below changes.
- Added removable module support

This driver as module is needed as per the oot development requirement.

Bug 3583612

Change-Id: I2772078fb96e1b172e45befe643b4c7c569866d9
Signed-off-by: Dipen Patel <dipenp@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2706260
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
Dipen Patel
2022-05-02 22:55:18 +00:00
committed by mobile promotions
parent c9217a1b82
commit e7c8a70f65
10 changed files with 2028 additions and 24 deletions

View File

@@ -0,0 +1,78 @@
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/tegra_gte/tegra-gte.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra T194 GTE (Generic hardware Timestamping Engine) bindings
maintainers:
- Dipen Patel <dipenp@nvidia.com>
description: |+
GTE is a hardware block which monitors various events for the state change and
timestamps them using TSC counter. Tegra implements LIC GTE and AON GTE
instances. LIC GTE is used only to monitor LIC IRQ lines and AON GTE is used
to monitor Always On domain GPIO lines.
properties:
compatible:
enum:
- nvidia,tegra194-gte-lic
- nvidia,tegra194-gte-aon
- nvidia,tegra234-gte-aon
reg:
maxItems: 1
description: physical base address.
interrupts:
description:
interrupt number
nvidia,int-threshold:
$ref: /schemas/types.yaml#/definitions/uint32
description:
GTE device generates its interrupt based on this u32 FIFO threshold
value. The recommended value is 1.
minimum: 1
maximum: 256
nvidia,num-slices:
$ref: /schemas/types.yaml#/definitions/uint32
description:
GTE lines are arranged in 32 bit slice where each bit represents different
line/signal that it can enable/configure for the timestamp. It is u32
property and the value depends on the GTE instance in the chip. Tegra194
LIC instance uses 11 slices while Tegra234 instance uses 17 slices to
represent interrupts line from LIC. The AON-GPIO GTE instances for both
Tegra194 and Tegra234 use 3 slices for the GPIO.
enum: [3, 11, 17]
required:
- compatible
- reg
- interrupts
- nvidia,num-slices
additionalProperties: false
examples:
- |
gte@3aa0000 {
interrupts = <0x00 0x0b 0x04>;
compatible = "nvidia,tegra194-gte-lic";
nvidia,int-threshold = <0x01>;
status = "okay";
nvidia,num-slices = <0x11>;
reg = <0x3aa0000 0x10000>;
};
gte@c1e0000 {
interrupts = <0x00 0x0d 0x04>;
compatible = "nvidia,tegra234-gte-aon";
nvidia,int-threshold = <0x01>;
status = "okay";
nvidia,num-slices = <0x03>;
reg = <0xc1e0000 0x10000>;
};

View File

@@ -0,0 +1,7 @@
Ioctl Numbers
21 February 2020
Dipen Patel
Code Seq#(hex) Include File Comments
========================================================
0xB5 00 linux/tegra-gte-ioctl.h

View File

@@ -32,4 +32,6 @@ obj-m += watchdog/
obj-m += video/tegra/ obj-m += video/tegra/
obj-m += virt/tegra/ obj-m += virt/tegra/
obj-m += media/ obj-m += media/
obj-m += staging/platform/tegra/gte/
obj-m += staging/platform/tegra/gte_test/

View File

@@ -0,0 +1 @@
obj-m += tegra194_gte.o

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
obj-m += tegra194_gte_test.o

View File

@@ -0,0 +1,355 @@
/*
* Copyright (c) 2020-2022, NVIDIA CORPORATION & AFFILIATES.All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <linux/version.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/tegra-gte.h>
#include <linux/gpio.h>
#include <linux/timer.h>
/*
* Sample GTE test driver demonstrating GTE API usage.
*
* Sample drivers monitors LIC IRQ provided by lic_irq module parameter and
* GPIO provided by gpio_in parameter.
*
* Note: gpio_out and gpio_in need to be shorted externally using some wire
* in order for this test driver to work for the GPIO monitoring.
*/
/*
* This represents global ID of the GPIO since global ID or logical
* partitioning of the GPIOs across various GPIO controller happens
* at the run time, user has to provide this parameter in order to
* request.
*
* gpio_in will be used in GTE to monitor the event and will be configured
* as input.
*/
static int gpio_in = -EINVAL;
module_param(gpio_in, int, 0660);
/*
* Same comment as gpio_in but will be used as output and to toggle gpio_in
* state.
*/
static int gpio_out = -EINVAL;
module_param(gpio_out, int, 0660);
/* IRQ number to monitor */
static int lic_irq = -EINVAL;
module_param(lic_irq, int, 0660);
static struct tegra_gte_test {
struct tegra_gte_ev_desc *data_lic;
struct tegra_gte_ev_desc *data_gpio;
int gpio_in_irq;
struct timer_list timer;
struct kobject *kobj;
} gte;
/*
* Sysfs attribute to register/unregister GTE gpio event for 1 and 0 values
*/
static ssize_t store_gpio_en_dis(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
int ret = count;
unsigned long val = 0;
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "nvidia,tegra194-gte-aon");
if (!np) {
np = of_find_compatible_node(NULL, NULL, "nvidia,tegra234-gte-aon");
if (!np) {
pr_err("Could not locate aon gte node\n");
return -EINVAL;
}
}
if (kstrtoul(buf, 10, &val) < 0) {
ret = -EINVAL;
goto error;
}
if (val == 1) {
if (gte.data_gpio) {
pr_info("gpio_in is already registered\n");
ret = -EEXIST;
goto error;
}
gte.data_gpio = tegra_gte_register_event(np, gpio_in);
if (IS_ERR(gte.data_gpio)) {
pr_err("Could not register gpio\n");
ret = PTR_ERR(gte.data_gpio);
gte.data_gpio = NULL;
goto error;
}
} else if (val == 0) {
if (!gte.data_gpio) {
pr_info("gpio_in is not registered\n");
ret = -EINVAL;
goto error;
}
ret = tegra_gte_unregister_event(gte.data_gpio);
if (ret == -EBUSY) {
/* User should retry */
pr_err("failed to unregister gpio in\n");
goto error;
} else { /* For anything else set data to null */
gte.data_gpio = NULL;
if (ret == 0)
ret = count;
}
} else {
ret = -EINVAL;
}
error:
of_node_put(np);
return ret;
}
/*
* Sysfs attribute to register/unregister GTE LIC IRQ event for 1 and 0 values
*/
static ssize_t store_lic_irq_en_dis(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
int ret = count;
unsigned long val = 0;
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "nvidia,tegra194-gte-lic");
if (!np) {
pr_err("Could not locate lic gte node\n");
return -EINVAL;
}
if (kstrtoul(buf, 10, &val) < 0) {
ret = -EINVAL;
goto error;
}
if (val == 1) {
if (gte.data_lic) {
pr_info("lic_irq is already registered\n");
ret = -EEXIST;
goto error;
}
gte.data_lic = tegra_gte_register_event(np, lic_irq);
if (IS_ERR(gte.data_lic)) {
pr_err("Could not register lic irq\n");
ret = PTR_ERR(gte.data_lic);
gte.data_lic = NULL;
goto error;
}
} else if (val == 0) {
if (!gte.data_lic) {
pr_info("lic_irq is not registered\n");
ret = -EINVAL;
goto error;
}
ret = tegra_gte_unregister_event(gte.data_lic);
if (ret == -EBUSY) {
/* User should retry */
pr_err("failed to unregister lic irq\n");
goto error;
} else { /* For anything else set data to null */
gte.data_lic = NULL;
if (ret == 0)
ret = count;
}
} else {
ret = -EINVAL;
}
error:
of_node_put(np);
return ret;
}
/* Shows LIC event timestamp information */
static ssize_t show_lic_irq_ts(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct tegra_gte_ev_detail hts;
if (tegra_gte_retrieve_event((gte.data_lic), &hts) != 0)
return -EINVAL;
pr_debug("Retrieved lic event ts_raw: %llu, ts_ns %llu\n",
hts.ts_raw, hts.ts_ns);
return scnprintf(buf, PAGE_SIZE, "ts_raw: %llu, ts_ns: %llu\n",
hts.ts_raw, hts.ts_ns);
}
struct kobj_attribute gpio_en_dis_attr =
__ATTR(gpio_en_dis, 0220, NULL, store_gpio_en_dis);
struct kobj_attribute lic_irq_en_dis_attr =
__ATTR(lic_irq_en_dis, 0220, NULL, store_lic_irq_en_dis);
struct kobj_attribute lic_irq_ts_attr =
__ATTR(lic_irq_ts, 0440, show_lic_irq_ts, NULL);
static struct attribute *attrs[] = {
&gpio_en_dis_attr.attr,
&lic_irq_en_dis_attr.attr,
&lic_irq_ts_attr.attr,
NULL,
};
static struct attribute_group tegra_gte_test_attr_group = {
.attrs = attrs,
};
static int tegra_gte_test_sysfs_create(void)
{
int ret;
/* Creates object under /sys/kernel/ */
gte.kobj = kobject_create_and_add("tegra_gte_test", kernel_kobj);
if (!gte.kobj)
return -ENOMEM;
ret = sysfs_create_group(gte.kobj, &tegra_gte_test_attr_group);
if (ret)
kobject_put(gte.kobj);
return ret;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
static void gpio_timer_cb(unsigned long data)
#else
static void gpio_timer_cb(struct timer_list *t)
#endif
{
gpio_set_value(gpio_out, !gpio_get_value(gpio_out));
mod_timer(&gte.timer, jiffies + msecs_to_jiffies(5000));
}
static irqreturn_t tegra_gte_test_gpio_isr(int irq, void *data)
{
struct tegra_gte_ev_detail hts;
struct tegra_gte_test *gte = data;
if (tegra_gte_retrieve_event((gte->data_gpio), &hts) != 0) {
pr_info("No timestamp available\n");
return IRQ_HANDLED;
}
pr_info("GPIO HW Timestamp: raw %llu, ns %llu\n",
hts.ts_raw, hts.ts_ns);
return IRQ_HANDLED;
}
static int __init tegra_gte_test_init(void)
{
int ret = 0;
if (gpio_out == -EINVAL || gpio_in == -EINVAL || lic_irq == EINVAL) {
pr_err("Invalid gpio_out, gpio_in and irq\n");
return -EINVAL;
}
gte.data_lic = NULL;
gte.data_gpio = NULL;
ret = gpio_request(gpio_out, "gte_test_gpio_out");
if (ret) {
pr_err("failed request gpio out\n");
return -EINVAL;
}
ret = gpio_direction_output(gpio_out, 0);
if (ret) {
pr_err("failed to set pin direction\n");
ret = -EINVAL;
goto free_gpio_out;
}
ret = gpio_request(gpio_in, "gte_test_gpio_in");
if (ret) {
pr_err("failed to request gpio in\n");
ret = -EINVAL;
goto free_gpio_out;
}
ret = gpio_direction_input(gpio_in);
if (ret) {
pr_err("failed to set pin direction\n");
ret = -EINVAL;
goto free_gpio_in;
}
/* IRQ setup */
ret = gpio_to_irq(gpio_in);
if (ret < 0) {
pr_err("failed to map GPIO to IRQ: %d\n", ret);
ret = -EINVAL;
goto free_gpio_in;
}
gte.gpio_in_irq = ret;
ret = request_irq(ret, tegra_gte_test_gpio_isr,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
"tegra_gte_test_isr", &gte);
if (ret) {
pr_err("failed to acquire IRQ\n");
ret = -EINVAL;
goto free_gpio_in;
}
ret = tegra_gte_test_sysfs_create();
if (ret != 0) {
pr_err("sysfs creation failed\n");
ret = -EINVAL;
goto free_irq;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
setup_timer(&gte.timer, gpio_timer_cb, 0);
#else
timer_setup(&gte.timer, gpio_timer_cb, 0);
#endif
mod_timer(&gte.timer, jiffies + msecs_to_jiffies(5000));
return 0;
free_irq:
free_irq(gte.gpio_in_irq, &gte);
free_gpio_in:
gpio_free(gpio_in);
free_gpio_out:
gpio_free(gpio_out);
return ret;
}
static void __exit tegra_gte_test_exit(void)
{
free_irq(gte.gpio_in_irq, &gte);
gpio_free(gpio_in);
gpio_free(gpio_out);
tegra_gte_unregister_event(gte.data_gpio);
tegra_gte_unregister_event(gte.data_lic);
kobject_put(gte.kobj);
del_timer(&gte.timer);
}
module_init(tegra_gte_test_init);
module_exit(tegra_gte_test_exit);
MODULE_AUTHOR("Dipen Patel <dipenp@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra GTE driver test");
MODULE_LICENSE("GPL v2");

View File

@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* /*
* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * Copyright (c) 2020-2022, NVIDIA CORPORATION & AFFILIATES.All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0
*/ */
#ifndef _LINUX_TEGRA_GTE_ENGINE_H #ifndef _TEGRA_GTE_H
#define _LINUX_TEGRA_GTE_ENGINE_H #define _TEGRA_GTE_H
#include <linux/device.h> #include <linux/device.h>
@@ -21,7 +22,6 @@ struct tegra_gte_ev_detail {
int dir; /* direction of the event */ int dir; /* direction of the event */
}; };
#ifdef CONFIG_TEGRA_HTS_GTE
/* /*
* GTE event registration function * GTE event registration function
* *
@@ -74,23 +74,4 @@ int tegra_gte_unregister_event(struct tegra_gte_ev_desc *desc);
int tegra_gte_retrieve_event(const struct tegra_gte_ev_desc *desc, int tegra_gte_retrieve_event(const struct tegra_gte_ev_desc *desc,
struct tegra_gte_ev_detail *hts); struct tegra_gte_ev_detail *hts);
#else /* ! CONFIG_TEGRA_HTS_GTE */
static inline struct tegra_gte_ev_desc *tegra_gte_register_event(
struct device_node *np, u32 ev_id)
{
return ERR_PTR(-ENOSYS);
}
static inline int tegra_gte_unregister_event(struct tegra_gte_ev_desc *desc)
{
return -ENOSYS;
}
static inline int tegra_gte_retrieve_event(const struct tegra_gte_ev_desc *desc,
struct tegra_gte_ev_detail *hts)
{
return -ENOSYS;
}
#endif /* ! CONFIG_TEGRA_HTS_GTE */
#endif #endif

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2020-2022, NVIDIA CORPORATION & AFFILIATES.All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef _TEGRA_GTE_IOCTL_H
#define _TEGRA_GTE_IOCTL_H
#include <linux/ioctl.h>
#include <linux/types.h>
/**
* GPIO event types
*/
#define TEGRA_GTE_EVENT_RISING_EDGE 0x1
#define TEGRA_GTE_EVENT_FALLING_EDGE 0x2
#define TEGRA_GTE_EVENT_REQ_BOTH_EDGES (TEGRA_GTE_EVENT_RISING_EDGE | \
TEGRA_GTE_EVENT_FALLING_EDGE)
/**
* Information about a GPIO event request
* @global_gpio_pin: global gpio pin number to monitor event
* @eventflags: desired flags for the desired GPIO event line, such as
* EVENT_RISING_EDGE or EVENT_FALLING_EDGE
* @fd: if successful this field will contain a valid anonymous file handle
* after a HTS_CREATE_GPIO_EVENT_IOCTL operation, zero or negative value
* means error
*/
struct tegra_gte_hts_event_req {
__u32 global_gpio_pin;
__u32 eventflags;
int fd;
};
/**
* struct hts_event_data - event data
* @timestamp: hardware timestamp in nanosecond
* @dir: direction of the event
*/
struct tegra_gte_hts_event_data {
__u64 timestamp;
int dir;
};
/**
* Event request IOCTL command
*/
#define TEGRA_GTE_HTS_CREATE_GPIO_EV_IOCTL \
_IOWR(0xB5, 0x0, \
struct tegra_gte_hts_event_req)
#endif

View File

@@ -0,0 +1,162 @@
/*
* Copyright (c) 2020-2022, NVIDIA CORPORATION & AFFILIATES.All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0
*/
/*
* tegra_gte_mon - monitor GPIO line events from userspace and hardware
* timestamp.
*
* Example Usage:
* tegra_gte_mon -d <device> -g <global gpio pin> -r -f
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <poll.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/tegra-gte-ioctl.h>
int monitor_device(const char *device_name,
unsigned int gnum,
unsigned int eventflags,
unsigned int loops)
{
struct tegra_gte_hts_event_req req = {0};
struct tegra_gte_hts_event_data event;
char *chrdev_name;
int fd;
int ret;
int i = 0;
ret = asprintf(&chrdev_name, "/dev/%s", device_name);
if (ret < 0)
return -ENOMEM;
fd = open(chrdev_name, 0);
if (fd == -1) {
ret = -errno;
perror("Error: ");
goto exit_close_error;
}
req.global_gpio_pin = gnum;
req.eventflags = eventflags;
ret = ioctl(fd, TEGRA_GTE_HTS_CREATE_GPIO_EV_IOCTL, &req);
if (ret == -1) {
ret = -errno;
fprintf(stderr, "Failed to issue GET EVENT "
"IOCTL (%d)\n",
ret);
goto exit_close_error;
}
fprintf(stdout, "Monitoring line %d on %s\n", gnum, device_name);
while (1) {
ret = read(req.fd, &event, sizeof(event));
if (ret == -1) {
if (errno == -EAGAIN) {
fprintf(stderr, "nothing available\n");
continue;
} else {
ret = -errno;
fprintf(stderr, "Failed to read event (%d)\n",
ret);
break;
}
}
if (ret != sizeof(event)) {
fprintf(stderr, "Reading event failed\n");
ret = -EIO;
break;
}
fprintf(stdout, "HW timestamp GPIO EVENT %" PRIu64 "\n",
event.timestamp);
i++;
if (i == loops)
break;
}
exit_close_error:
if (close(fd) == -1)
perror("Failed to close GPIO character device file");
free(chrdev_name);
return ret;
}
void print_usage(char *bin_name)
{
fprintf(stderr, "Usage: %s [options]...\n"
"Listen to events on GPIO lines, 0->1 1->0\n"
" -d <name> Listen using named HW ts engine device\n"
" -g <n> GPIO global id\n"
" -r Listen for rising edges\n"
" -f Listen for falling edges\n"
" [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n"
" -h This helptext\n"
"\n"
"Example:\n"
"%s -d gtechip0 -g 257 -r -f\n"
"(means GPIO 257 rising and falling edge monitoring)\n",
bin_name, bin_name
);
}
int main(int argc, char **argv)
{
const char *device_name = NULL;
unsigned int gnum = -1;
unsigned int loops = 0;
unsigned int eventflags = 0;
int c;
while ((c = getopt(argc, argv, "c:g:d:rfh")) != -1) {
switch (c) {
case 'c':
loops = strtoul(optarg, NULL, 10);
break;
case 'd':
device_name = optarg;
break;
case 'g':
gnum = strtoul(optarg, NULL, 10);
break;
case 'r':
eventflags |= TEGRA_GTE_EVENT_RISING_EDGE;
break;
case 'f':
eventflags |= TEGRA_GTE_EVENT_FALLING_EDGE;
break;
case 'h':
print_usage(argv[0]);
return 1;
}
}
if (!device_name || gnum == -1) {
print_usage(argv[0]);
return 1;
}
if (!eventflags) {
printf("No flags specified, listening on both rising and "
"falling edges\n");
eventflags = TEGRA_GTE_EVENT_REQ_BOTH_EDGES;
}
return monitor_device(device_name, gnum, eventflags, loops);
}