Merge tag 'arm-dt-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-2.6-microblaze.git] / drivers / gpio / gpio-tegra186.c
index 84c4f1e..54d9fa7 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 {
@@ -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);
@@ -547,16 +621,13 @@ static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain,
        return 0;
 }
 
-static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
-                                                unsigned int parent_hwirq,
-                                                unsigned int parent_type)
+static int tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
+                                               union gpio_irq_fwspec *gfwspec,
+                                               unsigned int parent_hwirq,
+                                               unsigned int parent_type)
 {
        struct tegra_gpio *gpio = gpiochip_get_data(chip);
-       struct irq_fwspec *fwspec;
-
-       fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL);
-       if (!fwspec)
-               return NULL;
+       struct irq_fwspec *fwspec = &gfwspec->fwspec;
 
        fwspec->fwnode = chip->irq.parent_domain->fwnode;
        fwspec->param_count = 3;
@@ -564,7 +635,7 @@ static void *tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip,
        fwspec->param[1] = parent_hwirq;
        fwspec->param[2] = parent_type;
 
-       return fwspec;
+       return 0;
 }
 
 static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip,
@@ -747,6 +818,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 +1066,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)    \