gpio: tegra186: Add HTE support
authorDipen Patel <dipenp@nvidia.com>
Fri, 22 Apr 2022 20:52:17 +0000 (13:52 -0700)
committerThierry Reding <treding@nvidia.com>
Wed, 4 May 2022 09:06:13 +0000 (11:06 +0200)
Tegra194 AON GPIO controller with the use of its internal hardware
timestamping engine (HTE), also known as GTE, can timestamp GPIO lines
through system counter. This patch implements enable/disable callbacks
for the GPIO controller. In enable call, it will set timestamp function
bit and GPIO line rising/falling edges in the config register. In
disable call, it restores the state.

Signed-off-by: Dipen Patel <dipenp@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpio/gpio-tegra186.c

index 031fe10..6a6852b 100644 (file)
@@ -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 {
@@ -194,6 +198,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);
@@ -726,6 +800,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;
 
@@ -977,6 +1055,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)    \