Merge tag 'v5.6-rc7' into devel
authorLinus Walleij <linus.walleij@linaro.org>
Fri, 27 Mar 2020 21:36:17 +0000 (22:36 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Fri, 27 Mar 2020 21:36:17 +0000 (22:36 +0100)
Linux 5.6-rc7

1  2 
drivers/gpio/gpiolib.c

diff --combined drivers/gpio/gpiolib.c
@@@ -136,7 -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,9 -301,6 +301,9 @@@ static struct gpio_desc *gpio_name_to_d
        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) {
                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)) {
@@@ -549,9 -546,6 +549,9 @@@ static long linehandle_set_config(struc
                        if (ret)
                                return ret;
                }
 +
 +              atomic_notifier_call_chain(&desc->gdev->notifier,
 +                                         GPIOLINE_CHANGED_CONFIG, desc);
        }
        return 0;
  }
@@@ -793,6 -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
@@@ -805,6 -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;
  };
  
@@@ -820,7 -817,7 +820,7 @@@ static __poll_t lineevent_poll(struct f
  
        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;
@@@ -833,52 -830,43 +833,52 @@@ static ssize_t lineevent_read(struct fi
                              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)
@@@ -957,7 -945,7 +957,7 @@@ static irqreturn_t lineevent_irq_thread
         * 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;
  
                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;
  }
@@@ -998,7 -983,7 +998,7 @@@ static irqreturn_t lineevent_irq_handle
         * 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;
  }
@@@ -1098,6 -1083,7 +1098,6 @@@ static int lineevent_create(struct gpio
  
        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,
@@@ -1153,79 -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)
                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;
                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;
  }
@@@ -1293,101 -1242,6 +1293,101 @@@ static long gpio_ioctl_compat(struct fi
  }
  #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
@@@ -1398,48 -1252,14 +1398,48 @@@ static int gpio_chrdev_open(struct inod
  {
        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;
  }
  
  /**
   */
  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,
@@@ -1677,8 -1491,6 +1677,8 @@@ int gpiochip_add_data_with_key(struct g
  
        spin_unlock_irqrestore(&gpio_lock, flags);
  
 +      ATOMIC_INIT_NOTIFIER_HEAD(&gdev->notifier);
 +
  #ifdef CONFIG_PINCTRL
        INIT_LIST_HEAD(&gdev->pin_ranges);
  #endif
@@@ -1800,8 -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);
  
        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");
  
  }
  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
@@@ -2745,10 -2606,7 +2745,10 @@@ static inline void gpiochip_irqchip_fre
   */
  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);
  
@@@ -2965,8 -2823,6 +2965,8 @@@ static int gpiod_request_commit(struct 
        }
  done:
        spin_unlock_irqrestore(&gpio_lock, flags);
 +      atomic_notifier_call_chain(&desc->gdev->notifier,
 +                                 GPIOLINE_CHANGED_REQUESTED, desc);
        return ret;
  }
  
@@@ -3060,16 -2916,10 +3060,16 @@@ static bool gpiod_free_commit(struct gp
                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;
  }
  
@@@ -3103,9 -2953,7 +3103,9 @@@ const char *gpiochip_is_requested(struc
        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;
@@@ -3187,13 -3035,33 +3187,33 @@@ EXPORT_SYMBOL_GPL(gpiochip_free_own_des
   * rely on gpio_request() having been called beforehand.
   */
  
- static int gpio_set_config(struct gpio_chip *gc, unsigned int offset,
-                          enum pin_config_param mode)
+ static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset,
+                             unsigned long config)
  {
        if (!gc->set_config)
                return -ENOTSUPP;
  
-       return gc->set_config(gc, offset, mode);
+       return gc->set_config(gc, offset, config);
+ }
+ static int gpio_set_config(struct gpio_chip *gc, unsigned int offset,
+                          enum pin_config_param mode)
+ {
+       unsigned long config;
+       unsigned arg;
+       switch (mode) {
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+       case PIN_CONFIG_BIAS_PULL_UP:
+               arg = 1;
+               break;
+       default:
+               arg = 0;
+       }
+       config = PIN_CONF_PACKED(mode, arg);
+       return gpio_do_set_config(gc, offset, config);
  }
  
  static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc)
@@@ -3429,7 -3297,7 +3449,7 @@@ int gpiod_set_debounce(struct gpio_des
        chip = desc->gdev->chip;
  
        config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce);
-       return gpio_set_config(chip, gpio_chip_hwgpio(desc), config);
+       return gpio_do_set_config(chip, gpio_chip_hwgpio(desc), config);
  }
  EXPORT_SYMBOL_GPL(gpiod_set_debounce);
  
@@@ -3463,7 -3331,7 +3483,7 @@@ int gpiod_set_transitory(struct gpio_de
        packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE,
                                          !transitory);
        gpio = gpio_chip_hwgpio(desc);
-       rc = gpio_set_config(chip, gpio, packed);
+       rc = gpio_do_set_config(chip, gpio, packed);
        if (rc == -ENOTSUPP) {
                dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n",
                                gpio);
@@@ -5241,15 -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);