gpiolib: cdev: consolidate edge detector configuration flags
authorKent Gibson <warthog618@gmail.com>
Thu, 14 Jul 2022 02:03:18 +0000 (10:03 +0800)
committerBartosz Golaszewski <brgl@bgdev.pl>
Tue, 19 Jul 2022 08:02:12 +0000 (10:02 +0200)
Combine the polarity_change flag, struct line eflags, and hte enable
flag into a single flag variable.

The combination of these flags describes the configuration state
of the edge detector, so formalize and clarify that by combining
them into a single variable, edflags, in struct line.

The edflags is a subset of the GPIO_V2_LINE_FLAGsb relevant to
the edge detector, and is also a superset of the eflags it replaces.
The eflags name is still used to describe the subset of edflags
corresponding to the rising/falling edge flags where edflags is
masked down to that subset.

This consolidation reduces the number of variables being passed,
simplifies state comparisons, and provides a more extensible
foundation should additional edge sources be integrated in the
future.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
drivers/gpio/gpiolib-cdev.c

index 5765379f4b549064704f17d2085ff4ceff3f12a0..01c76aa007018f40597bef0dcb5fc4cff34b4ede 100644 (file)
@@ -430,12 +430,15 @@ struct line {
        struct linereq *req;
        unsigned int irq;
        /*
-        * eflags is set by edge_detector_setup(), edge_detector_stop() and
-        * edge_detector_update(), which are themselves mutually exclusive,
-        * and is accessed by edge_irq_thread() and debounce_work_func(),
-        * which can both live with a slightly stale value.
+        * The flags for the active edge detector configuration.
+        *
+        * edflags is set by linereq_create(), linereq_free(), and
+        * linereq_set_config_unlocked(), which are themselves mutually
+        * exclusive, and is accessed by edge_irq_thread(),
+        * process_hw_ts_thread() and debounce_work_func(),
+        * which can all live with a slightly stale value.
         */
-       u64 eflags;
+       u64 edflags;
        /*
         * timestamp_ns and req_seqno are accessed only by
         * edge_irq_handler() and edge_irq_thread(), which are themselves
@@ -541,6 +544,12 @@ struct linereq {
         GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
         GPIO_V2_LINE_BIAS_FLAGS)
 
+/* subset of flags relevant for edge detector configuration */
+#define GPIO_V2_LINE_EDGE_DETECTOR_FLAGS \
+       (GPIO_V2_LINE_FLAG_ACTIVE_LOW | \
+        GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \
+        GPIO_V2_LINE_EDGE_FLAGS)
+
 static void linereq_put_event(struct linereq *lr,
                              struct gpio_v2_line_event *le)
 {
@@ -580,8 +589,8 @@ static enum hte_return process_hw_ts_thread(void *p)
        struct line *line;
        struct linereq *lr;
        struct gpio_v2_line_event le;
+       u64 edflags;
        int level;
-       u64 eflags;
 
        if (!p)
                return HTE_CB_HANDLED;
@@ -592,15 +601,15 @@ static enum hte_return process_hw_ts_thread(void *p)
        memset(&le, 0, sizeof(le));
 
        le.timestamp_ns = line->timestamp_ns;
-       eflags = READ_ONCE(line->eflags);
+       edflags = READ_ONCE(line->edflags);
 
-       switch (eflags) {
+       switch (edflags & GPIO_V2_LINE_EDGE_FLAGS) {
        case GPIO_V2_LINE_FLAG_EDGE_BOTH:
                level = (line->raw_level >= 0) ?
                                line->raw_level :
                                gpiod_get_raw_value_cansleep(line->desc);
 
-               if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
+               if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
                        level = !level;
 
                le.id = line_event_id(level);
@@ -681,7 +690,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
        }
        line->timestamp_ns = 0;
 
-       switch (READ_ONCE(line->eflags)) {
+       switch (READ_ONCE(line->edflags) & GPIO_V2_LINE_EDGE_FLAGS) {
        case GPIO_V2_LINE_FLAG_EDGE_BOTH:
                le.id = line_event_id(gpiod_get_value_cansleep(line->desc));
                break;
@@ -756,16 +765,13 @@ 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, diff_seqno;
-       u64 eflags;
+       u64 eflags, edflags = READ_ONCE(line->edflags);
+       int level = -1, diff_seqno;
 
-       if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) {
+       if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
                level = line->raw_level;
-               if (level < 0)
-                       level = gpiod_get_raw_value_cansleep(line->desc);
-       } else {
+       if (level < 0)
                level = gpiod_get_raw_value_cansleep(line->desc);
-       }
        if (level < 0) {
                pr_debug_ratelimited("debouncer failed to read line value\n");
                return;
@@ -777,12 +783,12 @@ static void debounce_work_func(struct work_struct *work)
        WRITE_ONCE(line->level, level);
 
        /* -- edge detection -- */
-       eflags = READ_ONCE(line->eflags);
+       eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
        if (!eflags)
                return;
 
        /* switch from physical level to logical - if they differ */
-       if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags))
+       if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
                level = !level;
 
        /* ignore edges that are not being monitored */
@@ -796,7 +802,7 @@ 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);
-       if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) {
+       if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) {
                /* discard events except the last one */
                line->total_discard_seq -= 1;
                diff_seqno = line->last_seqno - line->total_discard_seq -
@@ -843,8 +849,7 @@ static int hte_edge_setup(struct line *line, u64 eflags)
                                 process_hw_ts_thread, line);
 }
 
-static int debounce_setup(struct line *line,
-                         unsigned int debounce_period_us, bool hte_req)
+static int debounce_setup(struct line *line, unsigned int debounce_period_us)
 {
        unsigned long irqflags;
        int ret, level, irq;
@@ -864,7 +869,7 @@ static int debounce_setup(struct line *line,
                if (level < 0)
                        return level;
 
-               if (!hte_req) {
+               if (!test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) {
                        irq = gpiod_to_irq(line->desc);
                        if (irq < 0)
                                return -ENXIO;
@@ -915,19 +920,19 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc,
        return 0;
 }
 
-static void edge_detector_stop(struct line *line, bool hte_en)
+static void edge_detector_stop(struct line *line)
 {
-       if (line->irq && !hte_en) {
+       if (line->irq) {
                free_irq(line->irq, line);
                line->irq = 0;
        }
 
-       if (hte_en)
+       if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
                hte_ts_put(&line->hdesc);
 
        cancel_delayed_work_sync(&line->work);
        WRITE_ONCE(line->sw_debounced, 0);
-       WRITE_ONCE(line->eflags, 0);
+       WRITE_ONCE(line->edflags, 0);
        if (line->desc)
                WRITE_ONCE(line->desc->debounce_period_us, 0);
        /* do not change line->level - see comment in debounced_value() */
@@ -935,23 +940,23 @@ static void edge_detector_stop(struct line *line, bool hte_en)
 
 static int edge_detector_setup(struct line *line,
                               struct gpio_v2_line_config *lc,
-                              unsigned int line_idx,
-                              u64 eflags, bool hte_req)
+                              unsigned int line_idx, u64 edflags)
 {
        u32 debounce_period_us;
        unsigned long irqflags = 0;
+       u64 eflags;
        int irq, ret;
 
+       eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
        if (eflags && !kfifo_initialized(&line->req->events)) {
                ret = kfifo_alloc(&line->req->events,
                                  line->req->event_buffer_size, GFP_KERNEL);
                if (ret)
                        return ret;
        }
-       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, hte_req);
+               ret = debounce_setup(line, debounce_period_us);
                if (ret)
                        return ret;
                WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
@@ -961,8 +966,8 @@ 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);
+       if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)
+               return hte_edge_setup(line, edflags);
 
        irq = gpiod_to_irq(line->desc);
        if (irq < 0)
@@ -988,35 +993,29 @@ 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 flags, bool polarity_change,
-                               bool prev_hte_flag)
+                               unsigned int line_idx, u64 edflags)
 {
-       u64 eflags = flags & GPIO_V2_LINE_EDGE_FLAGS;
+       u64 active_edflags = READ_ONCE(line->edflags);
        unsigned int debounce_period_us =
                        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)
-           && !hte_change)
+       if ((active_edflags == edflags) &&
+           (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us))
                return 0;
 
        /* sw debounced and still will be...*/
        if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
-               WRITE_ONCE(line->eflags, eflags);
                WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
                return 0;
        }
 
        /* reconfiguring edge detection or sw debounce being disabled */
-       if ((line->irq && !READ_ONCE(line->sw_debounced)) || prev_hte_flag ||
+       if ((line->irq && !READ_ONCE(line->sw_debounced)) ||
+           (active_edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) ||
            (!debounce_period_us && READ_ONCE(line->sw_debounced)))
-               edge_detector_stop(line, prev_hte_flag);
+               edge_detector_stop(line);
 
-       return edge_detector_setup(line, lc, line_idx, eflags,
-                                  flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE);
+       return edge_detector_setup(line, lc, line_idx, edflags);
 }
 
 static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc,
@@ -1285,22 +1284,17 @@ static long linereq_set_config_unlocked(struct linereq *lr,
                                        struct gpio_v2_line_config *lc)
 {
        struct gpio_desc *desc;
+       struct line *line;
        unsigned int i;
-       u64 flags;
-       bool polarity_change;
-       bool prev_hte_flag;
+       u64 flags, edflags;
        int ret;
 
        for (i = 0; i < lr->num_lines; i++) {
+               line = &lr->lines[i];
                desc = lr->lines[i].desc;
                flags = gpio_v2_line_config_flags(lc, i);
-               polarity_change =
-                       (!!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);
+               edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
                /*
                 * Lines have to be requested explicitly for input
                 * or output, else the line will be treated "as is".
@@ -1308,7 +1302,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], prev_hte_flag);
+                       edge_detector_stop(line);
                        ret = gpiod_direction_output(desc, val);
                        if (ret)
                                return ret;
@@ -1317,12 +1311,13 @@ static long linereq_set_config_unlocked(struct linereq *lr,
                        if (ret)
                                return ret;
 
-                       ret = edge_detector_update(&lr->lines[i], lc, i,
-                                       flags, polarity_change, prev_hte_flag);
+                       ret = edge_detector_update(line, lc, i, edflags);
                        if (ret)
                                return ret;
                }
 
+               WRITE_ONCE(line->edflags, edflags);
+
                blocking_notifier_call_chain(&desc->gdev->notifier,
                                             GPIO_V2_LINE_CHANGED_CONFIG,
                                             desc);
@@ -1449,13 +1444,10 @@ 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++) {
                if (lr->lines[i].desc) {
-                       hte = !!test_bit(FLAG_EVENT_CLOCK_HTE,
-                                        &lr->lines[i].desc->flags);
-                       edge_detector_stop(&lr->lines[i], hte);
+                       edge_detector_stop(&lr->lines[i]);
                        gpiod_free(lr->lines[i].desc);
                }
        }
@@ -1491,7 +1483,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
        struct gpio_v2_line_config *lc;
        struct linereq *lr;
        struct file *file;
-       u64 flags;
+       u64 flags, edflags;
        unsigned int i;
        int fd, ret;
 
@@ -1565,6 +1557,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
                if (ret < 0)
                        goto out_free_linereq;
 
+               edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
                /*
                 * Lines have to be requested explicitly for input
                 * or output, else the line will be treated "as is".
@@ -1581,12 +1574,13 @@ 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_FLAG_EVENT_CLOCK_HTE);
+                                                 edflags);
                        if (ret)
                                goto out_free_linereq;
                }
 
+               lr->lines[i].edflags = edflags;
+
                blocking_notifier_call_chain(&desc->gdev->notifier,
                                             GPIO_V2_LINE_CHANGED_REQUESTED, desc);