diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-18 08:22:10 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-18 08:22:10 -0400 |
commit | 510965dd4a0a59504ba38455f77339ea8b4c6a70 (patch) | |
tree | 36492629ac68de94457482562660154f28e9e039 /drivers/gpio/gpiolib.c | |
parent | 40d7839879b4584f91522d841afb22ed401cf40f (diff) | |
parent | 03daa6f82f2b634019fe8261698f6af3c133497f (diff) | |
download | lwn-510965dd4a0a59504ba38455f77339ea8b4c6a70.tar.gz lwn-510965dd4a0a59504ba38455f77339ea8b4c6a70.zip |
Merge tag 'gpio-v4.1-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 v4.1 development cycle:
- A new GPIO hogging mechanism has been added. This can be used on
boards that want to drive some GPIO line high, low, or set it as
input on boot and then never touch it again. For some embedded
systems this is bliss and simplifies things to a great extent.
- Some API cleanup and closure: gpiod_get_array() and
gpiod_put_array() has been added to get and put GPIOs in bulk as
was possible with the non-descriptor API.
- Encapsulate cross-calls to the pin control subsystem in
<linux/gpio/driver.h>. Now this should be the only header any GPIO
driver needs to include or something is wrong. Cleanups
restricting drivers to this include are welcomed if tested.
- Sort the GPIO Kconfig and split it into submenus, as it was
becoming and unstructured, illogical and unnavigatable mess. I
hope this is easier to follow. Menus that require a certain
subsystem like I2C can now be hidden nicely for example, still
working on others.
- New drivers:
- New driver for the Altera Soft GPIO.
- The F7188x driver now handles the F71869 and F71869A variants.
- The MIPS Loongson driver has been moved to drivers/gpio for
consolidation and cleanup.
- Cleanups:
- The MAX732x is converted to use the GPIOLIB_IRQCHIP
infrastructure.
- The PCF857x is converted to use the GPIOLIB_IRQCHIP
infrastructure.
- Radical cleanup of the OMAP driver.
- Misc:
- Enable the DWAPB GPIO for all architectures. This is a "hard
IP" block from Synopsys which has started to turn up in so
diverse architectures as X86 Quark, ARC and a slew of ARM
systems. So even though it's not an expander, it's generic
enough to be available for all.
- We add a mock GPIO on Crystalcove PMIC after a long discussion
with Daniel Vetter et al, tracing back to the shootout at the
kernel summit where DRM drivers and sub-componentization was
discussed. In this case a mock GPIO is assumed to be the best
compromise gaining some reuse of infrastructure without making
DRM drivers overly complex at the same time. Let's see"
* tag 'gpio-v4.1-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (62 commits)
Revert "gpio: sch: use uapi/linux/pci_ids.h directly"
gpio: dwapb: remove dependencies
gpio: dwapb: enable for ARC
gpio: removing kfree remove functionality
gpio: mvebu: Fix mask/unmask managment per irq chip type
gpio: split GPIO drivers in submenus
gpio: move MFD GPIO drivers under their own comment
gpio: move BCM Kona Kconfig option
gpio: arrange SPI Kconfig symbols alphabetically
gpio: arrange PCI GPIO controllers alphabetically
gpio: arrange I2C Kconfig symbols alphabetically
gpio: arrange Kconfig symbols alphabetically
gpio: ich: Implement get_direction function
gpio: use (!foo) instead of (foo == NULL)
gpio: arizona: drop owner assignment from platform_drivers
gpio: max7300: remove 'ret' variable
gpio: use devm_kzalloc
gpio: sch: use uapi/linux/pci_ids.h directly
gpio: x-gene: fix devm_ioremap_resource() check
gpio: loongson: Add Loongson-3A/3B GPIO driver support
...
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r-- | drivers/gpio/gpiolib.c | 299 |
1 files changed, 265 insertions, 34 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1ca9295b2c10..59eaa23767d8 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -315,6 +315,7 @@ EXPORT_SYMBOL_GPL(gpiochip_add); /* Forward-declaration */ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); +static void gpiochip_free_hogs(struct gpio_chip *chip); /** * gpiochip_remove() - unregister a gpio_chip @@ -333,6 +334,7 @@ void gpiochip_remove(struct gpio_chip *chip) acpi_gpiochip_remove(chip); gpiochip_remove_pin_ranges(chip); + gpiochip_free_hogs(chip); of_gpiochip_remove(chip); spin_lock_irqsave(&gpio_lock, flags); @@ -866,6 +868,7 @@ static bool __gpiod_free(struct gpio_desc *desc) clear_bit(FLAG_REQUESTED, &desc->flags); clear_bit(FLAG_OPEN_DRAIN, &desc->flags); clear_bit(FLAG_OPEN_SOURCE, &desc->flags); + clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = true; } @@ -1659,19 +1662,18 @@ static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags) { - static const char * const suffixes[] = { "gpios", "gpio" }; char prop_name[32]; /* 32 is max size of property name */ enum of_gpio_flags of_flags; struct gpio_desc *desc; unsigned int i; - for (i = 0; i < ARRAY_SIZE(suffixes); i++) { + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id, - suffixes[i]); + gpio_suffixes[i]); else snprintf(prop_name, sizeof(prop_name), "%s", - suffixes[i]); + gpio_suffixes[i]); desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, &of_flags); @@ -1692,7 +1694,6 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags) { - static const char * const suffixes[] = { "gpios", "gpio" }; struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_gpio_info info; struct gpio_desc *desc; @@ -1700,13 +1701,13 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, int i; /* Try first from _DSD */ - for (i = 0; i < ARRAY_SIZE(suffixes); i++) { + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id && strcmp(con_id, "gpios")) { snprintf(propname, sizeof(propname), "%s-%s", - con_id, suffixes[i]); + con_id, gpio_suffixes[i]); } else { snprintf(propname, sizeof(propname), "%s", - suffixes[i]); + gpio_suffixes[i]); } desc = acpi_get_gpiod_by_index(adev, propname, idx, &info); @@ -1805,6 +1806,70 @@ static struct gpio_desc *gpiod_find(struct device *dev, const char *con_id, return desc; } +static int dt_gpio_count(struct device *dev, const char *con_id) +{ + int ret; + char propname[32]; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id) + snprintf(propname, sizeof(propname), "%s-%s", + con_id, gpio_suffixes[i]); + else + snprintf(propname, sizeof(propname), "%s", + gpio_suffixes[i]); + + ret = of_gpio_named_count(dev->of_node, propname); + if (ret >= 0) + break; + } + return ret; +} + +static int platform_gpio_count(struct device *dev, const char *con_id) +{ + struct gpiod_lookup_table *table; + struct gpiod_lookup *p; + unsigned int count = 0; + + table = gpiod_find_lookup_table(dev); + if (!table) + return -ENOENT; + + for (p = &table->table[0]; p->chip_label; p++) { + if ((con_id && p->con_id && !strcmp(con_id, p->con_id)) || + (!con_id && !p->con_id)) + count++; + } + if (!count) + return -ENOENT; + + return count; +} + +/** + * gpiod_count - return the number of GPIOs associated with a device / function + * or -ENOENT if no GPIO has been assigned to the requested function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + */ +int gpiod_count(struct device *dev, const char *con_id) +{ + int count = -ENOENT; + + if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) + count = dt_gpio_count(dev, con_id); + else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) + count = acpi_gpio_count(dev, con_id); + + if (count < 0) + count = platform_gpio_count(dev, con_id); + + return count; +} +EXPORT_SYMBOL_GPL(gpiod_count); + /** * gpiod_get - obtain a GPIO for a given GPIO function * @dev: GPIO consumer, can be NULL for system-global GPIOs @@ -1840,6 +1905,47 @@ struct gpio_desc *__must_check __gpiod_get_optional(struct device *dev, } EXPORT_SYMBOL_GPL(__gpiod_get_optional); + +/** + * gpiod_configure_flags - helper function to configure a given GPIO + * @desc: gpio whose value will be assigned + * @con_id: function within the GPIO consumer + * @lflags: gpio_lookup_flags - returned from of_find_gpio() or + * of_get_gpio_hog() + * @dflags: gpiod_flags - optional GPIO initialization flags + * + * Return 0 on success, -ENOENT if no GPIO has been assigned to the + * requested function and/or index, or another IS_ERR() code if an error + * occurred while trying to acquire the GPIO. + */ +static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, + unsigned long lflags, enum gpiod_flags dflags) +{ + int status; + + if (lflags & GPIO_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIO_OPEN_DRAIN) + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + if (lflags & GPIO_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); + + /* No particular flag request, return here... */ + if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { + pr_debug("no flags found for %s\n", con_id); + return 0; + } + + /* Process flags */ + if (dflags & GPIOD_FLAGS_BIT_DIR_OUT) + status = gpiod_direction_output(desc, + dflags & GPIOD_FLAGS_BIT_DIR_VAL); + else + status = gpiod_direction_input(desc); + + return status; +} + /** * gpiod_get_index - obtain a GPIO from a multi-index GPIO function * @dev: GPIO consumer, can be NULL for system-global GPIOs @@ -1865,13 +1971,15 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev, dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); - /* Using device tree? */ - if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) { - dev_dbg(dev, "using device tree for GPIO lookup\n"); - desc = of_find_gpio(dev, con_id, idx, &lookupflags); - } else if (IS_ENABLED(CONFIG_ACPI) && dev && ACPI_HANDLE(dev)) { - dev_dbg(dev, "using ACPI for GPIO lookup\n"); - desc = acpi_find_gpio(dev, con_id, idx, &lookupflags); + if (dev) { + /* Using device tree? */ + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + dev_dbg(dev, "using device tree for GPIO lookup\n"); + desc = of_find_gpio(dev, con_id, idx, &lookupflags); + } else if (ACPI_COMPANION(dev)) { + dev_dbg(dev, "using ACPI for GPIO lookup\n"); + desc = acpi_find_gpio(dev, con_id, idx, &lookupflags); + } } /* @@ -1889,28 +1997,10 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev, } status = gpiod_request(desc, con_id); - if (status < 0) return ERR_PTR(status); - if (lookupflags & GPIO_ACTIVE_LOW) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lookupflags & GPIO_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &desc->flags); - if (lookupflags & GPIO_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &desc->flags); - - /* No particular flag request, return here... */ - if (!(flags & GPIOD_FLAGS_BIT_DIR_SET)) - return desc; - - /* Process flags */ - if (flags & GPIOD_FLAGS_BIT_DIR_OUT) - status = gpiod_direction_output(desc, - flags & GPIOD_FLAGS_BIT_DIR_VAL); - else - status = gpiod_direction_input(desc); - + status = gpiod_configure_flags(desc, con_id, lookupflags, flags); if (status < 0) { dev_dbg(dev, "setup of GPIO %s failed\n", con_id); gpiod_put(desc); @@ -2006,6 +2096,132 @@ struct gpio_desc *__must_check __gpiod_get_index_optional(struct device *dev, EXPORT_SYMBOL_GPL(__gpiod_get_index_optional); /** + * gpiod_hog - Hog the specified GPIO desc given the provided flags + * @desc: gpio whose value will be assigned + * @name: gpio line name + * @lflags: gpio_lookup_flags - returned from of_find_gpio() or + * of_get_gpio_hog() + * @dflags: gpiod_flags - optional GPIO initialization flags + */ +int gpiod_hog(struct gpio_desc *desc, const char *name, + unsigned long lflags, enum gpiod_flags dflags) +{ + struct gpio_chip *chip; + struct gpio_desc *local_desc; + int hwnum; + int status; + + chip = gpiod_to_chip(desc); + hwnum = gpio_chip_hwgpio(desc); + + local_desc = gpiochip_request_own_desc(chip, hwnum, name); + if (IS_ERR(local_desc)) { + pr_debug("requesting own GPIO %s failed\n", name); + return PTR_ERR(local_desc); + } + + status = gpiod_configure_flags(desc, name, lflags, dflags); + if (status < 0) { + pr_debug("setup of GPIO %s failed\n", name); + gpiochip_free_own_desc(desc); + return status; + } + + /* Mark GPIO as hogged so it can be identified and removed later */ + set_bit(FLAG_IS_HOGGED, &desc->flags); + + pr_info("GPIO line %d (%s) hogged as %s%s\n", + desc_to_gpio(desc), name, + (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", + (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? + (dflags&GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low":""); + + return 0; +} + +/** + * gpiochip_free_hogs - Scan gpio-controller chip and release GPIO hog + * @chip: gpio chip to act on + * + * This is only used by of_gpiochip_remove to free hogged gpios + */ +static void gpiochip_free_hogs(struct gpio_chip *chip) +{ + int id; + + for (id = 0; id < chip->ngpio; id++) { + if (test_bit(FLAG_IS_HOGGED, &chip->desc[id].flags)) + gpiochip_free_own_desc(&chip->desc[id]); + } +} + +/** + * gpiod_get_array - obtain multiple GPIOs from a multi-index GPIO function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * This function acquires all the GPIOs defined under a given function. + * + * Return a struct gpio_descs containing an array of descriptors, -ENOENT if + * no GPIO has been assigned to the requested function, or another IS_ERR() + * code if an error occurred while trying to acquire the GPIOs. + */ +struct gpio_descs *__must_check gpiod_get_array(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_desc *desc; + struct gpio_descs *descs; + int count; + + count = gpiod_count(dev, con_id); + if (count < 0) + return ERR_PTR(count); + + descs = kzalloc(sizeof(*descs) + sizeof(descs->desc[0]) * count, + GFP_KERNEL); + if (!descs) + return ERR_PTR(-ENOMEM); + + for (descs->ndescs = 0; descs->ndescs < count; ) { + desc = gpiod_get_index(dev, con_id, descs->ndescs, flags); + if (IS_ERR(desc)) { + gpiod_put_array(descs); + return ERR_CAST(desc); + } + descs->desc[descs->ndescs] = desc; + descs->ndescs++; + } + return descs; +} +EXPORT_SYMBOL_GPL(gpiod_get_array); + +/** + * gpiod_get_array_optional - obtain multiple GPIOs from a multi-index GPIO + * function + * @dev: GPIO consumer, can be NULL for system-global GPIOs + * @con_id: function within the GPIO consumer + * @flags: optional GPIO initialization flags + * + * This is equivalent to gpiod_get_array(), except that when no GPIO was + * assigned to the requested function it will return NULL. + */ +struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, + const char *con_id, + enum gpiod_flags flags) +{ + struct gpio_descs *descs; + + descs = gpiod_get_array(dev, con_id, flags); + if (IS_ERR(descs) && (PTR_ERR(descs) == -ENOENT)) + return NULL; + + return descs; +} +EXPORT_SYMBOL_GPL(gpiod_get_array_optional); + +/** * gpiod_put - dispose of a GPIO descriptor * @desc: GPIO descriptor to dispose of * @@ -2017,6 +2233,21 @@ void gpiod_put(struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_put); +/** + * gpiod_put_array - dispose of multiple GPIO descriptors + * @descs: struct gpio_descs containing an array of descriptors + */ +void gpiod_put_array(struct gpio_descs *descs) +{ + unsigned int i; + + for (i = 0; i < descs->ndescs; i++) + gpiod_put(descs->desc[i]); + + kfree(descs); +} +EXPORT_SYMBOL_GPL(gpiod_put_array); + #ifdef CONFIG_DEBUG_FS static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) |