From 42112dd77b74220e6a1f4a71bb51ca3f583d3842 Mon Sep 17 00:00:00 2001 From: Dipen Patel Date: Fri, 22 Apr 2022 13:52:16 -0700 Subject: gpiolib: Add HTE support Some GPIO chip can provide hardware timestamp support on its GPIO lines , in order to support that, additional API needs to be added which can talk to both GPIO chip and HTE (hardware timestamping engine) providers if there is any dependencies. This patch introduces optional hooks to enable and disable hardware timestamping related features in the GPIO controller chip. Signed-off-by: Dipen Patel Reviewed-by: Linus Walleij Reported-by: kernel test robot Signed-off-by: Thierry Reding --- drivers/gpio/gpiolib.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.h | 1 + 2 files changed, 59 insertions(+) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e59884cc12a7..60cd7cc33647 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2434,6 +2434,64 @@ set_output_flag: } 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 diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 06f3faa9fbef..29a7d39062b3 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -158,6 +158,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; -- cgit v1.2.3 From 10e4afd6cc4bf1b6cc21ad9418bcebdcc18abefb Mon Sep 17 00:00:00 2001 From: Dipen Patel Date: Fri, 22 Apr 2022 13:52:17 -0700 Subject: gpio: tegra186: Add HTE support 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 Signed-off-by: Thierry Reding --- drivers/gpio/gpio-tegra186.c | 81 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 031fe105b58e..6a6852b4785e 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 + * Dipen Patel */ #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include @@ -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) \ -- cgit v1.2.3 From 2068339a6c35147847ba433fd0da67b313779059 Mon Sep 17 00:00:00 2001 From: Dipen Patel Date: Fri, 22 Apr 2022 13:52:18 -0700 Subject: gpiolib: cdev: Add hardware timestamp clock type This patch adds new clock type for the GPIO controller which can timestamp gpio lines in using hardware means. To expose such functionalities to the userspace, code has been added where during line create or set config API calls, it checks for new clock type and if requested, calls HTE API. During line change event, the HTE subsystem pushes timestamp data to userspace through gpiolib-cdev. Signed-off-by: Dipen Patel Acked-by: Linus Walleij Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Thierry Reding --- drivers/gpio/gpiolib-cdev.c | 252 ++++++++++++++++++++++++++++++++++++++------ include/uapi/linux/gpio.h | 3 + 2 files changed, 222 insertions(+), 33 deletions(-) (limited to 'drivers/gpio') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index ffa0256cad5a..d8dba8c9d2cf 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #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_LINE_FLAG_EVENT_CLOCK_HTE)) + return -EINVAL; + /* Edge detection requires explicit input. */ if ((flags & GPIO_V2_LINE_EDGE_FLAGS) && !(flags & GPIO_V2_LINE_FLAG_INPUT)) @@ -992,6 +1168,8 @@ static void gpio_v2_line_config_flags_to_desc_flags(u64 flags, assign_bit(FLAG_EVENT_CLOCK_REALTIME, flagsp, flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME); + assign_bit(FLAG_EVENT_CLOCK_HTE, flagsp, + flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); } static long linereq_get_values(struct linereq *lr, void __user *ip) @@ -1121,6 +1299,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, unsigned int i; u64 flags; bool polarity_change; + bool prev_hte_flag; int ret; for (i = 0; i < lr->num_lines; i++) { @@ -1130,6 +1309,8 @@ static long linereq_set_config_unlocked(struct linereq *lr, (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) != ((flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) != 0)); + prev_hte_flag = !!test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags); + gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags); /* * Lines have to be requested explicitly for input @@ -1138,7 +1319,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { int val = gpio_v2_line_config_output_value(lc, i); - edge_detector_stop(&lr->lines[i]); + edge_detector_stop(&lr->lines[i], prev_hte_flag); ret = gpiod_direction_output(desc, val); if (ret) return ret; @@ -1148,8 +1329,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, return ret; ret = edge_detector_update(&lr->lines[i], lc, i, - flags & GPIO_V2_LINE_EDGE_FLAGS, - polarity_change); + flags, polarity_change, prev_hte_flag); if (ret) return ret; } @@ -1278,9 +1458,12 @@ static ssize_t linereq_read(struct file *file, static void linereq_free(struct linereq *lr) { unsigned int i; + bool hte; for (i = 0; i < lr->num_lines; i++) { - edge_detector_stop(&lr->lines[i]); + hte = !!test_bit(FLAG_EVENT_CLOCK_HTE, + &lr->lines[i].desc->flags); + edge_detector_stop(&lr->lines[i], hte); if (lr->lines[i].desc) gpiod_free(lr->lines[i].desc); } @@ -1406,7 +1589,8 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) goto out_free_linereq; ret = edge_detector_setup(&lr->lines[i], lc, i, - flags & GPIO_V2_LINE_EDGE_FLAGS); + flags & GPIO_V2_LINE_EDGE_FLAGS, + flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); if (ret) goto out_free_linereq; } @@ -1959,6 +2143,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &desc->flags)) info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME; + else if (test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags)) + info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE; debounce_period_us = READ_ONCE(desc->debounce_period_us); if (debounce_period_us) { diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index eaaea3d8e6b4..cb9966d49a16 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -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), }; /** -- cgit v1.2.3