summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 18:50:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 18:50:36 +0000
commit50ba0232fd5312410f1b65247e774244f89a628e (patch)
treefd8f2fc78e9e548af0ff9590276602ee6125be00 /drivers/gpio/gpiolib.c
parentReleasing progress-linux version 6.7.12-1~progress7.99u1. (diff)
downloadlinux-50ba0232fd5312410f1b65247e774244f89a628e.tar.xz
linux-50ba0232fd5312410f1b65247e774244f89a628e.zip
Merging upstream version 6.8.9.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c184
1 files changed, 99 insertions, 85 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 2d8c07a44..d9da0331e 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -45,19 +45,6 @@
* GPIOs can sometimes cost only an instruction or two per bit.
*/
-
-/* When debugging, extend minimal trust to callers and platform code.
- * Also emit diagnostic messages that may help initial bringup, when
- * board setup or driver bugs are most common.
- *
- * Otherwise, minimize overhead in what may be bitbanging codepaths.
- */
-#ifdef DEBUG
-#define extra_checks 1
-#else
-#define extra_checks 0
-#endif
-
/* Device and char device-related information */
static DEFINE_IDA(gpio_ida);
static dev_t gpio_devt;
@@ -255,6 +242,20 @@ int gpio_device_get_base(struct gpio_device *gdev)
EXPORT_SYMBOL_GPL(gpio_device_get_base);
/**
+ * gpio_device_get_label() - Get the label of this GPIO device
+ * @gdev: GPIO device
+ *
+ * Returns:
+ * Pointer to the string containing the GPIO device label. The string's
+ * lifetime is tied to that of the underlying GPIO device.
+ */
+const char *gpio_device_get_label(struct gpio_device *gdev)
+{
+ return gdev->label;
+}
+EXPORT_SYMBOL(gpio_device_get_label);
+
+/**
* gpio_device_get_chip() - Get the gpio_chip implementation of this GPIO device
* @gdev: GPIO device
*
@@ -276,7 +277,7 @@ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev)
EXPORT_SYMBOL_GPL(gpio_device_get_chip);
/* dynamic allocation of GPIOs, e.g. on a hotplugged device */
-static int gpiochip_find_base(int ngpio)
+static int gpiochip_find_base_unlocked(int ngpio)
{
struct gpio_device *gdev;
int base = GPIO_DYNAMIC_BASE;
@@ -349,7 +350,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_direction);
* Return -EBUSY if the new chip overlaps with some other chip's integer
* space.
*/
-static int gpiodev_add_to_list(struct gpio_device *gdev)
+static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev)
{
struct gpio_device *prev, *next;
@@ -655,11 +656,6 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
static void gpiodev_release(struct device *dev)
{
struct gpio_device *gdev = to_gpio_device(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&gpio_lock, flags);
- list_del(&gdev->list);
- spin_unlock_irqrestore(&gpio_lock, flags);
ida_free(&gpio_ida, gdev->id);
kfree_const(gdev->label);
@@ -893,7 +889,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
*/
base = gc->base;
if (base < 0) {
- base = gpiochip_find_base(gc->ngpio);
+ base = gpiochip_find_base_unlocked(gc->ngpio);
if (base < 0) {
spin_unlock_irqrestore(&gpio_lock, flags);
ret = base;
@@ -913,7 +909,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
}
gdev->base = base;
- ret = gpiodev_add_to_list(gdev);
+ ret = gpiodev_add_to_list_unlocked(gdev);
if (ret) {
spin_unlock_irqrestore(&gpio_lock, flags);
chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
@@ -1009,15 +1005,15 @@ err_remove_of_chip:
of_gpiochip_remove(gc);
err_free_gpiochip_mask:
gpiochip_free_valid_mask(gc);
+err_remove_from_list:
+ spin_lock_irqsave(&gpio_lock, flags);
+ list_del(&gdev->list);
+ spin_unlock_irqrestore(&gpio_lock, flags);
if (gdev->dev.release) {
/* release() has been registered by gpiochip_setup_dev() */
gpio_device_put(gdev);
goto err_print_message;
}
-err_remove_from_list:
- spin_lock_irqsave(&gpio_lock, flags);
- list_del(&gdev->list);
- spin_unlock_irqrestore(&gpio_lock, flags);
err_free_label:
kfree_const(gdev->label);
err_free_descs:
@@ -1048,8 +1044,8 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key);
void gpiochip_remove(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
- unsigned long flags;
- unsigned int i;
+ unsigned long flags;
+ unsigned int i;
down_write(&gdev->sem);
@@ -1071,7 +1067,7 @@ void gpiochip_remove(struct gpio_chip *gc)
spin_lock_irqsave(&gpio_lock, flags);
for (i = 0; i < gdev->ngpio; i++) {
- if (gpiochip_is_requested(gc, i))
+ if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags))
break;
}
spin_unlock_irqrestore(&gpio_lock, flags);
@@ -1080,6 +1076,9 @@ void gpiochip_remove(struct gpio_chip *gc)
dev_crit(&gdev->dev,
"REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
+ scoped_guard(spinlock_irqsave, &gpio_lock)
+ list_del(&gdev->list);
+
/*
* The gpiochip side puts its use of the device to rest here:
* if there are no userspace clients, the chardev and device will
@@ -2190,10 +2189,10 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges);
*/
static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
{
- struct gpio_chip *gc = desc->gdev->chip;
- int ret;
- unsigned long flags;
- unsigned offset;
+ struct gpio_chip *gc = desc->gdev->chip;
+ unsigned long flags;
+ unsigned int offset;
+ int ret;
if (label) {
label = kstrdup_const(label, GFP_KERNEL);
@@ -2305,9 +2304,9 @@ int gpiod_request(struct gpio_desc *desc, const char *label)
static bool gpiod_free_commit(struct gpio_desc *desc)
{
- bool ret = false;
- unsigned long flags;
- struct gpio_chip *gc;
+ struct gpio_chip *gc;
+ unsigned long flags;
+ bool ret = false;
might_sleep();
@@ -2336,9 +2335,6 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
#ifdef CONFIG_OF_DYNAMIC
desc->hog = NULL;
#endif
-#ifdef CONFIG_GPIO_CDEV
- WRITE_ONCE(desc->debounce_period_us, 0);
-#endif
ret = true;
}
@@ -2358,38 +2354,58 @@ void gpiod_free(struct gpio_desc *desc)
return;
if (!gpiod_free_commit(desc))
- WARN_ON(extra_checks);
+ WARN_ON(1);
module_put(desc->gdev->owner);
gpio_device_put(desc->gdev);
}
/**
- * gpiochip_is_requested - return string iff signal was requested
- * @gc: controller managing the signal
- * @offset: of signal within controller's 0..(ngpio - 1) range
+ * gpiochip_dup_line_label - Get a copy of the consumer label.
+ * @gc: GPIO chip controlling this line.
+ * @offset: Hardware offset of the line.
*
- * Returns NULL if the GPIO is not currently requested, else a string.
- * The string returned is the label passed to gpio_request(); if none has been
- * passed it is a meaningless, non-NULL constant.
+ * Returns:
+ * Pointer to a copy of the consumer label if the line is requested or NULL
+ * if it's not. If a valid pointer was returned, it must be freed using
+ * kfree(). In case of a memory allocation error, the function returns %ENOMEM.
*
- * This function is for use by GPIO controller drivers. The label can
- * help with diagnostics, and knowing that the signal is used as a GPIO
- * can help avoid accidentally multiplexing it to another controller.
+ * Must not be called from atomic context.
*/
-const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset)
+char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset)
{
struct gpio_desc *desc;
+ char *label;
desc = gpiochip_get_desc(gc, offset);
if (IS_ERR(desc))
return NULL;
- if (test_bit(FLAG_REQUESTED, &desc->flags) == 0)
+ guard(spinlock_irqsave)(&gpio_lock);
+
+ if (!test_bit(FLAG_REQUESTED, &desc->flags))
return NULL;
- return desc->label;
+
+ /*
+ * FIXME: Once we mark gpiod_direction_input/output() and
+ * gpiod_get_direction() with might_sleep(), we'll be able to protect
+ * the GPIO descriptors with mutex (while value setting operations will
+ * become lockless).
+ *
+ * Until this happens, this allocation needs to be atomic.
+ */
+ label = kstrdup(desc->label, GFP_ATOMIC);
+ if (!label)
+ return ERR_PTR(-ENOMEM);
+
+ return label;
+}
+EXPORT_SYMBOL_GPL(gpiochip_dup_line_label);
+
+static inline const char *function_name_or_default(const char *con_id)
+{
+ return con_id ?: "(default)";
}
-EXPORT_SYMBOL_GPL(gpiochip_is_requested);
/**
* gpiochip_request_own_desc - Allow GPIO chip to request its own descriptor
@@ -2419,10 +2435,11 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
enum gpiod_flags dflags)
{
struct gpio_desc *desc = gpiochip_get_desc(gc, hwnum);
+ const char *name = function_name_or_default(label);
int ret;
if (IS_ERR(desc)) {
- chip_err(gc, "failed to get GPIO descriptor\n");
+ chip_err(gc, "failed to get GPIO %s descriptor\n", name);
return desc;
}
@@ -2432,8 +2449,8 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
ret = gpiod_configure_flags(desc, label, lflags, dflags);
if (ret) {
- chip_err(gc, "setup of own GPIO %s failed\n", label);
gpiod_free_commit(desc);
+ chip_err(gc, "setup of own GPIO %s failed\n", name);
return ERR_PTR(ret);
}
@@ -2569,8 +2586,8 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce)
*/
int gpiod_direction_input(struct gpio_desc *desc)
{
- struct gpio_chip *gc;
- int ret = 0;
+ struct gpio_chip *gc;
+ int ret = 0;
VALIDATE_DESC(desc);
gc = desc->gdev->chip;
@@ -2919,7 +2936,7 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des
static int gpiod_get_raw_value_commit(const struct gpio_desc *desc)
{
- struct gpio_chip *gc;
+ struct gpio_chip *gc;
int value;
gc = desc->gdev->chip;
@@ -3214,7 +3231,7 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value
static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
{
- struct gpio_chip *gc;
+ struct gpio_chip *gc;
gc = desc->gdev->chip;
trace_gpio_value(desc_to_gpio(desc), 0, value);
@@ -3721,7 +3738,7 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent);
*/
int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc)
{
- might_sleep_if(extra_checks);
+ might_sleep();
VALIDATE_DESC(desc);
return gpiod_get_raw_value_commit(desc);
}
@@ -3740,7 +3757,7 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc)
{
int value;
- might_sleep_if(extra_checks);
+ might_sleep();
VALIDATE_DESC(desc);
value = gpiod_get_raw_value_commit(desc);
if (value < 0)
@@ -3771,7 +3788,7 @@ int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- might_sleep_if(extra_checks);
+ might_sleep();
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
@@ -3797,7 +3814,7 @@ int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- might_sleep_if(extra_checks);
+ might_sleep();
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
@@ -3818,7 +3835,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);
*/
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
{
- might_sleep_if(extra_checks);
+ might_sleep();
VALIDATE_DESC_VOID(desc);
gpiod_set_raw_value_commit(desc, value);
}
@@ -3836,7 +3853,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_value_cansleep);
*/
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
{
- might_sleep_if(extra_checks);
+ might_sleep();
VALIDATE_DESC_VOID(desc);
gpiod_set_value_nocheck(desc, value);
}
@@ -3859,7 +3876,7 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- might_sleep_if(extra_checks);
+ might_sleep();
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
@@ -3901,7 +3918,7 @@ int gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_array *array_info,
unsigned long *value_bitmap)
{
- might_sleep_if(extra_checks);
+ might_sleep();
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(false, true, array_size,
@@ -4108,19 +4125,17 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode,
enum gpiod_flags *flags,
unsigned long *lookupflags)
{
+ const char *name = function_name_or_default(con_id);
struct gpio_desc *desc = ERR_PTR(-ENOENT);
if (is_of_node(fwnode)) {
- dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n",
- fwnode, con_id);
+ dev_dbg(consumer, "using DT '%pfw' for '%s' GPIO lookup\n", fwnode, name);
desc = of_find_gpio(to_of_node(fwnode), con_id, idx, lookupflags);
} else if (is_acpi_node(fwnode)) {
- dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n",
- fwnode, con_id);
+ dev_dbg(consumer, "using ACPI '%pfw' for '%s' GPIO lookup\n", fwnode, name);
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);
+ dev_dbg(consumer, "using swnode '%pfw' for '%s' GPIO lookup\n", fwnode, name);
desc = swnode_find_gpio(fwnode, con_id, idx, lookupflags);
}
@@ -4136,6 +4151,7 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
bool platform_lookup_allowed)
{
unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT;
+ const char *name = function_name_or_default(con_id);
struct gpio_desc *desc;
int ret;
@@ -4151,7 +4167,7 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
}
if (IS_ERR(desc)) {
- dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
+ dev_dbg(consumer, "No GPIO consumer %s found\n", name);
return desc;
}
@@ -4172,15 +4188,14 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
*
* FIXME: Make this more sane and safe.
*/
- dev_info(consumer,
- "nonexclusive access to GPIO for %s\n", con_id);
+ dev_info(consumer, "nonexclusive access to GPIO for %s\n", name);
return desc;
}
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);
+ dev_dbg(consumer, "setup of GPIO %s failed\n", name);
return ERR_PTR(ret);
}
@@ -4296,6 +4311,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional);
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
unsigned long lflags, enum gpiod_flags dflags)
{
+ const char *name = function_name_or_default(con_id);
int ret;
if (lflags & GPIO_ACTIVE_LOW)
@@ -4339,7 +4355,7 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
/* No particular flag request, return here... */
if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
- gpiod_dbg(desc, "no flags found for %s\n", con_id);
+ gpiod_dbg(desc, "no flags found for GPIO %s\n", name);
return 0;
}
@@ -4701,13 +4717,11 @@ core_initcall(gpiolib_dev_init);
static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev)
{
- struct gpio_chip *gc = gdev->chip;
- struct gpio_desc *desc;
- unsigned gpio = gdev->base;
- int value;
- bool is_out;
- bool is_irq;
- bool active_low;
+ struct gpio_chip *gc = gdev->chip;
+ bool active_low, is_irq, is_out;
+ unsigned int gpio = gdev->base;
+ struct gpio_desc *desc;
+ int value;
for_each_gpio_desc(gc, desc) {
if (test_bit(FLAG_REQUESTED, &desc->flags)) {