summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-08-29 20:21:56 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2023-08-29 20:21:56 +0300
commitf97e18a3f2fb78a4ed0d25e427535d9f853b9e9e (patch)
tree6693da811ca60834111b1143e856a088f7bee388 /drivers/gpio/gpiolib.c
parent41e97d7acf5a45f19ade9139ca178bf9e8e236bb (diff)
parent3d0957b07e27abd3237b1fe0c7f06485ba80852f (diff)
downloadlinux-f97e18a3f2fb78a4ed0d25e427535d9f853b9e9e.tar.xz
Merge tag 'gpio-updates-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski: "We have a lot of code refactoring using common helpers and ended up removing more lines then we're adding this release cycle. Nothing really stands out, just small updates all over the place. Core GPIOLIB updates: - wake-up poll() in user-space on device unbind - improve fwnode usage - interrupt domain handling improvements - correctly handle the ngpios property in gpio-mmio Driver cleanups: - remove unneeded calls to platform_set_drvdata() all around the place - remove unneeded of_match_ptr() expansions whenever a driver depends on CONFIG_OF - remove redundant calls to dev_err_probe() from gpio-omap and gpio-davinci Driver improvements: - use autopointers and guards from cleanup.h in gpio-sim - shrink code in gpio-sim using some common helpers - convert the idio family of drivers to using gpio-regmap - convert gpio-ws16c48 to using gpio-regmap - use devres to simplify code in gpio-pisosr and gpio-mxc - update gpio-sifive: support IRQ wake, improve interrupt handling, allow building as module - make gpio-ge and gpio-bcm-kona OF-independent (plus some minor tweaks) - add support for new models in gpio-pca953x and gpio-ds4520 - add runtime PM support to gpio-mxc - fix a build warning in gpio-mxs - add support for adding pin ranges to gpio-mlxbf3 - add counter/timer support to gpio-104-dio-48e - switch to dynamic GPIO base allocation in gpio-vf610 - minor oneliners here and there Device-tree bindings updates: - enable the gpio-line-names property in snps,dw-apb and STMPE GPIO - document new models in fsl-imx-gpio, ds4520 and pca95xx - convert the bindings for brcm,kona-gpio to YAML" * tag 'gpio-updates-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (94 commits) gpio: pca953x: add support for TCA9538 dt-bindings: gpio: pca95xx: document new tca9538 chip gpio: pca953x: Use i2c_get_match_data() gpio: mlxbf3: use capital "OR" for multiple licenses in SPDX gpio: pcf857x: Extend match data support for OF tables gpio: vf610: switch to dynamic allocat GPIO base gpiolib: provide and use gpiod_line_state_notify() gpio: cdev: wake up lineevent poll() on device unbind gpio: cdev: wake up linereq poll() on device unbind gpio: cdev: wake up chardev poll() on device unbind gpiolib: add a second blocking notifier to struct gpio_device gpio: cdev: open-code to_gpio_chardev_data() gpiolib: rename the gpio_device notifier gpio: mlxbf3: Support add_pin_ranges() gpio: mxc: Use helper function devm_clk_get_optional_enabled() gpio: pca9570: fix kerneldoc gpio: sim: simplify code with cleanup helpers gpio: sim: replace memmove() + strstrip() with skip_spaces() + strim() gpio: sim: simplify gpio_sim_device_config_live_store() gpio: mxc: release the parent IRQ in runtime suspend ...
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c203
1 files changed, 113 insertions, 90 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 76e0c38026c3..40a0022ea719 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -700,6 +700,40 @@ void *gpiochip_get_data(struct gpio_chip *gc)
}
EXPORT_SYMBOL_GPL(gpiochip_get_data);
+int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev)
+{
+ u32 ngpios = gc->ngpio;
+ int ret;
+
+ if (ngpios == 0) {
+ ret = device_property_read_u32(dev, "ngpios", &ngpios);
+ if (ret == -ENODATA)
+ /*
+ * -ENODATA means that there is no property found and
+ * we want to issue the error message to the user.
+ * Besides that, we want to return different error code
+ * to state that supplied value is not valid.
+ */
+ ngpios = 0;
+ else if (ret)
+ return ret;
+
+ gc->ngpio = ngpios;
+ }
+
+ if (gc->ngpio == 0) {
+ chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
+ return -EINVAL;
+ }
+
+ if (gc->ngpio > FASTPATH_NGPIO)
+ chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
+ gc->ngpio, FASTPATH_NGPIO);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_get_ngpios);
+
int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct lock_class_key *lock_key,
struct lock_class_key *request_key)
@@ -707,18 +741,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
struct gpio_device *gdev;
unsigned long flags;
unsigned int i;
- u32 ngpios = 0;
int base = 0;
int ret = 0;
/*
- * If the calling driver did not initialize firmware node, do it here
- * using the parent device, if any.
- */
- if (!gc->fwnode && gc->parent)
- gc->fwnode = dev_fwnode(gc->parent);
-
- /*
* First: allocate and populate the internal stat container, and
* set up the struct device.
*/
@@ -732,7 +758,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
gc->gpiodev = gdev;
gpiochip_set_data(gc, data);
- device_set_node(&gdev->dev, gc->fwnode);
+ /*
+ * If the calling driver did not initialize firmware node,
+ * do it here using the parent device, if any.
+ */
+ if (gc->fwnode)
+ device_set_node(&gdev->dev, gc->fwnode);
+ else if (gc->parent)
+ device_set_node(&gdev->dev, dev_fwnode(gc->parent));
gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
if (gdev->id < 0) {
@@ -753,36 +786,9 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
else
gdev->owner = THIS_MODULE;
- /*
- * Try the device properties if the driver didn't supply the number
- * of GPIO lines.
- */
- ngpios = gc->ngpio;
- if (ngpios == 0) {
- ret = device_property_read_u32(&gdev->dev, "ngpios", &ngpios);
- if (ret == -ENODATA)
- /*
- * -ENODATA means that there is no property found and
- * we want to issue the error message to the user.
- * Besides that, we want to return different error code
- * to state that supplied value is not valid.
- */
- ngpios = 0;
- else if (ret)
- goto err_free_dev_name;
-
- gc->ngpio = ngpios;
- }
-
- if (gc->ngpio == 0) {
- chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
- ret = -EINVAL;
+ ret = gpiochip_get_ngpios(gc, &gdev->dev);
+ if (ret)
goto err_free_dev_name;
- }
-
- if (gc->ngpio > FASTPATH_NGPIO)
- chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
- gc->ngpio, FASTPATH_NGPIO);
gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
if (!gdev->descs) {
@@ -841,7 +847,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
spin_unlock_irqrestore(&gpio_lock, flags);
- BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier);
+ BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
+ BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
init_rwsem(&gdev->sem);
#ifdef CONFIG_PINCTRL
@@ -947,7 +954,7 @@ err_print_message:
/* failures here can mean systems won't boot... */
if (ret != -EPROBE_DEFER) {
pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
- base, base + (int)ngpios - 1,
+ base, base + (int)gc->ngpio - 1,
gc->label ? : "generic", ret);
}
return ret;
@@ -1292,12 +1299,14 @@ static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops)
ops->free = irq_domain_free_irqs_common;
}
-static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
+static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
{
+ struct irq_domain *domain;
+
if (!gc->irq.child_to_parent_hwirq ||
!gc->irq.fwnode) {
chip_err(gc, "missing irqdomain vital data\n");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
if (!gc->irq.child_offset_to_irq)
@@ -1309,7 +1318,7 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops);
- gc->irq.domain = irq_domain_create_hierarchy(
+ domain = irq_domain_create_hierarchy(
gc->irq.parent_domain,
0,
gc->ngpio,
@@ -1317,12 +1326,12 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
&gc->irq.child_irq_domain_ops,
gc);
- if (!gc->irq.domain)
- return -ENOMEM;
+ if (!domain)
+ return ERR_PTR(-ENOMEM);
gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip);
- return 0;
+ return domain;
}
static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
@@ -1366,9 +1375,9 @@ EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell);
#else
-static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
+static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
{
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
@@ -1445,6 +1454,19 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
+static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev);
+ struct irq_domain *domain;
+
+ domain = irq_domain_create_simple(fwnode, gc->ngpio, gc->irq.first,
+ &gpiochip_domain_ops, gc);
+ if (!domain)
+ return ERR_PTR(-EINVAL);
+
+ return domain;
+}
+
/*
* TODO: move these activate/deactivate in under the hierarchicial
* irqchip implementation as static once SPMI and SSBI (all external
@@ -1623,6 +1645,31 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
}
}
+static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc,
+ struct irq_domain *domain,
+ bool allocated_externally)
+{
+ if (!domain)
+ return -EINVAL;
+
+ if (gc->to_irq)
+ chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
+
+ gc->to_irq = gpiochip_to_irq;
+ gc->irq.domain = domain;
+ gc->irq.domain_is_allocated_externally = allocated_externally;
+
+ /*
+ * Using barrier() here to prevent compiler from reordering
+ * gc->irq.initialized before adding irqdomain.
+ */
+ barrier();
+
+ gc->irq.initialized = true;
+
+ return 0;
+}
+
/**
* gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip
* @gc: the GPIO chip to add the IRQ chip to
@@ -1635,8 +1682,10 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
{
struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev);
struct irq_chip *irqchip = gc->irq.chip;
+ struct irq_domain *domain;
unsigned int type;
unsigned int i;
+ int ret;
if (!irqchip)
return 0;
@@ -1657,28 +1706,18 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
"%pfw: Ignoring %u default trigger\n", fwnode, type))
type = IRQ_TYPE_NONE;
- if (gc->to_irq)
- chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
-
- gc->to_irq = gpiochip_to_irq;
gc->irq.default_type = type;
gc->irq.lock_key = lock_key;
gc->irq.request_key = request_key;
/* If a parent irqdomain is provided, let's build a hierarchy */
if (gpiochip_hierarchy_is_hierarchical(gc)) {
- int ret = gpiochip_hierarchy_add_domain(gc);
- if (ret)
- return ret;
+ domain = gpiochip_hierarchy_create_domain(gc);
} else {
- gc->irq.domain = irq_domain_create_simple(fwnode,
- gc->ngpio,
- gc->irq.first,
- &gpiochip_domain_ops,
- gc);
- if (!gc->irq.domain)
- return -EINVAL;
+ domain = gpiochip_simple_create_domain(gc);
}
+ if (IS_ERR(domain))
+ return PTR_ERR(domain);
if (gc->irq.parent_handler) {
for (i = 0; i < gc->irq.num_parents; i++) {
@@ -1702,14 +1741,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
gpiochip_set_irq_hooks(gc);
- /*
- * Using barrier() here to prevent compiler from reordering
- * gc->irq.initialized before initialization of above
- * GPIO chip irq members.
- */
- barrier();
-
- gc->irq.initialized = true;
+ ret = gpiochip_irqchip_add_allocated_domain(gc, domain, false);
+ if (ret)
+ return ret;
acpi_gpiochip_request_interrupts(gc);
@@ -1780,22 +1814,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc)
int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
struct irq_domain *domain)
{
- if (!domain)
- return -EINVAL;
-
- gc->to_irq = gpiochip_to_irq;
- gc->irq.domain = domain;
- gc->irq.domain_is_allocated_externally = true;
-
- /*
- * Using barrier() here to prevent compiler from reordering
- * gc->irq.initialized before adding irqdomain.
- */
- barrier();
-
- gc->irq.initialized = true;
-
- return 0;
+ return gpiochip_irqchip_add_allocated_domain(gc, domain, true);
}
EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain);
@@ -2159,8 +2178,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
}
spin_unlock_irqrestore(&gpio_lock, flags);
- blocking_notifier_call_chain(&desc->gdev->notifier,
- GPIOLINE_CHANGED_RELEASED, desc);
+ gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
return ret;
}
@@ -3728,6 +3746,12 @@ int gpiod_set_array_value_cansleep(unsigned int array_size,
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
+void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action)
+{
+ blocking_notifier_call_chain(&desc->gdev->line_state_notifier,
+ action, desc);
+}
+
/**
* gpiod_add_lookup_table() - register GPIO device consumers
* @table: table of consumers to register
@@ -3995,8 +4019,7 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
return ERR_PTR(ret);
}
- blocking_notifier_call_chain(&desc->gdev->notifier,
- GPIOLINE_CHANGED_REQUESTED, desc);
+ gpiod_line_state_notify(desc, GPIOLINE_CHANGED_REQUESTED);
return desc;
}