summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c234
1 files changed, 159 insertions, 75 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 868128a676ba..8b4d721d6d63 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -1,3 +1,4 @@
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@@ -982,11 +983,12 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp)
struct gpio_device, chrdev);
/* Fail on open if the backing gpiochip is gone */
- if (!gdev || !gdev->chip)
+ if (!gdev->chip)
return -ENODEV;
get_device(&gdev->dev);
filp->private_data = gdev;
- return 0;
+
+ return nonseekable_open(inode, filp);
}
/**
@@ -1000,8 +1002,6 @@ static int gpio_chrdev_release(struct inode *inode, struct file *filp)
struct gpio_device *gdev = container_of(inode->i_cdev,
struct gpio_device, chrdev);
- if (!gdev)
- return -ENODEV;
put_device(&gdev->dev);
return 0;
}
@@ -1011,7 +1011,7 @@ static const struct file_operations gpio_fileops = {
.release = gpio_chrdev_release,
.open = gpio_chrdev_open,
.owner = THIS_MODULE,
- .llseek = noop_llseek,
+ .llseek = no_llseek,
.unlocked_ioctl = gpio_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = gpio_ioctl_compat,
@@ -1316,12 +1316,12 @@ void gpiochip_remove(struct gpio_chip *chip)
/* FIXME: should the legacy sysfs handling be moved to gpio_device? */
gpiochip_sysfs_unregister(gdev);
+ gpiochip_free_hogs(chip);
/* Numb the device, cancelling all outstanding operations */
gdev->chip = NULL;
gpiochip_irqchip_remove(chip);
acpi_gpiochip_remove(chip);
gpiochip_remove_pin_ranges(chip);
- gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
/*
* We accept no more calls into the driver from this point, so
@@ -1422,8 +1422,7 @@ void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip)
ret = devres_release(dev, devm_gpio_chip_release,
devm_gpio_chip_match, chip);
- if (!ret)
- WARN_ON(ret);
+ WARN_ON(ret);
}
EXPORT_SYMBOL_GPL(devm_gpiochip_remove);
@@ -1512,7 +1511,7 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
}
/**
- * gpiochip_set_chained_irqchip() - sets a chained irqchip to a gpiochip
+ * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip
* @gpiochip: the gpiochip to set the irqchip chain to
* @irqchip: the irqchip to chain to the gpiochip
* @parent_irq: the irq number corresponding to the parent IRQ for this
@@ -1521,10 +1520,10 @@ static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
* coming out of the gpiochip. If the interrupt is nested rather than
* cascaded, pass NULL in this handler argument
*/
-void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
- struct irq_chip *irqchip,
- int parent_irq,
- irq_flow_handler_t parent_handler)
+static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ int parent_irq,
+ irq_flow_handler_t parent_handler)
{
unsigned int offset;
@@ -1548,7 +1547,7 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
irq_set_chained_handler_and_data(parent_irq, parent_handler,
gpiochip);
- gpiochip->irq_parent = parent_irq;
+ gpiochip->irq_chained_parent = parent_irq;
}
/* Set the parent IRQ for all affected IRQs */
@@ -1559,9 +1558,48 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
parent_irq);
}
}
+
+/**
+ * gpiochip_set_chained_irqchip() - connects a chained irqchip to a gpiochip
+ * @gpiochip: the gpiochip to set the irqchip chain to
+ * @irqchip: the irqchip to chain to the gpiochip
+ * @parent_irq: the irq number corresponding to the parent IRQ for this
+ * chained irqchip
+ * @parent_handler: the parent interrupt handler for the accumulated IRQ
+ * coming out of the gpiochip. If the interrupt is nested rather than
+ * cascaded, pass NULL in this handler argument
+ */
+void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ int parent_irq,
+ irq_flow_handler_t parent_handler)
+{
+ gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq,
+ parent_handler);
+}
EXPORT_SYMBOL_GPL(gpiochip_set_chained_irqchip);
/**
+ * gpiochip_set_nested_irqchip() - connects a nested irqchip to a gpiochip
+ * @gpiochip: the gpiochip to set the irqchip nested handler to
+ * @irqchip: the irqchip to nest to the gpiochip
+ * @parent_irq: the irq number corresponding to the parent IRQ for this
+ * nested irqchip
+ */
+void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ int parent_irq)
+{
+ if (!gpiochip->irq_nested) {
+ chip_err(gpiochip, "tried to nest a chained gpiochip\n");
+ return;
+ }
+ gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq,
+ NULL);
+}
+EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip);
+
+/**
* gpiochip_irq_map() - maps an IRQ into a GPIO irqchip
* @d: the irqdomain used by this irqchip
* @irq: the global irq number used by this GPIO irqchip irq
@@ -1583,8 +1621,8 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
*/
irq_set_lockdep_class(irq, chip->lock_key);
irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler);
- /* Chips that can sleep need nested thread handlers */
- if (chip->can_sleep && !chip->irq_not_threaded)
+ /* Chips that use nested thread handlers have them marked */
+ if (chip->irq_nested)
irq_set_nested_thread(irq, 1);
irq_set_noprobe(irq);
@@ -1602,7 +1640,7 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq)
{
struct gpio_chip *chip = d->host_data;
- if (chip->can_sleep)
+ if (chip->irq_nested)
irq_set_nested_thread(irq, 0);
irq_set_chip_and_handler(irq, NULL, NULL);
irq_set_chip_data(irq, NULL);
@@ -1657,9 +1695,9 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
acpi_gpiochip_free_interrupts(gpiochip);
- if (gpiochip->irq_parent) {
- irq_set_chained_handler(gpiochip->irq_parent, NULL);
- irq_set_handler_data(gpiochip->irq_parent, NULL);
+ if (gpiochip->irq_chained_parent) {
+ irq_set_chained_handler(gpiochip->irq_chained_parent, NULL);
+ irq_set_handler_data(gpiochip->irq_chained_parent, NULL);
}
/* Remove all IRQ mappings and delete the domain */
@@ -1683,7 +1721,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
}
/**
- * gpiochip_irqchip_add() - adds an irqchip to a gpiochip
+ * gpiochip_irqchip_add_key() - adds an irqchip to a gpiochip
* @gpiochip: the gpiochip to add the irqchip to
* @irqchip: the irqchip to add to the gpiochip
* @first_irq: if not dynamically assigned, the base (first) IRQ to
@@ -1691,6 +1729,8 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
* @handler: the irq handler to use (often a predefined irq core function)
* @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE
* to have the core avoid setting up any default type in the hardware.
+ * @nested: whether this is a nested irqchip calling handle_nested_irq()
+ * in its IRQ handler
* @lock_key: lockdep class
*
* This function closely associates a certain irqchip with a certain
@@ -1707,12 +1747,13 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
* the pins on the gpiochip can generate a unique IRQ. Everything else
* need to be open coded.
*/
-int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
- struct irq_chip *irqchip,
- unsigned int first_irq,
- irq_flow_handler_t handler,
- unsigned int type,
- struct lock_class_key *lock_key)
+int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
+ struct irq_chip *irqchip,
+ unsigned int first_irq,
+ irq_flow_handler_t handler,
+ unsigned int type,
+ bool nested,
+ struct lock_class_key *lock_key)
{
struct device_node *of_node;
bool irq_base_set = false;
@@ -1726,6 +1767,7 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
pr_err("missing gpiochip .dev parent pointer\n");
return -EINVAL;
}
+ gpiochip->irq_nested = nested;
of_node = gpiochip->parent->of_node;
#ifdef CONFIG_OF_GPIO
/*
@@ -1796,7 +1838,7 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip,
return 0;
}
-EXPORT_SYMBOL_GPL(_gpiochip_irqchip_add);
+EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key);
#else /* CONFIG_GPIOLIB_IRQCHIP */
@@ -1832,6 +1874,19 @@ void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset)
}
EXPORT_SYMBOL_GPL(gpiochip_generic_free);
+/**
+ * gpiochip_generic_config() - apply configuration for a pin
+ * @chip: the gpiochip owning the GPIO
+ * @offset: the offset of the GPIO to apply the configuration
+ * @config: the configuration to be applied
+ */
+int gpiochip_generic_config(struct gpio_chip *chip, unsigned offset,
+ unsigned long config)
+{
+ return pinctrl_gpio_set_config(chip->gpiodev->base + offset, config);
+}
+EXPORT_SYMBOL_GPL(gpiochip_generic_config);
+
#ifdef CONFIG_PINCTRL
/**
@@ -2220,9 +2275,18 @@ int gpiod_direction_input(struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(gpiod_direction_input);
+static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset,
+ enum pin_config_param mode)
+{
+ unsigned long config = { PIN_CONF_PACKED(mode, 0) };
+
+ return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
+}
+
static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
{
struct gpio_chip *gc = desc->gdev->chip;
+ int val = !!value;
int ret;
/* GPIOs used for IRQs shall not be set as output */
@@ -2235,32 +2299,25 @@ static int _gpiod_direction_output_raw(struct gpio_desc *desc, int value)
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
/* First see if we can enable open drain in hardware */
- if (gc->set_single_ended) {
- ret = gc->set_single_ended(gc, gpio_chip_hwgpio(desc),
- LINE_MODE_OPEN_DRAIN);
- if (!ret)
- goto set_output_value;
- }
+ ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_DRIVE_OPEN_DRAIN);
+ if (!ret)
+ goto set_output_value;
/* Emulate open drain by not actively driving the line high */
- if (value)
+ if (val)
return gpiod_direction_input(desc);
}
else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
- if (gc->set_single_ended) {
- ret = gc->set_single_ended(gc, gpio_chip_hwgpio(desc),
- LINE_MODE_OPEN_SOURCE);
- if (!ret)
- goto set_output_value;
- }
+ ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_DRIVE_OPEN_SOURCE);
+ if (!ret)
+ goto set_output_value;
/* Emulate open source by not actively driving the line low */
- if (!value)
+ if (!val)
return gpiod_direction_input(desc);
} else {
- /* Make sure to disable open drain/source hardware, if any */
- if (gc->set_single_ended)
- gc->set_single_ended(gc,
- gpio_chip_hwgpio(desc),
- LINE_MODE_PUSH_PULL);
+ gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_DRIVE_PUSH_PULL);
}
set_output_value:
@@ -2271,10 +2328,10 @@ set_output_value:
return -EIO;
}
- ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), value);
+ ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val);
if (!ret)
set_bit(FLAG_IS_OUT, &desc->flags);
- trace_gpio_value(desc_to_gpio(desc), 0, value);
+ trace_gpio_value(desc_to_gpio(desc), 0, val);
trace_gpio_direction(desc_to_gpio(desc), 0, ret);
return ret;
}
@@ -2314,6 +2371,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
VALIDATE_DESC(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
+ else
+ value = !!value;
return _gpiod_direction_output_raw(desc, value);
}
EXPORT_SYMBOL_GPL(gpiod_direction_output);
@@ -2329,17 +2388,19 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output);
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
{
struct gpio_chip *chip;
+ unsigned long config;
VALIDATE_DESC(desc);
chip = desc->gdev->chip;
- if (!chip->set || !chip->set_debounce) {
+ if (!chip->set || !chip->set_config) {
gpiod_dbg(desc,
- "%s: missing set() or set_debounce() operations\n",
+ "%s: missing set() or set_config() operations\n",
__func__);
return -ENOTSUPP;
}
- return chip->set_debounce(chip, gpio_chip_hwgpio(desc), debounce);
+ config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce);
+ return chip->set_config(chip, gpio_chip_hwgpio(desc), config);
}
EXPORT_SYMBOL_GPL(gpiod_set_debounce);
@@ -2523,18 +2584,11 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
if (chip->set_multiple) {
chip->set_multiple(chip, mask, bits);
} else {
- int i;
- for (i = 0; i < chip->ngpio; i++) {
- if (mask[BIT_WORD(i)] == 0) {
- /* no more set bits in this mask word;
- * skip ahead to the next word */
- i = (BIT_WORD(i) + 1) * BITS_PER_LONG - 1;
- continue;
- }
- /* set outputs if the corresponding mask bit is set */
- if (__test_and_clear_bit(i, mask))
- chip->set(chip, i, test_bit(i, bits));
- }
+ unsigned int i;
+
+ /* set outputs if the corresponding mask bit is set */
+ for_each_set_bit(i, mask, chip->ngpio)
+ chip->set(chip, i, test_bit(i, bits));
}
}
@@ -2758,6 +2812,15 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
}
set_bit(FLAG_USED_AS_IRQ, &desc->flags);
+
+ /*
+ * If the consumer has not set up a label (such as when the
+ * IRQ is referenced from .to_irq()) we set up a label here
+ * so it is clear this is used as an interrupt.
+ */
+ if (!desc->label)
+ desc_set_label(desc, "interrupt");
+
return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
@@ -2772,10 +2835,17 @@ EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq);
*/
void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
{
- if (offset >= chip->ngpio)
+ struct gpio_desc *desc;
+
+ desc = gpiochip_get_desc(chip, offset);
+ if (IS_ERR(desc))
return;
- clear_bit(FLAG_USED_AS_IRQ, &chip->gpiodev->descs[offset].flags);
+ clear_bit(FLAG_USED_AS_IRQ, &desc->flags);
+
+ /* If we only had this marking, erase it */
+ if (desc->label && !strcmp(desc->label, "interrupt"))
+ desc_set_label(desc, NULL);
}
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
@@ -3170,7 +3240,7 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
/* Process flags */
if (dflags & GPIOD_FLAGS_BIT_DIR_OUT)
status = gpiod_direction_output(desc,
- dflags & GPIOD_FLAGS_BIT_DIR_VAL);
+ !!(dflags & GPIOD_FLAGS_BIT_DIR_VAL));
else
status = gpiod_direction_input(desc);
@@ -3246,6 +3316,8 @@ EXPORT_SYMBOL_GPL(gpiod_get_index);
* 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 in the consumer
+ * @dflags: GPIO initialization flags
*
* This function can be used for drivers that get their configuration
* from firmware.
@@ -3254,12 +3326,18 @@ EXPORT_SYMBOL_GPL(gpiod_get_index);
* underlying firmware interface and then makes sure that the GPIO
* descriptor is requested before it is returned to the caller.
*
+ * On successfull request the GPIO pin is configured in accordance with
+ * provided @dflags.
+ *
* In case of error an ERR_PTR() is returned.
*/
struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
- const char *propname)
+ const char *propname, int index,
+ enum gpiod_flags dflags,
+ const char *label)
{
struct gpio_desc *desc = ERR_PTR(-ENODEV);
+ unsigned long lflags = 0;
bool active_low = false;
bool single_ended = false;
int ret;
@@ -3270,8 +3348,8 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
if (is_of_node(fwnode)) {
enum of_gpio_flags flags;
- desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname, 0,
- &flags);
+ desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname,
+ index, &flags);
if (!IS_ERR(desc)) {
active_low = flags & OF_GPIO_ACTIVE_LOW;
single_ended = flags & OF_GPIO_SINGLE_ENDED;
@@ -3279,7 +3357,7 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
} else if (is_acpi_node(fwnode)) {
struct acpi_gpio_info info;
- desc = acpi_node_get_gpiod(fwnode, propname, 0, &info);
+ desc = acpi_node_get_gpiod(fwnode, propname, index, &info);
if (!IS_ERR(desc))
active_low = info.polarity == GPIO_ACTIVE_LOW;
}
@@ -3287,18 +3365,24 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
if (IS_ERR(desc))
return desc;
- ret = gpiod_request(desc, NULL);
+ ret = gpiod_request(desc, label);
if (ret)
return ERR_PTR(ret);
if (active_low)
- set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+ lflags |= GPIO_ACTIVE_LOW;
if (single_ended) {
if (active_low)
- set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+ lflags |= GPIO_OPEN_DRAIN;
else
- set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+ lflags |= GPIO_OPEN_SOURCE;
+ }
+
+ ret = gpiod_configure_flags(desc, propname, lflags, dflags);
+ if (ret < 0) {
+ gpiod_put(desc);
+ return ERR_PTR(ret);
}
return desc;