summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-12-15 09:45:51 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2022-12-15 09:45:51 -0800
commitc0f234ff90a211272138be1611ba53f3155ebd78 (patch)
tree956c32a903675d690631100ae829e810b4b4515b /drivers/gpio/gpiolib.c
parent9fa4abc9ad2a18410a7087e6cea15ad1ffb172c6 (diff)
parent11e47bbd700f31bd1ee9f8863381bc9e741c0e97 (diff)
downloadlinux-stable-c0f234ff90a211272138be1611ba53f3155ebd78.tar.gz
linux-stable-c0f234ff90a211272138be1611ba53f3155ebd78.tar.bz2
linux-stable-c0f234ff90a211272138be1611ba53f3155ebd78.zip
Merge tag 'gpio-updates-for-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski: "We have a new GPIO multiplexer driver, bunch of driver updates and refactoring in the core GPIO library. GPIO core: - teach gpiolib to work with software nodes for HW description - remove ARCH_NR_GPIOS treewide as we no longer impose any limit on the number of GPIOS since the allocation became entirely dynamic - add support for HW quirks for Cirrus CS42L56 codec, Marvell NFC controller, Freescale PCIe and Ethernet controller, Himax LCDs and Mediatek mt2701 - refactor OF quirk code - some general refactoring of the OF and ACPI code, adding new helpers, minor tweaks and fixes, making fwnode usage consistent etc. GPIO uAPI: - fix an issue where the user-space can trigger a NULL-pointer dereference in the kernel by opening a device file, forcing a driver unbind and then calling one of the syscalls on the associated file descriptor New drivers: - add gpio-latch: a new GPIO multiplexer based on latches connected to other GPIOs Driver updates: - convert i2c GPIO expanders to using .probe_new() - drop the gpio-sta2x11 driver - factor out common code for the ACCES IDIO-16 family of controllers and use this new library wherever applicable in drivers - add DT support to gpio-hisi - allow building gpio-davinci as a module and increase its maxItems property - add support for a new model to gpio-pca9570 - other minor changes to various drivers" * tag 'gpio-updates-for-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (66 commits) gpio: sim: set a limit on the number of GPIOs gpiolib: protect the GPIO device against being dropped while in use by user-space gpiolib: cdev: fix NULL-pointer dereferences gpiolib: Provide to_gpio_device() helper gpiolib: Unify access to the device properties gpio: Do not include <linux/kernel.h> when not really needed. gpio: pcf857x: Convert to i2c's .probe_new() gpio: pca953x: Convert to i2c's .probe_new() gpio: max732x: Convert to i2c's .probe_new() dt-bindings: gpio: gpio-davinci: Increase maxItems in gpio-line-names gpiolib: ensure that fwnode is properly set gpio: sl28cpld: Replace irqchip mask_invert with unmask_base gpiolib: of: Use correct fwnode for DT-probed chips gpiolib: of: Drop redundant check in of_mm_gpiochip_remove() gpiolib: of: Prepare of_mm_gpiochip_add_data() for fwnode gpiolib: add support for software nodes gpiolib: consolidate GPIO lookups gpiolib: acpi: avoid leaking ACPI details into upper gpiolib layers gpiolib: acpi: teach acpi_find_gpio() to handle data-only nodes gpiolib: acpi: change acpi_find_gpio() to accept firmware node ...
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c295
1 files changed, 155 insertions, 140 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a70522aef355..5a66d9616d7c 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -26,6 +26,7 @@
#include "gpiolib.h"
#include "gpiolib-of.h"
#include "gpiolib-acpi.h"
+#include "gpiolib-swnode.h"
#include "gpiolib-cdev.h"
#include "gpiolib-sysfs.h"
@@ -183,14 +184,14 @@ EXPORT_SYMBOL_GPL(gpiod_to_chip);
static int gpiochip_find_base(int ngpio)
{
struct gpio_device *gdev;
- int base = ARCH_NR_GPIOS - ngpio;
+ int base = GPIO_DYNAMIC_BASE;
- list_for_each_entry_reverse(gdev, &gpio_devices, list) {
+ list_for_each_entry(gdev, &gpio_devices, list) {
/* found a free space? */
- if (gdev->base + gdev->ngpio <= base)
+ if (gdev->base >= base + ngpio)
break;
- /* nope, check the space right before the chip */
- base = gdev->base - ngpio;
+ /* nope, check the space right after the chip */
+ base = gdev->base + gdev->ngpio;
}
if (gpio_is_valid(base)) {
@@ -366,12 +367,12 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
static int devprop_gpiochip_set_names(struct gpio_chip *chip)
{
struct gpio_device *gdev = chip->gpiodev;
- struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
+ struct device *dev = &gdev->dev;
const char **names;
int ret, i;
int count;
- count = fwnode_property_string_array_count(fwnode, "gpio-line-names");
+ count = device_property_string_array_count(dev, "gpio-line-names");
if (count < 0)
return 0;
@@ -384,7 +385,7 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip)
* gpiochips.
*/
if (count <= chip->offset) {
- dev_warn(&gdev->dev, "gpio-line-names too short (length %d), cannot map names for the gpiochip at offset %u\n",
+ dev_warn(dev, "gpio-line-names too short (length %d), cannot map names for the gpiochip at offset %u\n",
count, chip->offset);
return 0;
}
@@ -393,10 +394,10 @@ static int devprop_gpiochip_set_names(struct gpio_chip *chip)
if (!names)
return -ENOMEM;
- ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
+ ret = device_property_read_string_array(dev, "gpio-line-names",
names, count);
if (ret < 0) {
- dev_warn(&gdev->dev, "failed to read GPIO line names\n");
+ dev_warn(dev, "failed to read GPIO line names\n");
kfree(names);
return ret;
}
@@ -445,9 +446,22 @@ static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc)
return p;
}
+static unsigned int gpiochip_count_reserved_ranges(struct gpio_chip *gc)
+{
+ struct device *dev = &gc->gpiodev->dev;
+ int size;
+
+ /* Format is "start, count, ..." */
+ size = device_property_count_u32(dev, "gpio-reserved-ranges");
+ if (size > 0 && size % 2 == 0)
+ return size;
+
+ return 0;
+}
+
static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
{
- if (!(of_gpio_need_valid_mask(gc) || gc->init_valid_mask))
+ if (!(gpiochip_count_reserved_ranges(gc) || gc->init_valid_mask))
return 0;
gc->valid_mask = gpiochip_allocate_mask(gc);
@@ -457,8 +471,50 @@ static int gpiochip_alloc_valid_mask(struct gpio_chip *gc)
return 0;
}
+static int gpiochip_apply_reserved_ranges(struct gpio_chip *gc)
+{
+ struct device *dev = &gc->gpiodev->dev;
+ unsigned int size;
+ u32 *ranges;
+ int ret;
+
+ size = gpiochip_count_reserved_ranges(gc);
+ if (size == 0)
+ return 0;
+
+ ranges = kmalloc_array(size, sizeof(*ranges), GFP_KERNEL);
+ if (!ranges)
+ return -ENOMEM;
+
+ ret = device_property_read_u32_array(dev, "gpio-reserved-ranges",
+ ranges, size);
+ if (ret) {
+ kfree(ranges);
+ return ret;
+ }
+
+ while (size) {
+ u32 count = ranges[--size];
+ u32 start = ranges[--size];
+
+ if (start >= gc->ngpio || start + count > gc->ngpio)
+ continue;
+
+ bitmap_clear(gc->valid_mask, start, count);
+ }
+
+ kfree(ranges);
+ return 0;
+}
+
static int gpiochip_init_valid_mask(struct gpio_chip *gc)
{
+ int ret;
+
+ ret = gpiochip_apply_reserved_ranges(gc);
+ if (ret)
+ return ret;
+
if (gc->init_valid_mask)
return gc->init_valid_mask(gc,
gc->valid_mask,
@@ -493,7 +549,7 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
static void gpiodevice_release(struct device *dev)
{
- struct gpio_device *gdev = container_of(dev, struct gpio_device, dev);
+ struct gpio_device *gdev = to_gpio_device(dev);
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
@@ -627,7 +683,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* Assign fwnode depending on the result of the previous calls,
* if none of them succeed, assign it to the parent's one.
*/
- gdev->dev.fwnode = dev_fwnode(&gdev->dev) ?: fwnode;
+ gc->fwnode = gdev->dev.fwnode = dev_fwnode(&gdev->dev) ?: fwnode;
gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
if (gdev->id < 0) {
@@ -719,6 +775,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
* a poison instead.
*/
gc->base = base;
+ } else {
+ dev_warn(&gdev->dev,
+ "Static allocation of GPIO base is deprecated, use dynamic allocation.\n");
}
gdev->base = base;
@@ -735,6 +794,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
spin_unlock_irqrestore(&gpio_lock, flags);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier);
+ init_rwsem(&gdev->sem);
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
@@ -875,6 +935,8 @@ void gpiochip_remove(struct gpio_chip *gc)
unsigned long flags;
unsigned int i;
+ down_write(&gdev->sem);
+
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
gpiochip_sysfs_unregister(gdev);
gpiochip_free_hogs(gc);
@@ -909,6 +971,7 @@ void gpiochip_remove(struct gpio_chip *gc)
* gone.
*/
gcdev_unregister(gdev);
+ up_write(&gdev->sem);
put_device(&gdev->dev);
}
EXPORT_SYMBOL_GPL(gpiochip_remove);
@@ -3808,62 +3871,88 @@ static int platform_gpio_count(struct device *dev, const char *con_id)
return count;
}
-/**
- * fwnode_get_named_gpiod - obtain a GPIO from firmware node
- * @fwnode: handle of the firmware node
- * @propname: name of the firmware property representing the GPIO
- * @index: index of the GPIO to obtain for the consumer
- * @dflags: GPIO initialization flags
- * @label: label to attach to the requested GPIO
- *
- * This function can be used for drivers that get their configuration
- * from opaque firmware.
- *
- * The function properly finds the corresponding GPIO using whatever is the
- * underlying firmware interface and then makes sure that the GPIO
- * descriptor is requested before it is returned to the caller.
- *
- * Returns:
- * On successful request the GPIO pin is configured in accordance with
- * provided @dflags.
- *
- * In case of error an ERR_PTR() is returned.
- */
-static struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
- const char *propname, int index,
- enum gpiod_flags dflags,
- const char *label)
+static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode,
+ struct device *consumer,
+ const char *con_id,
+ unsigned int idx,
+ enum gpiod_flags *flags,
+ unsigned long *lookupflags)
{
- unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- struct gpio_desc *desc = ERR_PTR(-ENODEV);
- int ret;
+ struct gpio_desc *desc = ERR_PTR(-ENOENT);
if (is_of_node(fwnode)) {
- desc = gpiod_get_from_of_node(to_of_node(fwnode),
- propname, index,
- dflags,
- label);
- return desc;
+ dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n",
+ fwnode, con_id);
+ desc = of_find_gpio(to_of_node(fwnode), con_id, idx, lookupflags);
} else if (is_acpi_node(fwnode)) {
- struct acpi_gpio_info info;
+ dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n",
+ fwnode, con_id);
+ desc = acpi_find_gpio(fwnode, con_id, idx, flags, lookupflags);
+ } else if (is_software_node(fwnode)) {
+ dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n",
+ fwnode, con_id);
+ desc = swnode_find_gpio(fwnode, con_id, idx, lookupflags);
+ }
- desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
- if (IS_ERR(desc))
- return desc;
+ return desc;
+}
- acpi_gpio_update_gpiod_flags(&dflags, &info);
- acpi_gpio_update_gpiod_lookup_flags(&lflags, &info);
- } else {
- return ERR_PTR(-EINVAL);
+static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
+ struct fwnode_handle *fwnode,
+ const char *con_id,
+ unsigned int idx,
+ enum gpiod_flags flags,
+ const char *label,
+ bool platform_lookup_allowed)
+{
+ struct gpio_desc *desc = ERR_PTR(-ENOENT);
+ unsigned long lookupflags;
+ int ret;
+
+ if (!IS_ERR_OR_NULL(fwnode))
+ desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx,
+ &flags, &lookupflags);
+
+ if (gpiod_not_found(desc) && platform_lookup_allowed) {
+ /*
+ * Either we are not using DT or ACPI, or their lookup did not
+ * return a result. In that case, use platform lookup as a
+ * fallback.
+ */
+ dev_dbg(consumer, "using lookup tables for GPIO lookup\n");
+ desc = gpiod_find(consumer, con_id, idx, &lookupflags);
}
- /* Currently only ACPI takes this path */
+ if (IS_ERR(desc)) {
+ dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
+ return desc;
+ }
+
+ /*
+ * If a connection label was passed use that, else attempt to use
+ * the device name as label
+ */
ret = gpiod_request(desc, label);
- if (ret)
- return ERR_PTR(ret);
+ if (ret) {
+ if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
+ return ERR_PTR(ret);
+
+ /*
+ * This happens when there are several consumers for
+ * the same GPIO line: we just return here without
+ * further initialization. It is a bit of a hack.
+ * This is necessary to support fixed regulators.
+ *
+ * FIXME: Make this more sane and safe.
+ */
+ dev_info(consumer,
+ "nonexclusive access to GPIO for %s\n", con_id);
+ return desc;
+ }
- ret = gpiod_configure_flags(desc, propname, lflags, dflags);
+ ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
if (ret < 0) {
+ dev_dbg(consumer, "setup of GPIO %s failed\n", con_id);
gpiod_put(desc);
return ERR_PTR(ret);
}
@@ -3896,29 +3985,12 @@ static struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
* In case of error an ERR_PTR() is returned.
*/
struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode,
- const char *con_id, int index,
+ const char *con_id,
+ int index,
enum gpiod_flags flags,
const char *label)
{
- struct gpio_desc *desc;
- char prop_name[32]; /* 32 is max size of property name */
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) {
- if (con_id)
- snprintf(prop_name, sizeof(prop_name), "%s-%s",
- con_id, gpio_suffixes[i]);
- else
- snprintf(prop_name, sizeof(prop_name), "%s",
- gpio_suffixes[i]);
-
- desc = fwnode_get_named_gpiod(fwnode, prop_name, index, flags,
- label);
- if (!gpiod_not_found(desc))
- break;
- }
-
- return desc;
+ return gpiod_find_and_request(NULL, fwnode, con_id, index, flags, label, false);
}
EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index);
@@ -3937,6 +4009,8 @@ int gpiod_count(struct device *dev, const char *con_id)
count = of_gpio_get_count(dev, con_id);
else if (is_acpi_node(fwnode))
count = acpi_gpio_count(dev, con_id);
+ else if (is_software_node(fwnode))
+ count = swnode_gpio_count(fwnode, con_id);
if (count < 0)
count = platform_gpio_count(dev, con_id);
@@ -4072,70 +4146,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
unsigned int idx,
enum gpiod_flags flags)
{
- unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT;
- struct gpio_desc *desc = NULL;
- int ret;
- /* Maybe we have a device name, maybe not */
+ struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
const char *devname = dev ? dev_name(dev) : "?";
- const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL;
-
- dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id);
-
- /* Using device tree? */
- if (is_of_node(fwnode)) {
- dev_dbg(dev, "using device tree for GPIO lookup\n");
- desc = of_find_gpio(dev, con_id, idx, &lookupflags);
- } else if (is_acpi_node(fwnode)) {
- dev_dbg(dev, "using ACPI for GPIO lookup\n");
- desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags);
- }
-
- /*
- * Either we are not using DT or ACPI, or their lookup did not return
- * a result. In that case, use platform lookup as a fallback.
- */
- if (!desc || gpiod_not_found(desc)) {
- dev_dbg(dev, "using lookup tables for GPIO lookup\n");
- desc = gpiod_find(dev, con_id, idx, &lookupflags);
- }
-
- if (IS_ERR(desc)) {
- dev_dbg(dev, "No GPIO consumer %s found\n", con_id);
- return desc;
- }
-
- /*
- * If a connection label was passed use that, else attempt to use
- * the device name as label
- */
- ret = gpiod_request(desc, con_id ?: devname);
- if (ret) {
- if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
- return ERR_PTR(ret);
-
- /*
- * This happens when there are several consumers for
- * the same GPIO line: we just return here without
- * further initialization. It is a bit of a hack.
- * This is necessary to support fixed regulators.
- *
- * FIXME: Make this more sane and safe.
- */
- dev_info(dev, "nonexclusive access to GPIO for %s\n", con_id ?: devname);
- return desc;
- }
-
- ret = gpiod_configure_flags(desc, con_id, lookupflags, flags);
- if (ret < 0) {
- dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
- gpiod_put(desc);
- return ERR_PTR(ret);
- }
+ const char *label = con_id ?: devname;
- blocking_notifier_call_chain(&desc->gdev->notifier,
- GPIOLINE_CHANGED_REQUESTED, desc);
-
- return desc;
+ return gpiod_find_and_request(dev, fwnode, con_id, idx, flags, label, true);
}
EXPORT_SYMBOL_GPL(gpiod_get_index);