Commit 7e8d8523 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'gpio-fixes-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio fixes from Bartosz Golaszewski:

 - add a missing IS_ERR() check in gpio-nomadik

 - fix a NULL-pointer dereference in GPIO character device code

 - restore label matching in swnode-lookup due to reported regressions
   in existing users (this will get removed again once we audit and
   update all drivers)

 - fix remove path in GPIO sysfs code

 - normalize the return value of gpio_chip::get() in gpio-amd-fch

* tag 'gpio-fixes-for-v7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpio: amd-fch: ionly return allowed values from amd_fch_gpio_get()
  gpio: sysfs: fix chip removal with GPIOs exported over sysfs
  gpio: swnode: restore the swnode-name-against-chip-label matching
  gpio: cdev: Avoid NULL dereference in linehandle_create()
  gpio: nomadik: Add missing IS_ERR() check
parents 99e44722 fbd03587
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
 *
 */

#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -120,15 +121,15 @@ static int amd_fch_gpio_get(struct gpio_chip *gc,
			    unsigned int offset)
{
	unsigned long flags;
	int ret;
	u32 val;
	struct amd_fch_gpio_priv *priv = gpiochip_get_data(gc);
	void __iomem *ptr = amd_fch_gpio_addr(priv, offset);

	spin_lock_irqsave(&priv->lock, flags);
	ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_READ);
	val = readl_relaxed(ptr);
	spin_unlock_irqrestore(&priv->lock, flags);

	return ret;
	return FIELD_GET(AMD_FCH_GPIO_FLAG_READ, val);
}

static int amd_fch_gpio_request(struct gpio_chip *chip,
+3 −0
Original line number Diff line number Diff line
@@ -430,6 +430,9 @@ void nmk_gpio_dbg_show_one(struct seq_file *s, struct pinctrl_dev *pctldev,
#ifdef CONFIG_PINCTRL_NOMADIK
	if (mode == NMK_GPIO_ALT_C && pctldev) {
		desc = gpio_device_get_desc(chip->gpiodev, offset);
		if (IS_ERR(desc))
			return;

		mode = nmk_prcm_gpiocr_get_mode(pctldev, desc_to_gpio(desc));
	}
#endif
+1 −1
Original line number Diff line number Diff line
@@ -388,7 +388,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
	fd_publish(fdf);

	dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
		lh->num_descs);
		handlereq.lines);

	return 0;
}
+19 −0
Original line number Diff line number Diff line
@@ -42,6 +42,25 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)

fwnode_lookup:
	gdev = gpio_device_find_by_fwnode(fwnode);
	if (!gdev && gdev_node && gdev_node->name)
		/*
		 * FIXME: We shouldn't need to compare the GPIO controller's
		 * label against the software node that is supposedly attached
		 * to it. However there are currently GPIO users that - knowing
		 * the expected label of the GPIO chip whose pins they want to
		 * control - set up dummy software nodes named after those GPIO
		 * controllers, which aren't actually attached to them. In this
		 * case gpio_device_find_by_fwnode() will fail as no device on
		 * the GPIO bus is actually associated with the fwnode we're
		 * looking for.
		 *
		 * As a fallback: continue checking the label if we have no
		 * match. However, the situation described above is an abuse
		 * of the software node API and should be phased out and the
		 * following line - eventually removed.
		 */
		gdev = gpio_device_find_by_label(gdev_node->name);

	return gdev ?: ERR_PTR(-EPROBE_DEFER);
}

+55 −51
Original line number Diff line number Diff line
@@ -919,24 +919,12 @@ int gpiod_export_link(struct device *dev, const char *name,
}
EXPORT_SYMBOL_GPL(gpiod_export_link);

/**
 * gpiod_unexport - reverse effect of gpiod_export()
 * @desc: GPIO to make unavailable
 *
 * This is implicit on gpiod_free().
 */
void gpiod_unexport(struct gpio_desc *desc)
static void gpiod_unexport_unlocked(struct gpio_desc *desc)
{
	struct gpiod_data *tmp, *desc_data = NULL;
	struct gpiodev_data *gdev_data;
	struct gpio_device *gdev;

	if (!desc) {
		pr_warn("%s: invalid GPIO\n", __func__);
		return;
	}

	scoped_guard(mutex, &sysfs_lock) {
	if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags))
		return;

@@ -971,11 +959,28 @@ void gpiod_unexport(struct gpio_desc *desc)

	sysfs_remove_groups(desc_data->parent,
			    desc_data->chip_attr_groups);
	}

	mutex_destroy(&desc_data->mutex);
	kfree(desc_data);
}

/**
 * gpiod_unexport - reverse effect of gpiod_export()
 * @desc: GPIO to make unavailable
 *
 * This is implicit on gpiod_free().
 */
void gpiod_unexport(struct gpio_desc *desc)
{
	if (!desc) {
		pr_warn("%s: invalid GPIO\n", __func__);
		return;
	}

	guard(mutex)(&sysfs_lock);

	gpiod_unexport_unlocked(desc);
}
EXPORT_SYMBOL_GPL(gpiod_unexport);

int gpiochip_sysfs_register(struct gpio_device *gdev)
@@ -1054,29 +1059,28 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev)
	struct gpio_desc *desc;
	struct gpio_chip *chip;

	scoped_guard(mutex, &sysfs_lock) {
	guard(mutex)(&sysfs_lock);

	data = gdev_get_data(gdev);
	if (!data)
		return;

#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
		device_unregister(data->cdev_base);
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
		device_unregister(data->cdev_id);
		kfree(data);
	}

	guard(srcu)(&gdev->srcu);

	chip = srcu_dereference(gdev->chip, &gdev->srcu);
	if (!chip)
		return;

	/* unregister gpiod class devices owned by sysfs */
	for_each_gpio_desc_with_flag(chip, desc, GPIOD_FLAG_SYSFS) {
		gpiod_unexport(desc);
		gpiod_unexport_unlocked(desc);
		gpiod_free(desc);
	}

#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
	device_unregister(data->cdev_base);
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
	device_unregister(data->cdev_id);
	kfree(data);
}

/*