aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2019-05-10 11:43:46 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2019-05-10 11:43:46 -0700
commit2a267e7c41aa88215de2b542de797d03d16ecdfd (patch)
treeb949270835e304c8b60a40cde1b2c2e19c13b33a /drivers/gpio/gpiolib.c
parentInput: libps2 - mark expected switch fall-through (diff)
parentLinux 5.1 (diff)
downloadlinux-dev-2a267e7c41aa88215de2b542de797d03d16ecdfd.tar.xz
linux-dev-2a267e7c41aa88215de2b542de797d03d16ecdfd.zip
Merge tag 'v5.1' into next
Sync up with mainline to bring in the latest APIs.
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c182
1 files changed, 127 insertions, 55 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a2cbb474901c..bca3e7740ef6 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -828,7 +828,14 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
/* Do not leak kernel stack to userspace */
memset(&ge, 0, sizeof(ge));
- ge.timestamp = le->timestamp;
+ /*
+ * We may be running from a nested threaded interrupt in which case
+ * we didn't get the timestamp from lineevent_irq_handler().
+ */
+ if (!le->timestamp)
+ ge.timestamp = ktime_get_real_ns();
+ else
+ ge.timestamp = le->timestamp;
if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
&& le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
@@ -1372,7 +1379,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
status = gpiochip_add_irqchip(chip, lock_key, request_key);
if (status)
- goto err_remove_chip;
+ goto err_free_gpiochip_mask;
status = of_gpiochip_add(chip);
if (status)
@@ -1380,7 +1387,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
status = gpiochip_init_valid_mask(chip);
if (status)
- goto err_remove_chip;
+ goto err_remove_of_chip;
for (i = 0; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
@@ -1408,14 +1415,18 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
if (gpiolib_initialized) {
status = gpiochip_setup_dev(gdev);
if (status)
- goto err_remove_chip;
+ goto err_remove_acpi_chip;
}
return 0;
-err_remove_chip:
+err_remove_acpi_chip:
acpi_gpiochip_remove(chip);
+err_remove_of_chip:
gpiochip_free_hogs(chip);
of_gpiochip_remove(chip);
+err_remove_chip:
+ gpiochip_irqchip_remove(chip);
+err_free_gpiochip_mask:
gpiochip_free_valid_mask(chip);
err_remove_irqchip_mask:
gpiochip_irqchip_free_valid_mask(chip);
@@ -1512,19 +1523,6 @@ static void devm_gpio_chip_release(struct device *dev, void *res)
gpiochip_remove(chip);
}
-static int devm_gpio_chip_match(struct device *dev, void *res, void *data)
-
-{
- struct gpio_chip **r = res;
-
- if (!r || !*r) {
- WARN_ON(!r || !*r);
- return 0;
- }
-
- return *r == data;
-}
-
/**
* devm_gpiochip_add_data() - Resource manager gpiochip_add_data()
* @dev: pointer to the device that gpio_chip belongs to.
@@ -1565,23 +1563,6 @@ int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
/**
- * devm_gpiochip_remove() - Resource manager of gpiochip_remove()
- * @dev: device for which which resource was allocated
- * @chip: the chip to remove
- *
- * A gpio_chip with any GPIOs still requested may not be removed.
- */
-void devm_gpiochip_remove(struct device *dev, struct gpio_chip *chip)
-{
- int ret;
-
- ret = devres_release(dev, devm_gpio_chip_release,
- devm_gpio_chip_match, chip);
- WARN_ON(ret);
-}
-EXPORT_SYMBOL_GPL(devm_gpiochip_remove);
-
-/**
* gpiochip_find() - iterator for locating a specific gpio_chip
* @data: data to pass to match function
* @match: Callback function to check gpio_chip
@@ -1805,6 +1786,43 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
+/**
+ * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ
+ * @domain: The IRQ domain used by this IRQ chip
+ * @data: Outermost irq_data associated with the IRQ
+ * @reserve: If set, only reserve an interrupt vector instead of assigning one
+ *
+ * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be
+ * used as the activate function for the &struct irq_domain_ops. The host_data
+ * for the IRQ domain must be the &struct gpio_chip.
+ */
+int gpiochip_irq_domain_activate(struct irq_domain *domain,
+ struct irq_data *data, bool reserve)
+{
+ struct gpio_chip *chip = domain->host_data;
+
+ return gpiochip_lock_as_irq(chip, data->hwirq);
+}
+EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate);
+
+/**
+ * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ
+ * @domain: The IRQ domain used by this IRQ chip
+ * @data: Outermost irq_data associated with the IRQ
+ *
+ * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to
+ * be used as the deactivate function for the &struct irq_domain_ops. The
+ * host_data for the IRQ domain must be the &struct gpio_chip.
+ */
+void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
+ struct irq_data *data)
+{
+ struct gpio_chip *chip = domain->host_data;
+
+ return gpiochip_unlock_as_irq(chip, data->hwirq);
+}
+EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate);
+
static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
{
if (!gpiochip_irqchip_irq_valid(chip, offset))
@@ -2299,6 +2317,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
unsigned long flags;
unsigned offset;
+ if (label) {
+ label = kstrdup_const(label, GFP_KERNEL);
+ if (!label)
+ return -ENOMEM;
+ }
+
spin_lock_irqsave(&gpio_lock, flags);
/* NOTE: gpio_request() can be called in early boot,
@@ -2309,6 +2333,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
desc_set_label(desc, label ? : "?");
status = 0;
} else {
+ kfree_const(label);
status = -EBUSY;
goto done;
}
@@ -2325,6 +2350,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
if (status < 0) {
desc_set_label(desc, NULL);
+ kfree_const(label);
clear_bit(FLAG_REQUESTED, &desc->flags);
goto done;
}
@@ -2420,6 +2446,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
chip->free(chip, gpio_chip_hwgpio(desc));
spin_lock_irqsave(&gpio_lock, flags);
}
+ kfree_const(desc->label);
desc_set_label(desc, NULL);
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
clear_bit(FLAG_REQUESTED, &desc->flags);
@@ -2476,6 +2503,7 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* @chip: GPIO chip
* @hwnum: hardware number of the GPIO for which to request the descriptor
* @label: label for the GPIO
+ * @flags: flags for this GPIO or 0 if default
*
* Function allows GPIO chip drivers to request and use their own GPIO
* descriptors via gpiolib API. Difference to gpiod_request() is that this
@@ -2488,7 +2516,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested);
* code on failure.
*/
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
- const char *label)
+ const char *label,
+ enum gpiod_flags flags)
{
struct gpio_desc *desc = gpiochip_get_desc(chip, hwnum);
int err;
@@ -2502,6 +2531,13 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum,
if (err < 0)
return ERR_PTR(err);
+ err = gpiod_configure_flags(desc, label, 0, flags);
+ if (err) {
+ chip_err(chip, "setup of own GPIO %s failed\n", label);
+ gpiod_free_commit(desc);
+ return ERR_PTR(err);
+ }
+
return desc;
}
EXPORT_SYMBOL_GPL(gpiochip_request_own_desc);
@@ -2530,6 +2566,14 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc);
* rely on gpio_request() having been called beforehand.
*/
+static int gpio_set_config(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;
+}
+
/**
* gpiod_direction_input - set the GPIO direction to input
* @desc: GPIO to set to input
@@ -2577,20 +2621,19 @@ int gpiod_direction_input(struct gpio_desc *desc)
if (status == 0)
clear_bit(FLAG_IS_OUT, &desc->flags);
+ if (test_bit(FLAG_PULL_UP, &desc->flags))
+ gpio_set_config(chip, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_BIAS_PULL_UP);
+ else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
+ gpio_set_config(chip, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_BIAS_PULL_DOWN);
+
trace_gpio_direction(desc_to_gpio(desc), 1, status);
return status;
}
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_commit(struct gpio_desc *desc, int value)
{
struct gpio_chip *gc = desc->gdev->chip;
@@ -2687,8 +2730,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
gc = desc->gdev->chip;
if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) {
/* First see if we can enable open drain in hardware */
- ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_OPEN_DRAIN);
+ ret = gpio_set_config(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 */
@@ -2696,16 +2739,16 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
return gpiod_direction_input(desc);
}
else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) {
- ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_OPEN_SOURCE);
+ ret = gpio_set_config(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)
return gpiod_direction_input(desc);
} else {
- gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc),
- PIN_CONFIG_DRIVE_PUSH_PULL);
+ gpio_set_config(gc, gpio_chip_hwgpio(desc),
+ PIN_CONFIG_DRIVE_PUSH_PULL);
}
set_output_value:
@@ -3375,11 +3418,19 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep);
* @desc: gpio to set the consumer name on
* @name: the new consumer name
*/
-void gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
+int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
{
- VALIDATE_DESC_VOID(desc);
- /* Just overwrite whatever the previous name was */
- desc->label = name;
+ VALIDATE_DESC(desc);
+ if (name) {
+ name = kstrdup_const(name, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+ }
+
+ kfree_const(desc->label);
+ desc_set_label(desc, name);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
@@ -4054,6 +4105,17 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
if (lflags & GPIO_OPEN_SOURCE)
set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+ if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) {
+ gpiod_err(desc,
+ "both pull-up and pull-down enabled, invalid configuration\n");
+ return -EINVAL;
+ }
+
+ if (lflags & GPIO_PULL_UP)
+ set_bit(FLAG_PULL_UP, &desc->flags);
+ else if (lflags & GPIO_PULL_DOWN)
+ set_bit(FLAG_PULL_DOWN, &desc->flags);
+
status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY));
if (status < 0)
return status;
@@ -4205,6 +4267,8 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
transitory = flags & OF_GPIO_TRANSITORY;
ret = gpiod_request(desc, label);
+ if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
+ return desc;
if (ret)
return ERR_PTR(ret);
@@ -4346,7 +4410,15 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
chip = gpiod_to_chip(desc);
hwnum = gpio_chip_hwgpio(desc);
- local_desc = gpiochip_request_own_desc(chip, hwnum, name);
+ /*
+ * FIXME: not very elegant that we call gpiod_configure_flags()
+ * twice here (once inside gpiochip_request_own_desc() and
+ * again here), but the gpiochip_request_own_desc() is external
+ * and cannot really pass the lflags so this is the lesser evil
+ * at the moment. Pass zero as dflags on this first call so we
+ * don't screw anything up.
+ */
+ local_desc = gpiochip_request_own_desc(chip, hwnum, name, 0);
if (IS_ERR(local_desc)) {
status = PTR_ERR(local_desc);
pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n",