diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 20:43:39 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-29 20:43:39 +0300 |
commit | fa889d85551e0bd962fdefe1cc113f9ba1d04a36 (patch) | |
tree | 4f74147dc17e9fa39d06053a949c5491ccf2bace /drivers/gpio/gpiolib.c | |
parent | b3a6082223369203d7e7db7e81253ac761377644 (diff) | |
parent | 0282c72d30d32913d641dc81f3f38607ace98802 (diff) | |
download | linux-fa889d85551e0bd962fdefe1cc113f9ba1d04a36.tar.xz |
Merge tag 'gpio-v5.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"This is the bulk of GPIO changes for the v5.6 kernel cycle.
This is a pretty calm cycle so far, nothing special going on really.
Some more changes will come in from the irqchip and pin control trees.
I also deleted an orphan include file for FMC that was dangling since
subsystem was removed.
Core changes:
- Document the usecases for the kernelspace vs userspace handling of
GPIOs.
- Handle MSI (message signalled interrupts) properly in the core
hierarchical irqdomain code.
- Fix a rare race condition while initializing the descriptor array.
New drivers:
- Xylon LogiCVC GPIO driver.
- WDC934x GPIO controller driver.
Driver improvements:
- Implemented suspend/resume in the Tegra driver.
- MPC8xx edge detection fixup.
- Properly convert ThunderX to use hierarchical irqdomain with
GPIOLIB_IRQCHIP on top of the revert of the previous buggy
switchover. This time it works (hopefully).
Misc:
- Drop a FMC remnant file <linux/ipmi-fru.h>
- A slew of fixes"
* tag 'gpio-v5.6-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (48 commits)
MAINTAINERS: Replace Tien Hock Loh as Altera PIO maintainer
gpiolib: hold gpio devices lock until ->descs array is initialised
gpio: aspeed-sgpio: fixed typos
gpio: mvebu: clear irq in edge cause register before unmask edge irq
gpiolib: Lower verbosity when allocating hierarchy irq
gpiolib: Remove duplicated function gpio_do_set_config()
gpio: Fix the no return statement warning
gpio: wcd934x: Add support to wcd934x gpio controller
gpiolib: remove set but not used variable 'config'
gpio: vx855: fixed a typo
gpio: mockup: sort headers alphabetically
gpio: mockup: update the license tag
gpio: Remove the unused flags
gpiolib: Set lockdep class for hierarchical irq domains
gpio: thunderx: Switch to GPIOLIB_IRQCHIP
gpiolib: Add the support for the msi parent domain
gpiolib: Add support for the irqdomain which doesn't use irq_fwspec as arg
gpio: Add use guidance documentation
dt-bindings: gpio: wcd934x: Add bindings for gpio
gpio: altera: change to platform_get_irq_optional to avoid false-positive error
...
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 182 |
1 files changed, 83 insertions, 99 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index bcfbfded9ba3..9d9ba56cb73d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -140,7 +140,7 @@ EXPORT_SYMBOL_GPL(gpio_to_desc); * in the given chip for the specified hardware number. */ struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, - u16 hwnum) + unsigned int hwnum) { struct gpio_device *gdev = chip->gpiodev; @@ -232,15 +232,15 @@ int gpiod_get_direction(struct gpio_desc *desc) return -ENOTSUPP; ret = chip->get_direction(chip, offset); - if (ret > 0) { - /* GPIOF_DIR_IN, or other positive */ + if (ret < 0) + return ret; + + /* GPIOF_DIR_IN or other positive, otherwise GPIOF_DIR_OUT */ + if (ret > 0) ret = 1; - clear_bit(FLAG_IS_OUT, &desc->flags); - } - if (ret == 0) { - /* GPIOF_DIR_OUT */ - set_bit(FLAG_IS_OUT, &desc->flags); - } + + assign_bit(FLAG_IS_OUT, &desc->flags, !ret); + return ret; } EXPORT_SYMBOL_GPL(gpiod_get_direction); @@ -492,15 +492,6 @@ static int linehandle_validate_flags(u32 flags) return 0; } -static void linehandle_configure_flag(unsigned long *flagsp, - u32 bit, bool active) -{ - if (active) - set_bit(bit, flagsp); - else - clear_bit(bit, flagsp); -} - static long linehandle_set_config(struct linehandle_state *lh, void __user *ip) { @@ -522,22 +513,22 @@ static long linehandle_set_config(struct linehandle_state *lh, desc = lh->descs[i]; flagsp = &desc->flags; - linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW, + assign_bit(FLAG_ACTIVE_LOW, flagsp, lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); - linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN, + assign_bit(FLAG_OPEN_DRAIN, flagsp, lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); - linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE, + assign_bit(FLAG_OPEN_SOURCE, flagsp, lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); - linehandle_configure_flag(flagsp, FLAG_PULL_UP, + assign_bit(FLAG_PULL_UP, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); - linehandle_configure_flag(flagsp, FLAG_PULL_DOWN, + assign_bit(FLAG_PULL_DOWN, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); - linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE, + assign_bit(FLAG_BIAS_DISABLE, flagsp, lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); /* @@ -686,14 +677,13 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < handlereq.lines; i++) { u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc; + struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); - if (offset >= gdev->ngpio) { - ret = -EINVAL; + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); goto out_free_descs; } - desc = &gdev->descs[offset]; ret = gpiod_request(desc, lh->label); if (ret) goto out_free_descs; @@ -1018,8 +1008,9 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) lflags = eventreq.handleflags; eflags = eventreq.eventflags; - if (offset >= gdev->ngpio) - return -EINVAL; + desc = gpiochip_get_desc(gdev->chip, offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); /* Return an error if a unknown flag is set */ if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || @@ -1057,7 +1048,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) } } - desc = &gdev->descs[offset]; ret = gpiod_request(desc, le->label); if (ret) goto out_free_label; @@ -1184,10 +1174,11 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; - if (lineinfo.line_offset >= gdev->ngpio) - return -EINVAL; - desc = &gdev->descs[lineinfo.line_offset]; + desc = gpiochip_get_desc(chip, lineinfo.line_offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + if (desc->name) { strncpy(lineinfo.name, desc->name, sizeof(lineinfo.name)); @@ -1427,7 +1418,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, ret = gdev->id; goto err_free_gdev; } - dev_set_name(&gdev->dev, "gpiochip%d", gdev->id); + dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); device_initialize(&gdev->dev); dev_set_drvdata(&gdev->dev, gdev); if (chip->parent && chip->parent->driver) @@ -1452,7 +1443,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, if (chip->ngpio > FASTPATH_NGPIO) chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n", - chip->ngpio, FASTPATH_NGPIO); + chip->ngpio, FASTPATH_NGPIO); gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { @@ -1495,11 +1486,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, goto err_free_label; } - spin_unlock_irqrestore(&gpio_lock, flags); - for (i = 0; i < chip->ngpio; i++) gdev->descs[i].gdev = gdev; + spin_unlock_irqrestore(&gpio_lock, flags); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -1524,15 +1515,11 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, struct gpio_desc *desc = &gdev->descs[i]; if (chip->get_direction && gpiochip_line_is_valid(chip, i)) { - if (!chip->get_direction(chip, i)) - set_bit(FLAG_IS_OUT, &desc->flags); - else - clear_bit(FLAG_IS_OUT, &desc->flags); + assign_bit(FLAG_IS_OUT, + &desc->flags, !chip->get_direction(chip, i)); } else { - if (!chip->direction_input) - set_bit(FLAG_IS_OUT, &desc->flags); - else - clear_bit(FLAG_IS_OUT, &desc->flags); + assign_bit(FLAG_IS_OUT, + &desc->flags, !chip->direction_input); } } @@ -2003,7 +1990,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; struct irq_fwspec *fwspec = data; - struct irq_fwspec parent_fwspec; + void *parent_arg; unsigned int parent_hwirq; unsigned int parent_type; struct gpio_irq_chip *girq = &gc->irq; @@ -2019,7 +2006,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, if (ret) return ret; - chip_info(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); + chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq); ret = girq->child_to_parent_hwirq(gc, hwirq, type, &parent_hwirq, &parent_type); @@ -2027,7 +2014,7 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, chip_err(gc, "can't look up hwirq %lu\n", hwirq); return ret; } - chip_info(gc, "found parent hwirq %u\n", parent_hwirq); + chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq); /* * We set handle_bad_irq because the .set_type() should @@ -2042,23 +2029,27 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d, NULL, NULL); irq_set_probe(irq); - /* - * Create a IRQ fwspec to send up to the parent irqdomain: - * specify the hwirq we address on the parent and tie it - * all together up the chain. - */ - parent_fwspec.fwnode = d->parent->fwnode; /* This parent only handles asserted level IRQs */ - girq->populate_parent_fwspec(gc, &parent_fwspec, parent_hwirq, - parent_type); - chip_info(gc, "alloc_irqs_parent for %d parent hwirq %d\n", + parent_arg = girq->populate_parent_alloc_arg(gc, parent_hwirq, parent_type); + if (!parent_arg) + return -ENOMEM; + + chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n", irq, parent_hwirq); - ret = irq_domain_alloc_irqs_parent(d, irq, 1, &parent_fwspec); + irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key); + ret = irq_domain_alloc_irqs_parent(d, irq, 1, parent_arg); + /* + * If the parent irqdomain is msi, the interrupts have already + * been allocated, so the EEXIST is good. + */ + if (irq_domain_is_msi(d->parent) && (ret == -EEXIST)) + ret = 0; if (ret) chip_err(gc, "failed to allocate parent hwirq %d for hwirq %lu\n", parent_hwirq, hwirq); + kfree(parent_arg); return ret; } @@ -2095,8 +2086,8 @@ static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc) if (!gc->irq.child_offset_to_irq) gc->irq.child_offset_to_irq = gpiochip_child_offset_to_irq_noop; - if (!gc->irq.populate_parent_fwspec) - gc->irq.populate_parent_fwspec = + if (!gc->irq.populate_parent_alloc_arg) + gc->irq.populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops); @@ -2122,27 +2113,43 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) return !!gc->irq.parent_domain; } -void gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 2; fwspec->param[0] = parent_hwirq; fwspec->param[1] = parent_type; + + return fwspec; } EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_twocell); -void gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, - struct irq_fwspec *fwspec, +void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip, unsigned int parent_hwirq, unsigned int parent_type) { + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = chip->irq.parent_domain->fwnode; fwspec->param_count = 4; fwspec->param[0] = 0; fwspec->param[1] = parent_hwirq; fwspec->param[2] = 0; fwspec->param[3] = parent_type; + + return fwspec; } EXPORT_SYMBOL_GPL(gpiochip_populate_parent_fwspec_fourcell); @@ -2998,7 +3005,8 @@ EXPORT_SYMBOL_GPL(gpiochip_is_requested); * A pointer to the GPIO descriptor, or an ERR_PTR()-encoded negative error * code on failure. */ -struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, u16 hwnum, +struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip, + unsigned int hwnum, const char *label, enum gpio_lookup_flags lflags, enum gpiod_flags dflags) @@ -3050,25 +3058,13 @@ 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, +static int gpio_set_config(struct gpio_chip *gc, unsigned int offset, enum pin_config_param mode) { - unsigned long config; - unsigned arg; - - switch (mode) { - case PIN_CONFIG_BIAS_DISABLE: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_UP: - arg = 1; - break; - - default: - arg = 0; - } + if (!gc->set_config) + return -ENOTSUPP; - config = PIN_CONF_PACKED(mode, arg); - return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; + return gc->set_config(gc, offset, mode); } static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc) @@ -3302,15 +3298,9 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) VALIDATE_DESC(desc); chip = desc->gdev->chip; - if (!chip->set || !chip->set_config) { - gpiod_dbg(desc, - "%s: missing set() or set_config() operations\n", - __func__); - return -ENOTSUPP; - } config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); - return chip->set_config(chip, gpio_chip_hwgpio(desc), config); + return gpio_set_config(chip, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); @@ -3334,10 +3324,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for * persistence state. */ - if (transitory) - set_bit(FLAG_TRANSITORY, &desc->flags); - else - clear_bit(FLAG_TRANSITORY, &desc->flags); + assign_bit(FLAG_TRANSITORY, &desc->flags, transitory); /* If the driver supports it, set the persistence state now */ chip = desc->gdev->chip; @@ -3347,7 +3334,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, !transitory); gpio = gpio_chip_hwgpio(desc); - rc = chip->set_config(chip, gpio, packed); + rc = gpio_set_config(chip, gpio, packed); if (rc == -ENOTSUPP) { dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", gpio); @@ -3804,10 +3791,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, gpio_set_open_source_value_commit(desc, value); } else { __set_bit(hwgpio, mask); - if (value) - __set_bit(hwgpio, bits); - else - __clear_bit(hwgpio, bits); + __assign_bit(hwgpio, bits, value); count++; } i++; @@ -5124,7 +5108,7 @@ static int __init gpiolib_dev_init(void) return ret; } - ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpiochip"); + ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME); if (ret < 0) { pr_err("gpiolib: failed to allocate char dev region\n"); bus_unregister(&gpio_bus_type); |