Merge tag 'v5.6-rc7' into devel
[platform/kernel/linux-rpi.git] / drivers / gpio / gpiolib.c
index 4d0106c..e3616cc 100644 (file)
@@ -136,7 +136,7 @@ EXPORT_SYMBOL_GPL(gpio_to_desc);
  * @hwnum: hardware number of the GPIO for this chip
  *
  * Returns:
- * A pointer to the GPIO descriptor or %ERR_PTR(-EINVAL) if no GPIO exists
+ * A pointer to the GPIO descriptor or ``ERR_PTR(-EINVAL)`` if no GPIO exists
  * in the given chip for the specified hardware number.
  */
 struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
@@ -301,6 +301,9 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
        struct gpio_device *gdev;
        unsigned long flags;
 
+       if (!name)
+               return NULL;
+
        spin_lock_irqsave(&gpio_lock, flags);
 
        list_for_each_entry(gdev, &gpio_devices, list) {
@@ -309,7 +312,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
                for (i = 0; i != gdev->ngpio; ++i) {
                        struct gpio_desc *desc = &gdev->descs[i];
 
-                       if (!desc->name || !name)
+                       if (!desc->name)
                                continue;
 
                        if (!strcmp(desc->name, name)) {
@@ -546,6 +549,9 @@ static long linehandle_set_config(struct linehandle_state *lh,
                        if (ret)
                                return ret;
                }
+
+               atomic_notifier_call_chain(&desc->gdev->notifier,
+                                          GPIOLINE_CHANGED_CONFIG, desc);
        }
        return 0;
 }
@@ -787,8 +793,6 @@ out_free_lh:
  * @irq: the interrupt that trigger in response to events on this GPIO
  * @wait: wait queue that handles blocking reads of events
  * @events: KFIFO for the GPIO events
- * @read_lock: mutex lock to protect reads from colliding with adding
- * new events to the FIFO
  * @timestamp: cache for the timestamp storing it between hardirq
  * and IRQ thread, used to bring the timestamp close to the actual
  * event
@@ -801,7 +805,6 @@ struct lineevent_state {
        int irq;
        wait_queue_head_t wait;
        DECLARE_KFIFO(events, struct gpioevent_data, 16);
-       struct mutex read_lock;
        u64 timestamp;
 };
 
@@ -817,7 +820,7 @@ static __poll_t lineevent_poll(struct file *filep,
 
        poll_wait(filep, &le->wait, wait);
 
-       if (!kfifo_is_empty(&le->events))
+       if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock))
                events = EPOLLIN | EPOLLRDNORM;
 
        return events;
@@ -830,43 +833,52 @@ static ssize_t lineevent_read(struct file *filep,
                              loff_t *f_ps)
 {
        struct lineevent_state *le = filep->private_data;
-       unsigned int copied;
+       struct gpioevent_data ge;
+       ssize_t bytes_read = 0;
        int ret;
 
-       if (count < sizeof(struct gpioevent_data))
+       if (count < sizeof(ge))
                return -EINVAL;
 
        do {
+               spin_lock(&le->wait.lock);
                if (kfifo_is_empty(&le->events)) {
-                       if (filep->f_flags & O_NONBLOCK)
+                       if (bytes_read) {
+                               spin_unlock(&le->wait.lock);
+                               return bytes_read;
+                       }
+
+                       if (filep->f_flags & O_NONBLOCK) {
+                               spin_unlock(&le->wait.lock);
                                return -EAGAIN;
+                       }
 
-                       ret = wait_event_interruptible(le->wait,
+                       ret = wait_event_interruptible_locked(le->wait,
                                        !kfifo_is_empty(&le->events));
-                       if (ret)
+                       if (ret) {
+                               spin_unlock(&le->wait.lock);
                                return ret;
+                       }
                }
 
-               if (mutex_lock_interruptible(&le->read_lock))
-                       return -ERESTARTSYS;
-               ret = kfifo_to_user(&le->events, buf, count, &copied);
-               mutex_unlock(&le->read_lock);
-
-               if (ret)
-                       return ret;
-
-               /*
-                * If we couldn't read anything from the fifo (a different
-                * thread might have been faster) we either return -EAGAIN if
-                * the file descriptor is non-blocking, otherwise we go back to
-                * sleep and wait for more data to arrive.
-                */
-               if (copied == 0 && (filep->f_flags & O_NONBLOCK))
-                       return -EAGAIN;
+               ret = kfifo_out(&le->events, &ge, 1);
+               spin_unlock(&le->wait.lock);
+               if (ret != 1) {
+                       /*
+                        * This should never happen - we were holding the lock
+                        * from the moment we learned the fifo is no longer
+                        * empty until now.
+                        */
+                       ret = -EIO;
+                       break;
+               }
 
-       } while (copied == 0);
+               if (copy_to_user(buf + bytes_read, &ge, sizeof(ge)))
+                       return -EFAULT;
+               bytes_read += sizeof(ge);
+       } while (count >= bytes_read + sizeof(ge));
 
-       return copied;
+       return bytes_read;
 }
 
 static int lineevent_release(struct inode *inode, struct file *filep)
@@ -945,7 +957,7 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
         * we didn't get the timestamp from lineevent_irq_handler().
         */
        if (!le->timestamp)
-               ge.timestamp = ktime_get_real_ns();
+               ge.timestamp = ktime_get_ns();
        else
                ge.timestamp = le->timestamp;
 
@@ -968,9 +980,12 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
                return IRQ_NONE;
        }
 
-       ret = kfifo_put(&le->events, ge);
+       ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge,
+                                           1, &le->wait.lock);
        if (ret)
                wake_up_poll(&le->wait, EPOLLIN);
+       else
+               pr_debug_ratelimited("event FIFO is full - event dropped\n");
 
        return IRQ_HANDLED;
 }
@@ -983,7 +998,7 @@ static irqreturn_t lineevent_irq_handler(int irq, void *p)
         * Just store the timestamp in hardirq context so we get it as
         * close in time as possible to the actual event.
         */
-       le->timestamp = ktime_get_real_ns();
+       le->timestamp = ktime_get_ns();
 
        return IRQ_WAKE_THREAD;
 }
@@ -1083,7 +1098,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
 
        INIT_KFIFO(le->events);
        init_waitqueue_head(&le->wait);
-       mutex_init(&le->read_lock);
 
        /* Request a thread to read the events */
        ret = request_threaded_irq(le->irq,
@@ -1139,14 +1153,79 @@ out_free_le:
        return ret;
 }
 
+static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
+                                 struct gpioline_info *info)
+{
+       struct gpio_chip *chip = desc->gdev->chip;
+       unsigned long flags;
+
+       spin_lock_irqsave(&gpio_lock, flags);
+
+       if (desc->name) {
+               strncpy(info->name, desc->name, sizeof(info->name));
+               info->name[sizeof(info->name) - 1] = '\0';
+       } else {
+               info->name[0] = '\0';
+       }
+
+       if (desc->label) {
+               strncpy(info->consumer, desc->label, sizeof(info->consumer));
+               info->consumer[sizeof(info->consumer) - 1] = '\0';
+       } else {
+               info->consumer[0] = '\0';
+       }
+
+       /*
+        * Userspace only need to know that the kernel is using this GPIO so
+        * it can't use it.
+        */
+       info->flags = 0;
+       if (test_bit(FLAG_REQUESTED, &desc->flags) ||
+           test_bit(FLAG_IS_HOGGED, &desc->flags) ||
+           test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||
+           test_bit(FLAG_EXPORT, &desc->flags) ||
+           test_bit(FLAG_SYSFS, &desc->flags) ||
+           !pinctrl_gpio_can_use_line(chip->base + info->line_offset))
+               info->flags |= GPIOLINE_FLAG_KERNEL;
+       if (test_bit(FLAG_IS_OUT, &desc->flags))
+               info->flags |= GPIOLINE_FLAG_IS_OUT;
+       if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+               info->flags |= GPIOLINE_FLAG_ACTIVE_LOW;
+       if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
+               info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN |
+                               GPIOLINE_FLAG_IS_OUT);
+       if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
+               info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE |
+                               GPIOLINE_FLAG_IS_OUT);
+       if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
+               info->flags |= GPIOLINE_FLAG_BIAS_DISABLE;
+       if (test_bit(FLAG_PULL_DOWN, &desc->flags))
+               info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
+       if (test_bit(FLAG_PULL_UP, &desc->flags))
+               info->flags |= GPIOLINE_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);
+       struct notifier_block lineinfo_changed_nb;
+       unsigned long *watched_lines;
+};
+
 /*
  * gpio_ioctl() - ioctl handler for the GPIO chardev
  */
 static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
-       struct gpio_device *gdev = filp->private_data;
+       struct gpio_chardev_data *priv = filp->private_data;
+       struct gpio_device *gdev = priv->gdev;
        struct gpio_chip *chip = 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 */
        if (!chip)
@@ -1168,9 +1247,9 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
                        return -EFAULT;
                return 0;
-       } else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
+       } else if (cmd == GPIO_GET_LINEINFO_IOCTL ||
+                  cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) {
                struct gpioline_info lineinfo;
-               struct gpio_desc *desc;
 
                if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
                        return -EFAULT;
@@ -1179,57 +1258,29 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                if (IS_ERR(desc))
                        return PTR_ERR(desc);
 
-               if (desc->name) {
-                       strncpy(lineinfo.name, desc->name,
-                               sizeof(lineinfo.name));
-                       lineinfo.name[sizeof(lineinfo.name)-1] = '\0';
-               } else {
-                       lineinfo.name[0] = '\0';
-               }
-               if (desc->label) {
-                       strncpy(lineinfo.consumer, desc->label,
-                               sizeof(lineinfo.consumer));
-                       lineinfo.consumer[sizeof(lineinfo.consumer)-1] = '\0';
-               } else {
-                       lineinfo.consumer[0] = '\0';
-               }
-
-               /*
-                * Userspace only need to know that the kernel is using
-                * this GPIO so it can't use it.
-                */
-               lineinfo.flags = 0;
-               if (test_bit(FLAG_REQUESTED, &desc->flags) ||
-                   test_bit(FLAG_IS_HOGGED, &desc->flags) ||
-                   test_bit(FLAG_USED_AS_IRQ, &desc->flags) ||
-                   test_bit(FLAG_EXPORT, &desc->flags) ||
-                   test_bit(FLAG_SYSFS, &desc->flags) ||
-                   !pinctrl_gpio_can_use_line(chip->base + lineinfo.line_offset))
-                       lineinfo.flags |= GPIOLINE_FLAG_KERNEL;
-               if (test_bit(FLAG_IS_OUT, &desc->flags))
-                       lineinfo.flags |= GPIOLINE_FLAG_IS_OUT;
-               if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
-                       lineinfo.flags |= GPIOLINE_FLAG_ACTIVE_LOW;
-               if (test_bit(FLAG_OPEN_DRAIN, &desc->flags))
-                       lineinfo.flags |= (GPIOLINE_FLAG_OPEN_DRAIN |
-                                          GPIOLINE_FLAG_IS_OUT);
-               if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
-                       lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE |
-                                          GPIOLINE_FLAG_IS_OUT);
-               if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
-                       lineinfo.flags |= GPIOLINE_FLAG_BIAS_DISABLE;
-               if (test_bit(FLAG_PULL_DOWN, &desc->flags))
-                       lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
-               if (test_bit(FLAG_PULL_UP, &desc->flags))
-                       lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_UP;
+               gpio_desc_to_lineinfo(desc, &lineinfo);
 
                if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
                        return -EFAULT;
+
+               if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL)
+                       set_bit(gpio_chip_hwgpio(desc), priv->watched_lines);
+
                return 0;
        } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
                return linehandle_create(gdev, ip);
        } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
                return lineevent_create(gdev, ip);
+       } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) {
+               if (copy_from_user(&offset, ip, sizeof(offset)))
+                       return -EFAULT;
+
+               desc = gpiochip_get_desc(chip, offset);
+               if (IS_ERR(desc))
+                       return PTR_ERR(desc);
+
+               clear_bit(gpio_chip_hwgpio(desc), priv->watched_lines);
+               return 0;
        }
        return -EINVAL;
 }
@@ -1242,6 +1293,101 @@ static long gpio_ioctl_compat(struct file *filp, unsigned int cmd,
 }
 #endif
 
+static struct gpio_chardev_data *
+to_gpio_chardev_data(struct notifier_block *nb)
+{
+       return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb);
+}
+
+static int lineinfo_changed_notify(struct notifier_block *nb,
+                                  unsigned long action, void *data)
+{
+       struct gpio_chardev_data *priv = to_gpio_chardev_data(nb);
+       struct gpioline_info_changed chg;
+       struct gpio_desc *desc = data;
+       int ret;
+
+       if (!test_bit(gpio_chip_hwgpio(desc), priv->watched_lines))
+               return NOTIFY_DONE;
+
+       memset(&chg, 0, sizeof(chg));
+       chg.info.line_offset = gpio_chip_hwgpio(desc);
+       chg.event_type = action;
+       chg.timestamp = ktime_get_ns();
+       gpio_desc_to_lineinfo(desc, &chg.info);
+
+       ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock);
+       if (ret)
+               wake_up_poll(&priv->wait, EPOLLIN);
+       else
+               pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n");
+
+       return NOTIFY_OK;
+}
+
+static __poll_t lineinfo_watch_poll(struct file *filep,
+                                   struct poll_table_struct *pollt)
+{
+       struct gpio_chardev_data *priv = filep->private_data;
+       __poll_t events = 0;
+
+       poll_wait(filep, &priv->wait, pollt);
+
+       if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events,
+                                                &priv->wait.lock))
+               events = EPOLLIN | EPOLLRDNORM;
+
+       return events;
+}
+
+static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf,
+                                  size_t count, loff_t *off)
+{
+       struct gpio_chardev_data *priv = filep->private_data;
+       struct gpioline_info_changed event;
+       ssize_t bytes_read = 0;
+       int ret;
+
+       if (count < sizeof(event))
+               return -EINVAL;
+
+       do {
+               spin_lock(&priv->wait.lock);
+               if (kfifo_is_empty(&priv->events)) {
+                       if (bytes_read) {
+                               spin_unlock(&priv->wait.lock);
+                               return bytes_read;
+                       }
+
+                       if (filep->f_flags & O_NONBLOCK) {
+                               spin_unlock(&priv->wait.lock);
+                               return -EAGAIN;
+                       }
+
+                       ret = wait_event_interruptible_locked(priv->wait,
+                                       !kfifo_is_empty(&priv->events));
+                       if (ret) {
+                               spin_unlock(&priv->wait.lock);
+                               return ret;
+                       }
+               }
+
+               ret = kfifo_out(&priv->events, &event, 1);
+               spin_unlock(&priv->wait.lock);
+               if (ret != 1) {
+                       ret = -EIO;
+                       break;
+                       /* We should never get here. See lineevent_read(). */
+               }
+
+               if (copy_to_user(buf + bytes_read, &event, sizeof(event)))
+                       return -EFAULT;
+               bytes_read += sizeof(event);
+       } while (count >= bytes_read + sizeof(event));
+
+       return bytes_read;
+}
+
 /**
  * gpio_chrdev_open() - open the chardev for ioctl operations
  * @inode: inode for this chardev
@@ -1252,14 +1398,48 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp)
 {
        struct gpio_device *gdev = container_of(inode->i_cdev,
                                              struct gpio_device, chrdev);
+       struct gpio_chardev_data *priv;
+       int ret = -ENOMEM;
 
        /* Fail on open if the backing gpiochip is gone */
        if (!gdev->chip)
                return -ENODEV;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
+       if (!priv->watched_lines)
+               goto out_free_priv;
+
+       init_waitqueue_head(&priv->wait);
+       INIT_KFIFO(priv->events);
+       priv->gdev = gdev;
+
+       priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify;
+       ret = atomic_notifier_chain_register(&gdev->notifier,
+                                            &priv->lineinfo_changed_nb);
+       if (ret)
+               goto out_free_bitmap;
+
        get_device(&gdev->dev);
-       filp->private_data = gdev;
+       filp->private_data = priv;
+
+       ret = nonseekable_open(inode, filp);
+       if (ret)
+               goto out_unregister_notifier;
+
+       return ret;
 
-       return nonseekable_open(inode, filp);
+out_unregister_notifier:
+       atomic_notifier_chain_unregister(&gdev->notifier,
+                                        &priv->lineinfo_changed_nb);
+out_free_bitmap:
+       bitmap_free(priv->watched_lines);
+out_free_priv:
+       kfree(priv);
+       return ret;
 }
 
 /**
@@ -1270,17 +1450,23 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp)
  */
 static int gpio_chrdev_release(struct inode *inode, struct file *filp)
 {
-       struct gpio_device *gdev = container_of(inode->i_cdev,
-                                             struct gpio_device, chrdev);
+       struct gpio_chardev_data *priv = filp->private_data;
+       struct gpio_device *gdev = priv->gdev;
 
+       bitmap_free(priv->watched_lines);
+       atomic_notifier_chain_unregister(&gdev->notifier,
+                                        &priv->lineinfo_changed_nb);
        put_device(&gdev->dev);
+       kfree(priv);
+
        return 0;
 }
 
-
 static const struct file_operations gpio_fileops = {
        .release = gpio_chrdev_release,
        .open = gpio_chrdev_open,
+       .poll = lineinfo_watch_poll,
+       .read = lineinfo_watch_read,
        .owner = THIS_MODULE,
        .llseek = no_llseek,
        .unlocked_ioctl = gpio_ioctl,
@@ -1491,6 +1677,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
 
        spin_unlock_irqrestore(&gpio_lock, flags);
 
+       ATOMIC_INIT_NOTIFIER_HEAD(&gdev->notifier);
+
 #ifdef CONFIG_PINCTRL
        INIT_LIST_HEAD(&gdev->pin_ranges);
 #endif
@@ -1612,10 +1800,8 @@ EXPORT_SYMBOL_GPL(gpiochip_get_data);
 void gpiochip_remove(struct gpio_chip *chip)
 {
        struct gpio_device *gdev = chip->gpiodev;
-       struct gpio_desc *desc;
        unsigned long   flags;
-       unsigned        i;
-       bool            requested = false;
+       unsigned int    i;
 
        /* FIXME: should the legacy sysfs handling be moved to gpio_device? */
        gpiochip_sysfs_unregister(gdev);
@@ -1635,13 +1821,12 @@ void gpiochip_remove(struct gpio_chip *chip)
 
        spin_lock_irqsave(&gpio_lock, flags);
        for (i = 0; i < gdev->ngpio; i++) {
-               desc = &gdev->descs[i];
-               if (test_bit(FLAG_REQUESTED, &desc->flags))
-                       requested = true;
+               if (gpiochip_is_requested(chip, i))
+                       break;
        }
        spin_unlock_irqrestore(&gpio_lock, flags);
 
-       if (requested)
+       if (i != gdev->ngpio)
                dev_crit(&gdev->dev,
                         "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
 
@@ -1656,52 +1841,6 @@ void gpiochip_remove(struct gpio_chip *chip)
 }
 EXPORT_SYMBOL_GPL(gpiochip_remove);
 
-static void devm_gpio_chip_release(struct device *dev, void *res)
-{
-       struct gpio_chip *chip = *(struct gpio_chip **)res;
-
-       gpiochip_remove(chip);
-}
-
-/**
- * devm_gpiochip_add_data() - Resource managed gpiochip_add_data()
- * @dev: pointer to the device that gpio_chip belongs to.
- * @chip: the chip to register, with chip->base initialized
- * @data: driver-private data associated with this chip
- *
- * Context: potentially before irqs will work
- *
- * The gpio chip automatically be released when the device is unbound.
- *
- * Returns:
- * A negative errno if the chip can't be registered, such as because the
- * chip->base is invalid or already associated with a different chip.
- * Otherwise it returns zero as a success code.
- */
-int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
-                          void *data)
-{
-       struct gpio_chip **ptr;
-       int ret;
-
-       ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr),
-                            GFP_KERNEL);
-       if (!ptr)
-               return -ENOMEM;
-
-       ret = gpiochip_add_data(chip, data);
-       if (ret < 0) {
-               devres_free(ptr);
-               return ret;
-       }
-
-       *ptr = chip;
-       devres_add(dev, ptr);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
-
 /**
  * gpiochip_find() - iterator for locating a specific gpio_chip
  * @data: data to pass to match function
@@ -2606,7 +2745,10 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
  */
 int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset)
 {
-       return pinctrl_gpio_request(chip->gpiodev->base + offset);
+       if (!list_empty(&chip->gpiodev->pin_ranges))
+               return pinctrl_gpio_request(chip->gpiodev->base + offset);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(gpiochip_generic_request);
 
@@ -2823,6 +2965,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
        }
 done:
        spin_unlock_irqrestore(&gpio_lock, flags);
+       atomic_notifier_call_chain(&desc->gdev->notifier,
+                                  GPIOLINE_CHANGED_REQUESTED, desc);
        return ret;
 }
 
@@ -2916,10 +3060,16 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
                clear_bit(FLAG_PULL_DOWN, &desc->flags);
                clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
                clear_bit(FLAG_IS_HOGGED, &desc->flags);
+#ifdef CONFIG_OF_DYNAMIC
+               desc->hog = NULL;
+#endif
                ret = true;
        }
 
        spin_unlock_irqrestore(&gpio_lock, flags);
+       atomic_notifier_call_chain(&desc->gdev->notifier,
+                                  GPIOLINE_CHANGED_RELEASED, desc);
+
        return ret;
 }
 
@@ -2953,7 +3103,9 @@ const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
        if (offset >= chip->ngpio)
                return NULL;
 
-       desc = &chip->gpiodev->descs[offset];
+       desc = gpiochip_get_desc(chip, offset);
+       if (IS_ERR(desc))
+               return NULL;
 
        if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
                return NULL;
@@ -5109,10 +5261,15 @@ static int __init gpiolib_dev_init(void)
        if (ret < 0) {
                pr_err("gpiolib: failed to allocate char dev region\n");
                bus_unregister(&gpio_bus_type);
-       } else {
-               gpiolib_initialized = true;
-               gpiochip_setup_devs();
+               return ret;
        }
+
+       gpiolib_initialized = true;
+       gpiochip_setup_devs();
+
+       if (IS_ENABLED(CONFIG_OF_DYNAMIC))
+               WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier));
+
        return ret;
 }
 core_initcall(gpiolib_dev_init);