diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2020-07-09 17:06:28 +0200 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2020-07-09 17:06:28 +0200 |
commit | 4672a4a9fbfe316e1153682b0790af6446255a50 (patch) | |
tree | 7103f98cd5ae99b45e2d49ef9d8a98517b1f1064 | |
parent | Merge tag 'gpio-fixes-for-v5.8-rc3' of git://git.kernel.org/pub/scm/linux/ker... (diff) | |
parent | Merge branch 'lee-fixes' into devel (diff) | |
download | linux-4672a4a9fbfe316e1153682b0790af6446255a50.tar.xz linux-4672a4a9fbfe316e1153682b0790af6446255a50.zip |
Merge branch 'devel' into for-next
29 files changed, 1367 insertions, 1214 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt index dab537c20def..3126c3817e2a 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-pca953x.txt @@ -19,6 +19,7 @@ Required properties: nxp,pca9698 nxp,pcal6416 nxp,pcal6524 + nxp,pcal9535 nxp,pcal9555a maxim,max7310 maxim,max7312 diff --git a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt index 4fa4eb5507cd..f693e82b4c0f 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-zynq.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-zynq.txt @@ -6,7 +6,9 @@ Required properties: - First cell is the GPIO line number - Second cell is used to specify optional parameters (unused) -- compatible : Should be "xlnx,zynq-gpio-1.0" or "xlnx,zynqmp-gpio-1.0" +- compatible : Should be "xlnx,zynq-gpio-1.0" or + "xlnx,zynqmp-gpio-1.0" or "xlnx,versal-gpio-1.0 + or "xlnx,pmc-gpio-1.0 - clocks : Clock specifier (see clock bindings for details) - gpio-controller : Marks the device node as a GPIO controller. - interrupts : Interrupt specifier (see interrupt bindings for diff --git a/Documentation/driver-api/gpio/drivers-on-gpio.rst b/Documentation/driver-api/gpio/drivers-on-gpio.rst index 820b403d50f6..41ec3cc72d32 100644 --- a/Documentation/driver-api/gpio/drivers-on-gpio.rst +++ b/Documentation/driver-api/gpio/drivers-on-gpio.rst @@ -89,6 +89,13 @@ hardware descriptions such as device tree or ACPI: Consumer Electronics Control bus using only GPIO. It is used to communicate with devices on the HDMI bus. +- gpio-charger: drivers/power/supply/gpio-charger.c is used if you need to do + battery charging and all you have to go by to check the presence of the + AC charger or more complex tasks such as indicating charging status using + nothing but GPIO lines, this driver provides that and also a clearly defined + way to pass the charging parameters from hardware descriptions such as the + device tree. + Apart from this there are special GPIO drivers in subsystems like MMC/SD to read card detect and write protect GPIO lines, and in the TTY serial subsystem to emulate MCTRL (modem control) signals CTS/RTS by using two GPIO lines. The diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c index 26a531ebb6e9..734f0be4f14a 100644 --- a/arch/arm/plat-orion/gpio.c +++ b/arch/arm/plat-orion/gpio.c @@ -442,6 +442,7 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) struct orion_gpio_chip *ochip = gpiochip_get_data(chip); u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; + const char *label; int i; out = readl_relaxed(GPIO_OUT(ochip)); @@ -453,15 +454,10 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) edg_msk = readl_relaxed(GPIO_EDGE_MASK(ochip)); lvl_msk = readl_relaxed(GPIO_LEVEL_MASK(ochip)); - for (i = 0; i < chip->ngpio; i++) { - const char *label; + for_each_requested_gpio(chip, i, label) { u32 msk; bool is_out; - label = gpiochip_is_requested(chip, i); - if (!label) - continue; - msk = 1 << i; is_out = !(io_conf & msk); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c6b5c65c8405..05e0801c6a78 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -410,7 +410,7 @@ config GPIO_MXS config GPIO_OCTEON tristate "Cavium OCTEON GPIO" - depends on GPIOLIB && CAVIUM_OCTEON_SOC + depends on CAVIUM_OCTEON_SOC default y help Say yes here to support the on-chip GPIO lines on the OCTEON @@ -1117,7 +1117,7 @@ config GPIO_DLN2 config HTC_EGPIO bool "HTC EGPIO support" - depends on GPIOLIB && ARM + depends on ARM help This driver supports the CPLD egpio chip present on several HTC phones. It provides basic support for input diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1e4894e0bf0f..ef666cfef9d0 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o +obj-$(CONFIG_GPIOLIB) += gpiolib-cdev.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index b989c9352da2..e560e45e84f8 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -5,7 +5,7 @@ subsystem. GPIO descriptors Starting with commit 79a9becda894 the GPIO subsystem embarked on a journey -to move away from the global GPIO numberspace and toward a decriptor-based +to move away from the global GPIO numberspace and toward a descriptor-based approach. This means that GPIO consumers, drivers and machine descriptions ideally have no use or idea of the global GPIO numberspace that has/was used in the inception of the GPIO subsystem. diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 9b0adbdddbfc..424a3d25350b 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -10,6 +10,7 @@ #include <linux/bitmap.h> #include <linux/bitops.h> #include <linux/ctype.h> +#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> @@ -38,9 +39,9 @@ static DEFINE_IDR(gpio_aggregator_idr); static char *get_arg(char **args) { - char *start = *args, *end; + char *start, *end; - start = skip_spaces(start); + start = skip_spaces(*args); if (!*start) return NULL; @@ -111,55 +112,45 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, static int aggr_parse(struct gpio_aggregator *aggr) { - unsigned int first_index, last_index, i, n = 0; - char *name, *offsets, *first, *last, *next; char *args = aggr->args; - int error; + unsigned long *bitmap; + unsigned int i, n = 0; + char *name, *offsets; + int error = 0; + + bitmap = bitmap_alloc(ARCH_NR_GPIOS, GFP_KERNEL); + if (!bitmap) + return -ENOMEM; for (name = get_arg(&args), offsets = get_arg(&args); name; offsets = get_arg(&args)) { if (IS_ERR(name)) { pr_err("Cannot get GPIO specifier: %pe\n", name); - return PTR_ERR(name); + error = PTR_ERR(name); + goto free_bitmap; } if (!isrange(offsets)) { /* Named GPIO line */ error = aggr_add_gpio(aggr, name, U16_MAX, &n); if (error) - return error; + goto free_bitmap; name = offsets; continue; } /* GPIO chip + offset(s) */ - for (first = offsets; *first; first = next) { - next = strchrnul(first, ','); - if (*next) - *next++ = '\0'; - - last = strchr(first, '-'); - if (last) - *last++ = '\0'; - - if (kstrtouint(first, 10, &first_index)) { - pr_err("Cannot parse GPIO index %s\n", first); - return -EINVAL; - } - - if (!last) { - last_index = first_index; - } else if (kstrtouint(last, 10, &last_index)) { - pr_err("Cannot parse GPIO index %s\n", last); - return -EINVAL; - } - - for (i = first_index; i <= last_index; i++) { - error = aggr_add_gpio(aggr, name, i, &n); - if (error) - return error; - } + error = bitmap_parselist(offsets, bitmap, ARCH_NR_GPIOS); + if (error) { + pr_err("Cannot parse %s: %d\n", offsets, error); + goto free_bitmap; + } + + for_each_set_bit(i, bitmap, ARCH_NR_GPIOS) { + error = aggr_add_gpio(aggr, name, i, &n); + if (error) + goto free_bitmap; } name = get_arg(&args); @@ -167,10 +158,12 @@ static int aggr_parse(struct gpio_aggregator *aggr) if (!n) { pr_err("No GPIOs specified\n"); - return -EINVAL; + error = -EINVAL; } - return 0; +free_bitmap: + bitmap_free(bitmap); + return error; } static ssize_t new_device_store(struct device_driver *driver, const char *buf, diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index cc4ba71e4fe3..b7932ecc3b61 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -24,6 +24,7 @@ * @interrupt_trigger : specifies the hardware configured IRQ trigger type * (rising, falling, both, high) * @mapped_irq : kernel mapped irq number. +* @irq_chip : IRQ chip configuration */ struct altera_gpio_chip { struct of_mm_gpio_chip mmchip; @@ -69,7 +70,7 @@ static void altera_gpio_irq_mask(struct irq_data *d) raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags); } -/** +/* * This controller's IRQ type is synthesized in hardware, so this function * just checks if the requested set_type matches the synthesized IRQ type */ diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index b497a1d18ca9..8f1be34953ce 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -47,13 +47,13 @@ /** * struct it87_gpio - it87-specific GPIO chip - * @chip the underlying gpio_chip structure - * @lock a lock to avoid races between operations - * @io_base base address for gpio ports - * @io_size size of the port rage starting from io_base. - * @output_base Super I/O register address for Output Enable register - * @simple_base Super I/O 'Simple I/O' Enable register - * @simple_size Super IO 'Simple I/O' Enable register size; this is + * @chip: the underlying gpio_chip structure + * @lock: a lock to avoid races between operations + * @io_base: base address for gpio ports + * @io_size: size of the port rage starting from io_base. + * @output_base: Super I/O register address for Output Enable register + * @simple_base: Super I/O 'Simple I/O' Enable register + * @simple_size: Super IO 'Simple I/O' Enable register size; this is * required because IT87xx chips might only provide Simple I/O * switches on a subset of lines, whereas the others keep the * same status all time. diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 5fb0bcf31142..63472f308857 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -703,7 +703,7 @@ static int max732x_probe(struct i2c_client *client, if (ret) return ret; - if (pdata && pdata->setup) { + if (pdata->setup) { ret = pdata->setup(client, chip->gpio_chip.base, chip->gpio_chip.ngpio, pdata->context); if (ret < 0) diff --git a/drivers/gpio/gpio-mlxbf.c b/drivers/gpio/gpio-mlxbf.c index 894aaf55fc96..1fa9973f55b9 100644 --- a/drivers/gpio/gpio-mlxbf.c +++ b/drivers/gpio/gpio-mlxbf.c @@ -127,7 +127,7 @@ static int mlxbf_gpio_resume(struct platform_device *pdev) } #endif -static const struct acpi_device_id mlxbf_gpio_acpi_match[] = { +static const struct acpi_device_id __maybe_unused mlxbf_gpio_acpi_match[] = { { "MLNXBF02", 0 }, {} }; diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c index 94d5efce1721..befa5e109943 100644 --- a/drivers/gpio/gpio-mlxbf2.c +++ b/drivers/gpio/gpio-mlxbf2.c @@ -149,6 +149,8 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs) * Release the YU arm_gpio_lock after changing the direction mode. */ static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs) + __releases(&gs->gc.bgpio_lock) + __releases(yu_arm_gpio_lock_param.lock) { writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io); spin_unlock(&gs->gc.bgpio_lock); @@ -309,7 +311,7 @@ static int mlxbf2_gpio_resume(struct platform_device *pdev) } #endif -static const struct acpi_device_id mlxbf2_gpio_acpi_match[] = { +static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = { { "MLNXBF22", 0 }, {}, }; diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 604dfec353a1..1e866524a4bd 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -417,7 +417,7 @@ static int mpc8xxx_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn, mpc8xxx_gpio_irq_cascade, - IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade", + IRQF_SHARED, "gpio-cascade", mpc8xxx_gc); if (ret) { dev_err(&pdev->dev, "%s: failed to devm_request_irq(%d), ret = %d\n", diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index bd65114eb170..433e2c3f3fd5 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -846,6 +846,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; + const char *label; int i; regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out); @@ -857,15 +858,10 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) edg_msk = mvebu_gpio_read_edge_mask(mvchip); lvl_msk = mvebu_gpio_read_level_mask(mvchip); - for (i = 0; i < chip->ngpio; i++) { - const char *label; + for_each_requested_gpio(chip, i, label) { u32 msk; bool is_out; - label = gpiochip_is_requested(chip, i); - if (!label) - continue; - msk = BIT(i); is_out = !(io_conf & msk); diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index b8e2ecc3eade..b3afe39027bd 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -60,6 +60,7 @@ struct gpio_bank { struct clk *dbck; struct notifier_block nb; unsigned int is_suspended:1; + unsigned int needs_resume:1; u32 mod_usage; u32 irq_usage; u32 dbck_enable_mask; @@ -1504,9 +1505,34 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev) return 0; } +static int omap_gpio_suspend(struct device *dev) +{ + struct gpio_bank *bank = dev_get_drvdata(dev); + + if (bank->is_suspended) + return 0; + + bank->needs_resume = 1; + + return omap_gpio_runtime_suspend(dev); +} + +static int omap_gpio_resume(struct device *dev) +{ + struct gpio_bank *bank = dev_get_drvdata(dev); + + if (!bank->needs_resume) + return 0; + + bank->needs_resume = 0; + + return omap_gpio_runtime_resume(dev); +} + static const struct dev_pm_ops gpio_pm_ops = { SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL) + SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume) }; static struct platform_driver omap_gpio_driver = { diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index a3b9bdedbe44..9c90cf3aac5a 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -89,6 +89,7 @@ static const struct i2c_device_id pca953x_id[] = { { "pcal6416", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal6524", 24 | PCA953X_TYPE | PCA_LATCH_INT, }, + { "pcal9535", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "pcal9555a", 16 | PCA953X_TYPE | PCA_LATCH_INT, }, { "max7310", 8 | PCA953X_TYPE, }, @@ -1234,6 +1235,7 @@ static const struct of_device_id pca953x_dt_ids[] = { { .compatible = "nxp,pcal6416", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_LATCH_INT), }, + { .compatible = "nxp,pcal9535", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_LATCH_INT), }, { .compatible = "maxim,max7310", .data = OF_953X( 8, 0), }, diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 05000cace9b2..938285190566 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -48,7 +48,7 @@ enum { * struct sprd_pmic_eic - PMIC EIC controller * @chip: the gpio_chip structure. * @intc: the irq_chip structure. - * @regmap: the regmap from the parent device. + * @map: the regmap from the parent device. * @offset: the EIC controller's offset address of the PMIC. * @reg: the array to cache the EIC registers. * @buslock: for bus lock/sync and unlock. diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index 4d47b2c41186..b7c950658170 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -49,7 +49,7 @@ struct sama5d2_piobu { struct regmap *regmap; }; -/** +/* * sama5d2_piobu_setup_pin() - prepares a pin for set_direction call * * Do not consider pin for tamper detection (normal and backup modes) @@ -73,7 +73,7 @@ static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin) return regmap_update_bits(piobu->regmap, PIOBU_WKPR, mask, 0); } -/** +/* * sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register */ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin, @@ -88,7 +88,7 @@ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin, return regmap_update_bits(piobu->regmap, reg, mask, value); } -/** +/* * sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU * register */ @@ -108,7 +108,7 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin, return val & mask; } -/** +/* * sama5d2_piobu_get_direction() - gpiochip get_direction */ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, @@ -123,7 +123,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, GPIO_LINE_DIRECTION_OUT; } -/** +/* * sama5d2_piobu_direction_input() - gpiochip direction_input */ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, @@ -132,7 +132,7 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip, return sama5d2_piobu_write_value(chip, pin, PIOBU_DIRECTION, PIOBU_IN); } -/** +/* * sama5d2_piobu_direction_output() - gpiochip direction_output */ static int sama5d2_piobu_direction_output(struct gpio_chip *chip, @@ -147,7 +147,7 @@ static int sama5d2_piobu_direction_output(struct gpio_chip *chip, val); } -/** +/* * sama5d2_piobu_get() - gpiochip get */ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) @@ -166,7 +166,7 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) return !!ret; } -/** +/* * sama5d2_piobu_set() - gpiochip set */ static void sama5d2_piobu_set(struct gpio_chip *chip, unsigned int pin, diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c index 31f332074d7d..fdd3d497b535 100644 --- a/drivers/gpio/gpio-syscon.c +++ b/drivers/gpio/gpio-syscon.c @@ -24,16 +24,16 @@ /** * struct syscon_gpio_data - Configuration for the device. - * compatible: SYSCON driver compatible string. - * flags: Set of GPIO_SYSCON_FEAT_ flags: + * @compatible: SYSCON driver compatible string. + * @flags: Set of GPIO_SYSCON_FEAT_ flags: * GPIO_SYSCON_FEAT_IN: GPIOs supports input, * GPIO_SYSCON_FEAT_OUT: GPIOs supports output, * GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction. - * bit_count: Number of bits used as GPIOs. - * dat_bit_offset: Offset (in bits) to the first GPIO bit. - * dir_bit_offset: Optional offset (in bits) to the first bit to switch + * @bit_count: Number of bits used as GPIOs. + * @dat_bit_offset: Offset (in bits) to the first GPIO bit. + * @dir_bit_offset: Optional offset (in bits) to the first bit to switch * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag). - * set: HW specific callback to assigns output value + * @set: HW specific callback to assigns output value * for signal "offset" */ diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 31b5072b2df0..e2cac12092af 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -121,6 +121,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) struct xra1403 *xra = gpiochip_get_data(chip); int value[XRA_LAST]; int i; + const char *label; unsigned int gcr; unsigned int gsr; @@ -136,12 +137,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR]; gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR]; - for (i = 0; i < chip->ngpio; i++) { - const char *label = gpiochip_is_requested(chip, i); - - if (!label) - continue; - + for_each_requested_gpio(chip, i, label) { seq_printf(s, " gpio-%-3d (%-12s) %s %s\n", chip->base + i, label, (gcr & BIT(i)) ? "in" : "out", diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 05ba16fffdad..53d1387592fd 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -10,6 +10,7 @@ #include <linux/gpio/driver.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/spinlock.h> #include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -21,6 +22,9 @@ /* Maximum banks */ #define ZYNQ_GPIO_MAX_BANK 4 #define ZYNQMP_GPIO_MAX_BANK 6 +#define VERSAL_GPIO_MAX_BANK 4 +#define PMC_GPIO_MAX_BANK 5 +#define VERSAL_UNUSED_BANKS 2 #define ZYNQ_GPIO_BANK0_NGPIO 32 #define ZYNQ_GPIO_BANK1_NGPIO 22 @@ -95,6 +99,7 @@ /* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */ #define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0) #define GPIO_QUIRK_DATA_RO_BUG BIT(1) +#define GPIO_QUIRK_VERSAL BIT(2) struct gpio_regs { u32 datamsw[ZYNQMP_GPIO_MAX_BANK]; @@ -116,6 +121,7 @@ struct gpio_regs { * @irq: interrupt for the GPIO device * @p_data: pointer to platform data * @context: context registers + * @dirlock: lock used for direction in/out synchronization */ struct zynq_gpio { struct gpio_chip chip; @@ -124,6 +130,7 @@ struct zynq_gpio { int irq; const struct zynq_platform_data *p_data; struct gpio_regs context; + spinlock_t dirlock; /* lock */ }; /** @@ -196,6 +203,8 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num, gpio->p_data->bank_min[bank]; return; } + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank = bank + VERSAL_UNUSED_BANKS; } /* default */ @@ -297,6 +306,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) { u32 reg; unsigned int bank_num, bank_pin_num; + unsigned long flags; struct zynq_gpio *gpio = gpiochip_get_data(chip); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); @@ -310,9 +320,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) return -EINVAL; /* clear the bit in direction mode reg to set the pin as input */ + spin_lock_irqsave(&gpio->dirlock, flags); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg &= ~BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); + spin_unlock_irqrestore(&gpio->dirlock, flags); return 0; } @@ -334,11 +346,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, { u32 reg; unsigned int bank_num, bank_pin_num; + unsigned long flags; struct zynq_gpio *gpio = gpiochip_get_data(chip); zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); /* set the GPIO pin as output */ + spin_lock_irqsave(&gpio->dirlock, flags); reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); reg |= BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); @@ -347,6 +361,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); reg |= BIT(bank_pin_num); writel_relaxed(reg, gpio->base_addr + ZYNQ_GPIO_OUTEN_OFFSET(bank_num)); + spin_unlock_irqrestore(&gpio->dirlock, flags); /* set the state of the pin */ zynq_gpio_set_value(chip, pin, state); @@ -647,6 +662,8 @@ static void zynq_gpio_irqhandler(struct irq_desc *desc) int_enb = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTMASK_OFFSET(bank_num)); zynq_gpio_handle_bank_irq(gpio, bank_num, int_sts & ~int_enb); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } chained_irq_exit(irqchip, desc); @@ -676,6 +693,8 @@ static void zynq_gpio_save_context(struct zynq_gpio *gpio) gpio->context.int_any[bank_num] = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_INTANY_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } } @@ -707,6 +726,8 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio) writel_relaxed(~(gpio->context.int_en[bank_num]), gpio->base_addr + ZYNQ_GPIO_INTEN_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; } } @@ -715,6 +736,9 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev) struct zynq_gpio *gpio = dev_get_drvdata(dev); struct irq_data *data = irq_get_irq_data(gpio->irq); + if (!device_may_wakeup(dev)) + disable_irq(gpio->irq); + if (!irqd_is_wakeup_set(data)) { zynq_gpio_save_context(gpio); return pm_runtime_force_suspend(dev); @@ -729,6 +753,9 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev) struct irq_data *data = irq_get_irq_data(gpio->irq); int ret; + if (!device_may_wakeup(dev)) + enable_irq(gpio->irq); + if (!irqd_is_wakeup_set(data)) { ret = pm_runtime_force_resume(dev); zynq_gpio_restore_context(gpio); @@ -778,6 +805,31 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = { zynq_gpio_runtime_resume, NULL) }; +static const struct zynq_platform_data versal_gpio_def = { + .label = "versal_gpio", + .quirks = GPIO_QUIRK_VERSAL, + .ngpio = 58, + .max_bank = VERSAL_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[3] = 26, + .bank_max[3] = 57, /* Bank 3 is connected to FMIOs (32 pins) */ +}; + +static const struct zynq_platform_data pmc_gpio_def = { + .label = "pmc_gpio", + .ngpio = 116, + .max_bank = PMC_GPIO_MAX_BANK, + .bank_min[0] = 0, + .bank_max[0] = 25, /* 0 to 25 are connected to MIOs (26 pins) */ + .bank_min[1] = 26, + .bank_max[1] = 51, /* Bank 1 are connected to MIOs (26 pins) */ + .bank_min[3] = 52, + .bank_max[3] = 83, /* Bank 3 is connected to EMIOs (32 pins) */ + .bank_min[4] = 84, + .bank_max[4] = 115, /* Bank 4 is connected to EMIOs (32 pins) */ +}; + static const struct zynq_platform_data zynqmp_gpio_def = { .label = "zynqmp_gpio", .quirks = GPIO_QUIRK_DATA_RO_BUG, @@ -815,6 +867,8 @@ static const struct zynq_platform_data zynq_gpio_def = { static const struct of_device_id zynq_gpio_of_match[] = { { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def }, { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, + { .compatible = "xlnx,versal-gpio-1.0", .data = &versal_gpio_def }, + { .compatible = "xlnx,pmc-gpio-1.0", .data = &pmc_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); @@ -876,7 +930,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) /* Retrieve GPIO clock */ gpio->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(gpio->clk)) { - dev_err(&pdev->dev, "input clock not found.\n"); + if (PTR_ERR(gpio->clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "input clock not found.\n"); return PTR_ERR(gpio->clk); } ret = clk_prepare_enable(gpio->clk); @@ -885,6 +940,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) return ret; } + spin_lock_init(&gpio->dirlock); + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); @@ -892,9 +949,12 @@ static int zynq_gpio_probe(struct platform_device *pdev) goto err_pm_dis; /* disable interrupts for all banks */ - for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) + for (bank_num = 0; bank_num < gpio->p_data->max_bank; bank_num++) { writel_relaxed(ZYNQ_GPIO_IXR_DISABLE_ALL, gpio->base_addr + ZYNQ_GPIO_INTDIS_OFFSET(bank_num)); + if (gpio->p_data->quirks & GPIO_QUIRK_VERSAL) + bank_num = bank_num + VERSAL_UNUSED_BANKS; + } /* Set up the GPIO irqchip */ girq = &chip->irq; @@ -919,6 +979,8 @@ static int zynq_gpio_probe(struct platform_device *pdev) goto err_pm_put; } + irq_set_status_flags(gpio->irq, IRQ_DISABLE_UNLAZY); + device_init_wakeup(&pdev->dev, 1); pm_runtime_put(&pdev->dev); return 0; diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c new file mode 100644 index 000000000000..b8b872724628 --- /dev/null +++ b/drivers/gpio/gpiolib-cdev.c @@ -0,0 +1,1154 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/bitmap.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include <linux/pinctrl/consumer.h> +#include <linux/cdev.h> +#include <linux/uaccess.h> +#include <linux/compat.h> +#include <linux/anon_inodes.h> +#include <linux/file.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/timekeeping.h> +#include <uapi/linux/gpio.h> + +#include "gpiolib.h" +#include "gpiolib-cdev.h" + +/* Character device interface to GPIO. + * + * The GPIO character device, /dev/gpiochipN, provides userspace an + * interface to gpiolib GPIOs via ioctl()s. + */ + +/* + * GPIO line handle management + */ + +/** + * struct linehandle_state - contains the state of a userspace handle + * @gdev: the GPIO device the handle pertains to + * @label: consumer label used to tag descriptors + * @descs: the GPIO descriptors held by this handle + * @numdescs: the number of descriptors held in the descs array + */ +struct linehandle_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *descs[GPIOHANDLES_MAX]; + u32 numdescs; +}; + +#define GPIOHANDLE_REQUEST_VALID_FLAGS \ + (GPIOHANDLE_REQUEST_INPUT | \ + GPIOHANDLE_REQUEST_OUTPUT | \ + GPIOHANDLE_REQUEST_ACTIVE_LOW | \ + GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ + GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ + GPIOHANDLE_REQUEST_BIAS_DISABLE | \ + GPIOHANDLE_REQUEST_OPEN_DRAIN | \ + GPIOHANDLE_REQUEST_OPEN_SOURCE) + +static int linehandle_validate_flags(u32 flags) +{ + /* Return an error if an unknown flag is set */ + if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) + return -EINVAL; + + /* + * Do not allow both INPUT & OUTPUT flags to be set as they are + * contradictory. + */ + if ((flags & GPIOHANDLE_REQUEST_INPUT) && + (flags & GPIOHANDLE_REQUEST_OUTPUT)) + return -EINVAL; + + /* + * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If + * the hardware actually supports enabling both at the same time the + * electrical result would be disastrous. + */ + if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ + if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && + ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) + return -EINVAL; + + /* Bias flags only allowed for input or output mode. */ + if (!((flags & GPIOHANDLE_REQUEST_INPUT) || + (flags & GPIOHANDLE_REQUEST_OUTPUT)) && + ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + return 0; +} + +static long linehandle_set_config(struct linehandle_state *lh, + void __user *ip) +{ + struct gpiohandle_config gcnf; + struct gpio_desc *desc; + int i, ret; + u32 lflags; + unsigned long *flagsp; + + if (copy_from_user(&gcnf, ip, sizeof(gcnf))) + return -EFAULT; + + lflags = gcnf.flags; + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + for (i = 0; i < lh->numdescs; i++) { + desc = lh->descs[i]; + flagsp = &desc->flags; + + assign_bit(FLAG_ACTIVE_LOW, flagsp, + lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); + + assign_bit(FLAG_OPEN_DRAIN, flagsp, + lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); + + assign_bit(FLAG_OPEN_SOURCE, flagsp, + lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); + + assign_bit(FLAG_PULL_UP, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); + + assign_bit(FLAG_PULL_DOWN, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); + + assign_bit(FLAG_BIAS_DISABLE, flagsp, + lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!gcnf.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + return ret; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + return ret; + } + + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_CONFIG, desc); + } + return 0; +} + +static long linehandle_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct linehandle_state *lh = filep->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + DECLARE_BITMAP(vals, GPIOHANDLES_MAX); + int i; + + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + /* NOTE: It's ok to read values of output lines. */ + int ret = gpiod_get_array_value_complex(false, + true, + lh->numdescs, + lh->descs, + NULL, + vals); + if (ret) + return ret; + + memset(&ghd, 0, sizeof(ghd)); + for (i = 0; i < lh->numdescs; i++) + ghd.values[i] = test_bit(i, vals); + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { + /* + * All line descriptors were created at once with the same + * flags so just check if the first one is really output. + */ + if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags)) + return -EPERM; + + if (copy_from_user(&ghd, ip, sizeof(ghd))) + return -EFAULT; + + /* Clamp all values to [0,1] */ + for (i = 0; i < lh->numdescs; i++) + __assign_bit(i, vals, ghd.values[i]); + + /* Reuse the array setting function */ + return gpiod_set_array_value_complex(false, + true, + lh->numdescs, + lh->descs, + NULL, + vals); + } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { + return linehandle_set_config(lh, ip); + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static int linehandle_release(struct inode *inode, struct file *filep) +{ + struct linehandle_state *lh = filep->private_data; + struct gpio_device *gdev = lh->gdev; + int i; + + for (i = 0; i < lh->numdescs; i++) + gpiod_free(lh->descs[i]); + kfree(lh->label); + kfree(lh); + put_device(&gdev->dev); + return 0; +} + +static const struct file_operations linehandle_fileops = { + .release = linehandle_release, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = linehandle_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = linehandle_ioctl_compat, +#endif +}; + +static int linehandle_create(struct gpio_device *gdev, void __user *ip) +{ + struct gpiohandle_request handlereq; + struct linehandle_state *lh; + struct file *file; + int fd, i, count = 0, ret; + u32 lflags; + + if (copy_from_user(&handlereq, ip, sizeof(handlereq))) + return -EFAULT; + if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) + return -EINVAL; + + lflags = handlereq.flags; + + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + lh = kzalloc(sizeof(*lh), GFP_KERNEL); + if (!lh) + return -ENOMEM; + lh->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; + if (strlen(handlereq.consumer_label)) { + lh->label = kstrdup(handlereq.consumer_label, + GFP_KERNEL); + if (!lh->label) { + ret = -ENOMEM; + goto out_free_lh; + } + } + + /* Request each GPIO */ + for (i = 0; i < handlereq.lines; i++) { + u32 offset = handlereq.lineoffsets[i]; + struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + + if (IS_ERR(desc)) { + ret = PTR_ERR(desc); + goto out_free_descs; + } + + ret = gpiod_request(desc, lh->label); + if (ret) + goto out_free_descs; + lh->descs[i] = desc; + count = i + 1; + + if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); + + ret = gpiod_set_transitory(desc, false); + if (ret < 0) + goto out_free_descs; + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!handlereq.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + goto out_free_descs; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_descs; + } + + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); + + dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", + offset); + } + /* Let i point at the last handle */ + i--; + lh->numdescs = handlereq.lines; + + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_descs; + } + + file = anon_inode_getfile("gpio-linehandle", + &linehandle_fileops, + lh, + O_RDONLY | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto out_put_unused_fd; + } + + handlereq.fd = fd; + if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { + /* + * fput() will trigger the release() callback, so do not go onto + * the regular error cleanup path here. + */ + fput(file); + put_unused_fd(fd); + return -EFAULT; + } + + fd_install(fd, file); + + dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", + lh->numdescs); + + return 0; + +out_put_unused_fd: + put_unused_fd(fd); +out_free_descs: + for (i = 0; i < count; i++) + gpiod_free(lh->descs[i]); + kfree(lh->label); +out_free_lh: + kfree(lh); + put_device(&gdev->dev); + return ret; +} + +/* + * GPIO line event management + */ + +/** + * struct lineevent_state - contains the state of a userspace event + * @gdev: the GPIO device the event pertains to + * @label: consumer label used to tag descriptors + * @desc: the GPIO descriptor held by this event + * @eflags: the event flags this line was requested with + * @irq: the interrupt that trigger in response to events on this GPIO + * @wait: wait queue that handles blocking reads of events + * @events: KFIFO for the GPIO events + * @timestamp: cache for the timestamp storing it between hardirq + * and IRQ thread, used to bring the timestamp close to the actual + * event + */ +struct lineevent_state { + struct gpio_device *gdev; + const char *label; + struct gpio_desc *desc; + u32 eflags; + int irq; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioevent_data, 16); + u64 timestamp; +}; + +#define GPIOEVENT_REQUEST_VALID_FLAGS \ + (GPIOEVENT_REQUEST_RISING_EDGE | \ + GPIOEVENT_REQUEST_FALLING_EDGE) + +static __poll_t lineevent_poll(struct file *filep, + struct poll_table_struct *wait) +{ + struct lineevent_state *le = filep->private_data; + __poll_t events = 0; + + poll_wait(filep, &le->wait, wait); + + if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + + +static ssize_t lineevent_read(struct file *filep, + char __user *buf, + size_t count, + loff_t *f_ps) +{ + struct lineevent_state *le = filep->private_data; + struct gpioevent_data ge; + ssize_t bytes_read = 0; + int ret; + + if (count < sizeof(ge)) + return -EINVAL; + + do { + spin_lock(&le->wait.lock); + if (kfifo_is_empty(&le->events)) { + if (bytes_read) { + spin_unlock(&le->wait.lock); + return bytes_read; + } + + if (filep->f_flags & O_NONBLOCK) { + spin_unlock(&le->wait.lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_locked(le->wait, + !kfifo_is_empty(&le->events)); + if (ret) { + spin_unlock(&le->wait.lock); + return ret; + } + } + + ret = kfifo_out(&le->events, &ge, 1); + spin_unlock(&le->wait.lock); + if (ret != 1) { + /* + * This should never happen - we were holding the lock + * from the moment we learned the fifo is no longer + * empty until now. + */ + ret = -EIO; + break; + } + + if (copy_to_user(buf + bytes_read, &ge, sizeof(ge))) + return -EFAULT; + bytes_read += sizeof(ge); + } while (count >= bytes_read + sizeof(ge)); + + return bytes_read; +} + +static int lineevent_release(struct inode *inode, struct file *filep) +{ + struct lineevent_state *le = filep->private_data; + struct gpio_device *gdev = le->gdev; + + free_irq(le->irq, le); + gpiod_free(le->desc); + kfree(le->label); + kfree(le); + put_device(&gdev->dev); + return 0; +} + +static long lineevent_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct lineevent_state *le = filep->private_data; + void __user *ip = (void __user *)arg; + struct gpiohandle_data ghd; + + /* + * We can get the value for an event line but not set it, + * because it is input by definition. + */ + if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { + int val; + + memset(&ghd, 0, sizeof(ghd)); + + val = gpiod_get_value_cansleep(le->desc); + if (val < 0) + return val; + ghd.values[0] = val; + + if (copy_to_user(ip, &ghd, sizeof(ghd))) + return -EFAULT; + + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations lineevent_fileops = { + .release = lineevent_release, + .read = lineevent_read, + .poll = lineevent_poll, + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = lineevent_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lineevent_ioctl_compat, +#endif +}; + +static irqreturn_t lineevent_irq_thread(int irq, void *p) +{ + struct lineevent_state *le = p; + struct gpioevent_data ge; + int ret; + + /* Do not leak kernel stack to userspace */ + memset(&ge, 0, sizeof(ge)); + + /* + * 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_ns(); + else + ge.timestamp = le->timestamp; + + if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE + && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + int level = gpiod_get_value_cansleep(le->desc); + + if (level) + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + else + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { + /* Emit low-to-high event */ + ge.id = GPIOEVENT_EVENT_RISING_EDGE; + } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + /* Emit high-to-low event */ + ge.id = GPIOEVENT_EVENT_FALLING_EDGE; + } else { + return IRQ_NONE; + } + + ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge, + 1, &le->wait.lock); + if (ret) + wake_up_poll(&le->wait, EPOLLIN); + else + pr_debug_ratelimited("event FIFO is full - event dropped\n"); + + return IRQ_HANDLED; +} + +static irqreturn_t lineevent_irq_handler(int irq, void *p) +{ + struct lineevent_state *le = p; + + /* + * Just store the timestamp in hardirq context so we get it as + * close in time as possible to the actual event. + */ + le->timestamp = ktime_get_ns(); + + return IRQ_WAKE_THREAD; +} + +static int lineevent_create(struct gpio_device *gdev, void __user *ip) +{ + struct gpioevent_request eventreq; + struct lineevent_state *le; + struct gpio_desc *desc; + struct file *file; + u32 offset; + u32 lflags; + u32 eflags; + int fd; + int ret; + int irqflags = 0; + + if (copy_from_user(&eventreq, ip, sizeof(eventreq))) + return -EFAULT; + + offset = eventreq.lineoffset; + lflags = eventreq.handleflags; + eflags = eventreq.eventflags; + + 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) || + (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) + return -EINVAL; + + /* This is just wrong: we don't look for events on output lines */ + if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || + (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + le = kzalloc(sizeof(*le), GFP_KERNEL); + if (!le) + return -ENOMEM; + le->gdev = gdev; + get_device(&gdev->dev); + + /* Make sure this is terminated */ + eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0'; + if (strlen(eventreq.consumer_label)) { + le->label = kstrdup(eventreq.consumer_label, + GFP_KERNEL); + if (!le->label) { + ret = -ENOMEM; + goto out_free_le; + } + } + + ret = gpiod_request(desc, le->label); + if (ret) + goto out_free_label; + le->desc = desc; + le->eflags = eflags; + + if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) + set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); + + ret = gpiod_direction_input(desc); + if (ret) + goto out_free_desc; + + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); + + le->irq = gpiod_to_irq(desc); + if (le->irq <= 0) { + ret = -ENODEV; + goto out_free_desc; + } + + if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) + irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; + if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) + irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; + irqflags |= IRQF_ONESHOT; + + INIT_KFIFO(le->events); + init_waitqueue_head(&le->wait); + + /* Request a thread to read the events */ + ret = request_threaded_irq(le->irq, + lineevent_irq_handler, + lineevent_irq_thread, + irqflags, + le->label, + le); + if (ret) + goto out_free_desc; + + fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto out_free_irq; + } + + file = anon_inode_getfile("gpio-event", + &lineevent_fileops, + le, + O_RDONLY | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto out_put_unused_fd; + } + + eventreq.fd = fd; + if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { + /* + * fput() will trigger the release() callback, so do not go onto + * the regular error cleanup path here. + */ + fput(file); + put_unused_fd(fd); + return -EFAULT; + } + + fd_install(fd, file); + + return 0; + +out_put_unused_fd: + put_unused_fd(fd); +out_free_irq: + free_irq(le->irq, le); +out_free_desc: + gpiod_free(le->desc); +out_free_label: + kfree(le->label); +out_free_le: + kfree(le); + put_device(&gdev->dev); + return ret; +} + +static void gpio_desc_to_lineinfo(struct gpio_desc *desc, + struct gpioline_info *info) +{ + struct gpio_chip *gc = desc->gdev->chip; + bool ok_for_pinctrl; + unsigned long flags; + + /* + * This function takes a mutex so we must check this before taking + * the spinlock. + * + * FIXME: find a non-racy way to retrieve this information. Maybe a + * lock common to both frameworks? + */ + ok_for_pinctrl = + pinctrl_gpio_can_use_line(gc->base + info->line_offset); + + spin_lock_irqsave(&gpio_lock, flags); + + if (desc->name) { + strncpy(info->name, desc->name, sizeof(info->name)); + info->name[sizeof(info->name) - 1] = '\0'; + } else { + info->name[0] = '\0'; + } + + if (desc->label) { + strncpy(info->consumer, desc->label, sizeof(info->consumer)); + info->consumer[sizeof(info->consumer) - 1] = '\0'; + } else { + info->consumer[0] = '\0'; + } + + /* + * Userspace only need to know that the kernel is using this GPIO so + * it can't use it. + */ + info->flags = 0; + if (test_bit(FLAG_REQUESTED, &desc->flags) || + test_bit(FLAG_IS_HOGGED, &desc->flags) || + test_bit(FLAG_USED_AS_IRQ, &desc->flags) || + test_bit(FLAG_EXPORT, &desc->flags) || + test_bit(FLAG_SYSFS, &desc->flags) || + !ok_for_pinctrl) + info->flags |= GPIOLINE_FLAG_KERNEL; + if (test_bit(FLAG_IS_OUT, &desc->flags)) + info->flags |= GPIOLINE_FLAG_IS_OUT; + if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + info->flags |= GPIOLINE_FLAG_ACTIVE_LOW; + if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN | + GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) + info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE | + GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_DISABLE; + if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; + if (test_bit(FLAG_PULL_UP, &desc->flags)) + info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +struct gpio_chardev_data { + struct gpio_device *gdev; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioline_info_changed, 32); + struct notifier_block lineinfo_changed_nb; + unsigned long *watched_lines; +}; + +/* + * gpio_ioctl() - ioctl handler for the GPIO chardev + */ +static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gpio_chardev_data *priv = filp->private_data; + struct gpio_device *gdev = priv->gdev; + struct gpio_chip *gc = gdev->chip; + void __user *ip = (void __user *)arg; + struct gpio_desc *desc; + __u32 offset; + int hwgpio; + + /* We fail any subsequent ioctl():s when the chip is gone */ + if (!gc) + return -ENODEV; + + /* Fill in the struct and pass to userspace */ + if (cmd == GPIO_GET_CHIPINFO_IOCTL) { + struct gpiochip_info chipinfo; + + memset(&chipinfo, 0, sizeof(chipinfo)); + + strncpy(chipinfo.name, dev_name(&gdev->dev), + sizeof(chipinfo.name)); + chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; + strncpy(chipinfo.label, gdev->label, + sizeof(chipinfo.label)); + chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; + chipinfo.lines = gdev->ngpio; + if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) + return -EFAULT; + return 0; + } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { + struct gpioline_info lineinfo; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + + desc = gpiochip_get_desc(gc, lineinfo.line_offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + hwgpio = gpio_chip_hwgpio(desc); + + gpio_desc_to_lineinfo(desc, &lineinfo); + + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) + return -EFAULT; + return 0; + } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { + return linehandle_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { + return lineevent_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { + struct gpioline_info lineinfo; + + if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) + return -EFAULT; + + desc = gpiochip_get_desc(gc, lineinfo.line_offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + hwgpio = gpio_chip_hwgpio(desc); + + if (test_bit(hwgpio, priv->watched_lines)) + return -EBUSY; + + gpio_desc_to_lineinfo(desc, &lineinfo); + + if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) + return -EFAULT; + + set_bit(hwgpio, priv->watched_lines); + return 0; + } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { + if (copy_from_user(&offset, ip, sizeof(offset))) + return -EFAULT; + + desc = gpiochip_get_desc(gc, offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + hwgpio = gpio_chip_hwgpio(desc); + + if (!test_bit(hwgpio, priv->watched_lines)) + return -EBUSY; + + clear_bit(hwgpio, priv->watched_lines); + return 0; + } + return -EINVAL; +} + +#ifdef CONFIG_COMPAT +static long gpio_ioctl_compat(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static struct gpio_chardev_data * +to_gpio_chardev_data(struct notifier_block *nb) +{ + return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); +} + +static int lineinfo_changed_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct gpio_chardev_data *priv = to_gpio_chardev_data(nb); + struct gpioline_info_changed chg; + struct gpio_desc *desc = data; + int ret; + + if (!test_bit(gpio_chip_hwgpio(desc), priv->watched_lines)) + return NOTIFY_DONE; + + memset(&chg, 0, sizeof(chg)); + chg.info.line_offset = gpio_chip_hwgpio(desc); + chg.event_type = action; + chg.timestamp = ktime_get_ns(); + gpio_desc_to_lineinfo(desc, &chg.info); + + ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock); + if (ret) + wake_up_poll(&priv->wait, EPOLLIN); + else + pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); + + return NOTIFY_OK; +} + +static __poll_t lineinfo_watch_poll(struct file *filep, + struct poll_table_struct *pollt) +{ + struct gpio_chardev_data *priv = filep->private_data; + __poll_t events = 0; + + poll_wait(filep, &priv->wait, pollt); + + if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, + &priv->wait.lock)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + +static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_chardev_data *priv = filep->private_data; + struct gpioline_info_changed event; + ssize_t bytes_read = 0; + int ret; + + if (count < sizeof(event)) + return -EINVAL; + + do { + spin_lock(&priv->wait.lock); + if (kfifo_is_empty(&priv->events)) { + if (bytes_read) { + spin_unlock(&priv->wait.lock); + return bytes_read; + } + + if (filep->f_flags & O_NONBLOCK) { + spin_unlock(&priv->wait.lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_locked(priv->wait, + !kfifo_is_empty(&priv->events)); + if (ret) { + spin_unlock(&priv->wait.lock); + return ret; + } + } + + ret = kfifo_out(&priv->events, &event, 1); + spin_unlock(&priv->wait.lock); + if (ret != 1) { + ret = -EIO; + break; + /* We should never get here. See lineevent_read(). */ + } + + if (copy_to_user(buf + bytes_read, &event, sizeof(event))) + return -EFAULT; + bytes_read += sizeof(event); + } while (count >= bytes_read + sizeof(event)); + + return bytes_read; +} + +/** + * gpio_chrdev_open() - open the chardev for ioctl operations + * @inode: inode for this chardev + * @filp: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_open(struct inode *inode, struct file *filp) +{ + struct gpio_device *gdev = container_of(inode->i_cdev, + struct gpio_device, chrdev); + struct gpio_chardev_data *priv; + int ret = -ENOMEM; + + /* Fail on open if the backing gpiochip is gone */ + if (!gdev->chip) + return -ENODEV; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + if (!priv->watched_lines) + goto out_free_priv; + + init_waitqueue_head(&priv->wait); + INIT_KFIFO(priv->events); + priv->gdev = gdev; + + priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; + ret = atomic_notifier_chain_register(&gdev->notifier, + &priv->lineinfo_changed_nb); + if (ret) + goto out_free_bitmap; + + get_device(&gdev->dev); + filp->private_data = priv; + + ret = nonseekable_open(inode, filp); + if (ret) + goto out_unregister_notifier; + + return ret; + +out_unregister_notifier: + atomic_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); +out_free_bitmap: + bitmap_free(priv->watched_lines); +out_free_priv: + kfree(priv); + return ret; +} + +/** + * gpio_chrdev_release() - close chardev after ioctl operations + * @inode: inode for this chardev + * @filp: file struct for storing private data + * Returns 0 on success + */ +static int gpio_chrdev_release(struct inode *inode, struct file *filp) +{ + struct gpio_chardev_data *priv = filp->private_data; + struct gpio_device *gdev = priv->gdev; + + bitmap_free(priv->watched_lines); + atomic_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); + put_device(&gdev->dev); + kfree(priv); + + return 0; +} + +static const struct file_operations gpio_fileops = { + .release = gpio_chrdev_release, + .open = gpio_chrdev_open, + .poll = lineinfo_watch_poll, + .read = lineinfo_watch_read, + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = gpio_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gpio_ioctl_compat, +#endif +}; + +int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) +{ + int ret; + + cdev_init(&gdev->chrdev, &gpio_fileops); + gdev->chrdev.owner = THIS_MODULE; + gdev->dev.devt = MKDEV(MAJOR(devt), gdev->id); + + ret = cdev_device_add(&gdev->chrdev, &gdev->dev); + if (ret) + return ret; + + chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", + MAJOR(devt), gdev->id); + + return 0; +} + +void gpiolib_cdev_unregister(struct gpio_device *gdev) +{ + cdev_device_del(&gdev->chrdev, &gdev->dev); +} diff --git a/drivers/gpio/gpiolib-cdev.h b/drivers/gpio/gpiolib-cdev.h new file mode 100644 index 000000000000..973578e7ad10 --- /dev/null +++ b/drivers/gpio/gpiolib-cdev.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef GPIOLIB_CDEV_H +#define GPIOLIB_CDEV_H + +#include <linux/device.h> + +int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt); +void gpiolib_cdev_unregister(struct gpio_device *gdev); + +#endif /* GPIOLIB_CDEV_H */ diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 219eb0054233..236985714ea9 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -25,6 +25,9 @@ /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI + * @dev: Consuming device + * @con_id: Function within the GPIO consumer + * * Some elder GPIO controllers need special quirks. Currently we handle * the Freescale GPIO controller with bindings that doesn't use the * established "cs-gpios" for chip selects but instead rely on diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 23e3d335cd54..82371fe2ccc6 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -365,7 +365,7 @@ static DEVICE_ATTR_RW(active_low); static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; umode_t mode = attr->mode; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4fa075d49fbc..291c088a5964 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -17,20 +17,15 @@ #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> #include <linux/pinctrl/consumer.h> -#include <linux/cdev.h> #include <linux/fs.h> -#include <linux/uaccess.h> #include <linux/compat.h> -#include <linux/anon_inodes.h> #include <linux/file.h> -#include <linux/kfifo.h> -#include <linux/poll.h> -#include <linux/timekeeping.h> #include <uapi/linux/gpio.h> #include "gpiolib.h" #include "gpiolib-of.h" #include "gpiolib-acpi.h" +#include "gpiolib-cdev.h" #define CREATE_TRACE_POINTS #include <trace/events/gpio.h> @@ -425,1105 +420,6 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); -/* - * GPIO line handle management - */ - -/** - * struct linehandle_state - contains the state of a userspace handle - * @gdev: the GPIO device the handle pertains to - * @label: consumer label used to tag descriptors - * @descs: the GPIO descriptors held by this handle - * @numdescs: the number of descriptors held in the descs array - */ -struct linehandle_state { - struct gpio_device *gdev; - const char *label; - struct gpio_desc *descs[GPIOHANDLES_MAX]; - u32 numdescs; -}; - -#define GPIOHANDLE_REQUEST_VALID_FLAGS \ - (GPIOHANDLE_REQUEST_INPUT | \ - GPIOHANDLE_REQUEST_OUTPUT | \ - GPIOHANDLE_REQUEST_ACTIVE_LOW | \ - GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ - GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ - GPIOHANDLE_REQUEST_BIAS_DISABLE | \ - GPIOHANDLE_REQUEST_OPEN_DRAIN | \ - GPIOHANDLE_REQUEST_OPEN_SOURCE) - -static int linehandle_validate_flags(u32 flags) -{ - /* Return an error if an unknown flag is set */ - if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) - return -EINVAL; - - /* - * Do not allow both INPUT & OUTPUT flags to be set as they are - * contradictory. - */ - if ((flags & GPIOHANDLE_REQUEST_INPUT) && - (flags & GPIOHANDLE_REQUEST_OUTPUT)) - return -EINVAL; - - /* - * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If - * the hardware actually supports enabling both at the same time the - * electrical result would be disastrous. - */ - if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && - (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ - if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && - ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) - return -EINVAL; - - /* Bias flags only allowed for input or output mode. */ - if (!((flags & GPIOHANDLE_REQUEST_INPUT) || - (flags & GPIOHANDLE_REQUEST_OUTPUT)) && - ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) - return -EINVAL; - - /* Only one bias flag can be set. */ - if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && - (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || - ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && - (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) - return -EINVAL; - - return 0; -} - -static long linehandle_set_config(struct linehandle_state *lh, - void __user *ip) -{ - struct gpiohandle_config gcnf; - struct gpio_desc *desc; - int i, ret; - u32 lflags; - unsigned long *flagsp; - - if (copy_from_user(&gcnf, ip, sizeof(gcnf))) - return -EFAULT; - - lflags = gcnf.flags; - ret = linehandle_validate_flags(lflags); - if (ret) - return ret; - - for (i = 0; i < lh->numdescs; i++) { - desc = lh->descs[i]; - flagsp = &desc->flags; - - assign_bit(FLAG_ACTIVE_LOW, flagsp, - lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); - - assign_bit(FLAG_OPEN_DRAIN, flagsp, - lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); - - assign_bit(FLAG_OPEN_SOURCE, flagsp, - lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); - - assign_bit(FLAG_PULL_UP, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); - - assign_bit(FLAG_PULL_DOWN, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); - - assign_bit(FLAG_BIAS_DISABLE, flagsp, - lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); - - /* - * Lines have to be requested explicitly for input - * or output, else the line will be treated "as is". - */ - if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { - int val = !!gcnf.default_values[i]; - - ret = gpiod_direction_output(desc, val); - if (ret) - return ret; - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); - if (ret) - return ret; - } - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_CONFIG, desc); - } - return 0; -} - -static long linehandle_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct linehandle_state *lh = filep->private_data; - void __user *ip = (void __user *)arg; - struct gpiohandle_data ghd; - DECLARE_BITMAP(vals, GPIOHANDLES_MAX); - int i; - - if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - /* NOTE: It's ok to read values of output lines. */ - int ret = gpiod_get_array_value_complex(false, - true, - lh->numdescs, - lh->descs, - NULL, - vals); - if (ret) - return ret; - - memset(&ghd, 0, sizeof(ghd)); - for (i = 0; i < lh->numdescs; i++) - ghd.values[i] = test_bit(i, vals); - - if (copy_to_user(ip, &ghd, sizeof(ghd))) - return -EFAULT; - - return 0; - } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { - /* - * All line descriptors were created at once with the same - * flags so just check if the first one is really output. - */ - if (!test_bit(FLAG_IS_OUT, &lh->descs[0]->flags)) - return -EPERM; - - if (copy_from_user(&ghd, ip, sizeof(ghd))) - return -EFAULT; - - /* Clamp all values to [0,1] */ - for (i = 0; i < lh->numdescs; i++) - __assign_bit(i, vals, ghd.values[i]); - - /* Reuse the array setting function */ - return gpiod_set_array_value_complex(false, - true, - lh->numdescs, - lh->descs, - NULL, - vals); - } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { - return linehandle_set_config(lh, ip); - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long linehandle_ioctl_compat(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - return linehandle_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static int linehandle_release(struct inode *inode, struct file *filep) -{ - struct linehandle_state *lh = filep->private_data; - struct gpio_device *gdev = lh->gdev; - int i; - - for (i = 0; i < lh->numdescs; i++) - gpiod_free(lh->descs[i]); - kfree(lh->label); - kfree(lh); - put_device(&gdev->dev); - return 0; -} - -static const struct file_operations linehandle_fileops = { - .release = linehandle_release, - .owner = THIS_MODULE, - .llseek = noop_llseek, - .unlocked_ioctl = linehandle_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = linehandle_ioctl_compat, -#endif -}; - -static int linehandle_create(struct gpio_device *gdev, void __user *ip) -{ - struct gpiohandle_request handlereq; - struct linehandle_state *lh; - struct file *file; - int fd, i, count = 0, ret; - u32 lflags; - - if (copy_from_user(&handlereq, ip, sizeof(handlereq))) - return -EFAULT; - if ((handlereq.lines == 0) || (handlereq.lines > GPIOHANDLES_MAX)) - return -EINVAL; - - lflags = handlereq.flags; - - ret = linehandle_validate_flags(lflags); - if (ret) - return ret; - - lh = kzalloc(sizeof(*lh), GFP_KERNEL); - if (!lh) - return -ENOMEM; - lh->gdev = gdev; - get_device(&gdev->dev); - - /* Make sure this is terminated */ - handlereq.consumer_label[sizeof(handlereq.consumer_label)-1] = '\0'; - if (strlen(handlereq.consumer_label)) { - lh->label = kstrdup(handlereq.consumer_label, - GFP_KERNEL); - if (!lh->label) { - ret = -ENOMEM; - goto out_free_lh; - } - } - - /* Request each GPIO */ - for (i = 0; i < handlereq.lines; i++) { - u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); - - if (IS_ERR(desc)) { - ret = PTR_ERR(desc); - goto out_free_descs; - } - - ret = gpiod_request(desc, lh->label); - if (ret) - goto out_free_descs; - lh->descs[i] = desc; - count = i + 1; - - if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) - set_bit(FLAG_OPEN_DRAIN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) - set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) - set_bit(FLAG_BIAS_DISABLE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) - set_bit(FLAG_PULL_DOWN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) - set_bit(FLAG_PULL_UP, &desc->flags); - - ret = gpiod_set_transitory(desc, false); - if (ret < 0) - goto out_free_descs; - - /* - * Lines have to be requested explicitly for input - * or output, else the line will be treated "as is". - */ - if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { - int val = !!handlereq.default_values[i]; - - ret = gpiod_direction_output(desc, val); - if (ret) - goto out_free_descs; - } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { - ret = gpiod_direction_input(desc); - if (ret) - goto out_free_descs; - } - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); - - dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", - offset); - } - /* Let i point at the last handle */ - i--; - lh->numdescs = handlereq.lines; - - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_descs; - } - - file = anon_inode_getfile("gpio-linehandle", - &linehandle_fileops, - lh, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - handlereq.fd = fd; - if (copy_to_user(ip, &handlereq, sizeof(handlereq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); - return -EFAULT; - } - - fd_install(fd, file); - - dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n", - lh->numdescs); - - return 0; - -out_put_unused_fd: - put_unused_fd(fd); -out_free_descs: - for (i = 0; i < count; i++) - gpiod_free(lh->descs[i]); - kfree(lh->label); -out_free_lh: - kfree(lh); - put_device(&gdev->dev); - return ret; -} - -/* - * GPIO line event management - */ - -/** - * struct lineevent_state - contains the state of a userspace event - * @gdev: the GPIO device the event pertains to - * @label: consumer label used to tag descriptors - * @desc: the GPIO descriptor held by this event - * @eflags: the event flags this line was requested with - * @irq: the interrupt that trigger in response to events on this GPIO - * @wait: wait queue that handles blocking reads of events - * @events: KFIFO for the GPIO events - * @timestamp: cache for the timestamp storing it between hardirq - * and IRQ thread, used to bring the timestamp close to the actual - * event - */ -struct lineevent_state { - struct gpio_device *gdev; - const char *label; - struct gpio_desc *desc; - u32 eflags; - int irq; - wait_queue_head_t wait; - DECLARE_KFIFO(events, struct gpioevent_data, 16); - u64 timestamp; -}; - -#define GPIOEVENT_REQUEST_VALID_FLAGS \ - (GPIOEVENT_REQUEST_RISING_EDGE | \ - GPIOEVENT_REQUEST_FALLING_EDGE) - -static __poll_t lineevent_poll(struct file *filep, - struct poll_table_struct *wait) -{ - struct lineevent_state *le = filep->private_data; - __poll_t events = 0; - - poll_wait(filep, &le->wait, wait); - - if (!kfifo_is_empty_spinlocked_noirqsave(&le->events, &le->wait.lock)) - events = EPOLLIN | EPOLLRDNORM; - - return events; -} - - -static ssize_t lineevent_read(struct file *filep, - char __user *buf, - size_t count, - loff_t *f_ps) -{ - struct lineevent_state *le = filep->private_data; - struct gpioevent_data ge; - ssize_t bytes_read = 0; - int ret; - - if (count < sizeof(ge)) - return -EINVAL; - - do { - spin_lock(&le->wait.lock); - if (kfifo_is_empty(&le->events)) { - if (bytes_read) { - spin_unlock(&le->wait.lock); - return bytes_read; - } - - if (filep->f_flags & O_NONBLOCK) { - spin_unlock(&le->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(le->wait, - !kfifo_is_empty(&le->events)); - if (ret) { - spin_unlock(&le->wait.lock); - return ret; - } - } - - ret = kfifo_out(&le->events, &ge, 1); - spin_unlock(&le->wait.lock); - if (ret != 1) { - /* - * This should never happen - we were holding the lock - * from the moment we learned the fifo is no longer - * empty until now. - */ - ret = -EIO; - break; - } - - if (copy_to_user(buf + bytes_read, &ge, sizeof(ge))) - return -EFAULT; - bytes_read += sizeof(ge); - } while (count >= bytes_read + sizeof(ge)); - - return bytes_read; -} - -static int lineevent_release(struct inode *inode, struct file *filep) -{ - struct lineevent_state *le = filep->private_data; - struct gpio_device *gdev = le->gdev; - - free_irq(le->irq, le); - gpiod_free(le->desc); - kfree(le->label); - kfree(le); - put_device(&gdev->dev); - return 0; -} - -static long lineevent_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - struct lineevent_state *le = filep->private_data; - void __user *ip = (void __user *)arg; - struct gpiohandle_data ghd; - - /* - * We can get the value for an event line but not set it, - * because it is input by definition. - */ - if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - int val; - - memset(&ghd, 0, sizeof(ghd)); - - val = gpiod_get_value_cansleep(le->desc); - if (val < 0) - return val; - ghd.values[0] = val; - - if (copy_to_user(ip, &ghd, sizeof(ghd))) - return -EFAULT; - - return 0; - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long lineevent_ioctl_compat(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - return lineevent_ioctl(filep, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static const struct file_operations lineevent_fileops = { - .release = lineevent_release, - .read = lineevent_read, - .poll = lineevent_poll, - .owner = THIS_MODULE, - .llseek = noop_llseek, - .unlocked_ioctl = lineevent_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lineevent_ioctl_compat, -#endif -}; - -static irqreturn_t lineevent_irq_thread(int irq, void *p) -{ - struct lineevent_state *le = p; - struct gpioevent_data ge; - int ret; - - /* Do not leak kernel stack to userspace */ - memset(&ge, 0, sizeof(ge)); - - /* - * 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_ns(); - else - ge.timestamp = le->timestamp; - - if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE - && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { - int level = gpiod_get_value_cansleep(le->desc); - if (level) - /* Emit low-to-high event */ - ge.id = GPIOEVENT_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { - /* Emit low-to-high event */ - ge.id = GPIOEVENT_EVENT_RISING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { - /* Emit high-to-low event */ - ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else { - return IRQ_NONE; - } - - ret = kfifo_in_spinlocked_noirqsave(&le->events, &ge, - 1, &le->wait.lock); - if (ret) - wake_up_poll(&le->wait, EPOLLIN); - else - pr_debug_ratelimited("event FIFO is full - event dropped\n"); - - return IRQ_HANDLED; -} - -static irqreturn_t lineevent_irq_handler(int irq, void *p) -{ - struct lineevent_state *le = p; - - /* - * Just store the timestamp in hardirq context so we get it as - * close in time as possible to the actual event. - */ - le->timestamp = ktime_get_ns(); - - return IRQ_WAKE_THREAD; -} - -static int lineevent_create(struct gpio_device *gdev, void __user *ip) -{ - struct gpioevent_request eventreq; - struct lineevent_state *le; - struct gpio_desc *desc; - struct file *file; - u32 offset; - u32 lflags; - u32 eflags; - int fd; - int ret; - int irqflags = 0; - - if (copy_from_user(&eventreq, ip, sizeof(eventreq))) - return -EFAULT; - - offset = eventreq.lineoffset; - lflags = eventreq.handleflags; - eflags = eventreq.eventflags; - - 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) || - (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) - return -EINVAL; - - /* This is just wrong: we don't look for events on output lines */ - if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || - (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* Only one bias flag can be set. */ - if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && - (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || - ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && - (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) - return -EINVAL; - - le = kzalloc(sizeof(*le), GFP_KERNEL); - if (!le) - return -ENOMEM; - le->gdev = gdev; - get_device(&gdev->dev); - - /* Make sure this is terminated */ - eventreq.consumer_label[sizeof(eventreq.consumer_label)-1] = '\0'; - if (strlen(eventreq.consumer_label)) { - le->label = kstrdup(eventreq.consumer_label, - GFP_KERNEL); - if (!le->label) { - ret = -ENOMEM; - goto out_free_le; - } - } - - ret = gpiod_request(desc, le->label); - if (ret) - goto out_free_label; - le->desc = desc; - le->eflags = eflags; - - if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) - set_bit(FLAG_ACTIVE_LOW, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) - set_bit(FLAG_BIAS_DISABLE, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) - set_bit(FLAG_PULL_DOWN, &desc->flags); - if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) - set_bit(FLAG_PULL_UP, &desc->flags); - - ret = gpiod_direction_input(desc); - if (ret) - goto out_free_desc; - - atomic_notifier_call_chain(&desc->gdev->notifier, - GPIOLINE_CHANGED_REQUESTED, desc); - - le->irq = gpiod_to_irq(desc); - if (le->irq <= 0) { - ret = -ENODEV; - goto out_free_desc; - } - - if (eflags & GPIOEVENT_REQUEST_RISING_EDGE) - irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING; - if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE) - irqflags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ? - IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING; - irqflags |= IRQF_ONESHOT; - - INIT_KFIFO(le->events); - init_waitqueue_head(&le->wait); - - /* Request a thread to read the events */ - ret = request_threaded_irq(le->irq, - lineevent_irq_handler, - lineevent_irq_thread, - irqflags, - le->label, - le); - if (ret) - goto out_free_desc; - - fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); - if (fd < 0) { - ret = fd; - goto out_free_irq; - } - - file = anon_inode_getfile("gpio-event", - &lineevent_fileops, - le, - O_RDONLY | O_CLOEXEC); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto out_put_unused_fd; - } - - eventreq.fd = fd; - if (copy_to_user(ip, &eventreq, sizeof(eventreq))) { - /* - * fput() will trigger the release() callback, so do not go onto - * the regular error cleanup path here. - */ - fput(file); - put_unused_fd(fd); - return -EFAULT; - } - - fd_install(fd, file); - - return 0; - -out_put_unused_fd: - put_unused_fd(fd); -out_free_irq: - free_irq(le->irq, le); -out_free_desc: - gpiod_free(le->desc); -out_free_label: - kfree(le->label); -out_free_le: - kfree(le); - put_device(&gdev->dev); - return ret; -} - -static void gpio_desc_to_lineinfo(struct gpio_desc *desc, - struct gpioline_info *info) -{ - struct gpio_chip *gc = desc->gdev->chip; - bool ok_for_pinctrl; - unsigned long flags; - - /* - * This function takes a mutex so we must check this before taking - * the spinlock. - * - * FIXME: find a non-racy way to retrieve this information. Maybe a - * lock common to both frameworks? - */ - ok_for_pinctrl = - pinctrl_gpio_can_use_line(gc->base + info->line_offset); - - spin_lock_irqsave(&gpio_lock, flags); - - if (desc->name) { - strncpy(info->name, desc->name, sizeof(info->name)); - info->name[sizeof(info->name) - 1] = '\0'; - } else { - info->name[0] = '\0'; - } - - if (desc->label) { - strncpy(info->consumer, desc->label, sizeof(info->consumer)); - info->consumer[sizeof(info->consumer) - 1] = '\0'; - } else { - info->consumer[0] = '\0'; - } - - /* - * Userspace only need to know that the kernel is using this GPIO so - * it can't use it. - */ - info->flags = 0; - if (test_bit(FLAG_REQUESTED, &desc->flags) || - test_bit(FLAG_IS_HOGGED, &desc->flags) || - test_bit(FLAG_USED_AS_IRQ, &desc->flags) || - test_bit(FLAG_EXPORT, &desc->flags) || - test_bit(FLAG_SYSFS, &desc->flags) || - !ok_for_pinctrl) - info->flags |= GPIOLINE_FLAG_KERNEL; - if (test_bit(FLAG_IS_OUT, &desc->flags)) - info->flags |= GPIOLINE_FLAG_IS_OUT; - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) - info->flags |= GPIOLINE_FLAG_ACTIVE_LOW; - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_DRAIN | - GPIOLINE_FLAG_IS_OUT); - if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) - info->flags |= (GPIOLINE_FLAG_OPEN_SOURCE | - GPIOLINE_FLAG_IS_OUT); - if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_DISABLE; - if (test_bit(FLAG_PULL_DOWN, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; - if (test_bit(FLAG_PULL_UP, &desc->flags)) - info->flags |= GPIOLINE_FLAG_BIAS_PULL_UP; - - spin_unlock_irqrestore(&gpio_lock, flags); -} - -struct gpio_chardev_data { - struct gpio_device *gdev; - wait_queue_head_t wait; - DECLARE_KFIFO(events, struct gpioline_info_changed, 32); - struct notifier_block lineinfo_changed_nb; - unsigned long *watched_lines; -}; - -/* - * gpio_ioctl() - ioctl handler for the GPIO chardev - */ -static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->gdev; - struct gpio_chip *gc = gdev->chip; - void __user *ip = (void __user *)arg; - struct gpio_desc *desc; - __u32 offset; - int hwgpio; - - /* We fail any subsequent ioctl():s when the chip is gone */ - if (!gc) - return -ENODEV; - - /* Fill in the struct and pass to userspace */ - if (cmd == GPIO_GET_CHIPINFO_IOCTL) { - struct gpiochip_info chipinfo; - - memset(&chipinfo, 0, sizeof(chipinfo)); - - strncpy(chipinfo.name, dev_name(&gdev->dev), - sizeof(chipinfo.name)); - chipinfo.name[sizeof(chipinfo.name)-1] = '\0'; - strncpy(chipinfo.label, gdev->label, - sizeof(chipinfo.label)); - chipinfo.label[sizeof(chipinfo.label)-1] = '\0'; - chipinfo.lines = gdev->ngpio; - if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) - return -EFAULT; - return 0; - } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { - struct gpioline_info lineinfo; - - if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) - return -EFAULT; - - desc = gpiochip_get_desc(gc, lineinfo.line_offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - hwgpio = gpio_chip_hwgpio(desc); - - gpio_desc_to_lineinfo(desc, &lineinfo); - - if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) - return -EFAULT; - return 0; - } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { - return linehandle_create(gdev, ip); - } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { - return lineevent_create(gdev, ip); - } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { - struct gpioline_info lineinfo; - - if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) - return -EFAULT; - - desc = gpiochip_get_desc(gc, lineinfo.line_offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - hwgpio = gpio_chip_hwgpio(desc); - - if (test_bit(hwgpio, priv->watched_lines)) - return -EBUSY; - - gpio_desc_to_lineinfo(desc, &lineinfo); - - if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) - return -EFAULT; - - set_bit(hwgpio, priv->watched_lines); - return 0; - } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { - if (copy_from_user(&offset, ip, sizeof(offset))) - return -EFAULT; - - desc = gpiochip_get_desc(gc, offset); - if (IS_ERR(desc)) - return PTR_ERR(desc); - - hwgpio = gpio_chip_hwgpio(desc); - - if (!test_bit(hwgpio, priv->watched_lines)) - return -EBUSY; - - clear_bit(hwgpio, priv->watched_lines); - return 0; - } - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static long gpio_ioctl_compat(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - return gpio_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -static struct gpio_chardev_data * -to_gpio_chardev_data(struct notifier_block *nb) -{ - return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); -} - -static int lineinfo_changed_notify(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct gpio_chardev_data *priv = to_gpio_chardev_data(nb); - struct gpioline_info_changed chg; - struct gpio_desc *desc = data; - int ret; - - if (!test_bit(gpio_chip_hwgpio(desc), priv->watched_lines)) - return NOTIFY_DONE; - - memset(&chg, 0, sizeof(chg)); - chg.info.line_offset = gpio_chip_hwgpio(desc); - chg.event_type = action; - chg.timestamp = ktime_get_ns(); - gpio_desc_to_lineinfo(desc, &chg.info); - - ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock); - if (ret) - wake_up_poll(&priv->wait, EPOLLIN); - else - pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); - - return NOTIFY_OK; -} - -static __poll_t lineinfo_watch_poll(struct file *filep, - struct poll_table_struct *pollt) -{ - struct gpio_chardev_data *priv = filep->private_data; - __poll_t events = 0; - - poll_wait(filep, &priv->wait, pollt); - - if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, - &priv->wait.lock)) - events = EPOLLIN | EPOLLRDNORM; - - return events; -} - -static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, - size_t count, loff_t *off) -{ - struct gpio_chardev_data *priv = filep->private_data; - struct gpioline_info_changed event; - ssize_t bytes_read = 0; - int ret; - - if (count < sizeof(event)) - return -EINVAL; - - do { - spin_lock(&priv->wait.lock); - if (kfifo_is_empty(&priv->events)) { - if (bytes_read) { - spin_unlock(&priv->wait.lock); - return bytes_read; - } - - if (filep->f_flags & O_NONBLOCK) { - spin_unlock(&priv->wait.lock); - return -EAGAIN; - } - - ret = wait_event_interruptible_locked(priv->wait, - !kfifo_is_empty(&priv->events)); - if (ret) { - spin_unlock(&priv->wait.lock); - return ret; - } - } - - ret = kfifo_out(&priv->events, &event, 1); - spin_unlock(&priv->wait.lock); - if (ret != 1) { - ret = -EIO; - break; - /* We should never get here. See lineevent_read(). */ - } - - if (copy_to_user(buf + bytes_read, &event, sizeof(event))) - return -EFAULT; - bytes_read += sizeof(event); - } while (count >= bytes_read + sizeof(event)); - - return bytes_read; -} - -/** - * gpio_chrdev_open() - open the chardev for ioctl operations - * @inode: inode for this chardev - * @filp: file struct for storing private data - * Returns 0 on success - */ -static int gpio_chrdev_open(struct inode *inode, struct file *filp) -{ - struct gpio_device *gdev = container_of(inode->i_cdev, - struct gpio_device, chrdev); - struct gpio_chardev_data *priv; - int ret = -ENOMEM; - - /* Fail on open if the backing gpiochip is gone */ - if (!gdev->chip) - return -ENODEV; - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); - if (!priv->watched_lines) - goto out_free_priv; - - init_waitqueue_head(&priv->wait); - INIT_KFIFO(priv->events); - priv->gdev = gdev; - - priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; - ret = atomic_notifier_chain_register(&gdev->notifier, - &priv->lineinfo_changed_nb); - if (ret) - goto out_free_bitmap; - - get_device(&gdev->dev); - filp->private_data = priv; - - ret = nonseekable_open(inode, filp); - if (ret) - goto out_unregister_notifier; - - return ret; - -out_unregister_notifier: - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); -out_free_bitmap: - bitmap_free(priv->watched_lines); -out_free_priv: - kfree(priv); - return ret; -} - -/** - * gpio_chrdev_release() - close chardev after ioctl operations - * @inode: inode for this chardev - * @filp: file struct for storing private data - * Returns 0 on success - */ -static int gpio_chrdev_release(struct inode *inode, struct file *filp) -{ - struct gpio_chardev_data *priv = filp->private_data; - struct gpio_device *gdev = priv->gdev; - - bitmap_free(priv->watched_lines); - atomic_notifier_chain_unregister(&gdev->notifier, - &priv->lineinfo_changed_nb); - put_device(&gdev->dev); - kfree(priv); - - return 0; -} - -static const struct file_operations gpio_fileops = { - .release = gpio_chrdev_release, - .open = gpio_chrdev_open, - .poll = lineinfo_watch_poll, - .read = lineinfo_watch_read, - .owner = THIS_MODULE, - .llseek = no_llseek, - .unlocked_ioctl = gpio_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = gpio_ioctl_compat, -#endif -}; - static void gpiodevice_release(struct device *dev) { struct gpio_device *gdev = dev_get_drvdata(dev); @@ -1539,17 +435,10 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) { int ret; - cdev_init(&gdev->chrdev, &gpio_fileops); - gdev->chrdev.owner = THIS_MODULE; - gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id); - - ret = cdev_device_add(&gdev->chrdev, &gdev->dev); + ret = gpiolib_cdev_register(gdev, gpio_devt); if (ret) return ret; - chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", - MAJOR(gpio_devt), gdev->id); - ret = gpiochip_sysfs_register(gdev); if (ret) goto err_remove_device; @@ -1562,7 +451,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) return 0; err_remove_device: - cdev_device_del(&gdev->chrdev, &gdev->dev); + gpiolib_cdev_unregister(gdev); return ret; } @@ -1884,7 +773,7 @@ void gpiochip_remove(struct gpio_chip *gc) * be removed, else it will be dangling until the last user is * gone. */ - cdev_device_del(&gdev->chrdev, &gdev->dev); + gpiolib_cdev_unregister(gdev); put_device(&gdev->dev); } EXPORT_SYMBOL_GPL(gpiochip_remove); @@ -3705,10 +2594,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, bitmap_xor(value_bitmap, value_bitmap, array_info->invert_mask, array_size); - if (bitmap_full(array_info->get_mask, array_size)) - return 0; - i = find_first_zero_bit(array_info->get_mask, array_size); + if (i == array_size) + return 0; } else { array_info = NULL; } @@ -3989,10 +2877,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, gpio_chip_set_multiple(array_info->chip, array_info->set_mask, value_bitmap); - if (bitmap_full(array_info->set_mask, array_size)) - return 0; - i = find_first_zero_bit(array_info->set_mask, array_size); + if (i == array_size) + return 0; } else { array_info = NULL; } diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 52386ad29f28..9c5213087659 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1486,14 +1486,11 @@ static void at91_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) int i; struct at91_gpio_chip *at91_gpio = gpiochip_get_data(chip); void __iomem *pio = at91_gpio->regbase; + const char *gpio_label; - for (i = 0; i < chip->ngpio; i++) { + for_each_requested_gpio(chip, i, gpio_label) { unsigned mask = pin_to_mask(i); - const char *gpio_label; - gpio_label = gpiochip_is_requested(chip, i); - if (!gpio_label) - continue; mode = at91_gpio->ops->get_periph(pio, mask); seq_printf(s, "[%s] GPIO%s%d: ", gpio_label, chip->label, i); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c4f272af7af5..db82451776fc 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -474,6 +474,22 @@ struct gpio_chip { extern const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset); +/** + * for_each_requested_gpio_in_range - iterates over requested GPIOs in a given range + * @chip: the chip to query + * @i: loop variable + * @base: first GPIO in the range + * @size: amount of GPIOs to check starting from @base + * @label: label of current GPIO + */ +#define for_each_requested_gpio_in_range(chip, i, base, size, label) \ + for (i = 0; i < size; i++) \ + if ((label = gpiochip_is_requested(chip, base + i)) == NULL) {} else + +/* Iterates over all requested GPIO of the given @chip */ +#define for_each_requested_gpio(chip, i, label) \ + for_each_requested_gpio_in_range(chip, i, 0, chip->ngpio, label) + /* add/remove chips */ extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, @@ -481,7 +497,7 @@ extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, /** * gpiochip_add_data() - register a gpio_chip - * @chip: the chip to register, with chip->base initialized + * @gc: the chip to register, with chip->base initialized * @data: driver-private data associated with this chip * * Context: potentially before irqs will work |