diff --git a/Documentation/devicetree/bindings/gpio/airoha,en7523-gpio.yaml b/Documentation/devicetree/bindings/gpio/airoha,en7523-gpio.yaml new file mode 100644 index 000000000000..7c41d8e814cd --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/airoha,en7523-gpio.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/airoha,en7523-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Airoha EN7523 GPIO controller + +maintainers: + - John Crispin + +description: | + Airoha's GPIO controller on their ARM EN7523 SoCs consists of two banks of 32 + GPIOs. + +properties: + $nodename: + pattern: "^gpio@[0-9a-f]+$" + + compatible: + items: + - const: airoha,en7523-gpio + + reg: + description: | + The first tuple points to the input register. + The second and third tuple point to the direction registers + The fourth tuple points to the output register + maxItems: 4 + + "#gpio-cells": + const: 2 + + gpio-controller: true + +required: + - compatible + - reg + - "#gpio-cells" + - gpio-controller + +additionalProperties: false + +examples: + - | + gpio0: gpio@1fbf0200 { + compatible = "airoha,en7523-gpio"; + reg = <0x1fbf0204 0x4>, + <0x1fbf0200 0x4>, + <0x1fbf0220 0x4>, + <0x1fbf0214 0x4>; + gpio-controller; + #gpio-cells = <2>; + }; + + gpio1: gpio@1fbf0270 { + compatible = "airoha,en7523-gpio"; + reg = <0x1fbf0270 0x4>, + <0x1fbf0260 0x4>, + <0x1fbf0264 0x4>, + <0x1fbf0278 0x4>; + gpio-controller; + #gpio-cells = <2>; + }; + +... diff --git a/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.txt b/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.txt deleted file mode 100644 index d04236558619..000000000000 --- a/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.txt +++ /dev/null @@ -1,27 +0,0 @@ -Faraday Technology FTGPIO010 GPIO Controller - -Required properties: - -- compatible : Should be one of - "cortina,gemini-gpio", "faraday,ftgpio010" - "moxa,moxart-gpio", "faraday,ftgpio010" - "faraday,ftgpio010" -- reg : Should contain registers location and length -- interrupts : Should contain the interrupt line for the GPIO block -- gpio-controller : marks this as a GPIO controller -- #gpio-cells : Should be 2, see gpio/gpio.txt -- interrupt-controller : marks this as an interrupt controller -- #interrupt-cells : a standard two-cell interrupt flag, see - interrupt-controller/interrupts.txt - -Example: - -gpio@4d000000 { - compatible = "cortina,gemini-gpio", "faraday,ftgpio010"; - reg = <0x4d000000 0x100>; - interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; -}; diff --git a/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.yaml b/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.yaml new file mode 100644 index 000000000000..640da5b9b0cc --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/faraday,ftgpio010.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/faraday,ftgpio010.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Faraday Technology FTGPIO010 GPIO Controller + +maintainers: + - Linus Walleij + +properties: + compatible: + oneOf: + - items: + - const: cortina,gemini-gpio + - const: faraday,ftgpio010 + - items: + - const: moxa,moxart-gpio + - const: faraday,ftgpio010 + - const: faraday,ftgpio010 + + reg: + maxItems: 1 + + resets: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + description: Should contain the interrupt line for the GPIO block + + gpio-controller: true + "#gpio-cells": + const: 2 + + interrupt-controller: true + "#interrupt-cells": + const: 2 + +required: + - compatible + - reg + - interrupts + - "#gpio-cells" + - interrupt-controller + - "#interrupt-cells" + +additionalProperties: false + +examples: + - | + #include + gpio@4d000000 { + compatible = "cortina,gemini-gpio", "faraday,ftgpio010"; + reg = <0x4d000000 0x100>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml b/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml index e1359391d3a4..d2c39dba56ad 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-vf610.yaml @@ -25,7 +25,9 @@ properties: - const: fsl,imx7ulp-gpio - const: fsl,vf610-gpio - items: - - const: fsl,imx8ulp-gpio + - enum: + - fsl,imx93-gpio + - fsl,imx8ulp-gpio - const: fsl,imx7ulp-gpio reg: diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index a8895d339bfe..5663e71b751f 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -213,7 +213,7 @@ Example of two SOC GPIO banks defined as gpio-controller nodes: gpio-controller; #gpio-cells = <2>; - line_b { + line_b-hog { gpio-hog; gpios = <6 0>; output-low; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1c211b4c63be..b2d313fe3fc5 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -247,6 +247,16 @@ config GPIO_EM help Say yes here to support GPIO on Renesas Emma Mobile SoCs. +config GPIO_EN7523 + tristate "Airoha GPIO support" + depends on ARCH_AIROHA + default ARCH_AIROHA + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y or M here to support the GPIO controller block on the + Airoha EN7523 SoC. It supports two banks of 32 GPIOs. + config GPIO_EP93XX def_bool y depends on ARCH_EP93XX @@ -1380,7 +1390,7 @@ config GPIO_TPS65912 This driver supports TPS65912 GPIO chip. config GPIO_TPS68470 - bool "TPS68470 GPIO" + tristate "TPS68470 GPIO" depends on INTEL_SKL_INT3472 help Select this option to enable GPIO driver for the TPS68470 @@ -1390,10 +1400,6 @@ config GPIO_TPS68470 input or output as appropriate, the sensor related GPIOs are "output only" GPIOs. - This driver config is bool, as the GPIO functionality - of the TPS68470 must be available before dependent - drivers are loaded. - config GPIO_TQMX86 tristate "TQ-Systems QTMX86 GPIO" depends on MFD_TQMX86 || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index edbaa3cb343c..1a14e248bdbd 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o obj-$(CONFIG_GPIO_EM) += gpio-em.o +obj-$(CONFIG_GPIO_EN7523) += gpio-en7523.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o diff --git a/drivers/gpio/gpio-altera-a10sr.c b/drivers/gpio/gpio-altera-a10sr.c index 6af51feda06f..be1ed7ee5225 100644 --- a/drivers/gpio/gpio-altera-a10sr.c +++ b/drivers/gpio/gpio-altera-a10sr.c @@ -10,6 +10,7 @@ #include #include #include +#include /** * struct altr_a10sr_gpio - Altera Max5 GPIO device private data structure @@ -88,7 +89,7 @@ static int altr_a10sr_gpio_probe(struct platform_device *pdev) gpio->gp = altr_a10sr_gc; gpio->gp.parent = pdev->dev.parent; - gpio->gp.of_node = pdev->dev.of_node; + gpio->gp.fwnode = dev_fwnode(&pdev->dev); return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio); } diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 5a909f3c79e8..b55c74a5e064 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -370,7 +370,14 @@ static int crystalcove_gpio_probe(struct platform_device *pdev) return retval; } - return devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); + retval = devm_gpiochip_add_data(&pdev->dev, &cg->chip, cg); + if (retval) + return retval; + + /* Distuingish IRQ domain from others sharing (MFD) the same fwnode */ + irq_domain_update_bus_token(cg->chip.irq.domain, DOMAIN_BUS_WIRED); + + return 0; } static struct platform_driver crystalcove_gpio_driver = { diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c new file mode 100644 index 000000000000..f836a8db4c1d --- /dev/null +++ b/drivers/gpio/gpio-en7523.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#define AIROHA_GPIO_MAX 32 + +/** + * airoha_gpio_ctrl - Airoha GPIO driver data + * @gc: Associated gpio_chip instance. + * @data: The data register. + * @dir0: The direction register for the lower 16 pins. + * @dir1: The direction register for the higher 16 pins. + * @output: The output enable register. + */ +struct airoha_gpio_ctrl { + struct gpio_chip gc; + void __iomem *data; + void __iomem *dir[2]; + void __iomem *output; +}; + +static struct airoha_gpio_ctrl *gc_to_ctrl(struct gpio_chip *gc) +{ + return container_of(gc, struct airoha_gpio_ctrl, gc); +} + +static int airoha_dir_set(struct gpio_chip *gc, unsigned int gpio, + int val, int out) +{ + struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); + u32 dir = ioread32(ctrl->dir[gpio / 16]); + u32 output = ioread32(ctrl->output); + u32 mask = BIT((gpio % 16) * 2); + + if (out) { + dir |= mask; + output |= BIT(gpio); + } else { + dir &= ~mask; + output &= ~BIT(gpio); + } + + iowrite32(dir, ctrl->dir[gpio / 16]); + + if (out) + gc->set(gc, gpio, val); + + iowrite32(output, ctrl->output); + + return 0; +} + +static int airoha_dir_out(struct gpio_chip *gc, unsigned int gpio, + int val) +{ + return airoha_dir_set(gc, gpio, val, 1); +} + +static int airoha_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + return airoha_dir_set(gc, gpio, 0, 0); +} + +static int airoha_get_dir(struct gpio_chip *gc, unsigned int gpio) +{ + struct airoha_gpio_ctrl *ctrl = gc_to_ctrl(gc); + u32 dir = ioread32(ctrl->dir[gpio / 16]); + u32 mask = BIT((gpio % 16) * 2); + + return (dir & mask) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int airoha_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct airoha_gpio_ctrl *ctrl; + int err; + + ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->data = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctrl->data)) + return PTR_ERR(ctrl->data); + + ctrl->dir[0] = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(ctrl->dir[0])) + return PTR_ERR(ctrl->dir[0]); + + ctrl->dir[1] = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(ctrl->dir[1])) + return PTR_ERR(ctrl->dir[1]); + + ctrl->output = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(ctrl->output)) + return PTR_ERR(ctrl->output); + + err = bgpio_init(&ctrl->gc, dev, 4, ctrl->data, NULL, + NULL, NULL, NULL, 0); + if (err) + return dev_err_probe(dev, err, "unable to init generic GPIO"); + + ctrl->gc.ngpio = AIROHA_GPIO_MAX; + ctrl->gc.owner = THIS_MODULE; + ctrl->gc.direction_output = airoha_dir_out; + ctrl->gc.direction_input = airoha_dir_in; + ctrl->gc.get_direction = airoha_get_dir; + + return devm_gpiochip_add_data(dev, &ctrl->gc, ctrl); +} + +static const struct of_device_id airoha_gpio_of_match[] = { + { .compatible = "airoha,en7523-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, airoha_gpio_of_match); + +static struct platform_driver airoha_gpio_driver = { + .driver = { + .name = "airoha-gpio", + .of_match_table = airoha_gpio_of_match, + }, + .probe = airoha_gpio_probe, +}; +module_platform_driver(airoha_gpio_driver); + +MODULE_DESCRIPTION("Airoha GPIO support"); +MODULE_AUTHOR("John Crispin "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 42c4d9d0cd50..f3d1baeacbe9 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -409,6 +409,9 @@ static int mrfld_gpio_add_pin_ranges(struct gpio_chip *chip) int retval; pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv); + if (!pinctrl_dev_name) + return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) { range = &mrfld_gpio_ranges[i]; retval = gpiochip_add_pin_range(&priv->chip, pinctrl_dev_name, diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 7f5bc10a6479..ff2d2a1f9c73 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -691,7 +691,6 @@ static int tegra_gpio_probe(struct platform_device *pdev) tgi->gc.base = 0; tgi->gc.ngpio = tgi->bank_count * 32; tgi->gc.parent = &pdev->dev; - tgi->gc.of_node = pdev->dev.of_node; tgi->ic.name = "GPIO"; tgi->ic.irq_ack = tegra_gpio_irq_ack; diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c index 423b7bc30ae8..aaddcabe9b35 100644 --- a/drivers/gpio/gpio-tps68470.c +++ b/drivers/gpio/gpio-tps68470.c @@ -154,5 +154,8 @@ static struct platform_driver tps68470_gpio_driver = { }, .probe = tps68470_gpio_probe, }; +module_platform_driver(tps68470_gpio_driver); -builtin_platform_driver(tps68470_gpio_driver) +MODULE_ALIAS("platform:tps68470-gpio"); +MODULE_DESCRIPTION("GPIO driver for TPS68470 PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index d918d2df4de2..69854fd2382a 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Digital I/O driver for Technologic Systems I2C FPGA Core * * Copyright (C) 2015, 2018 Technologic Systems * Copyright (C) 2016 Savoir-Faire Linux - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #include diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 91dcf2c6cdd8..ae1ce319cd78 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -711,14 +711,12 @@ static int of_gpiochip_scan_gpios(struct gpio_chip *chip) static void of_gpiochip_remove_hog(struct gpio_chip *chip, struct device_node *hog) { - struct gpio_desc *descs = chip->gpiodev->descs; + struct gpio_desc *desc; unsigned int i; - for (i = 0; i < chip->ngpio; i++) { - if (test_bit(FLAG_IS_HOGGED, &descs[i].flags) && - descs[i].hog == hog) - gpiochip_free_own_desc(&descs[i]); - } + for_each_gpio_desc_with_flag(i, chip, desc, FLAG_IS_HOGGED) + if (desc->hog == hog) + gpiochip_free_own_desc(desc); } static int of_gpiochip_match_node(struct gpio_chip *chip, void *data) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 44c1ad51b3fe..d44ffea038f5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -13,6 +13,7 @@ #include "gpiolib.h" #include "gpiolib-sysfs.h" +#define GPIO_IRQF_TRIGGER_NONE 0 #define GPIO_IRQF_TRIGGER_FALLING BIT(0) #define GPIO_IRQF_TRIGGER_RISING BIT(1) #define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ @@ -61,17 +62,16 @@ static ssize_t direction_show(struct device *dev, { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - ssize_t status; + int value; mutex_lock(&data->mutex); gpiod_get_direction(desc); - status = sysfs_emit(buf, "%s\n", - test_bit(FLAG_IS_OUT, &desc->flags) ? "out" : "in"); + value = !!test_bit(FLAG_IS_OUT, &desc->flags); mutex_unlock(&data->mutex); - return status; + return sysfs_emit(buf, "%s\n", value ? "out" : "in"); } static ssize_t direction_store(struct device *dev, @@ -108,12 +108,13 @@ static ssize_t value_show(struct device *dev, mutex_lock(&data->mutex); status = gpiod_get_value_cansleep(desc); - if (status >= 0) - status = sysfs_emit(buf, "%zd\n", status); mutex_unlock(&data->mutex); - return status; + if (status < 0) + return status; + + return sysfs_emit(buf, "%zd\n", status); } static ssize_t value_store(struct device *dev, @@ -121,24 +122,18 @@ static ssize_t value_store(struct device *dev, { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - ssize_t status = 0; + ssize_t status; + long value; + + status = kstrtol(buf, 0, &value); mutex_lock(&data->mutex); if (!test_bit(FLAG_IS_OUT, &desc->flags)) { status = -EPERM; - } else { - long value; - - if (size <= 2 && isdigit(buf[0]) && - (size == 1 || buf[1] == '\n')) - value = buf[0] - '0'; - else - status = kstrtol(buf, 0, &value); - if (status == 0) { - gpiod_set_value_cansleep(desc, value); - status = size; - } + } else if (status == 0) { + gpiod_set_value_cansleep(desc, value); + status = size; } mutex_unlock(&data->mutex); @@ -224,54 +219,41 @@ static void gpio_sysfs_free_irq(struct device *dev) sysfs_put(data->value_kn); } -static const struct { - const char *name; - unsigned char flags; -} trigger_types[] = { - { "none", 0 }, - { "falling", GPIO_IRQF_TRIGGER_FALLING }, - { "rising", GPIO_IRQF_TRIGGER_RISING }, - { "both", GPIO_IRQF_TRIGGER_BOTH }, +static const char * const trigger_names[] = { + [GPIO_IRQF_TRIGGER_NONE] = "none", + [GPIO_IRQF_TRIGGER_FALLING] = "falling", + [GPIO_IRQF_TRIGGER_RISING] = "rising", + [GPIO_IRQF_TRIGGER_BOTH] = "both", }; static ssize_t edge_show(struct device *dev, struct device_attribute *attr, char *buf) { struct gpiod_data *data = dev_get_drvdata(dev); - ssize_t status = 0; - int i; + int flags; mutex_lock(&data->mutex); - for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { - if (data->irq_flags == trigger_types[i].flags) - break; - } - if (i < ARRAY_SIZE(trigger_types)) - status = sysfs_emit(buf, "%s\n", trigger_types[i].name); + flags = data->irq_flags; mutex_unlock(&data->mutex); - return status; + if (flags >= ARRAY_SIZE(trigger_names)) + return 0; + + return sysfs_emit(buf, "%s\n", trigger_names[flags]); } static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct gpiod_data *data = dev_get_drvdata(dev); - unsigned char flags; ssize_t status = size; - int i; + int flags; - for (i = 0; i < ARRAY_SIZE(trigger_types); i++) { - if (sysfs_streq(trigger_types[i].name, buf)) - break; - } - - if (i == ARRAY_SIZE(trigger_types)) - return -EINVAL; - - flags = trigger_types[i].flags; + flags = sysfs_match_string(trigger_names, buf); + if (flags < 0) + return flags; mutex_lock(&data->mutex); @@ -324,16 +306,15 @@ static ssize_t active_low_show(struct device *dev, { struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; - ssize_t status; + int value; mutex_lock(&data->mutex); - status = sysfs_emit(buf, "%d\n", - !!test_bit(FLAG_ACTIVE_LOW, &desc->flags)); + value = !!test_bit(FLAG_ACTIVE_LOW, &desc->flags); mutex_unlock(&data->mutex); - return status; + return sysfs_emit(buf, "%d\n", value); } static ssize_t active_low_store(struct device *dev, @@ -343,11 +324,13 @@ static ssize_t active_low_store(struct device *dev, ssize_t status; long value; + status = kstrtol(buf, 0, &value); + if (status) + return status; + mutex_lock(&data->mutex); - status = kstrtol(buf, 0, &value); - if (status == 0) - status = gpio_sysfs_set_active_low(dev, value); + status = gpio_sysfs_set_active_low(dev, value); mutex_unlock(&data->mutex); @@ -790,11 +773,8 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) mutex_unlock(&sysfs_lock); /* unregister gpiod class devices owned by sysfs */ - for (i = 0; i < chip->ngpio; i++) { - desc = &gdev->descs[i]; - if (test_and_clear_bit(FLAG_SYSFS, &desc->flags)) - gpiod_free(desc); - } + for_each_gpio_desc_with_flag(i, chip, desc, FLAG_SYSFS) + gpiod_free(desc); } static int __init gpiolib_sysfs_init(void) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6630d92e30ad..e59884cc12a7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -262,14 +262,14 @@ static int gpiodev_add_to_list(struct gpio_device *gdev) return 0; } - next = list_entry(gpio_devices.next, struct gpio_device, list); + next = list_first_entry(&gpio_devices, struct gpio_device, list); if (gdev->base + gdev->ngpio <= next->base) { /* add before first entry */ list_add(&gdev->list, &gpio_devices); return 0; } - prev = list_entry(gpio_devices.prev, struct gpio_device, list); + prev = list_last_entry(&gpio_devices, struct gpio_device, list); if (prev->base + prev->ngpio <= gdev->base) { /* add behind last entry */ list_add_tail(&gdev->list, &gpio_devices); @@ -3951,23 +3951,21 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, * If a connection label was passed use that, else attempt to use * the device name as label */ - ret = gpiod_request(desc, con_id ? con_id : devname); + ret = gpiod_request(desc, con_id ?: devname); if (ret) { - if (ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { - /* - * This happens when there are several consumers for - * the same GPIO line: we just return here without - * further initialization. It is a bit if a hack. - * This is necessary to support fixed regulators. - * - * FIXME: Make this more sane and safe. - */ - dev_info(dev, "nonexclusive access to GPIO for %s\n", - con_id ? con_id : devname); - return desc; - } else { + if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) return ERR_PTR(ret); - } + + /* + * This happens when there are several consumers for + * the same GPIO line: we just return here without + * further initialization. It is a bit of a hack. + * This is necessary to support fixed regulators. + * + * FIXME: Make this more sane and safe. + */ + dev_info(dev, "nonexclusive access to GPIO for %s\n", con_id ?: devname); + return desc; } ret = gpiod_configure_flags(desc, con_id, lookupflags, flags); @@ -4122,12 +4120,11 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, */ static void gpiochip_free_hogs(struct gpio_chip *gc) { + struct gpio_desc *desc; int id; - for (id = 0; id < gc->ngpio; id++) { - if (test_bit(FLAG_IS_HOGGED, &gc->gpiodev->descs[id].flags)) - gpiochip_free_own_desc(&gc->gpiodev->descs[id]); - } + for_each_gpio_desc_with_flag(id, gc, desc, FLAG_IS_HOGGED) + gpiochip_free_own_desc(desc); } /** @@ -4446,7 +4443,7 @@ static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) if (list_is_last(&gdev->list, &gpio_devices)) ret = NULL; else - ret = list_entry(gdev->list.next, struct gpio_device, list); + ret = list_first_entry(&gdev->list, struct gpio_device, list); spin_unlock_irqrestore(&gpio_lock, flags); s->private = "\n"; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index c31f4626915d..06f3faa9fbef 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -37,6 +37,9 @@ * or name of the IP component in a System on Chip. * @data: per-instance data assigned by the driver * @list: links gpio_device:s together for traversal + * @notifier: used to notify subscribers about lines being requested, released + * or reconfigured + * @pin_ranges: range of pins served by the GPIO driver * * This state container holds most of the runtime variable data * for a GPIO device and can hold references and live on after the @@ -72,6 +75,20 @@ struct gpio_device { /* gpio suffixes used for ACPI and device tree lookup */ static __maybe_unused const char * const gpio_suffixes[] = { "gpios", "gpio" }; +/** + * struct gpio_array - Opaque descriptor for a structure of GPIO array attributes + * + * @desc: Array of pointers to the GPIO descriptors + * @size: Number of elements in desc + * @chip: Parent GPIO chip + * @get_mask: Get mask used in fastpath + * @set_mask: Set mask used in fastpath + * @invert_mask: Invert mask used in fastpath + * + * This structure is attached to struct gpiod_descs obtained from + * gpiod_get_array() and can be passed back to get/set array functions in order + * to activate fast processing path if applicable. + */ struct gpio_array { struct gpio_desc **desc; unsigned int size; @@ -82,6 +99,13 @@ struct gpio_array { }; struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum); + +#define for_each_gpio_desc_with_flag(i, gc, desc, flag) \ + for (i = 0, desc = gpiochip_get_desc(gc, i); \ + i < gc->ngpio; \ + i++, desc = gpiochip_get_desc(gc, i)) \ + if (!test_bit(flag, &desc->flags)) {} else + int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, @@ -96,6 +120,23 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, extern spinlock_t gpio_lock; extern struct list_head gpio_devices; + +/** + * struct gpio_desc - Opaque descriptor for a GPIO + * + * @gdev: Pointer to the parent GPIO device + * @flags: Binary descriptor flags + * @label: Name of the consumer + * @name: Line name + * @hog: Pointer to the device node that hogs this line (if any) + * @debounce_period_us: Debounce period in microseconds + * + * These are obtained using gpiod_get() and are preferable to the old + * integer-based handles. + * + * Contrary to integers, a pointer to a &struct gpio_desc is guaranteed to be + * valid until the GPIO is released. + */ struct gpio_desc { struct gpio_device *gdev; unsigned long flags; diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 3ad67b4a72be..c3aa8b330e1c 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -8,27 +8,16 @@ #include struct device; - -/** - * Opaque descriptor for a GPIO. These are obtained using gpiod_get() and are - * preferable to the old integer-based handles. - * - * Contrary to integers, a pointer to a gpio_desc is guaranteed to be valid - * until the GPIO is released. - */ struct gpio_desc; - -/** - * Opaque descriptor for a structure of GPIO array attributes. This structure - * is attached to struct gpiod_descs obtained from gpiod_get_array() and can be - * passed back to get/set array functions in order to activate fast processing - * path if applicable. - */ struct gpio_array; /** - * Struct containing an array of descriptors that can be obtained using - * gpiod_get_array(). + * struct gpio_descs - Struct containing an array of descriptors that can be + * obtained using gpiod_get_array() + * + * @info: Pointer to the opaque gpio_array structure + * @ndescs: Number of held descriptors + * @desc: Array of pointers to GPIO descriptors */ struct gpio_descs { struct gpio_array *info; @@ -43,8 +32,16 @@ struct gpio_descs { #define GPIOD_FLAGS_BIT_NONEXCLUSIVE BIT(4) /** - * Optional flags that can be passed to one of gpiod_* to configure direction - * and output value. These values cannot be OR'd. + * enum gpiod_flags - Optional flags that can be passed to one of gpiod_* to + * configure direction and output value. These values + * cannot be OR'd. + * + * @GPIOD_ASIS: Don't change anything + * @GPIOD_IN: Set lines to input mode + * @GPIOD_OUT_LOW: Set lines to output and drive them low + * @GPIOD_OUT_HIGH: Set lines to output and drive them high + * @GPIOD_OUT_LOW_OPEN_DRAIN: Set lines to open-drain output and drive them low + * @GPIOD_OUT_HIGH_OPEN_DRAIN: Set lines to open-drain output and drive them high */ enum gpiod_flags { GPIOD_ASIS = 0,