Commit 29814363 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hte/for-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux

Pull hardware timestamping subsystem from Thierry Reding:
 "This contains the new HTE (hardware timestamping engine) subsystem
  that has been in the works for a couple of months now.

  The infrastructure provided allows for drivers to register as hardware
  timestamp providers, while consumers will be able to request events
  that they are interested in (such as GPIOs and IRQs) to be timestamped
  by the hardware providers.

  Note that this currently supports only one provider, but there seems
  to be enough interest in this functionality and we expect to see more
  drivers added once this is merged"

[ Linus Walleij mentions the Intel PMC in the Elkhart and Tiger Lake
  platforms as another future timestamp provider ]

* tag 'hte/for-5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
  dt-bindings: timestamp: Correct id path
  dt-bindings: Renamed hte directory to timestamp
  hte: Uninitialized variable in hte_ts_get()
  hte: Fix off by one in hte_push_ts_ns()
  hte: Fix possible use-after-free in tegra_hte_test_remove()
  hte: Remove unused including <linux/version.h>
  MAINTAINERS: Add HTE Subsystem
  hte: Add Tegra HTE test driver
  tools: gpio: Add new hardware clock type
  gpiolib: cdev: Add hardware timestamp clock type
  gpio: tegra186: Add HTE support
  gpiolib: Add HTE support
  dt-bindings: Add HTE bindings
  hte: Add Tegra194 HTE kernel provider
  drivers: Add hardware timestamp engine (HTE) subsystem
  Documentation: Add HTE subsystem guide
parents 71e80720 5dad4ecc
# 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
# 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";
};
# 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>;
};
...
.. 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>/``.
.. 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
.. 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.
......@@ -137,6 +137,7 @@ needed).
scheduler/index
mhi/index
peci/index
hte/index
Architecture-agnostic documentation
-----------------------------------
......
......@@ -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
......
......@@ -237,4 +237,6 @@ source "drivers/most/Kconfig"
source "drivers/peci/Kconfig"
source "drivers/hte/Kconfig"
endmenu
......@@ -187,3 +187,4 @@ obj-$(CONFIG_INTERCONNECT) += interconnect/
obj-$(CONFIG_COUNTER) += counter/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_PECI) += peci/
obj-$(CONFIG_HTE) += hte/
// 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) \
......
This diff is collapsed.
......@@ -2453,6 +2453,64 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
}
EXPORT_SYMBOL_GPL(gpiod_direction_output);
/**
* gpiod_enable_hw_timestamp_ns - Enable hardware timestamp in nanoseconds.
*
* @desc: GPIO to enable.
* @flags: Flags related to GPIO edge.
*
* Return 0 in case of success, else negative error code.
*/
int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
int ret = 0;
struct gpio_chip *gc;
VALIDATE_DESC(desc);
gc = desc->gdev->chip;
if (!gc->en_hw_timestamp) {
gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
return -ENOTSUPP;
}
ret = gc->en_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
if (ret)
gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
return ret;
}
EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns);
/**
* gpiod_disable_hw_timestamp_ns - Disable hardware timestamp.
*
* @desc: GPIO to disable.
* @flags: Flags related to GPIO edge, same value as used during enable call.
*
* Return 0 in case of success, else negative error code.
*/
int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
{
int ret = 0;
struct gpio_chip *gc;
VALIDATE_DESC(desc);
gc = desc->gdev->chip;
if (!gc->dis_hw_timestamp) {
gpiod_warn(desc, "%s: hw ts not supported\n", __func__);
return -ENOTSUPP;
}
ret = gc->dis_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags);
if (ret)
gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
return ret;
}
EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns);
/**
* gpiod_set_config - sets @config for a GPIO
* @desc: descriptor of the GPIO for which to set the configuration
......
......@@ -161,6 +161,7 @@ struct gpio_desc {
#define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */
#define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
#define FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
#define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
/* Connection label */
const char *label;
......
# SPDX-License-Identifier: GPL-2.0-only
menuconfig HTE
bool "Hardware Timestamping Engine (HTE) Support"
help
Hardware Timestamping Engine (HTE) Support.
Some devices provide a hardware timestamping engine which can
timestamp certain device lines/signals in realtime. It comes with a
benefit for the applications needing accurate timestamping event with
less jitter. This framework provides a generic interface to such HTE
providers and consumer devices.
If unsure, say no.
if HTE
config HTE_TEGRA194
tristate "NVIDIA Tegra194 HTE Support"
depends on ARCH_TEGRA_194_SOC
help
Enable this option for integrated hardware timestamping engine also
known as generic timestamping engine (GTE) support on NVIDIA Tegra194
systems-on-chip. The driver supports 352 LIC IRQs and 39 AON GPIOs
lines for timestamping in realtime.
config HTE_TEGRA194_TEST
tristate "NVIDIA Tegra194 HTE Test"
depends on HTE_TEGRA194
help
The NVIDIA Tegra194 GTE test driver demonstrates how to use HTE
framework to timestamp GPIO and LIC IRQ lines.
endif
obj-$(CONFIG_HTE) += hte.o
obj-$(CONFIG_HTE_TEGRA194) += hte-tegra194.o
obj-$(CONFIG_HTE_TEGRA194_TEST) += hte-tegra194-test.o
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021-2022 NVIDIA Corporation
*
* Author: Dipen Patel <dipenp@nvidia.com>
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/hte.h>
/*
* This sample HTE GPIO test driver demonstrates HTE API usage by enabling
* hardware timestamp on gpio_in and specified LIC IRQ lines.
*
* Note: gpio_out and gpio_in need to be shorted externally in order for this
* test driver to work for the GPIO monitoring. The test driver has been
* tested on Jetson AGX Xavier platform by shorting pin 32 and 16 on 40 pin
* header.
*
* Device tree snippet to activate this driver:
* tegra_hte_test {
* compatible = "nvidia,tegra194-hte-test";
* in-gpio = <&gpio_aon TEGRA194_AON_GPIO(BB, 1)>;
* out-gpio = <&gpio_aon TEGRA194_AON_GPIO(BB, 0)>;
* timestamps = <&tegra_hte_aon TEGRA194_AON_GPIO(BB, 1)>,
* <&tegra_hte_lic 0x19>;
* timestamp-names = "hte-gpio", "hte-i2c-irq";
* status = "okay";
* };
*
* How to run test driver:
* - Load test driver.
* - For the GPIO, at regular interval gpio_out pin toggles triggering
* HTE for rising edge on gpio_in pin.
*
* - For the LIC IRQ line, it uses 0x19 interrupt which is i2c controller 1.
* - Run i2cdetect -y 1 1>/dev/null, this command will generate i2c bus
* transactions which creates timestamp data.
* - It prints below message for both the lines.
* HW timestamp(<line id>:<ts seq number>): <timestamp>, edge: <edge>.
* - Unloading the driver disables and deallocate the HTE.
*/
static struct tegra_hte_test {
int gpio_in_irq;
struct device *pdev;
struct gpio_desc *gpio_in;
struct gpio_desc *gpio_out;
struct hte_ts_desc *desc;
struct timer_list timer;
struct kobject *kobj;
} hte;
static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
{
char *edge;
struct hte_ts_desc *desc = p;
if (!ts || !p)
return HTE_CB_HANDLED;
if (ts->raw_level < 0)
edge = "Unknown";
pr_info("HW timestamp(%u: %llu): %llu, edge: %s\n",
desc->attr.line_id, ts->seq, ts->tsc,
(ts->raw_level >= 0) ? ((ts->raw_level == 0) ?
"falling" : "rising") : edge);
return HTE_CB_HANDLED;
}
static void gpio_timer_cb(struct timer_list *t)
{
(void)t;
gpiod_set_value(hte.gpio_out, !gpiod_get_value(hte.gpio_out));
mod_timer(&hte.timer, jiffies + msecs_to_jiffies(8000));
}
static irqreturn_t tegra_hte_test_gpio_isr(int irq, void *data)
{
(void)irq;
(void)data;
return IRQ_HANDLED;
}
static const struct of_device_id tegra_hte_test_of_match[] = {
{ .compatible = "nvidia,tegra194-hte-test"},
{ }
};
MODULE_DEVICE_TABLE(of, tegra_hte_test_of_match);
static int tegra_hte_test_probe(struct platform_device *pdev)
{
int ret = 0;
int i, cnt;
dev_set_drvdata(&pdev->dev, &hte);
hte.pdev = &pdev->dev;
hte.gpio_out = gpiod_get(&pdev->dev, "out", 0);
if (IS_ERR(hte.gpio_out)) {
dev_err(&pdev->dev, "failed to get gpio out\n");
ret = -EINVAL;
goto out;
}
hte.gpio_in = gpiod_get(&pdev->dev, "in", 0);
if (IS_ERR(hte.gpio_in)) {
dev_err(&pdev->dev, "failed to get gpio in\n");
ret = -EINVAL;
goto free_gpio_out;
}
ret = gpiod_direction_output(hte.gpio_out, 0);
if (ret) {
dev_err(&pdev->dev, "failed to set output\n");
ret = -EINVAL;
goto free_gpio_in;
}
ret = gpiod_direction_input(hte.gpio_in);
if (ret) {
dev_err(&pdev->dev, "failed to set input\n");
ret = -EINVAL;
goto free_gpio_in;
}
ret = gpiod_to_irq(hte.gpio_in);
if (ret < 0) {
dev_err(&pdev->dev, "failed to map GPIO to IRQ: %d\n", ret);
ret = -ENXIO;
goto free_gpio_in;
}
hte.gpio_in_irq = ret;
ret = request_irq(ret, tegra_hte_test_gpio_isr,
IRQF_TRIGGER_RISING,
"tegra_hte_gpio_test_isr", &hte);
if (ret) {
dev_err(&pdev->dev, "failed to acquire IRQ\n");
ret = -ENXIO;
goto free_irq;
}
cnt = of_hte_req_count(hte.pdev);
if (cnt < 0)
goto free_irq;
dev_info(&pdev->dev, "Total requested lines:%d\n", cnt);
hte.desc = devm_kzalloc(hte.pdev, sizeof(*hte.desc) * cnt, GFP_KERNEL);
if (!hte.desc) {
ret = -ENOMEM;
goto free_irq;
}
for (i = 0; i < cnt; i++) {
if (i == 0)
/*
* GPIO hte init, line_id and name will be parsed from
* the device tree node. The edge_flag is implicitly
* set by request_irq call. Only line_data is needed to be
* set.
*/
hte_init_line_attr(&hte.desc[i], 0, 0, NULL,
hte.gpio_in);
else
/*
* same comment as above except that IRQ does not need
* line data.
*/
hte_init_line_attr(&hte.desc[i], 0, 0, NULL, NULL);
ret = hte_ts_get(hte.pdev, &hte.desc[i], i);
if (ret)
goto ts_put;
ret = devm_hte_request_ts_ns(hte.pdev, &hte.desc[i],
process_hw_ts, NULL,
&hte.desc[i]);
if (ret) /* no need to ts_put, request API takes care */
goto free_irq;
}
timer_setup(&hte.timer, gpio_timer_cb, 0);
mod_timer(&hte.timer, jiffies + msecs_to_jiffies(5000));
return 0;
ts_put:
cnt = i;
for (i = 0; i < cnt; i++)
hte_ts_put(&hte.desc[i]);
free_irq:
free_irq(hte.gpio_in_irq, &hte);
free_gpio_in:
gpiod_put(hte.gpio_in);
free_gpio_out:
gpiod_put(hte.gpio_out);
out:
return ret;
}
static int tegra_hte_test_remove(struct platform_device *pdev)
{
(void)pdev;
free_irq(hte.gpio_in_irq, &hte);
gpiod_put(hte.gpio_in);
gpiod_put(hte.gpio_out);
del_timer_sync(&hte.timer);
return 0;
}
static struct platform_driver tegra_hte_test_driver = {
.probe = tegra_hte_test_probe,
.remove = tegra_hte_test_remove,
.driver = {
.name = "tegra_hte_test",
.of_match_table = tegra_hte_test_of_match,
},
};
module_platform_driver(tegra_hte_test_driver);
MODULE_AUTHOR("Dipen Patel <dipenp@nvidia.com>");
MODULE_LICENSE("GPL");
This diff is collapsed.
This diff is collapsed.
......@@ -109,6 +109,8 @@ int gpiod_get_direction(struct gpio_desc *desc);
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags);
int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags);
/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
......@@ -350,8 +352,18 @@ static inline int gpiod_direction_output_raw(struct gpio_desc *desc, int value)
WARN_ON(desc);
return -ENOSYS;
}
static inline int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc,
unsigned long flags)
{
WARN_ON(desc);
return -ENOSYS;
}
static inline int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc,
unsigned long flags)
{
WARN_ON(desc);
return -ENOSYS;
}
static inline int gpiod_get_value(const struct gpio_desc *desc)
{
/* GPIO can never have been requested */
......
......@@ -333,6 +333,10 @@ struct gpio_irq_chip {
* @add_pin_ranges: optional routine to initialize pin ranges, to be used when
* requires special mapping of the pins that provides GPIO functionality.
* It is called after adding GPIO chip and before adding IRQ chip.
* @en_hw_timestamp: Dependent on GPIO chip, an optional routine to
* enable hardware timestamp.
* @dis_hw_timestamp: Dependent on GPIO chip, an optional routine to
* disable hardware timestamp.
* @base: identifies the first GPIO number handled by this chip;
* or, if negative during registration, requests dynamic ID allocation.
* DEPRECATION: providing anything non-negative and nailing the base
......@@ -429,6 +433,12 @@ struct gpio_chip {
int (*add_pin_ranges)(struct gpio_chip *gc);
int (*en_hw_timestamp)(struct gpio_chip *gc,
u32 offset,
unsigned long flags);
int (*dis_hw_timestamp)(struct gpio_chip *gc,
u32 offset,
unsigned long flags);
int base;
u16 ngpio;
u16 offset;
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_HTE_H
#define __LINUX_HTE_H
#include <linux/errno.h>
struct hte_chip;
struct hte_device;
struct of_phandle_args;
/**
* enum hte_edge - HTE line edge flags.
*
* @HTE_EDGE_NO_SETUP: No edge setup. In this case consumer will setup edges,
* for example during request irq call.
* @HTE_RISING_EDGE_TS: Rising edge.
* @HTE_FALLING_EDGE_TS: Falling edge.
*
*/
enum hte_edge {
HTE_EDGE_NO_SETUP = 1U << 0,
HTE_RISING_EDGE_TS = 1U << 1,
HTE_FALLING_EDGE_TS = 1U << 2,
};
/**
* enum hte_return - HTE subsystem return values used during callback.
*
* @HTE_CB_HANDLED: The consumer handled the data.
* @HTE_RUN_SECOND_CB: The consumer needs further processing, in that case
* HTE subsystem calls secondary callback provided by the consumer where it
* is allowed to sleep.
*/
enum hte_return {
HTE_CB_HANDLED,
HTE_RUN_SECOND_CB,
};
/**
* struct hte_ts_data - HTE timestamp data.
*
* @tsc: Timestamp value.
* @seq: Sequence counter of the timestamps.
* @raw_level: Level of the line at the timestamp if provider supports it,
* -1 otherwise.
*/
struct hte_ts_data {
u64 tsc;
u64 seq;
int raw_level;
};
/**
* struct hte_clk_info - Clock source info that HTE provider uses to timestamp.
*
* @hz: Supported clock rate in HZ, for example 1KHz clock = 1000.
* @type: Supported clock type.
*/
struct hte_clk_info {
u64 hz;
clockid_t type;
};
/**
* typedef hte_ts_cb_t - HTE timestamp data processing primary callback.
*
* The callback is used to push timestamp data to the client and it is
* not allowed to sleep.
*
* @ts: HW timestamp data.
* @data: Client supplied data.
*/
typedef enum hte_return (*hte_ts_cb_t)(struct hte_ts_data *ts, void *data);
/**
* typedef hte_ts_sec_cb_t - HTE timestamp data processing secondary callback.
*
* This is used when the client needs further processing where it is
* allowed to sleep.
*
* @data: Client supplied data.
*
*/
typedef enum hte_return (*hte_ts_sec_cb_t)(void *data);
/**
* struct hte_line_attr - Line attributes.
*
* @line_id: The logical ID understood by the consumers and providers.
* @line_data: Line data related to line_id.
* @edge_flags: Edge setup flags.
* @name: Descriptive name of the entity that is being monitored for the
* hardware timestamping. If null, HTE core will construct the name.
*
*/
struct hte_line_attr {
u32 line_id;
void *line_data;
unsigned long edge_flags;
const char *name;
};
/**
* struct hte_ts_desc - HTE timestamp descriptor.
*
* This structure is a communication token between consumers to subsystem
* and subsystem to providers.
*
* @attr: The line attributes.
* @hte_data: Subsystem's private data, set by HTE subsystem.
*/
struct hte_ts_desc {
struct hte_line_attr attr;
void *hte_data;
};
/**
* struct hte_ops - HTE operations set by providers.
*
* @request: Hook for requesting a HTE timestamp. Returns 0 on success,
* non-zero for failures.
* @release: Hook for releasing a HTE timestamp. Returns 0 on success,
* non-zero for failures.
* @enable: Hook to enable the specified timestamp. Returns 0 on success,
* non-zero for failures.
* @disable: Hook to disable specified timestamp. Returns 0 on success,
* non-zero for failures.
* @get_clk_src_info: Hook to get the clock information the provider uses
* to timestamp. Returns 0 for success and negative error code for failure. On
* success HTE subsystem fills up provided struct hte_clk_info.
*
* xlated_id parameter is used to communicate between HTE subsystem and the
* providers and is translated by the provider.
*/
struct hte_ops {
int (*request)(struct hte_chip *chip, struct hte_ts_desc *desc,
u32 xlated_id);
int (*release)(struct hte_chip *chip, struct hte_ts_desc *desc,
u32 xlated_id);
int (*enable)(struct hte_chip *chip, u32 xlated_id);
int (*disable)(struct hte_chip *chip, u32 xlated_id);
int (*get_clk_src_info)(struct hte_chip *chip,
struct hte_clk_info *ci);
};
/**
* struct hte_chip - Abstract HTE chip.
*
* @name: functional name of the HTE IP block.
* @dev: device providing the HTE.
* @ops: callbacks for this HTE.
* @nlines: number of lines/signals supported by this chip.
* @xlate_of: Callback which translates consumer supplied logical ids to
* physical ids, return 0 for the success and negative for the failures.
* It stores (between 0 to @nlines) in xlated_id parameter for the success.
* @xlate_plat: Same as above but for the consumers with no DT node.
* @match_from_linedata: Match HTE device using the line_data.
* @of_hte_n_cells: Number of cells used to form the HTE specifier.
* @gdev: HTE subsystem abstract device, internal to the HTE subsystem.
* @data: chip specific private data.
*/
struct hte_chip {
const char *name;
struct device *dev;
const struct hte_ops *ops;
u32 nlines;
int (*xlate_of)(struct hte_chip *gc,
const struct of_phandle_args *args,
struct hte_ts_desc *desc, u32 *xlated_id);
int (*xlate_plat)(struct hte_chip *gc, struct hte_ts_desc *desc,
u32 *xlated_id);
bool (*match_from_linedata)(const struct hte_chip *chip,
const struct hte_ts_desc *hdesc);
u8 of_hte_n_cells;
struct hte_device *gdev;
void *data;
};
#if IS_ENABLED(CONFIG_HTE)
/* HTE APIs for the providers */
int devm_hte_register_chip(struct hte_chip *chip);
int hte_push_ts_ns(const struct hte_chip *chip, u32 xlated_id,
struct hte_ts_data *data);
/* HTE APIs for the consumers */
int hte_init_line_attr(struct hte_ts_desc *desc, u32 line_id,
unsigned long edge_flags, const char *name,
void *data);
int hte_ts_get(struct device *dev, struct hte_ts_desc *desc, int index);
int hte_ts_put(struct hte_ts_desc *desc);
int hte_request_ts_ns(struct hte_ts_desc *desc, hte_ts_cb_t cb,
hte_ts_sec_cb_t tcb, void *data);
int devm_hte_request_ts_ns(struct device *dev, struct hte_ts_desc *desc,
hte_ts_cb_t cb, hte_ts_sec_cb_t tcb, void *data);
int of_hte_req_count(struct device *dev);
int hte_enable_ts(struct hte_ts_desc *desc);
int hte_disable_ts(struct hte_ts_desc *desc);
int hte_get_clk_src_info(const struct hte_ts_desc *desc,
struct hte_clk_info *ci);
#else /* !CONFIG_HTE */
static inline int devm_hte_register_chip(struct hte_chip *chip)
{
return -EOPNOTSUPP;
}
static inline int hte_push_ts_ns(const struct hte_chip *chip,
u32 xlated_id,
const struct hte_ts_data *data)
{
return -EOPNOTSUPP;
}
static inline int hte_init_line_attr(struct hte_ts_desc *desc, u32 line_id,
unsigned long edge_flags,
const char *name, void *data)
{
return -EOPNOTSUPP;
}
static inline int hte_ts_get(struct device *dev, struct hte_ts_desc *desc,
int index)
{
return -EOPNOTSUPP;
}
static inline int hte_ts_put(struct hte_ts_desc *desc)
{
return -EOPNOTSUPP;
}
static inline int hte_request_ts_ns(struct hte_ts_desc *desc, hte_ts_cb_t cb,
hte_ts_sec_cb_t tcb, void *data)
{
return -EOPNOTSUPP;
}
static inline int devm_hte_request_ts_ns(struct device *dev,
struct hte_ts_desc *desc,
hte_ts_cb_t cb,
hte_ts_sec_cb_t tcb,
void *data)
{
return -EOPNOTSUPP;
}
static inline int of_hte_req_count(struct device *dev)
{
return -EOPNOTSUPP;
}
static inline int hte_enable_ts(struct hte_ts_desc *desc)
{
return -EOPNOTSUPP;
}
static inline int hte_disable_ts(struct hte_ts_desc *desc)
{
return -EOPNOTSUPP;
}
static inline int hte_get_clk_src_info(const struct hte_ts_desc *desc,
struct hte_clk_info *ci)
{
return -EOPNOTSUPP;
}
#endif /* !CONFIG_HTE */
#endif
......@@ -66,6 +66,8 @@ struct gpiochip_info {
* @GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN: line has pull-down bias enabled
* @GPIO_V2_LINE_FLAG_BIAS_DISABLED: line has bias disabled
* @GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME: line events contain REALTIME timestamps
* @GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE: line events contain timestamps from
* hardware timestamp engine
*/
enum gpio_v2_line_flag {
GPIO_V2_LINE_FLAG_USED = _BITULL(0),
......@@ -80,6 +82,7 @@ enum gpio_v2_line_flag {
GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = _BITULL(9),
GPIO_V2_LINE_FLAG_BIAS_DISABLED = _BITULL(10),
GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME = _BITULL(11),
GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE = _BITULL(12),
};
/**
......
......@@ -149,6 +149,7 @@ void print_usage(void)
" -r Listen for rising edges\n"
" -f Listen for falling edges\n"
" -w Report the wall-clock time for events\n"
" -t Report the hardware timestamp for events\n"
" -b <n> Debounce the line with period n microseconds\n"
" [-c <n>] Do <n> loops (optional, infinite loop if not stated)\n"
" -? This helptext\n"
......@@ -174,7 +175,7 @@ int main(int argc, char **argv)
memset(&config, 0, sizeof(config));
config.flags = GPIO_V2_LINE_FLAG_INPUT;
while ((c = getopt(argc, argv, "c:n:o:b:dsrfw?")) != -1) {
while ((c = getopt(argc, argv, "c:n:o:b:dsrfwt?")) != -1) {
switch (c) {
case 'c':
loops = strtoul(optarg, NULL, 10);
......@@ -208,6 +209,9 @@ int main(int argc, char **argv)
case 'w':
config.flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME;
break;
case 't':
config.flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE;
break;
case '?':
print_usage();
return -1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment