diff options
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-aspeed-sgpio.c (renamed from drivers/gpio/sgpio-aspeed.c) | 0 | ||||
-rw-r--r-- | drivers/gpio/gpio-max77620.c | 231 | ||||
-rw-r--r-- | drivers/gpio/gpio-mpc8xxx.c | 30 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra186.c | 97 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-devres.c | 33 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 60 |
9 files changed, 298 insertions, 170 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 38e096e6925f..088a8a0f8add 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -120,6 +120,14 @@ config GPIO_ASPEED help Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers. +config GPIO_ASPEED_SGPIO + bool "Aspeed SGPIO support" + depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y here to support Aspeed AST2500 SGPIO functionality. + config GPIO_ATH79 tristate "Atheros AR71XX/AR724X/AR913X GPIO support" default y if ATH79 @@ -531,6 +539,7 @@ config GPIO_TEGRA186 depends on ARCH_TEGRA_186_SOC || COMPILE_TEST depends on OF_GPIO select GPIOLIB_IRQCHIP + select IRQ_DOMAIN_HIERARCHY help Say yes here to support GPIO pins on NVIDIA Tegra186 SoCs. @@ -1320,7 +1329,7 @@ config GPIO_BT8XX The card needs to be physically altered for using it as a GPIO card. For more information on how to build a GPIO card from a BT8xx TV card, see the documentation file at - Documentation/driver-api/bt8xxgpio.rst + Documentation/driver-api/gpio/bt8xxgpio.rst If unsure, say N. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d2fd19c15bae..e4599f90f702 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o +obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/gpio-aspeed-sgpio.c index 7e99860ca447..7e99860ca447 100644 --- a/drivers/gpio/sgpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 47d05e357e61..3ece63ae6f93 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -18,109 +18,115 @@ struct max77620_gpio { struct gpio_chip gpio_chip; struct regmap *rmap; struct device *dev; + struct mutex buslock; /* irq_bus_lock */ + unsigned int irq_type[8]; + bool irq_enabled[8]; }; -static const struct regmap_irq max77620_gpio_irqs[] = { - [0] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 0, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [1] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 1, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [2] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 2, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [3] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 3, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [4] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 4, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [5] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 5, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [6] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 6, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [7] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 7, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, -}; +static irqreturn_t max77620_gpio_irqhandler(int irq, void *data) +{ + struct max77620_gpio *gpio = data; + unsigned int value, offset; + unsigned long pending; + int err; + + err = regmap_read(gpio->rmap, MAX77620_REG_IRQ_LVL2_GPIO, &value); + if (err < 0) { + dev_err(gpio->dev, "REG_IRQ_LVL2_GPIO read failed: %d\n", err); + return IRQ_NONE; + } + + pending = value; + + for_each_set_bit(offset, &pending, 8) { + unsigned int virq; + + virq = irq_find_mapping(gpio->gpio_chip.irq.domain, offset); + handle_nested_irq(virq); + } + + return IRQ_HANDLED; +} + +static void max77620_gpio_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + + gpio->irq_enabled[data->hwirq] = false; +} -static const struct regmap_irq_chip max77620_gpio_irq_chip = { - .name = "max77620-gpio", - .irqs = max77620_gpio_irqs, - .num_irqs = ARRAY_SIZE(max77620_gpio_irqs), - .num_regs = 1, - .num_type_reg = 8, - .irq_reg_stride = 1, - .type_reg_stride = 1, - .status_base = MAX77620_REG_IRQ_LVL2_GPIO, - .type_base = MAX77620_REG_GPIO0, +static void max77620_gpio_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + + gpio->irq_enabled[data->hwirq] = true; +} + +static int max77620_gpio_set_irq_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + unsigned int irq_type; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + irq_type = MAX77620_CNFG_GPIO_INT_RISING; + break; + + case IRQ_TYPE_EDGE_FALLING: + irq_type = MAX77620_CNFG_GPIO_INT_FALLING; + break; + + case IRQ_TYPE_EDGE_BOTH: + irq_type = MAX77620_CNFG_GPIO_INT_RISING | + MAX77620_CNFG_GPIO_INT_FALLING; + break; + + default: + return -EINVAL; + } + + gpio->irq_type[data->hwirq] = irq_type; + + return 0; +} + +static void max77620_gpio_bus_lock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + + mutex_lock(&gpio->buslock); +} + +static void max77620_gpio_bus_sync_unlock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + unsigned int value, offset = data->hwirq; + int err; + + value = gpio->irq_enabled[offset] ? gpio->irq_type[offset] : 0; + + err = regmap_update_bits(gpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_INT_MASK, value); + if (err < 0) + dev_err(chip->parent, "failed to update interrupt mask: %d\n", + err); + + mutex_unlock(&gpio->buslock); +} + +static struct irq_chip max77620_gpio_irqchip = { + .name = "max77620-gpio", + .irq_mask = max77620_gpio_irq_mask, + .irq_unmask = max77620_gpio_irq_unmask, + .irq_set_type = max77620_gpio_set_irq_type, + .irq_bus_lock = max77620_gpio_bus_lock, + .irq_bus_sync_unlock = max77620_gpio_bus_sync_unlock, + .flags = IRQCHIP_MASK_ON_SUSPEND, }; static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) @@ -254,14 +260,6 @@ static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset, return -ENOTSUPP; } -static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) -{ - struct max77620_gpio *mgpio = gpiochip_get_data(gc); - struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent); - - return regmap_irq_get_virq(chip->gpio_irq_data, offset); -} - static int max77620_gpio_probe(struct platform_device *pdev) { struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -287,7 +285,6 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; mgpio->gpio_chip.set = max77620_gpio_set; mgpio->gpio_chip.set_config = max77620_gpio_set_config; - mgpio->gpio_chip.to_irq = max77620_gpio_to_irq; mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; mgpio->gpio_chip.can_sleep = 1; mgpio->gpio_chip.base = -1; @@ -303,15 +300,21 @@ static int max77620_gpio_probe(struct platform_device *pdev) return ret; } - ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, gpio_irq, - IRQF_ONESHOT, -1, - &max77620_gpio_irq_chip, - &chip->gpio_irq_data); + mutex_init(&mgpio->buslock); + + gpiochip_irqchip_add_nested(&mgpio->gpio_chip, &max77620_gpio_irqchip, + 0, handle_edge_irq, IRQ_TYPE_NONE); + + ret = request_threaded_irq(gpio_irq, NULL, max77620_gpio_irqhandler, + IRQF_ONESHOT, "max77620-gpio", mgpio); if (ret < 0) { - dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret); + dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); return ret; } + gpiochip_set_nested_irqchip(&mgpio->gpio_chip, &max77620_gpio_irqchip, + gpio_irq); + return 0; } diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 16a47de29c94..58ff37219fce 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -22,6 +22,7 @@ #include <linux/irq.h> #include <linux/gpio/driver.h> #include <linux/bitops.h> +#include <linux/interrupt.h> #define MPC8XXX_GPIO_PINS 32 @@ -127,20 +128,19 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) return -ENXIO; } -static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc) +static irqreturn_t mpc8xxx_gpio_irq_cascade(int irq, void *data) { - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = data; struct gpio_chip *gc = &mpc8xxx_gc->gc; - unsigned int mask; + unsigned long mask; + int i; mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_IER) & gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR); - if (mask) - generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, - 32 - ffs(mask))); - if (chip->irq_eoi) - chip->irq_eoi(&desc->irq_data); + for_each_set_bit(i, &mask, 32) + generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, 31 - i)); + + return IRQ_HANDLED; } static void mpc8xxx_irq_unmask(struct irq_data *d) @@ -409,8 +409,16 @@ static int mpc8xxx_probe(struct platform_device *pdev) if (devtype->gpio_dir_in_init) devtype->gpio_dir_in_init(gc); - irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, - mpc8xxx_gpio_irq_cascade, mpc8xxx_gc); + ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn, + mpc8xxx_gpio_irq_cascade, + IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade", + mpc8xxx_gc); + if (ret) { + dev_err(&pdev->dev, "%s: failed to devm_request_irq(%d), ret = %d\n", + np->full_name, mpc8xxx_gc->irqn, ret); + goto err; + } + return 0; err: iounmap(mpc8xxx_gc->regs); diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index a9058fda187e..8a2a69178925 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -53,6 +53,7 @@ struct tegra_gpio_soc { const struct tegra_gpio_port *ports; unsigned int num_ports; const char *name; + unsigned int instance; }; struct tegra_gpio { @@ -327,7 +328,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) else irq_set_handler_locked(data, handle_edge_irq); - return 0; + return irq_chip_set_type_parent(data, type); } static void tegra186_gpio_irq(struct irq_desc *desc) @@ -367,39 +368,80 @@ skip: chained_irq_exit(chip, desc); } -static int tegra186_gpio_irq_domain_xlate(struct irq_domain *domain, - struct device_node *np, - const u32 *spec, unsigned int size, - unsigned long *hwirq, - unsigned int *type) +static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) { struct tegra_gpio *gpio = gpiochip_get_data(domain->host_data); unsigned int port, pin, i, offset = 0; - if (size < 2) + if (WARN_ON(gpio->gpio.of_gpio_n_cells < 2)) + return -EINVAL; + + if (WARN_ON(fwspec->param_count < gpio->gpio.of_gpio_n_cells)) return -EINVAL; - port = spec[0] / 8; - pin = spec[0] % 8; + port = fwspec->param[0] / 8; + pin = fwspec->param[0] % 8; - if (port >= gpio->soc->num_ports) { - dev_err(gpio->gpio.parent, "invalid port number: %u\n", port); + if (port >= gpio->soc->num_ports) return -EINVAL; - } for (i = 0; i < port; i++) offset += gpio->soc->ports[i].pins; - *type = spec[1] & IRQ_TYPE_SENSE_MASK; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; *hwirq = offset + pin; return 0; } -static const struct irq_domain_ops tegra186_gpio_irq_domain_ops = { - .map = gpiochip_irq_map, - .unmap = gpiochip_irq_unmap, - .xlate = tegra186_gpio_irq_domain_xlate, +static void tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, + struct irq_fwspec *fwspec, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + + fwspec->param_count = 3; + fwspec->param[0] = gpio->soc->instance; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = parent_type; +} + +static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip, + unsigned int hwirq, + unsigned int type, + unsigned int *parent_hwirq, + unsigned int *parent_type) +{ + *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq); + *parent_type = type; + + return 0; +} + +static unsigned int tegra186_gpio_child_offset_to_irq(struct gpio_chip *chip, + unsigned int offset) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + unsigned int i; + + for (i = 0; i < gpio->soc->num_ports; i++) { + if (offset < gpio->soc->ports[i].pins) + break; + + offset -= gpio->soc->ports[i].pins; + } + + return offset + i * 8; +} + +static const struct of_device_id tegra186_pmc_of_match[] = { + { .compatible = "nvidia,tegra186-pmc" }, + { .compatible = "nvidia,tegra194-pmc" }, + { /* sentinel */ } }; static int tegra186_gpio_probe(struct platform_device *pdev) @@ -407,6 +449,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) unsigned int i, j, offset; struct gpio_irq_chip *irq; struct tegra_gpio *gpio; + struct device_node *np; struct resource *res; char **names; int err; @@ -487,10 +530,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->intc.irq_mask = tegra186_irq_mask; gpio->intc.irq_unmask = tegra186_irq_unmask; gpio->intc.irq_set_type = tegra186_irq_set_type; + gpio->intc.irq_set_wake = irq_chip_set_wake_parent; irq = &gpio->gpio.irq; irq->chip = &gpio->intc; - irq->domain_ops = &tegra186_gpio_irq_domain_ops; + irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); + irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; + irq->populate_parent_fwspec = tegra186_gpio_populate_parent_fwspec; + irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq; + irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate; irq->handler = handle_simple_irq; irq->default_type = IRQ_TYPE_NONE; irq->parent_handler = tegra186_gpio_irq; @@ -498,6 +546,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->num_parents = gpio->num_irq; irq->parents = gpio->irq; + np = of_find_matching_node(NULL, tegra186_pmc_of_match); + if (np) { + irq->parent_domain = irq_find_host(np); + of_node_put(np); + + if (!irq->parent_domain) + return -EPROBE_DEFER; + } + irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, sizeof(*irq->map), GFP_KERNEL); if (!irq->map) @@ -564,6 +621,7 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .num_ports = ARRAY_SIZE(tegra186_main_ports), .ports = tegra186_main_ports, .name = "tegra186-gpio", + .instance = 0, }; #define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \ @@ -589,6 +647,7 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .num_ports = ARRAY_SIZE(tegra186_aon_ports), .ports = tegra186_aon_ports, .name = "tegra186-gpio-aon", + .instance = 1, }; #define TEGRA194_MAIN_GPIO_PORT(port, base, count, controller) \ @@ -634,6 +693,7 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .num_ports = ARRAY_SIZE(tegra194_main_ports), .ports = tegra194_main_ports, .name = "tegra194-gpio", + .instance = 0, }; #define TEGRA194_AON_GPIO_PORT(port, base, count, controller) \ @@ -656,6 +716,7 @@ static const struct tegra_gpio_soc tegra194_aon_soc = { .num_ports = ARRAY_SIZE(tegra194_aon_ports), .ports = tegra194_aon_ports, .name = "tegra194-gpio-aon", + .instance = 1, }; static const struct of_device_id tegra186_gpio_of_match[] = { diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 98e3c20d9730..4421be09b960 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -185,12 +185,11 @@ struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, EXPORT_SYMBOL_GPL(devm_gpiod_get_from_of_node); /** - * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a - * device's child node + * devm_fwnode_gpiod_get_index - get a GPIO descriptor from a given node * @dev: GPIO consumer + * @fwnode: firmware node containing GPIO reference * @con_id: function within the GPIO consumer * @index: index of the GPIO to obtain in the consumer - * @child: firmware node (child of @dev) * @flags: GPIO initialization flags * @label: label to attach to the requested GPIO * @@ -200,35 +199,21 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get_from_of_node); * On successful request the GPIO pin is configured in accordance with * provided @flags. */ -struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev, - const char *con_id, int index, - struct fwnode_handle *child, - enum gpiod_flags flags, - const char *label) +struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, + struct fwnode_handle *fwnode, + const char *con_id, int index, + enum gpiod_flags flags, + const char *label) { - char prop_name[32]; /* 32 is max size of property name */ struct gpio_desc **dr; struct gpio_desc *desc; - unsigned int i; dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), GFP_KERNEL); if (!dr) return ERR_PTR(-ENOMEM); - for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id) - snprintf(prop_name, sizeof(prop_name), "%s-%s", - con_id, gpio_suffixes[i]); - else - snprintf(prop_name, sizeof(prop_name), "%s", - gpio_suffixes[i]); - - desc = fwnode_get_named_gpiod(child, prop_name, index, flags, - label); - if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) - break; - } + desc = fwnode_gpiod_get_index(fwnode, con_id, index, flags, label); if (IS_ERR(desc)) { devres_free(dr); return desc; @@ -239,7 +224,7 @@ struct gpio_desc *devm_fwnode_get_index_gpiod_from_child(struct device *dev, return desc; } -EXPORT_SYMBOL_GPL(devm_fwnode_get_index_gpiod_from_child); +EXPORT_SYMBOL_GPL(devm_fwnode_gpiod_get_index); /** * devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional() diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 576c7419bbc1..99adc801a067 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -84,8 +84,9 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, /** * of_gpio_need_valid_mask() - figure out if the OF GPIO driver needs * to set the .valid_mask - * @dev: the device for the GPIO provider - * @return: true if the valid mask needs to be set + * @gc: the target gpio_chip + * + * Return: true if the valid mask needs to be set */ bool of_gpio_need_valid_mask(const struct gpio_chip *gc) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e8964493c571..ec92ed3f3055 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -421,6 +421,8 @@ struct linehandle_state { (GPIOHANDLE_REQUEST_INPUT | \ GPIOHANDLE_REQUEST_OUTPUT | \ GPIOHANDLE_REQUEST_ACTIVE_LOW | \ + GPIOHANDLE_REQUEST_PULL_UP | \ + GPIOHANDLE_REQUEST_PULL_DOWN | \ GPIOHANDLE_REQUEST_OPEN_DRAIN | \ GPIOHANDLE_REQUEST_OPEN_SOURCE) @@ -592,6 +594,10 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); ret = gpiod_set_transitory(desc, false); if (ret < 0) @@ -1091,6 +1097,10 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE | GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_PULL_DOWN; + if (test_bit(FLAG_PULL_UP, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_PULL_UP; if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; @@ -2764,6 +2774,8 @@ static bool gpiod_free_commit(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_PULL_UP, &desc->flags); + clear_bit(FLAG_PULL_DOWN, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = true; } @@ -4319,6 +4331,54 @@ static int platform_gpio_count(struct device *dev, const char *con_id) } /** + * fwnode_gpiod_get_index - obtain a GPIO from firmware node + * @fwnode: handle of the firmware node + * @con_id: function within the GPIO consumer + * @index: index of the GPIO to obtain for the consumer + * @flags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * This function can be used for drivers that get their configuration + * from opaque firmware. + * + * The function properly finds the corresponding GPIO using whatever is the + * underlying firmware interface and then makes sure that the GPIO + * descriptor is requested before it is returned to the caller. + * + * Returns: + * On successful request the GPIO pin is configured in accordance with + * provided @flags. + * + * In case of error an ERR_PTR() is returned. + */ +struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, + const char *con_id, int index, + enum gpiod_flags flags, + const char *label) +{ + struct gpio_desc *desc; + char prop_name[32]; /* 32 is max size of property name */ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-%s", + con_id, gpio_suffixes[i]); + else + snprintf(prop_name, sizeof(prop_name), "%s", + gpio_suffixes[i]); + + desc = fwnode_get_named_gpiod(fwnode, prop_name, index, flags, + label); + if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) + break; + } + + return desc; +} +EXPORT_SYMBOL_GPL(fwnode_gpiod_get_index); + +/** * 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 |