diff options
24 files changed, 2930 insertions, 37 deletions
diff --git a/Documentation/devicetree/bindings/timestamp/hardware-timestamps-common.yaml b/Documentation/devicetree/bindings/timestamp/hardware-timestamps-common.yaml new file mode 100644 index 000000000000..fd6a7b51f571 --- /dev/null +++ b/Documentation/devicetree/bindings/timestamp/hardware-timestamps-common.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timestamp/hardware-timestamps-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hardware timestamp providers + +maintainers: + - Dipen Patel <dipenp@nvidia.com> + +description: + Some devices/SoCs have hardware timestamp engines (HTE) which can use + hardware means to timestamp entity in realtime. The entity could be anything + from GPIOs, IRQs, Bus and so on. The hardware timestamp engine present + itself as a provider with the bindings described in this document. + +properties: + $nodename: + pattern: "^timestamp(@.*|-[0-9a-f])?$" + + "#timestamp-cells": + description: + Number of cells in a HTE specifier. + +required: + - "#timestamp-cells" + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/timestamp/hte-consumer.yaml b/Documentation/devicetree/bindings/timestamp/hte-consumer.yaml new file mode 100644 index 000000000000..6456515c3d26 --- /dev/null +++ b/Documentation/devicetree/bindings/timestamp/hte-consumer.yaml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timestamp/hte-consumer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: HTE Consumer Device Tree Bindings + +maintainers: + - Dipen Patel <dipenp@nvidia.com> + +select: true + +properties: + timestamps: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: + The list of HTE provider phandle. The first cell must represent the + provider phandle followed by the line identifiers. The meaning of the + line identifier and exact number of arguments must be specified in the + HTE provider device tree binding document. + + timestamp-names: + $ref: /schemas/types.yaml#/definitions/string-array + description: + An optional string property to label each line specifier present in the + timestamp property. + +dependencies: + timestamp-names: [ timestamps ] + +additionalProperties: true + +examples: + - | + hte_tegra_consumer { + timestamps = <&tegra_hte_aon 0x9>, <&tegra_hte_lic 0x19>; + timestamp-names = "hte-gpio", "hte-i2c"; + }; diff --git a/Documentation/devicetree/bindings/timestamp/nvidia,tegra194-hte.yaml b/Documentation/devicetree/bindings/timestamp/nvidia,tegra194-hte.yaml new file mode 100644 index 000000000000..c31e207d1652 --- /dev/null +++ b/Documentation/devicetree/bindings/timestamp/nvidia,tegra194-hte.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timestamp/nvidia,tegra194-hte.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Tegra194 on chip generic hardware timestamping engine (HTE) + +maintainers: + - Dipen Patel <dipenp@nvidia.com> + +description: + Tegra SoC has two instances of generic hardware timestamping engines (GTE) + known as GTE GPIO and GTE IRQ, which can monitor subset of GPIO and on chip + IRQ lines for the state change respectively, upon detection it will record + timestamp (taken from system counter) in its internal hardware FIFO. It has + a bitmap array arranged in 32bit slices where each bit represent signal/line + to enable or disable for the hardware timestamping. The GTE GPIO monitors + GPIO lines from the AON (always on) GPIO controller. + +properties: + compatible: + enum: + - nvidia,tegra194-gte-aon + - nvidia,tegra194-gte-lic + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + nvidia,int-threshold: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + HTE device generates its interrupt based on this u32 FIFO threshold + value. The recommended value is 1. + minimum: 1 + maximum: 256 + + nvidia,slices: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + HTE 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 depends on the HTE instance in the chip. The value 3 is for + GPIO GTE and 11 for IRQ GTE. + enum: [3, 11] + + '#timestamp-cells': + description: + This represents number of line id arguments as specified by the + consumers. For the GTE IRQ, this is IRQ number as mentioned in the + SoC technical reference manual. For the GTE GPIO, its value is same as + mentioned in the nvidia GPIO device tree binding document. + const: 1 + +required: + - compatible + - reg + - interrupts + - nvidia,slices + - "#timestamp-cells" + +additionalProperties: false + +examples: + - | + tegra_hte_aon: timestamp@c1e0000 { + compatible = "nvidia,tegra194-gte-aon"; + reg = <0xc1e0000 0x10000>; + interrupts = <0 13 0x4>; + nvidia,int-threshold = <1>; + nvidia,slices = <3>; + #timestamp-cells = <1>; + }; + + - | + tegra_hte_lic: timestamp@3aa0000 { + compatible = "nvidia,tegra194-gte-lic"; + reg = <0x3aa0000 0x10000>; + interrupts = <0 11 0x4>; + nvidia,int-threshold = <1>; + nvidia,slices = <11>; + #timestamp-cells = <1>; + }; + +... diff --git a/Documentation/hte/hte.rst b/Documentation/hte/hte.rst new file mode 100644 index 000000000000..153f3233c100 --- /dev/null +++ b/Documentation/hte/hte.rst @@ -0,0 +1,79 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +============================================ +The Linux Hardware Timestamping Engine (HTE) +============================================ + +:Author: Dipen Patel + +Introduction +------------ + +Certain devices have built in hardware timestamping engines which can +monitor sets of system signals, lines, buses etc... in realtime for state +change; upon detecting the change they can automatically store the timestamp at +the moment of occurrence. Such functionality may help achieve better accuracy +in obtaining timestamps than using software counterparts i.e. ktime and +friends. + +This document describes the API that can be used by hardware timestamping +engine provider and consumer drivers that want to use the hardware timestamping +engine (HTE) framework. Both consumers and providers must include +``#include <linux/hte.h>``. + +The HTE framework APIs for the providers +---------------------------------------- + +.. kernel-doc:: drivers/hte/hte.c + :functions: devm_hte_register_chip hte_push_ts_ns + +The HTE framework APIs for the consumers +---------------------------------------- + +.. kernel-doc:: drivers/hte/hte.c + :functions: hte_init_line_attr hte_ts_get hte_ts_put devm_hte_request_ts_ns hte_request_ts_ns hte_enable_ts hte_disable_ts of_hte_req_count hte_get_clk_src_info + +The HTE framework public structures +----------------------------------- +.. kernel-doc:: include/linux/hte.h + +More on the HTE timestamp data +------------------------------ +The ``struct hte_ts_data`` is used to pass timestamp details between the +consumers and the providers. It expresses timestamp data in nanoseconds in +u64. An example of the typical timestamp data life cycle, for the GPIO line is +as follows:: + + - Monitors GPIO line change. + - Detects the state change on GPIO line. + - Converts timestamps in nanoseconds. + - Stores GPIO raw level in raw_level variable if the provider has that + hardware capability. + - Pushes this hte_ts_data object to HTE subsystem. + - HTE subsystem increments seq counter and invokes consumer provided callback. + Based on callback return value, the HTE core invokes secondary callback in + the thread context. + +HTE subsystem debugfs attributes +-------------------------------- +HTE subsystem creates debugfs attributes at ``/sys/kernel/debug/hte/``. +It also creates line/signal-related debugfs attributes at +``/sys/kernel/debug/hte/<provider>/<label or line id>/``. Note that these +attributes are read-only. + +`ts_requested` + The total number of entities requested from the given provider, + where entity is specified by the provider and could represent + lines, GPIO, chip signals, buses etc... + The attribute will be available at + ``/sys/kernel/debug/hte/<provider>/``. + +`total_ts` + The total number of entities supported by the provider. + The attribute will be available at + ``/sys/kernel/debug/hte/<provider>/``. + +`dropped_timestamps` + The dropped timestamps for a given line. + The attribute will be available at + ``/sys/kernel/debug/hte/<provider>/<label or line id>/``. diff --git a/Documentation/hte/index.rst b/Documentation/hte/index.rst new file mode 100644 index 000000000000..9f43301c05dc --- /dev/null +++ b/Documentation/hte/index.rst @@ -0,0 +1,22 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================ +The Linux Hardware Timestamping Engine (HTE) +============================================ + +The HTE Subsystem +================= + +.. toctree:: + :maxdepth: 1 + + hte + +HTE Tegra Provider +================== + +.. toctree:: + :maxdepth: 1 + + tegra194-hte + diff --git a/Documentation/hte/tegra194-hte.rst b/Documentation/hte/tegra194-hte.rst new file mode 100644 index 000000000000..41983e04d2a0 --- /dev/null +++ b/Documentation/hte/tegra194-hte.rst @@ -0,0 +1,49 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +HTE Kernel provider driver +========================== + +Description +----------- +The Nvidia tegra194 HTE provider driver implements two GTE +(Generic Timestamping Engine) instances: 1) GPIO GTE and 2) LIC +(Legacy Interrupt Controller) IRQ GTE. Both GTE instances get the +timestamp from the system counter TSC which has 31.25MHz clock rate, and the +driver converts clock tick rate to nanoseconds before storing it as timestamp +value. + +GPIO GTE +-------- + +This GTE instance timestamps GPIO in real time. For that to happen GPIO +needs to be configured as input. The always on (AON) GPIO controller instance +supports timestamping GPIOs in real time and it has 39 GPIO lines. The GPIO GTE +and AON GPIO controller are tightly coupled as it requires very specific bits +to be set in GPIO config register before GPIO GTE can be used, for that GPIOLIB +adds two optional APIs as below. The GPIO GTE code supports both kernel +and userspace consumers. The kernel space consumers can directly talk to HTE +subsystem while userspace consumers timestamp requests go through GPIOLIB CDEV +framework to HTE subsystem. + +.. kernel-doc:: drivers/gpio/gpiolib.c + :functions: gpiod_enable_hw_timestamp_ns gpiod_disable_hw_timestamp_ns + +For userspace consumers, GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE flag must be +specified during IOCTL calls. Refer to ``tools/gpio/gpio-event-mon.c``, which +returns the timestamp in nanoseconds. + +LIC (Legacy Interrupt Controller) IRQ GTE +----------------------------------------- + +This GTE instance timestamps LIC IRQ lines in real time. There are 352 IRQ +lines which this instance can add timestamps to in real time. The hte +devicetree binding described at ``Documentation/devicetree/bindings/hte/`` +provides an example of how a consumer can request an IRQ line. Since it is a +one-to-one mapping with IRQ GTE provider, consumers can simply specify the IRQ +number that they are interested in. There is no userspace consumer support for +this GTE instance in the HTE framework. + +The provider source code of both IRQ and GPIO GTE instances is located at +``drivers/hte/hte-tegra194.c``. The test driver +``drivers/hte/hte-tegra194-test.c`` demonstrates HTE API usage for both IRQ +and GPIO GTE. diff --git a/Documentation/index.rst b/Documentation/index.rst index 67036a05b771..8f9be0e658b4 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -137,6 +137,7 @@ needed). scheduler/index mhi/index peci/index + hte/index Architecture-agnostic documentation ----------------------------------- diff --git a/MAINTAINERS b/MAINTAINERS index 76c221313a43..20bdf22601c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9077,6 +9077,14 @@ L: linux-input@vger.kernel.org S: Maintained F: drivers/input/touchscreen/htcpen.c +HTE SUBSYSTEM +M: Dipen Patel <dipenp@nvidia.com> +S: Maintained +F: Documentation/devicetree/bindings/timestamp/ +F: Documentation/hte/ +F: drivers/hte/ +F: include/linux/hte.h + HTS221 TEMPERATURE-HUMIDITY IIO DRIVER M: Lorenzo Bianconi <lorenzo@kernel.org> L: linux-iio@vger.kernel.org diff --git a/drivers/Kconfig b/drivers/Kconfig index 0cbbe2c58648..38af943294ca 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -237,4 +237,6 @@ source "drivers/most/Kconfig" source "drivers/peci/Kconfig" +source "drivers/hte/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index f7f6cb748481..091627d60991 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -187,3 +187,4 @@ obj-$(CONFIG_INTERCONNECT) += interconnect/ obj-$(CONFIG_COUNTER) += counter/ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_PECI) += peci/ +obj-$(CONFIG_HTE) += hte/ diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 84c4f1e9fb0c..de28a68daea0 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2017 NVIDIA Corporation + * Copyright (c) 2016-2022 NVIDIA Corporation * * Author: Thierry Reding <treding@nvidia.com> + * Dipen Patel <dpatel@nvidia.com> */ #include <linux/gpio/driver.h> @@ -11,6 +12,7 @@ #include <linux/module.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/hte.h> #include <dt-bindings/gpio/tegra186-gpio.h> #include <dt-bindings/gpio/tegra194-gpio.h> @@ -36,6 +38,7 @@ #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL BIT(4) #define TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE BIT(5) #define TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT BIT(6) +#define TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC BIT(7) #define TEGRA186_GPIO_DEBOUNCE_CONTROL 0x04 #define TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(x) ((x) & 0xff) @@ -76,6 +79,7 @@ struct tegra_gpio_soc { const struct tegra186_pin_range *pin_ranges; unsigned int num_pin_ranges; const char *pinmux; + bool has_gte; }; struct tegra_gpio { @@ -193,6 +197,76 @@ static int tegra186_gpio_direction_output(struct gpio_chip *chip, return 0; } +#define HTE_BOTH_EDGES (HTE_RISING_EDGE_TS | HTE_FALLING_EDGE_TS) + +static int tegra186_gpio_en_hw_ts(struct gpio_chip *gc, u32 offset, + unsigned long flags) +{ + struct tegra_gpio *gpio; + void __iomem *base; + int value; + + if (!gc) + return -EINVAL; + + gpio = gpiochip_get_data(gc); + if (!gpio) + return -ENODEV; + + base = tegra186_gpio_get_base(gpio, offset); + if (WARN_ON(base == NULL)) + return -EINVAL; + + value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC; + + if (flags == HTE_BOTH_EDGES) { + value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE; + } else if (flags == HTE_RISING_EDGE_TS) { + value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE; + value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; + } else if (flags == HTE_FALLING_EDGE_TS) { + value |= TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE; + } + + writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + + return 0; +} + +static int tegra186_gpio_dis_hw_ts(struct gpio_chip *gc, u32 offset, + unsigned long flags) +{ + struct tegra_gpio *gpio; + void __iomem *base; + int value; + + if (!gc) + return -EINVAL; + + gpio = gpiochip_get_data(gc); + if (!gpio) + return -ENODEV; + + base = tegra186_gpio_get_base(gpio, offset); + if (WARN_ON(base == NULL)) + return -EINVAL; + + value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TIMESTAMP_FUNC; + if (flags == HTE_BOTH_EDGES) { + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE; + } else if (flags == HTE_RISING_EDGE_TS) { + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE; + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL; + } else if (flags == HTE_FALLING_EDGE_TS) { + value &= ~TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_SINGLE_EDGE; + } + writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + + return 0; +} + static int tegra186_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio *gpio = gpiochip_get_data(chip); @@ -747,6 +821,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.set = tegra186_gpio_set; gpio->gpio.set_config = tegra186_gpio_set_config; gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges; + if (gpio->soc->has_gte) { + gpio->gpio.en_hw_timestamp = tegra186_gpio_en_hw_ts; + gpio->gpio.dis_hw_timestamp = tegra186_gpio_dis_hw_ts; + } gpio->gpio.base = -1; @@ -991,6 +1069,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .name = "tegra194-gpio-aon", .instance = 1, .num_irqs_per_bank = 8, + .has_gte = true, }; #define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index c2900b1be69d..f5aa5f93342a 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -24,6 +24,7 @@ #include <linux/timekeeping.h> #include <linux/uaccess.h> #include <linux/workqueue.h> +#include <linux/hte.h> #include <uapi/linux/gpio.h> #include "gpiolib.h" @@ -464,6 +465,25 @@ struct line { * stale value. */ unsigned int level; + /* + * -- hte specific fields -- + */ + struct hte_ts_desc hdesc; + /* + * HTE provider sets line level at the time of event. The valid + * value is 0 or 1 and negative value for an error. + */ + int raw_level; + /* + * when sw_debounce is set on HTE enabled line, this is running + * counter of the discarded events. + */ + u32 total_discard_seq; + /* + * when sw_debounce is set on HTE enabled line, this variable records + * last sequence number before debounce period expires. + */ + u32 last_seqno; }; /** @@ -518,6 +538,7 @@ struct linereq { GPIO_V2_LINE_DRIVE_FLAGS | \ GPIO_V2_LINE_EDGE_FLAGS | \ GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME | \ + GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ GPIO_V2_LINE_BIAS_FLAGS) static void linereq_put_event(struct linereq *lr, @@ -542,10 +563,98 @@ static u64 line_event_timestamp(struct line *line) { if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags)) return ktime_get_real_ns(); + else if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) + return line->timestamp_ns; return ktime_get_ns(); } +static enum hte_return process_hw_ts_thread(void *p) +{ + struct line *line; + struct linereq *lr; + struct gpio_v2_line_event le; + int level; + u64 eflags; + + if (!p) + return HTE_CB_HANDLED; + + line = p; + lr = line->req; + + memset(&le, 0, sizeof(le)); + + le.timestamp_ns = line->timestamp_ns; + eflags = READ_ONCE(line->eflags); + + if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { + if (line->raw_level >= 0) { + if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) + level = !line->raw_level; + else + level = line->raw_level; + } else { + level = gpiod_get_value_cansleep(line->desc); + } + + if (level) + le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; + else + le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; + } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { + /* Emit low-to-high event */ + le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; + } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { + /* Emit high-to-low event */ + le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; + } else { + return HTE_CB_HANDLED; + } + le.line_seqno = line->line_seqno; + le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno; + le.offset = gpio_chip_hwgpio(line->desc); + + linereq_put_event(lr, &le); + + return HTE_CB_HANDLED; +} + +static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) +{ + struct line *line; + struct linereq *lr; + int diff_seqno = 0; + + if (!ts || !p) + return HTE_CB_HANDLED; + + line = p; + line->timestamp_ns = ts->tsc; + line->raw_level = ts->raw_level; + lr = line->req; + + if (READ_ONCE(line->sw_debounced)) { + line->total_discard_seq++; + line->last_seqno = ts->seq; + mod_delayed_work(system_wq, &line->work, + usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us))); + } else { + if (unlikely(ts->seq < line->line_seqno)) + return HTE_CB_HANDLED; + + diff_seqno = ts->seq - line->line_seqno; + line->line_seqno = ts->seq; + if (lr->num_lines != 1) + line->req_seqno = atomic_add_return(diff_seqno, + &lr->seqno); + + return HTE_RUN_SECOND_CB; + } + + return HTE_CB_HANDLED; +} + static irqreturn_t edge_irq_thread(int irq, void *p) { struct line *line = p; @@ -651,10 +760,16 @@ static void debounce_work_func(struct work_struct *work) struct gpio_v2_line_event le; struct line *line = container_of(work, struct line, work.work); struct linereq *lr; - int level; + int level, diff_seqno; u64 eflags; - level = gpiod_get_raw_value_cansleep(line->desc); + if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { + level = line->raw_level; + if (level < 0) + level = gpiod_get_raw_value_cansleep(line->desc); + } else { + level = gpiod_get_raw_value_cansleep(line->desc); + } if (level < 0) { pr_debug_ratelimited("debouncer failed to read line value\n"); return; @@ -685,10 +800,21 @@ static void debounce_work_func(struct work_struct *work) lr = line->req; le.timestamp_ns = line_event_timestamp(line); le.offset = gpio_chip_hwgpio(line->desc); - line->line_seqno++; - le.line_seqno = line->line_seqno; - le.seqno = (lr->num_lines == 1) ? - le.line_seqno : atomic_inc_return(&lr->seqno); + if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { + /* discard events except the last one */ + line->total_discard_seq -= 1; + diff_seqno = line->last_seqno - line->total_discard_seq - + line->line_seqno; + line->line_seqno = line->last_seqno - line->total_discard_seq; + le.line_seqno = line->line_seqno; + le.seqno = (lr->num_lines == 1) ? + le.line_seqno : atomic_add_return(diff_seqno, &lr->seqno); + } else { + line->line_seqno++; + le.line_seqno = line->line_seqno; + le.seqno = (lr->num_lines == 1) ? + le.line_seqno : atomic_inc_return(&lr->seqno); + } if (level) /* Emit low-to-high event */ @@ -700,8 +826,34 @@ static void debounce_work_func(struct work_struct *work) linereq_put_event(lr, &le); } +static int hte_edge_setup(struct line *line, u64 eflags) +{ + int ret; + unsigned long flags = 0; + struct hte_ts_desc *hdesc = &line->hdesc; + + if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_FALLING_EDGE_TS : HTE_RISING_EDGE_TS; + if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_RISING_EDGE_TS : HTE_FALLING_EDGE_TS; + + line->total_discard_seq = 0; + + hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, + NULL, line->desc); + + ret = hte_ts_get(NULL, hdesc, 0); + if (ret) + return ret; + + return hte_request_ts_ns(hdesc, process_hw_ts, + process_hw_ts_thread, line); +} + static int debounce_setup(struct line *line, - unsigned int debounce_period_us) + unsigned int debounce_period_us, bool hte_req) { unsigned long irqflags; int ret, level, irq; @@ -721,19 +873,27 @@ static int debounce_setup(struct line *line, if (level < 0) return level; - irq = gpiod_to_irq(line->desc); - if (irq < 0) - return -ENXIO; + if (!hte_req) { + irq = gpiod_to_irq(line->desc); + if (irq < 0) + return -ENXIO; - WRITE_ONCE(line->level, level); - irqflags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING; - ret = request_irq(irq, debounce_irq_handler, irqflags, - line->req->label, line); - if (ret) - return ret; + irqflags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING; + ret = request_irq(irq, debounce_irq_handler, irqflags, + line->req->label, line); + if (ret) + return ret; + line->irq = irq; + } else { + ret = hte_edge_setup(line, + GPIO_V2_LINE_FLAG_EDGE_RISING | + GPIO_V2_LINE_FLAG_EDGE_FALLING); + if (ret) + return ret; + } + WRITE_ONCE(line->level, level); WRITE_ONCE(line->sw_debounced, 1); - line->irq = irq; } return 0; } @@ -766,13 +926,16 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc, return 0; } -static void edge_detector_stop(struct line *line) +static void edge_detector_stop(struct line *line, bool hte_en) { - if (line->irq) { + if (line->irq && !hte_en) { free_irq(line->irq, line); line->irq = 0; } + if (hte_en) + hte_ts_put(&line->hdesc); + cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); WRITE_ONCE(line->eflags, 0); @@ -784,7 +947,7 @@ static void edge_detector_stop(struct line *line) static int edge_detector_setup(struct line *line, struct gpio_v2_line_config *lc, unsigned int line_idx, - u64 eflags) + u64 eflags, bool hte_req) { u32 debounce_period_us; unsigned long irqflags = 0; @@ -799,7 +962,7 @@ static int edge_detector_setup(struct line *line, WRITE_ONCE(line->eflags, eflags); if (gpio_v2_line_config_debounced(lc, line_idx)) { debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - ret = debounce_setup(line, debounce_period_us); + ret = debounce_setup(line, debounce_period_us, hte_req); if (ret) return ret; WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); @@ -809,6 +972,9 @@ static int edge_detector_setup(struct line *line, if (!eflags || READ_ONCE(line->sw_debounced)) return 0; + if (hte_req) + return hte_edge_setup(line, eflags); + irq = gpiod_to_irq(line->desc); if (irq < 0) return -ENXIO; @@ -834,13 +1000,18 @@ static int edge_detector_setup(struct line *line, static int edge_detector_update(struct line *line, struct gpio_v2_line_config *lc, unsigned int line_idx, - u64 eflags, bool polarity_change) + u64 flags, bool polarity_change, + bool prev_hte_flag) { + u64 eflags = flags & GPIO_V2_LINE_EDGE_FLAGS; unsigned int debounce_period_us = - gpio_v2_line_config_debounce_period(lc, line_idx); + gpio_v2_line_config_debounce_period(lc, line_idx); + bool hte_change = (prev_hte_flag != + ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) != 0)); if ((READ_ONCE(line->eflags) == eflags) && !polarity_change && - (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) + (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us) + && !hte_change) return 0; /* sw debounced and still will be...*/ @@ -851,11 +1022,12 @@ static int edge_detector_update(struct line *line, } /* reconfiguring edge detection or sw debounce being disabled */ - if ((line->irq && !READ_ONCE(line->sw_debounced)) || + if ((line->irq && !READ_ONCE(line->sw_debounced)) || prev_hte_flag || (!debounce_period_us && READ_ONCE(line->sw_debounced))) - edge_detector_stop(line); + edge_detector_stop(line, prev_hte_flag); - return edge_detector_setup(line, lc, line_idx, eflags); + return edge_detector_setup(line, lc, line_idx, eflags, + flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); } static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc, @@ -891,7 +1063,6 @@ static int gpio_v2_line_flags_validate(u64 flags) /* Return an error if an unknown flag is set */ if (flags & ~GPIO_V2_LINE_VALID_FLAGS) return -EINVAL; - /* * Do not allow both INPUT and OUTPUT flags to be set as they are * contradictory. @@ -900,6 +1071,11 @@ static int gpio_v2_line_flags_validate(u64 flags) (flags & GPIO_V2_LINE_FLAG_OUTPUT)) return -EINVAL; + /* Only allow one event clock source */ + if ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) && + (flags & GPIO_V2_LI |
