}
 
                blocking_notifier_call_chain(&desc->gdev->notifier,
-                                            GPIOLINE_CHANGED_CONFIG, desc);
+                                            GPIO_V2_LINE_CHANGED_CONFIG,
+                                            desc);
        }
        return 0;
 }
                }
 
                blocking_notifier_call_chain(&desc->gdev->notifier,
-                                            GPIOLINE_CHANGED_REQUESTED, desc);
+                                            GPIO_V2_LINE_CHANGED_REQUESTED, desc);
 
                dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
                        offset);
                }
 
                blocking_notifier_call_chain(&desc->gdev->notifier,
-                                            GPIOLINE_CHANGED_REQUESTED, desc);
+                                            GPIO_V2_LINE_CHANGED_REQUESTED, desc);
 
                dev_dbg(&gdev->dev, "registered chardev handle for line %d\n",
                        offset);
                goto out_free_le;
 
        blocking_notifier_call_chain(&desc->gdev->notifier,
-                                    GPIOLINE_CHANGED_REQUESTED, desc);
+                                    GPIO_V2_LINE_CHANGED_REQUESTED, desc);
 
        irq = gpiod_to_irq(desc);
        if (irq <= 0) {
        return ret;
 }
 
+static void gpio_v2_line_info_to_v1(struct gpio_v2_line_info *info_v2,
+                                   struct gpioline_info *info_v1)
+{
+       u64 flagsv2 = info_v2->flags;
+
+       memcpy(info_v1->name, info_v2->name, sizeof(info_v1->name));
+       memcpy(info_v1->consumer, info_v2->consumer, sizeof(info_v1->consumer));
+       info_v1->line_offset = info_v2->offset;
+       info_v1->flags = 0;
+
+       if (flagsv2 & GPIO_V2_LINE_FLAG_USED)
+               info_v1->flags |= GPIOLINE_FLAG_KERNEL;
+
+       if (flagsv2 & GPIO_V2_LINE_FLAG_OUTPUT)
+               info_v1->flags |= GPIOLINE_FLAG_IS_OUT;
+
+       if (flagsv2 & GPIO_V2_LINE_FLAG_ACTIVE_LOW)
+               info_v1->flags |= GPIOLINE_FLAG_ACTIVE_LOW;
+
+       if (flagsv2 & GPIO_V2_LINE_FLAG_OPEN_DRAIN)
+               info_v1->flags |= GPIOLINE_FLAG_OPEN_DRAIN;
+       if (flagsv2 & GPIO_V2_LINE_FLAG_OPEN_SOURCE)
+               info_v1->flags |= GPIOLINE_FLAG_OPEN_SOURCE;
+
+       if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_PULL_UP)
+               info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_UP;
+       if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN)
+               info_v1->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
+       if (flagsv2 & GPIO_V2_LINE_FLAG_BIAS_DISABLED)
+               info_v1->flags |= GPIOLINE_FLAG_BIAS_DISABLE;
+}
+
+static void gpio_v2_line_info_changed_to_v1(
+               struct gpio_v2_line_info_changed *lic_v2,
+               struct gpioline_info_changed *lic_v1)
+{
+       gpio_v2_line_info_to_v1(&lic_v2->info, &lic_v1->info);
+       lic_v1->timestamp = lic_v2->timestamp_ns;
+       lic_v1->event_type = lic_v2->event_type;
+}
+
 #endif /* CONFIG_GPIO_CDEV_V1 */
 
 static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
-                                 struct gpioline_info *info)
+                                 struct gpio_v2_line_info *info)
 {
        struct gpio_chip *gc = desc->gdev->chip;
        bool ok_for_pinctrl;
        unsigned long flags;
 
        memset(info, 0, sizeof(*info));
-       info->line_offset = gpio_chip_hwgpio(desc);
+       info->offset = gpio_chip_hwgpio(desc);
 
        /*
         * This function takes a mutex so we must check this before taking
         * lock common to both frameworks?
         */
        ok_for_pinctrl =
-               pinctrl_gpio_can_use_line(gc->base + info->line_offset);
+               pinctrl_gpio_can_use_line(gc->base + info->offset);
 
        spin_lock_irqsave(&gpio_lock, flags);
 
            test_bit(FLAG_EXPORT, &desc->flags) ||
            test_bit(FLAG_SYSFS, &desc->flags) ||
            !ok_for_pinctrl)
-               info->flags |= GPIOLINE_FLAG_KERNEL;
+               info->flags |= GPIO_V2_LINE_FLAG_USED;
+
        if (test_bit(FLAG_IS_OUT, &desc->flags))
-               info->flags |= GPIOLINE_FLAG_IS_OUT;
+               info->flags |= GPIO_V2_LINE_FLAG_OUTPUT;
+       else
+               info->flags |= GPIO_V2_LINE_FLAG_INPUT;
+
        if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
-               info->flags |= GPIOLINE_FLAG_ACTIVE_LOW;
+               info->flags |= GPIO_V2_LINE_FLAG_ACTIVE_LOW;
+
        if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
-               info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN |
-                               GPIOLINE_FLAG_IS_OUT);
+               info->flags |= GPIO_V2_LINE_FLAG_OPEN_DRAIN;
        if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
-               info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE |
-                               GPIOLINE_FLAG_IS_OUT);
+               info->flags |= GPIO_V2_LINE_FLAG_OPEN_SOURCE;
+
        if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
-               info->flags |= GPIOLINE_FLAG_BIAS_DISABLE;
+               info->flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
        if (test_bit(FLAG_PULL_DOWN, &desc->flags))
-               info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
+               info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
        if (test_bit(FLAG_PULL_UP, &desc->flags))
-               info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP;
+               info->flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
 
        spin_unlock_irqrestore(&gpio_lock, flags);
 }
 struct gpio_chardev_data {
        struct gpio_device *gdev;
        wait_queue_head_t wait;
-       DECLARE_KFIFO(events, struct gpioline_info_changed, 32);
+       DECLARE_KFIFO(events, struct gpio_v2_line_info_changed, 32);
        struct notifier_block lineinfo_changed_nb;
        unsigned long *watched_lines;
+#ifdef CONFIG_GPIO_CDEV_V1
+       atomic_t watch_abi_version;
+#endif
 };
 
+#ifdef CONFIG_GPIO_CDEV_V1
+/*
+ * returns 0 if the versions match, else the previously selected ABI version
+ */
+static int lineinfo_ensure_abi_version(struct gpio_chardev_data *cdata,
+                                      unsigned int version)
+{
+       int abiv = atomic_cmpxchg(&cdata->watch_abi_version, 0, version);
+
+       if (abiv == version)
+               return 0;
+
+       return abiv;
+}
+#endif
+
+static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
+                       bool watch)
+{
+       struct gpio_desc *desc;
+       struct gpio_v2_line_info lineinfo;
+
+       if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
+               return -EFAULT;
+
+       if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding)))
+               return -EINVAL;
+
+       desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset);
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       if (watch) {
+#ifdef CONFIG_GPIO_CDEV_V1
+               if (lineinfo_ensure_abi_version(cdev, 2))
+                       return -EPERM;
+#endif
+               if (test_and_set_bit(lineinfo.offset, cdev->watched_lines))
+                       return -EBUSY;
+       }
+       gpio_desc_to_lineinfo(desc, &lineinfo);
+
+       if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
+               if (watch)
+                       clear_bit(lineinfo.offset, cdev->watched_lines);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
 /*
  * gpio_ioctl() - ioctl handler for the GPIO chardev
  */
        struct gpio_device *gdev = cdev->gdev;
        struct gpio_chip *gc = gdev->chip;
        void __user *ip = (void __user *)arg;
-       struct gpio_desc *desc;
        __u32 offset;
 
        /* We fail any subsequent ioctl():s when the chip is gone */
                return 0;
 #ifdef CONFIG_GPIO_CDEV_V1
        } else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
+               struct gpio_desc *desc;
                struct gpioline_info lineinfo;
+               struct gpio_v2_line_info lineinfo_v2;
 
                if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
                        return -EFAULT;
                if (IS_ERR(desc))
                        return PTR_ERR(desc);
 
-               gpio_desc_to_lineinfo(desc, &lineinfo);
+               gpio_desc_to_lineinfo(desc, &lineinfo_v2);
+               gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
 
                if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
                        return -EFAULT;
        } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
                return lineevent_create(gdev, ip);
        } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) {
+               struct gpio_desc *desc;
                struct gpioline_info lineinfo;
+               struct gpio_v2_line_info lineinfo_v2;
 
                if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
                        return -EFAULT;
                if (IS_ERR(desc))
                        return PTR_ERR(desc);
 
+               if (lineinfo_ensure_abi_version(cdev, 1))
+                       return -EPERM;
+
                if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines))
                        return -EBUSY;
 
-               gpio_desc_to_lineinfo(desc, &lineinfo);
+               gpio_desc_to_lineinfo(desc, &lineinfo_v2);
+               gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
 
                if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
                        clear_bit(lineinfo.line_offset, cdev->watched_lines);
 
                return 0;
 #endif /* CONFIG_GPIO_CDEV_V1 */
+       } else if (cmd == GPIO_V2_GET_LINEINFO_IOCTL ||
+                  cmd == GPIO_V2_GET_LINEINFO_WATCH_IOCTL) {
+               return lineinfo_get(cdev, ip,
+                                   cmd == GPIO_V2_GET_LINEINFO_WATCH_IOCTL);
        } else if (cmd == GPIO_V2_GET_LINE_IOCTL) {
                return linereq_create(gdev, ip);
        } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) {
                                   unsigned long action, void *data)
 {
        struct gpio_chardev_data *cdev = to_gpio_chardev_data(nb);
-       struct gpioline_info_changed chg;
+       struct gpio_v2_line_info_changed chg;
        struct gpio_desc *desc = data;
        int ret;
 
 
        memset(&chg, 0, sizeof(chg));
        chg.event_type = action;
-       chg.timestamp = ktime_get_ns();
+       chg.timestamp_ns = ktime_get_ns();
        gpio_desc_to_lineinfo(desc, &chg.info);
 
        ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock);
                                   size_t count, loff_t *off)
 {
        struct gpio_chardev_data *cdev = file->private_data;
-       struct gpioline_info_changed event;
+       struct gpio_v2_line_info_changed event;
        ssize_t bytes_read = 0;
        int ret;
+       size_t event_size;
 
-       if (count < sizeof(event))
+#ifndef CONFIG_GPIO_CDEV_V1
+       event_size = sizeof(struct gpio_v2_line_info_changed);
+       if (count < event_size)
                return -EINVAL;
+#endif
 
        do {
                spin_lock(&cdev->wait.lock);
                                return ret;
                        }
                }
-
+#ifdef CONFIG_GPIO_CDEV_V1
+               /* must be after kfifo check so watch_abi_version is set */
+               if (atomic_read(&cdev->watch_abi_version) == 2)
+                       event_size = sizeof(struct gpio_v2_line_info_changed);
+               else
+                       event_size = sizeof(struct gpioline_info_changed);
+               if (count < event_size) {
+                       spin_unlock(&cdev->wait.lock);
+                       return -EINVAL;
+               }
+#endif
                ret = kfifo_out(&cdev->events, &event, 1);
                spin_unlock(&cdev->wait.lock);
                if (ret != 1) {
                        /* We should never get here. See lineevent_read(). */
                }
 
-               if (copy_to_user(buf + bytes_read, &event, sizeof(event)))
+#ifdef CONFIG_GPIO_CDEV_V1
+               if (event_size == sizeof(struct gpio_v2_line_info_changed)) {
+                       if (copy_to_user(buf + bytes_read, &event, event_size))
+                               return -EFAULT;
+               } else {
+                       struct gpioline_info_changed event_v1;
+
+                       gpio_v2_line_info_changed_to_v1(&event, &event_v1);
+                       if (copy_to_user(buf + bytes_read, &event_v1,
+                                        event_size))
+                               return -EFAULT;
+               }
+#else
+               if (copy_to_user(buf + bytes_read, &event, event_size))
                        return -EFAULT;
-               bytes_read += sizeof(event);
+#endif
+               bytes_read += event_size;
        } while (count >= bytes_read + sizeof(event));
 
        return bytes_read;