From e7c8a70f65cc359e8797bfe760dda063f8b2ca68 Mon Sep 17 00:00:00 2001 From: Dipen Patel Date: Mon, 2 May 2022 22:55:18 +0000 Subject: [PATCH] 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 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2706260 Reviewed-by: Bitan Biswas GVS: Gerrit_Virtual_Submit --- .../bindings/tegra_gte/tegra-gte.yaml | 78 + Documentation/ioctl/ioctl-number.txt | 7 + drivers/Makefile | 2 + drivers/staging/platform/tegra/gte/Makefile | 1 + .../staging/platform/tegra/gte/tegra194_gte.c | 1363 +++++++++++++++++ .../staging/platform/tegra/gte_test/Makefile | 1 + .../tegra/gte_test/tegra194_gte_test.c | 355 +++++ include/linux/tegra-gte.h | 29 +- include/uapi/linux/tegra-gte-ioctl.h | 54 + tools/tegra-gte/tegra_gte_mon.c | 162 ++ 10 files changed, 2028 insertions(+), 24 deletions(-) create mode 100644 Documentation/devicetree/bindings/tegra_gte/tegra-gte.yaml create mode 100644 Documentation/ioctl/ioctl-number.txt create mode 100644 drivers/staging/platform/tegra/gte/Makefile create mode 100644 drivers/staging/platform/tegra/gte/tegra194_gte.c create mode 100644 drivers/staging/platform/tegra/gte_test/Makefile create mode 100644 drivers/staging/platform/tegra/gte_test/tegra194_gte_test.c create mode 100644 include/uapi/linux/tegra-gte-ioctl.h create mode 100644 tools/tegra-gte/tegra_gte_mon.c diff --git a/Documentation/devicetree/bindings/tegra_gte/tegra-gte.yaml b/Documentation/devicetree/bindings/tegra_gte/tegra-gte.yaml new file mode 100644 index 00000000..2fa9a1cd --- /dev/null +++ b/Documentation/devicetree/bindings/tegra_gte/tegra-gte.yaml @@ -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 + +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>; + }; diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt new file mode 100644 index 00000000..480d1f91 --- /dev/null +++ b/Documentation/ioctl/ioctl-number.txt @@ -0,0 +1,7 @@ +Ioctl Numbers +21 February 2020 +Dipen Patel + +Code Seq#(hex) Include File Comments +======================================================== +0xB5 00 linux/tegra-gte-ioctl.h diff --git a/drivers/Makefile b/drivers/Makefile index dfcec02d..e3c83b8c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -32,4 +32,6 @@ obj-m += watchdog/ obj-m += video/tegra/ obj-m += virt/tegra/ obj-m += media/ +obj-m += staging/platform/tegra/gte/ +obj-m += staging/platform/tegra/gte_test/ diff --git a/drivers/staging/platform/tegra/gte/Makefile b/drivers/staging/platform/tegra/gte/Makefile new file mode 100644 index 00000000..29207eb0 --- /dev/null +++ b/drivers/staging/platform/tegra/gte/Makefile @@ -0,0 +1 @@ +obj-m += tegra194_gte.o diff --git a/drivers/staging/platform/tegra/gte/tegra194_gte.c b/drivers/staging/platform/tegra/gte/tegra194_gte.c new file mode 100644 index 00000000..f70fb8a0 --- /dev/null +++ b/drivers/staging/platform/tegra/gte/tegra194_gte.c @@ -0,0 +1,1363 @@ +/* + * Copyright (c) 2020-2022, NVIDIA CORPORATION & AFFILIATES.All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GTE_SUSPEND 0 +#define GTE_DEV_IN_REG 0 + +/* GTE source clock TSC is 31.25MHz */ +#define GTE_TS_NS 32ULL +#define GTE_TS_NS_SHIFT __builtin_ctz(GTE_TS_NS) + +/* char device related */ +static DEFINE_IDA(gte_ida); +static dev_t gte_devt; +#define GTE_DEV_MAX 2 +static struct bus_type gte_bus_type = { + .name = "gte", +}; + +/* Global list of the probed GTE devices */ +static DEFINE_MUTEX(gte_list_lock); +static LIST_HEAD(gte_devices); + +/* AON GTE event map For slice 1 */ +#define NV_AON_GTE_SLICE1_IRQ_GPIO_28 12 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_29 13 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_30 14 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_31 15 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_32 16 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_33 17 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_34 18 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_35 19 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_36 20 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_37 21 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_38 22 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_39 23 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_40 24 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_41 25 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_42 26 +#define NV_AON_GTE_SLICE1_IRQ_GPIO_43 27 + +/* AON GTE event map For slice 2 */ +#define NV_AON_GTE_SLICE2_IRQ_GPIO_0 0 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_1 1 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_2 2 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_3 3 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_4 4 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_5 5 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_6 6 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_7 7 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_8 8 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_9 9 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_10 10 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_11 11 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_12 12 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_13 13 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_14 14 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_15 15 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_16 16 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_17 17 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_18 18 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_19 19 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_20 20 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_21 21 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_22 22 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_23 23 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_24 24 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_25 25 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_26 26 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_27 27 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_28 28 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_29 29 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_30 30 +#define NV_AON_GTE_SLICE2_IRQ_GPIO_31 31 + +#define GTE_TECTRL 0x0 +#define GTE_TETSCH 0x4 +#define GTE_TETSCL 0x8 +#define GTE_TESRC 0xC +#define GTE_TECCV 0x10 +#define GTE_TEPCV 0x14 +#define GTE_TEENCV 0x18 +#define GTE_TECMD 0x1C +#define GTE_TESTATUS 0x20 +#define GTE_SLICE0_TETEN 0x40 +#define GTE_SLICE1_TETEN 0x60 + +#define GTE_SLICE_SIZE (GTE_SLICE1_TETEN - GTE_SLICE0_TETEN) + +#define GTE_TECTRL_ENABLE_MASK 0x1 +#define GTE_TECTRL_ENABLE_DISABLE 0x0 +#define GTE_TECTRL_ENABLE_ENABLE 0x1 + +#define GTE_TECTRL_OCCU_SHIFT 0x8 +#define GTE_TECTRL_INTR_SHIFT 0x1 +#define GTE_TECTRL_INTR_ENABLE 0x1 + +#define GTE_TESRC_SLICE_SHIFT 16 +#define GTE_TESRC_SLICE_DEFAULT_MASK 0xFF + +#define GTE_TECMD_CMD_POP 0x1 + +#define GTE_TESTATUS_OCCUPANCY_SHIFT 8 +#define GTE_TESTATUS_OCCUPANCY_MASK 0xFF + +#define GTE_EVENT_REGISTERED 0 +#define GTE_EVENT_UNREGISTERING 1 + +#define GTE_EV_FIFO_EL 32 +#define GTE_MAX_EV_NAME_SZ 9 + +struct gte_slices { + u32 r_val; + unsigned long flags; + /* to prevent events mapped to same slice updating its register */ + spinlock_t s_lock; +}; + +struct tegra_gte_ev_mapped { + u32 pin; + u32 slice; + u32 bit_index; +}; + +struct tegra_gte_ev_table { + int map_sz; + const struct tegra_gte_ev_mapped *map; +}; + +struct tegra_gte_dev { + int gte_irq; + int num_events; + int id; + unsigned long flags; + u32 itr_thrshld; + u32 conf_rval; + atomic_t usage; + bool is_chardev; + struct device c_dev; + struct cdev chrdev; + struct device *pdev; + struct kobject *kobj; + struct gte_slices *sl; + struct tegra_gte_event_info *ev; + const struct tegra_gte_ev_table *ev_map; + struct list_head list; + void __iomem *regs; + spinlock_t lock; /* Hardware access lock */ +}; + +struct tegra_gte_ev_el { + int dir; + u64 tsc; +}; + +struct tegra_gte_event_info { + u32 reg; + unsigned long flags; + atomic_t usage; + atomic_t dropped_evs; + spinlock_t lock; /* Sync ev_fifo accesses */ + struct mutex ev_lock; + struct kobject kobj; + struct kfifo ev_fifo; + struct tegra_gte_dev *dev; + struct tegra_gte_ev_desc pv; +}; + +static const struct tegra_gte_ev_mapped tegra194_aon_gpio_map[] = { + /* pin num, slice, bit_index */ + [0] = {11, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_0}, + [1] = {10, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_1}, + [2] = {9, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_2}, + [3] = {8, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_3}, + [4] = {7, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_4}, + [5] = {6, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_5}, + [6] = {5, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_6}, + [7] = {4, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_7}, + [8] = {3, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_8}, + [9] = {2, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_9}, + [10] = {1, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_10}, + [11] = {0, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_11}, + [12] = {22, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_12}, + [13] = {21, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_13}, + [14] = {20, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_14}, + [15] = {19, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_15}, + [16] = {18, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_16}, + [17] = {17, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_17}, + [18] = {16, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_18}, + [19] = {15, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_19}, + [20] = {14, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_20}, + [21] = {13, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_21}, + [22] = {12, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_22}, + [23] = {29, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_23}, + [24] = {28, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_24}, + [25] = {27, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_25}, + [26] = {26, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_26}, + [27] = {25, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_27}, + [28] = {24, 1, NV_AON_GTE_SLICE1_IRQ_GPIO_28}, + [29] = {23, 1, NV_AON_GTE_SLICE1_IRQ_GPIO_29}, +}; + +static const struct tegra_gte_ev_mapped tegra234_aon_gpio_map[] = { + /* pin num, slice, bit_index */ + [0] = {11, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_0}, + [1] = {10, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_1}, + [2] = {9, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_2}, + [3] = {8, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_3}, + [4] = {7, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_4}, + [5] = {6, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_5}, + [6] = {5, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_6}, + [7] = {4, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_7}, + [8] = {3, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_8}, + [9] = {2, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_9}, + [10] = {1, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_10}, + [11] = {0, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_11}, + [12] = {22, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_12}, + [13] = {21, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_13}, + [14] = {20, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_14}, + [15] = {19, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_15}, + [16] = {18, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_16}, + [17] = {17, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_17}, + [18] = {16, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_18}, + [19] = {15, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_19}, + [20] = {14, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_20}, + [21] = {13, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_21}, + [22] = {12, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_22}, + [23] = {31, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_23}, + [24] = {30, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_24}, + [25] = {29, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_25}, + [26] = {28, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_26}, + [27] = {27, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_27}, + [28] = {26, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_28}, + [29] = {25, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_29}, + [30] = {24, 2, NV_AON_GTE_SLICE1_IRQ_GPIO_30}, + [31] = {23, 2, NV_AON_GTE_SLICE2_IRQ_GPIO_31}, +}; + +static const struct tegra_gte_ev_table t194_aon_gte_map = { + .map_sz = ARRAY_SIZE(tegra194_aon_gpio_map), + .map = tegra194_aon_gpio_map, +}; + +static const struct tegra_gte_ev_table t234_aon_gte_map = { + .map_sz = ARRAY_SIZE(tegra234_aon_gpio_map), + .map = tegra234_aon_gpio_map, +}; + +static inline u32 tegra_gte_readl(struct tegra_gte_dev *gte, u32 reg) +{ + return readl(gte->regs + reg); +} + +static inline void tegra_gte_writel(struct tegra_gte_dev *gte, u32 reg, + u32 val) +{ + writel(val, gte->regs + reg); +} + +/* Event specific sysfs management */ + +static struct kobj_type gte_ev_kobj_type = { + .sysfs_ops = &kobj_sysfs_ops, +}; + +static void tegra_gte_read_fifo(struct tegra_gte_dev *gte); + +/* Stat for the dropped events due to FIFO overflow */ +static ssize_t show_num_dropped_events(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct tegra_gte_event_info *ev = + container_of(kobj, struct tegra_gte_event_info, kobj); + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&ev->dropped_evs)); +} + +/* Total timestamp elements available to retrieve */ +static ssize_t show_num_events_avail(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct tegra_gte_event_info *ev = + container_of(kobj, struct tegra_gte_event_info, kobj); + return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ev->usage)); +} + +struct kobj_attribute num_events_avail_attr = + __ATTR(num_events_avail, 0400, show_num_events_avail, NULL); +struct kobj_attribute num_dropped_events_attr = + __ATTR(num_dropped_events, 0400, show_num_dropped_events, NULL); + +static struct attribute *ev_attrs[] = { + &num_events_avail_attr.attr, + &num_dropped_events_attr.attr, + NULL, +}; + +static struct attribute_group event_attr_group = { + .attrs = ev_attrs, +}; + +/* + * Clears the resources related to event, resets the FIFO, removes the sysfs + * related to event + */ +int tegra_gte_unregister_event(struct tegra_gte_ev_desc *data) +{ + u32 slice, val; + int ev_id, ret; + unsigned long flags; + struct tegra_gte_dev *gte_dev; + struct tegra_gte_ev_desc *pri; + struct tegra_gte_event_info *ev; + + if (!data) + return -EINVAL; + + pri = data; + ev = container_of(pri, struct tegra_gte_event_info, pv); + gte_dev = ev->dev; + ev_id = pri->id; + slice = pri->slice; + + set_bit(GTE_EVENT_UNREGISTERING, &ev->flags); + + if (!test_bit(GTE_EVENT_REGISTERED, &ev->flags)) { + dev_dbg(gte_dev->pdev, "Event:%d is not registered", ev_id); + ret = -EUSERS; + goto clear_unregister; + } + + spin_lock(>e_dev->sl[slice].s_lock); + if (test_bit(GTE_SUSPEND, >e_dev->sl[slice].flags)) { + spin_unlock(>e_dev->sl[slice].s_lock); + dev_dbg(gte_dev->pdev, "device suspended\n"); + ret = -EBUSY; + goto clear_unregister; + } + + val = tegra_gte_readl(gte_dev, ev->reg); + val = val & (~(1 << pri->ev_bit)); + tegra_gte_writel(gte_dev, ev->reg, val); + spin_unlock(>e_dev->sl[slice].s_lock); + + spin_lock_irqsave(&ev->lock, flags); + clear_bit(GTE_EVENT_REGISTERED, &ev->flags); + spin_unlock_irqrestore(&ev->lock, flags); + + atomic_dec(>e_dev->usage); + atomic_set(&ev->usage, 0); + atomic_set(&ev->dropped_evs, 0); + memset(&ev->pv, 0, sizeof(ev->pv)); + kobject_put(&ev->kobj); + memset(&ev->kobj, 0, sizeof(ev->kobj)); + kfifo_free(&ev->ev_fifo); + + ret = 0; + + module_put(gte_dev->pdev->driver->owner); + + dev_dbg(gte_dev->pdev, "%s: event id:%d, slice:%d", + __func__, ev_id, slice); + +clear_unregister: + clear_bit(GTE_EVENT_UNREGISTERING, &ev->flags); + + return ret; +} +EXPORT_SYMBOL(tegra_gte_unregister_event); + +static int tegra_gte_convert_to_offset(u32 ev_id, + struct tegra_gte_dev *gte_dev) +{ + struct gpio_chip *chip; + int offset; + + dev_dbg(gte_dev->pdev, "gpio gte is requested\n"); + + chip = gpiod_to_chip(gpio_to_desc(ev_id)); + if (!chip) { + dev_err(gte_dev->pdev, + "GPIO controller not found for the gpio: %u\n", + ev_id); + return -EINVAL; + } + + offset = (int)(ev_id - chip->base); + if (offset < 0) { + dev_err(gte_dev->pdev, "Invalid gpio pin: %u\n", ev_id); + return -EINVAL; + } + + return offset; +} + +static void tegra_gte_map_to_ev_id(u32 eid, struct tegra_gte_dev *gdev, + u32 *mapped) +{ + const struct tegra_gte_ev_mapped *m; + int nums, i; + + if (gdev->ev_map) { + m = gdev->ev_map->map; + nums = gdev->ev_map->map_sz; + for (i = 0; i < nums; i++) { + if (m[i].pin == eid) { + *mapped = (m[i].slice << 5) + m[i].bit_index; + return; + } + } + } + *mapped = eid; +} + +static struct tegra_gte_ev_desc *__gte_register_event(u32 eid, + struct tegra_gte_dev *gte_dev) +{ + u32 slice, sl_bit_shift, ev_bit, offset, val, reg; + int ret, sysfs_created = 0; + struct tegra_gte_event_info *ev; + + sl_bit_shift = __builtin_ctz(GTE_SLICE_SIZE); + + if (!try_module_get(gte_dev->pdev->driver->owner)) + return ERR_PTR(-ENODEV); + + tegra_gte_map_to_ev_id(eid, gte_dev, &offset); + + if (offset > gte_dev->num_events) { + dev_err(gte_dev->pdev, + "Invalid event id:%u and GTE event offset: %u", + eid, offset); + ret = -EINVAL; + goto put_mod; + } + + ev = gte_dev->ev; + if (test_bit(GTE_EVENT_UNREGISTERING, &ev[offset].flags)) { + dev_err(gte_dev->pdev, "Event:%d is getting unregistered", + offset); + ret = -EUSERS; + goto put_mod; + } + + /* + * There a chance that both kernel client driver and userspace calling + * register API with same event id, lock here. + */ + mutex_lock(&ev[offset].ev_lock); + + if (test_bit(GTE_EVENT_REGISTERED, &ev[offset].flags)) { + dev_err(gte_dev->pdev, "Event:%d is already registered", + offset); + ret = -EUSERS; + goto error_unlock; + } + + slice = offset >> 5; + ev_bit = offset & (32 - 1); + + if (kfifo_alloc(&ev[offset].ev_fifo, + GTE_EV_FIFO_EL * sizeof(struct tegra_gte_ev_el), GFP_KERNEL)) { + dev_err(gte_dev->pdev, "Fifo allocation failed"); + ret = -ENOMEM; + goto error_unlock; + } + + reg = (slice << sl_bit_shift) + GTE_SLICE0_TETEN; + + ev[offset].reg = reg; + ev[offset].pv.ev_bit = ev_bit; + ev[offset].pv.slice = slice; + ev[offset].pv.id = offset; + + ret = kobject_init_and_add(&ev[offset].kobj, >e_ev_kobj_type, + gte_dev->kobj, "event%d", offset); + if (!ret) { + ret = sysfs_create_group(&ev[offset].kobj, + &event_attr_group); + if (ret) { + kobject_put(&ev[offset].kobj); + memset(&ev[offset].kobj, 0, sizeof(ev[offset].kobj)); + } else { + sysfs_created = 1; + } + } else { + kobject_put(&ev[offset].kobj); + } + + spin_lock(>e_dev->sl[slice].s_lock); + if (test_bit(GTE_SUSPEND, >e_dev->sl[slice].flags)) { + spin_unlock(>e_dev->sl[slice].s_lock); + if (sysfs_created == 1) { + kobject_put(&ev[offset].kobj); + memset(&ev[offset].kobj, 0, sizeof(ev[offset].kobj)); + } + dev_dbg(gte_dev->pdev, "device suspended"); + ret = -EBUSY; + goto error_fifo_free; + } + + val = tegra_gte_readl(gte_dev, reg); + val = val | (1 << ev_bit); + tegra_gte_writel(gte_dev, reg, val); + spin_unlock(>e_dev->sl[slice].s_lock); + + atomic_inc(>e_dev->usage); + set_bit(GTE_EVENT_REGISTERED, &ev[offset].flags); + dev_dbg(gte_dev->pdev, + "%s: slice: %u, bit: %u, offset: %d, reg = 0x%x", + __func__, slice, ev_bit, offset, reg); + mutex_unlock(&ev[offset].ev_lock); + + return &ev[offset].pv; + +error_fifo_free: + kfifo_free(&ev[offset].ev_fifo); +error_unlock: + mutex_unlock(&ev[offset].ev_lock); +put_mod: + module_put(gte_dev->pdev->driver->owner); + + return ERR_PTR(ret); +} + +/* + * registers the event with GTE, allocates FIFO, creates sysfs for the event + * stats. Also asks GPIO driver to enable timestamp bit in case of the GPIO + * event to monitor. + */ +struct tegra_gte_ev_desc *tegra_gte_register_event(struct device_node *np, + u32 ev_id) +{ + struct tegra_gte_dev *gte_dev = NULL; + struct tegra_gte_ev_desc *desc; + int offset; + + if (!np) { + pr_err("Node empty\n"); + return ERR_PTR(-EINVAL); + } + + mutex_lock(>e_list_lock); + + list_for_each_entry(gte_dev, >e_devices, list) { + if (gte_dev->pdev->of_node == np) { + dev_dbg(gte_dev->pdev, "found the match\n"); + break; + } + } + + if (!gte_dev) { + pr_err("No device found\n"); + mutex_unlock(>e_list_lock); + return ERR_PTR(-ENODEV); + } + + set_bit(GTE_DEV_IN_REG, >e_dev->flags); + mutex_unlock(>e_list_lock); + + if (!of_device_is_compatible(np, "nvidia,tegra194-gte-lic")) { + offset = tegra_gte_convert_to_offset(ev_id, gte_dev); + if (offset < 0) + return ERR_PTR(offset); + } else { + offset = ev_id; + } + + desc = __gte_register_event(offset, gte_dev); + + clear_bit(GTE_DEV_IN_REG, >e_dev->flags); + + return desc; +} +EXPORT_SYMBOL(tegra_gte_register_event); + +int tegra_gte_retrieve_event(const struct tegra_gte_ev_desc *data, + struct tegra_gte_ev_detail *hts) +{ + int ret, ev_id; + unsigned long flags; + struct tegra_gte_ev_el el; + const struct tegra_gte_ev_desc *pri; + struct tegra_gte_event_info *ev; + struct tegra_gte_dev *gte_dev; + + if (!data || !hts) + return -EINVAL; + + pri = data; + ev = container_of(pri, struct tegra_gte_event_info, pv); + ev_id = pri->id; + gte_dev = ev->dev; + + /* test - read from HW to make sure we are not missing anything */ + tegra_gte_read_fifo(gte_dev); + + spin_lock_irqsave(&ev->lock, flags); + + if (!test_bit(GTE_EVENT_REGISTERED, &ev->flags)) { + dev_dbg(gte_dev->pdev, "Event: %d is not registered", ev_id); + ret = -EINVAL; + goto unlock; + } + + if (kfifo_is_empty(&ev->ev_fifo)) { + dev_dbg(gte_dev->pdev, "Event: %d fifo is empty", ev_id); + ret = -EAGAIN; + goto unlock; + } + + ret = kfifo_out(&ev->ev_fifo, (unsigned char *)&el, sizeof(el)); + if (unlikely(ret != sizeof(el))) { + dev_dbg(gte_dev->pdev, + "Event: %d retrieved element is in improper size", + ev_id); + ret = -EINVAL; + goto unlock; + } + + hts->dir = el.dir; + hts->ts_raw = el.tsc; + hts->ts_ns = el.tsc << GTE_TS_NS_SHIFT; + atomic_dec(&ev->usage); + ret = 0; + +unlock: + spin_unlock_irqrestore(&ev->lock, flags); + return ret; +} +EXPORT_SYMBOL(tegra_gte_retrieve_event); + +/* + * GPIO event monitoring from userspace management. Only GPIO type is supported + * to be monitored and timestamp using GTE from the userspace. + */ +struct gte_uspace_event_state { + struct tegra_gte_dev *gdev; + u32 eflags; + u32 gpio_in; + int irq; + char *irqname; + char *label; + wait_queue_head_t wait; + struct mutex read_lock; + DECLARE_KFIFO(events, struct tegra_gte_hts_event_data, GTE_EV_FIFO_EL); + struct tegra_gte_ev_desc *gte_data; +}; + +static unsigned int gte_event_poll(struct file *filep, + struct poll_table_struct *wait) +{ + struct gte_uspace_event_state *le = filep->private_data; + unsigned int events = 0; + + poll_wait(filep, &le->wait, wait); + + if (!kfifo_is_empty(&le->events)) + events = POLLIN | POLLRDNORM; + + return events; +} + + +static ssize_t gte_event_read(struct file *filep, char __user *buf, + size_t count, + loff_t *f_ps) +{ + struct gte_uspace_event_state *le = filep->private_data; + unsigned int copied; + int ret; + + if (count < sizeof(struct tegra_gte_hts_event_data)) + return -EINVAL; + + do { + if (kfifo_is_empty(&le->events)) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(le->wait, + !kfifo_is_empty(&le->events)); + if (ret) + return ret; + } + + if (mutex_lock_interruptible(&le->read_lock)) + return -ERESTARTSYS; + ret = kfifo_to_user(&le->events, buf, count, &copied); + mutex_unlock(&le->read_lock); + + if (ret) + return ret; + + /* + * If we couldn't read anything from the fifo (a different + * thread might have been faster) we either return -EAGAIN if + * the file descriptor is non-blocking, otherwise we go back to + * sleep and wait for more data to arrive. + */ + if (copied == 0 && (filep->f_flags & O_NONBLOCK)) + return -EAGAIN; + + } while (copied == 0); + + return copied; +} + +static int gte_event_release(struct inode *inode, struct file *filep) +{ + struct gte_uspace_event_state *le = filep->private_data; + struct tegra_gte_dev *gdev = le->gdev; + + tegra_gte_unregister_event(le->gte_data); + free_irq(le->irq, le); + gpio_free(le->gpio_in); + kfree(le->irqname); + kfree(le->label); + kfree(le); + put_device(&gdev->c_dev); + return 0; +} + +static const struct file_operations gte_event_fileops = { + .release = gte_event_release, + .read = gte_event_read, + .poll = gte_event_poll, + .owner = THIS_MODULE, + .llseek = noop_llseek, +}; + +static irqreturn_t gte_event_irq_thread(int irq, void *p) +{ + int ret; + struct gte_uspace_event_state *le = p; + struct tegra_gte_hts_event_data ge; + struct tegra_gte_ev_detail hw; + + memset(&ge, 0, sizeof(ge)); + ret = tegra_gte_retrieve_event(le->gte_data, &hw); + if (ret == 0) { + ge.timestamp = hw.ts_ns; + ge.dir = hw.dir; + } else { + dev_dbg(le->gdev->pdev, "failed to retrieve event\n"); + return IRQ_HANDLED; + } + + ret = kfifo_put(&le->events, ge); + if (ret != 0) + wake_up_poll(&le->wait, POLLIN); + + return IRQ_HANDLED; +} + +#define GPIOEVENT_REQUEST_VALID_FLAGS (TEGRA_GTE_EVENT_RISING_EDGE | \ + TEGRA_GTE_EVENT_FALLING_EDGE) + +static int gte_event_create(struct tegra_gte_dev *gdev, void __user *ip) +{ + struct tegra_gte_hts_event_req eventreq; + struct gte_uspace_event_state *le; + struct file *file; + int offset; + u32 eflags; + int fd; + int ret; + int irqflags = 0; + + if (copy_from_user(&eventreq, ip, sizeof(eventreq))) + return -EFAULT; + + offset = tegra_gte_convert_to_offset(eventreq.global_gpio_pin, gdev); + if (offset < 0) + return -EINVAL; + + eflags = eventreq.eventflags; + /* Return an error if a unknown flag is set */ + if (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS) { + dev_err(gdev->pdev, "Invalid event flags\n"); + return -EINVAL; + } + + le = kzalloc(sizeof(*le), GFP_KERNEL); + if (!le) + return -ENOMEM; + + get_device(&gdev->c_dev); + + le->gdev = gdev; + + le->label = kzalloc(GTE_MAX_EV_NAME_SZ, GFP_KERNEL); + if (!le->label) { + ret = -ENOMEM; + goto out_free_le; + } + scnprintf(le->label, GTE_MAX_EV_NAME_SZ, "gevent%d", offset); + + ret = gpio_request(eventreq.global_gpio_pin, le->label); + if (ret) { + dev_err(gdev->pdev, "failed to request gpio\n"); + ret = -EINVAL; + goto out_free_label; + } + + ret = gpio_direction_input(eventreq.global_gpio_pin); + if (ret) { + dev_err(gdev->pdev, "failed to set pin direction\n"); + ret = -EINVAL; + goto out_free_label; + } + + /* IRQ setup */ + ret = gpio_to_irq(eventreq.global_gpio_pin); + if (ret < 0) { + dev_err(gdev->pdev, "failed to map GPIO to IRQ: %d\n", ret); + ret = -EINVAL; + goto out_free_label; + } + le->irq = ret; + + if (eflags & TEGRA_GTE_EVENT_RISING_EDGE) + irqflags |= IRQF_TRIGGER_RISING; + if (eflags & TEGRA_GTE_EVENT_FALLING_EDGE) + irqflags |= IRQF_TRIGGER_FALLING; + irqflags |= IRQF_ONESHOT; + irqflags |= IRQF_SHARED; + + le->irqname = kzalloc(GTE_MAX_EV_NAME_SZ, GFP_KERNEL); + if (!le->irqname) { + ret = -ENOMEM; + goto out_free_label; + } + + scnprintf(le->irqname, GTE_MAX_EV_NAME_SZ, "event%d", offset); + + /* Request a thread to read the events */ + ret = request_threaded_irq(le->irq, NULL, gte_event_irq_thread, + irqflags, le->irqname, le); + if (ret) { + dev_err(gdev->pdev, "failed request irq\n"); + goto out_free_name; + } + + le->gpio_in = eventreq.global_gpio_pin; + INIT_KFIFO(le->events); + init_waitqueue_head(&le->wait); + mutex_init(&le->read_lock); + + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_irq; + } + + file = anon_inode_getfile("gte-gpio-event", >e_event_fileops, le, + O_RDONLY | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + dev_err(gdev->pdev, "failed to create file\n"); + goto out_put_unused_fd; + } + + eventreq.fd = fd; + + le->gte_data = __gte_register_event(offset, gdev); + if (IS_ERR(le->gte_data)) { + ret = PTR_ERR(le->gte_data); + dev_err(gdev->pdev, "failed gte register event\n"); + goto out_put_file; + } + + if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { + dev_err(gdev->pdev, "failed to copy user\n"); + ret = -EFAULT; + goto out_put_file; + } + + fd_install(fd, file); + dev_dbg(gdev->pdev, "GPIO event has been created\n"); + + return 0; + +out_put_file: + fput(file); +out_put_unused_fd: + put_unused_fd(fd); +out_free_irq: + free_irq(le->irq, le); +out_free_name: + kfree(le->irqname); +out_free_label: + kfree(le->label); +out_free_le: + kfree(le); + put_device(&gdev->c_dev); + return ret; +} + +static long gte_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct tegra_gte_dev *gdev = filp->private_data; + void __user *ip = (void __user *)arg; + + if (!gdev) + return -ENODEV; + + if (cmd == TEGRA_GTE_HTS_CREATE_GPIO_EV_IOCTL) + return gte_event_create(gdev, ip); + + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long gte_ioctl_compat(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return gte_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static int gte_chrdev_open(struct inode *inode, struct file *filp) +{ + struct tegra_gte_dev *gdev = container_of(inode->i_cdev, + struct tegra_gte_dev, + chrdev); + + get_device(&gdev->c_dev); + filp->private_data = gdev; + + return nonseekable_open(inode, filp); +} + +static int gte_chrdev_release(struct inode *inode, struct file *filp) +{ + struct tegra_gte_dev *gdev = container_of(inode->i_cdev, + struct tegra_gte_dev, + chrdev); + + put_device(&gdev->c_dev); + return 0; +} + +static const struct file_operations gte_fileops = { + .release = gte_chrdev_release, + .open = gte_chrdev_open, + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = gte_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gte_ioctl_compat, +#endif +}; + +static void tegra_gte_read_fifo(struct tegra_gte_dev *gte) +{ + u32 tsh, tsl, src, pv, cv, acv, slice, bit_index, ev_id; + u64 tsc; + int dir; + unsigned long flags, hw_spin_lock_flags; + struct tegra_gte_ev_el ts; + struct tegra_gte_event_info *ev = gte->ev; + + spin_lock_irqsave(>e->lock, hw_spin_lock_flags); + + while ((tegra_gte_readl(gte, GTE_TESTATUS) >> + GTE_TESTATUS_OCCUPANCY_SHIFT) & + GTE_TESTATUS_OCCUPANCY_MASK) { + tsh = tegra_gte_readl(gte, GTE_TETSCH); + tsl = tegra_gte_readl(gte, GTE_TETSCL); + tsc = (((u64)tsh << 32) | tsl); + + src = tegra_gte_readl(gte, GTE_TESRC); + slice = (src >> GTE_TESRC_SLICE_SHIFT) & + GTE_TESRC_SLICE_DEFAULT_MASK; + + pv = tegra_gte_readl(gte, GTE_TEPCV); + cv = tegra_gte_readl(gte, GTE_TECCV); + acv = pv ^ cv; + + while (acv) { + bit_index = __builtin_ctz(acv); + if ((pv >> bit_index) & BIT(0)) + dir = TEGRA_GTE_EVENT_RISING_EDGE; + else + dir = TEGRA_GTE_EVENT_FALLING_EDGE; + + ev_id = bit_index + (slice << 5); + spin_lock_irqsave(&ev[ev_id].lock, flags); + if (test_bit(GTE_EVENT_REGISTERED, &ev[ev_id].flags)) { + ts.tsc = tsc; + ts.dir = dir; + dev_dbg(gte->pdev, "ISR for ev id:%d, ts:%llu", + ev_id, tsc); + if (kfifo_avail(&ev[ev_id].ev_fifo) >= + sizeof(ts)) { + kfifo_in(&ev[ev_id].ev_fifo, + (unsigned char *)&ts, + sizeof(ts)); + atomic_inc(&ev[ev_id].usage); + } else { + atomic_inc(&ev[ev_id].dropped_evs); + } + } + spin_unlock_irqrestore(&ev[ev_id].lock, flags); + acv &= ~BIT(bit_index); + } + + tegra_gte_writel(gte, GTE_TECMD, GTE_TECMD_CMD_POP); + } + + spin_unlock_irqrestore(>e->lock, hw_spin_lock_flags); +} + +static irqreturn_t tegra_gte_isr(int irq, void *dev_id) +{ + struct tegra_gte_dev *gte = dev_id; + + tegra_gte_read_fifo(gte); + + return IRQ_HANDLED; +} + +/* Device specific sysfs management */ + +/* Shows total numbers of events registered with the given device */ +static ssize_t show_events_registered(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tegra_gte_dev *gte_dev = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(>e_dev->usage)); +} + +static DEVICE_ATTR(events_registered, 0444, show_events_registered, NULL); + +static struct attribute *gte_dev_attrs[] = { + &dev_attr_events_registered.attr, + NULL, +}; + +static struct attribute_group gte_def_attr_group = { + .attrs = gte_dev_attrs, +}; + +static int tegra_gte_sysfs_create(struct platform_device *pdev) +{ + return sysfs_create_group(&pdev->dev.kobj, >e_def_attr_group); +} + +static int tegra_gte_chardv_create(struct tegra_gte_dev *gdev) +{ + int ret; + + ret = bus_register(>e_bus_type); + if (ret < 0) { + dev_err(gdev->pdev, "could not register gte bus type\n"); + return ret; + } + + ret = alloc_chrdev_region(>e_devt, 0, GTE_DEV_MAX, "gtechip"); + if (ret < 0) { + dev_err(gdev->pdev, "failed to allocate char dev region\n"); + bus_unregister(>e_bus_type); + return ret; + } + + gdev->c_dev.bus = >e_bus_type; + gdev->id = ida_simple_get(>e_ida, 0, 0, GFP_KERNEL); + + dev_set_name(&gdev->c_dev, "gtechip%d", gdev->id); + device_initialize(&gdev->c_dev); + dev_set_drvdata(&gdev->c_dev, gdev); + + cdev_init(&gdev->chrdev, >e_fileops); + gdev->chrdev.owner = THIS_MODULE; + gdev->chrdev.kobj.parent = &gdev->c_dev.kobj; + gdev->c_dev.devt = MKDEV(MAJOR(gte_devt), gdev->id); + ret = cdev_add(&gdev->chrdev, gdev->c_dev.devt, 1); + if (ret < 0) + dev_warn(gdev->pdev, "failed to add char device %d:%d\n", + MAJOR(gte_devt), gdev->id); + else + dev_dbg(gdev->pdev, "added GTE chardev (%d:%d)\n", + MAJOR(gte_devt), gdev->id); + ret = device_add(&gdev->c_dev); + if (ret) { + dev_err(gdev->pdev, "failed to add char dev:%d\n", ret); + goto err_remove_chardev; + } + + gdev->is_chardev = true; + + return 0; + +err_remove_chardev: + ida_simple_remove(>e_ida, gdev->id); + cdev_del(&gdev->chrdev); + unregister_chrdev_region(gte_devt, GTE_DEV_MAX); + bus_unregister(>e_bus_type); + + return ret; +} + +static const struct of_device_id tegra_gte_of_match[] = { + { .compatible = "nvidia,tegra194-gte-lic"}, + { .compatible = "nvidia,tegra194-gte-aon", .data = &t194_aon_gte_map}, + { .compatible = "nvidia,tegra234-gte-aon", .data = &t234_aon_gte_map}, + { } +}; +MODULE_DEVICE_TABLE(of, tegra_gte_of_match); + +static void tegra_gte_add_to_list(struct tegra_gte_dev *gdev) +{ + mutex_lock(>e_list_lock); + list_add_tail(&gdev->list, >e_devices); + mutex_unlock(>e_list_lock); +} + +static void tegra_gte_init_and_enable(struct tegra_gte_dev *gte) +{ + u32 val = 0, slices; + int i; + struct tegra_gte_event_info *ev = gte->ev; + + slices = gte->num_events >> 5; + atomic_set(>e->usage, 0); + + spin_lock_init(>e->lock); + + for (i = 0; i < slices; i++) { + gte->sl[i].flags = 0; + spin_lock_init(>e->sl[i].s_lock); + } + for (i = 0; i < gte->num_events; i++) { + ev[i].flags = 0; + ev[i].dev = gte; + atomic_set(&ev[i].usage, 0); + atomic_set(&ev[i].dropped_evs, 0); + spin_lock_init(&ev[i].lock); + mutex_init(&ev[i].ev_lock); + } + + val = GTE_TECTRL_ENABLE_ENABLE | + (GTE_TECTRL_INTR_ENABLE << GTE_TECTRL_INTR_SHIFT) | + (gte->itr_thrshld << GTE_TECTRL_OCCU_SHIFT); + tegra_gte_writel(gte, GTE_TECTRL, val); + + tegra_gte_add_to_list(gte); +} + +static void tegra_gte_chardv_remove(struct tegra_gte_dev *gdev) +{ + device_del(&gdev->c_dev); + cdev_del(&gdev->chrdev); + unregister_chrdev_region(gte_devt, GTE_DEV_MAX); + bus_unregister(>e_bus_type); + ida_simple_remove(>e_ida, gdev->id); +} + +static int tegra_gte_disable(struct tegra_gte_dev *gte) +{ + int ret = 0; + + mutex_lock(>e_list_lock); + if (test_bit(GTE_DEV_IN_REG, >e->flags)) { + ret = -EBUSY; + goto unlock; + } + + __list_del_entry(>e->list); + tegra_gte_writel(gte, GTE_TECTRL, 0); + +unlock: + mutex_unlock(>e_list_lock); + + return ret; +} + +static int tegra_gte_remove(struct platform_device *pdev) +{ + struct tegra_gte_dev *gte_dev = dev_get_drvdata(&pdev->dev); + + if (tegra_gte_disable(gte_dev)) + return -EBUSY; + + sysfs_remove_group(&pdev->dev.kobj, >e_def_attr_group); + if (gte_dev->is_chardev) + tegra_gte_chardv_remove(gte_dev); + + return 0; +} + +static int tegra_gte_probe(struct platform_device *pdev) +{ + int ret; + u32 slices; + struct resource *res; + struct device *dev; + struct tegra_gte_dev *gte_dev; + + dev = &pdev->dev; + + gte_dev = devm_kzalloc(dev, sizeof(*gte_dev), GFP_KERNEL); + if (!gte_dev) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, gte_dev); + gte_dev->pdev = dev; + gte_dev->ev_map = of_device_get_match_data(&pdev->dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mem resource\n"); + return -EINVAL; + } + + gte_dev->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(gte_dev->regs)) + return PTR_ERR(gte_dev->regs); + + ret = of_property_read_u32(dev->of_node, "nvidia,int-threshold", + >e_dev->itr_thrshld); + if (ret != 0) + gte_dev->itr_thrshld = 1; + + ret = of_property_read_u32(dev->of_node, "nvidia,num-slices", &slices); + if (ret != 0) { + dev_err(dev, "Could not read slices\n"); + return -EINVAL; + } + + gte_dev->num_events = slices << 5; + + gte_dev->sl = devm_kzalloc(dev, sizeof(struct gte_slices) * slices, + GFP_KERNEL); + if (!gte_dev->sl) + return -ENOMEM; + + gte_dev->ev = devm_kzalloc(dev, sizeof(struct tegra_gte_event_info) * + gte_dev->num_events, GFP_KERNEL); + if (!gte_dev->ev) + return -ENOMEM; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "get irq failed.\n"); + return ret; + } + gte_dev->gte_irq = ret; + ret = devm_request_irq(dev, gte_dev->gte_irq, tegra_gte_isr, 0, + dev_name(dev), gte_dev); + if (ret < 0) { + dev_err(dev, "request irq failed.\n"); + return ret; + } + gte_dev->kobj = &dev->kobj; + + if (!of_device_is_compatible(dev->of_node, "nvidia,tegra194-gte-lic")) + tegra_gte_chardv_create(gte_dev); + + ret = tegra_gte_sysfs_create(pdev); + if (ret) + dev_err(dev, "sysfs creation failed\n"); + + tegra_gte_init_and_enable(gte_dev); + + dev_dbg(gte_dev->pdev, "events: %d, slices:%d", + gte_dev->num_events, slices); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tegra_gte_resume_early(struct device *dev) +{ + int i; + struct tegra_gte_dev *gte_dev = dev_get_drvdata(dev); + u32 slices = gte_dev->num_events >> 5; + u32 sl_bit_shift = __builtin_ctz(GTE_SLICE_SIZE); + + tegra_gte_writel(gte_dev, GTE_TECTRL, gte_dev->conf_rval); + + for (i = 0; i < slices; i++) { + spin_lock(>e_dev->sl[i].s_lock); + tegra_gte_writel(gte_dev, + ((i << sl_bit_shift) + GTE_SLICE0_TETEN), + gte_dev->sl[i].r_val); + clear_bit(GTE_SUSPEND, >e_dev->sl[i].flags); + spin_unlock(>e_dev->sl[i].s_lock); + } + + return 0; +} + +static int tegra_gte_suspend_late(struct device *dev) +{ + int i; + struct tegra_gte_dev *gte_dev = dev_get_drvdata(dev); + u32 slices = gte_dev->num_events >> 5; + u32 sl_bit_shift = __builtin_ctz(GTE_SLICE_SIZE); + + gte_dev->conf_rval = tegra_gte_readl(gte_dev, GTE_TECTRL); + for (i = 0; i < slices; i++) { + spin_lock(>e_dev->sl[i].s_lock); + gte_dev->sl[i].r_val = tegra_gte_readl(gte_dev, + ((i << sl_bit_shift) + GTE_SLICE0_TETEN)); + set_bit(GTE_SUSPEND, >e_dev->sl[i].flags); + spin_unlock(>e_dev->sl[i].s_lock); + } + + return 0; +} +#endif + +static const struct dev_pm_ops tegra_gte_pm = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(tegra_gte_suspend_late, + tegra_gte_resume_early) +}; + +static struct platform_driver tegra_gte_driver = { + .probe = tegra_gte_probe, + .remove = tegra_gte_remove, + .driver = { + .name = "tegra_gte", + .pm = &tegra_gte_pm, + .of_match_table = tegra_gte_of_match, + }, +}; + +module_platform_driver(tegra_gte_driver); + +MODULE_AUTHOR("Dipen Patel "); +MODULE_DESCRIPTION("NVIDIA Tegra GTE (Generic Timestamping Engine) driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/staging/platform/tegra/gte_test/Makefile b/drivers/staging/platform/tegra/gte_test/Makefile new file mode 100644 index 00000000..200516ba --- /dev/null +++ b/drivers/staging/platform/tegra/gte_test/Makefile @@ -0,0 +1 @@ +obj-m += tegra194_gte_test.o diff --git a/drivers/staging/platform/tegra/gte_test/tegra194_gte_test.c b/drivers/staging/platform/tegra/gte_test/tegra194_gte_test.c new file mode 100644 index 00000000..0c98cbca --- /dev/null +++ b/drivers/staging/platform/tegra/gte_test/tegra194_gte_test.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2020-2022, NVIDIA CORPORATION & AFFILIATES.All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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(>e.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", >e); + 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(>e.timer, gpio_timer_cb, 0); +#else + timer_setup(>e.timer, gpio_timer_cb, 0); +#endif + + mod_timer(>e.timer, jiffies + msecs_to_jiffies(5000)); + + return 0; + +free_irq: + free_irq(gte.gpio_in_irq, >e); +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, >e); + 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(>e.timer); +} + +module_init(tegra_gte_test_init); +module_exit(tegra_gte_test_exit); +MODULE_AUTHOR("Dipen Patel "); +MODULE_DESCRIPTION("NVIDIA Tegra GTE driver test"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/tegra-gte.h b/include/linux/tegra-gte.h index 4c570bf9..f737a2bc 100644 --- a/include/linux/tegra-gte.h +++ b/include/linux/tegra-gte.h @@ -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 -#define _LINUX_TEGRA_GTE_ENGINE_H +#ifndef _TEGRA_GTE_H +#define _TEGRA_GTE_H #include @@ -21,7 +22,6 @@ struct tegra_gte_ev_detail { int dir; /* direction of the event */ }; -#ifdef CONFIG_TEGRA_HTS_GTE /* * 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, 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 diff --git a/include/uapi/linux/tegra-gte-ioctl.h b/include/uapi/linux/tegra-gte-ioctl.h new file mode 100644 index 00000000..176982c8 --- /dev/null +++ b/include/uapi/linux/tegra-gte-ioctl.h @@ -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 +#include + +/** + * 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 diff --git a/tools/tegra-gte/tegra_gte_mon.c b/tools/tegra-gte/tegra_gte_mon.c new file mode 100644 index 00000000..46e008b3 --- /dev/null +++ b/tools/tegra-gte/tegra_gte_mon.c @@ -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 -g -r -f + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 Listen using named HW ts engine device\n" + " -g GPIO global id\n" + " -r Listen for rising edges\n" + " -f Listen for falling edges\n" + " [-c ] Do 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); +}