summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-20 16:10:54 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-20 16:10:54 -0800
commit7e8d85235677b6571857c26854ad1d4edc64c50c (patch)
tree1b5467c61e72ef3f63430fe9533ff81d6545eeb8
parent99e447220b938dfed6488db95a2930b57ea849ba (diff)
parentfbd03587ba732c612b8a569d1cf5bed72bd3a27c (diff)
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
-rw-r--r--drivers/gpio/gpio-amd-fch.c7
-rw-r--r--drivers/gpio/gpio-nomadik.c3
-rw-r--r--drivers/gpio/gpiolib-cdev.c2
-rw-r--r--drivers/gpio/gpiolib-swnode.c19
-rw-r--r--drivers/gpio/gpiolib-sysfs.c106
5 files changed, 82 insertions, 55 deletions
diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c
index e6c6c3ec7656..9f329938202b 100644
--- a/drivers/gpio/gpio-amd-fch.c
+++ b/drivers/gpio/gpio-amd-fch.c
@@ -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,
diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c
index 97c5cd33279d..e22b713166d7 100644
--- a/drivers/gpio/gpio-nomadik.c
+++ b/drivers/gpio/gpio-nomadik.c
@@ -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
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 2adc3c070908..189127721e38 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -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;
}
diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c
index 21478b45c127..0d7f3f09a0b4 100644
--- a/drivers/gpio/gpiolib-swnode.c
+++ b/drivers/gpio/gpiolib-swnode.c
@@ -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);
}
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index cd553acf3055..d4a46a0a37d8 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -919,63 +919,68 @@ 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__);
+ if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags))
return;
- }
- scoped_guard(mutex, &sysfs_lock) {
- if (!test_bit(GPIOD_FLAG_EXPORT, &desc->flags))
- return;
-
- gdev = gpiod_to_gpio_device(desc);
- gdev_data = gdev_get_data(gdev);
- if (!gdev_data)
- return;
+ gdev = gpiod_to_gpio_device(desc);
+ gdev_data = gdev_get_data(gdev);
+ if (!gdev_data)
+ return;
- list_for_each_entry(tmp, &gdev_data->exported_lines, list) {
- if (gpiod_is_equal(desc, tmp->desc)) {
- desc_data = tmp;
- break;
- }
+ list_for_each_entry(tmp, &gdev_data->exported_lines, list) {
+ if (gpiod_is_equal(desc, tmp->desc)) {
+ desc_data = tmp;
+ break;
}
+ }
- if (!desc_data)
- return;
+ if (!desc_data)
+ return;
- list_del(&desc_data->list);
- clear_bit(GPIOD_FLAG_EXPORT, &desc->flags);
+ list_del(&desc_data->list);
+ clear_bit(GPIOD_FLAG_EXPORT, &desc->flags);
#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
- sysfs_put(desc_data->value_kn);
- device_unregister(desc_data->dev);
-
- /*
- * Release irq after deregistration to prevent race with
- * edge_store.
- */
- if (desc_data->irq_flags)
- gpio_sysfs_free_irq(desc_data);
+ sysfs_put(desc_data->value_kn);
+ device_unregister(desc_data->dev);
+
+ /*
+ * Release irq after deregistration to prevent race with
+ * edge_store.
+ */
+ if (desc_data->irq_flags)
+ gpio_sysfs_free_irq(desc_data);
#endif /* CONFIG_GPIO_SYSFS_LEGACY */
- sysfs_remove_groups(desc_data->parent,
- desc_data->chip_attr_groups);
- }
+ 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) {
- data = gdev_get_data(gdev);
- if (!data)
- return;
+ guard(mutex)(&sysfs_lock);
-#if IS_ENABLED(CONFIG_GPIO_SYSFS_LEGACY)
- device_unregister(data->cdev_base);
-#endif /* CONFIG_GPIO_SYSFS_LEGACY */
- device_unregister(data->cdev_id);
- kfree(data);
- }
+ data = gdev_get_data(gdev);
+ if (!data)
+ return;
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);
}
/*