diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-31 21:25:27 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-31 21:25:27 +0100 |
commit | 9798f5178f5791f964562eccedcf4dabe02fd825 (patch) | |
tree | d0d76b4202eaa3d3be3aaa05206e57e02e16ac5b /drivers/gpio | |
parent | Merge tag 'leds_for_4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/gi... (diff) | |
parent | gpio: Timestamp events in hardirq handler (diff) | |
download | linux-9798f5178f5791f964562eccedcf4dabe02fd825.tar.xz linux-9798f5178f5791f964562eccedcf4dabe02fd825.zip |
Merge tag 'gpio-v4.16-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"The is the bulk of GPIO changes for the v4.16 kernel cycle. It is
pretty calm this time around I think. I even got time to get to things
like starting to clean up header includes.
Core changes:
- Disallow open drain and open source flags to be set simultaneously.
This doesn't make electrical sense, and would the hardware actually
respond to this setting, the result would be short circuit.
- ACPI GPIO has a new core infrastructure for handling quirks. The
quirks are there to deal with broken ACPI tables centrally instead
of pushing the work to individual drivers. In the world of BIOS
writers, the ACPI tables are perfect. Until they find a mistake in
it. When such a mistake is found, we can patch it with a quirk. It
should never happen, the problem is that it happens. So we
accomodate for it.
- Several documentation updates.
- Revert the patch setting up initial direction state from reading
the device. This was causing bad things for drivers that can't read
status on all its pins. It is only affecting debugfs information
quality.
- Label descriptors with the device name if no explicit label is
passed in.
- Pave the ground for transitioning SPI and regulators to use GPIO
descriptors by implementing some quirks in the device tree GPIO
parsing code.
New drivers:
- New driver for the Access PCIe IDIO 24 family.
Other:
- Major refactorings and improvements to the GPIO mockup driver used
for test and verification.
- Moved the AXP209 driver over to pin control since it gained a pin
control back-end. These patches will appear (with the same hashes)
in the pin control pull request as well.
- Convert the onewire GPIO driver w1-gpio to use descriptors. This is
merged here since the W1 maintainers send very few pull requests
and he ACKed it.
- Start to clean up driver headers using <linux/gpio.h> to just use
<linux/gpio/driver.h> as appropriate"
* tag 'gpio-v4.16-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (103 commits)
gpio: Timestamp events in hardirq handler
gpio: Fix kernel stack leak to userspace
gpio: Fix a documentation spelling mistake
gpio: Documentation update
gpiolib: remove redundant initialization of pointer desc
gpio: of: Fix NPE from OF flags
gpio: stmpe: Delete an unnecessary variable initialisation in stmpe_gpio_probe()
gpio: stmpe: Move an assignment in stmpe_gpio_probe()
gpio: stmpe: Improve a size determination in stmpe_gpio_probe()
gpio: stmpe: Use seq_putc() in stmpe_dbg_show()
gpio: No NULL owner
gpio: stmpe: i2c transfer are forbiden in atomic context
gpio: davinci: Include proper header
gpio: da905x: Include proper header
gpio: cs5535: Include proper header
gpio: crystalcove: Include proper header
gpio: bt8xx: Include proper header
gpio: bcm-kona: Include proper header
gpio: arizona: Include proper header
gpio: amd8111: Include proper header
...
Diffstat (limited to 'drivers/gpio')
35 files changed, 1951 insertions, 533 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d6a8e851ad13..8dbb2280538d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -122,12 +122,6 @@ config GPIO_ATH79 Select this option to enable GPIO driver for Atheros AR71XX/AR724X/AR913X SoC devices. -config GPIO_AXP209 - tristate "X-Powers AXP209 PMIC GPIO Support" - depends on MFD_AXP20X - help - Say yes to enable GPIO support for the AXP209 PMIC - config GPIO_BCM_KONA bool "Broadcom Kona GPIO" depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST) @@ -704,6 +698,22 @@ config GPIO_TS5500 blocks of the TS-5500: DIO1, DIO2 and the LCD port, and the TS-5600 LCD port. +config GPIO_WINBOND + tristate "Winbond Super I/O GPIO support" + depends on ISA_BUS_API + help + This option enables support for GPIOs found on Winbond Super I/O + chips. + Currently, only W83627UHG (also known as Nuvoton NCT6627UD) is + supported. + + You will need to provide a module parameter "gpios", or a + boot-time parameter "gpio_winbond.gpios" with a bitmask of GPIO + ports to enable (bit 0 is GPIO1, bit 1 is GPIO2, etc.). + + To compile this driver as a module, choose M here: the module will + be called gpio-winbond. + config GPIO_WS16C48 tristate "WinSystems WS16C48 GPIO support" depends on ISA_BUS_API @@ -1234,6 +1244,16 @@ config GPIO_PCI_IDIO_16 low). Input filter control is not supported by this driver, and the input filters are deactivated by this driver. +config GPIO_PCIE_IDIO_24 + tristate "ACCES PCIe-IDIO-24 GPIO support" + select GPIOLIB_IRQCHIP + help + Enables GPIO support for the ACCES PCIe-IDIO-24 family (PCIe-IDIO-24, + PCIe-IDI-24, PCIe-IDO-24, PCIe-IDIO-12). An interrupt is generated + when any of the inputs change state (low to high or high to low). + Input filter control is not supported by this driver, and the input + filters are deactivated by this driver. + config GPIO_RDC321X tristate "RDC R-321x GPIO support" select MFD_CORE diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4bc24febb889..cccb0d40846c 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -32,7 +32,6 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o -obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o @@ -96,6 +95,7 @@ obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o +obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o @@ -140,6 +140,7 @@ obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o +obj-$(CONFIG_GPIO_WINBOND) += gpio-winbond.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index afbff155a0ba..e82cc763633c 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -125,6 +125,48 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, EXPORT_SYMBOL(devm_gpiod_get_index); /** + * devm_gpiod_get_from_of_node() - obtain a GPIO from an OF node + * @dev: device for lifecycle management + * @node: handle of the OF node + * @propname: name of the DT property representing the GPIO + * @index: index of the GPIO to obtain for the consumer + * @dflags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * Returns: + * On successful request the GPIO pin is configured in accordance with + * provided @dflags. + * + * In case of error an ERR_PTR() is returned. + */ +struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev, + struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + struct gpio_desc **dr; + struct gpio_desc *desc; + + dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + desc = gpiod_get_from_of_node(node, propname, index, dflags, label); + if (IS_ERR(desc)) { + devres_free(dr); + return desc; + } + + *dr = desc; + devres_add(dev, dr); + + return desc; +} +EXPORT_SYMBOL(devm_gpiod_get_from_of_node); + +/** * devm_fwnode_get_index_gpiod_from_child - get a GPIO descriptor from a * device's child node * @dev: GPIO consumer diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c index 15a1f4b348c4..fb7b620763a2 100644 --- a/drivers/gpio/gpio-74x164.c +++ b/drivers/gpio/gpio-74x164.c @@ -9,12 +9,11 @@ * published by the Free Software Foundation. */ -#include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/mutex.h> #include <linux/spi/spi.h> -#include <linux/gpio.h> -#include <linux/of_gpio.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/module.h> diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c index abf199609546..21452622d954 100644 --- a/drivers/gpio/gpio-adp5520.c +++ b/drivers/gpio/gpio-adp5520.c @@ -12,8 +12,7 @@ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/mfd/adp5520.h> - -#include <linux/gpio.h> +#include <linux/gpio/driver.h> struct adp5520_gpio { struct device *master; diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index e717f8dc3966..3530ccd17e04 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -12,7 +12,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/i2c.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/interrupt.h> #include <linux/irq.h> diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 8e76d390e653..8c3ff6e2366f 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -18,7 +18,8 @@ #include <linux/io.h> #include <linux/module.h> -#include <linux/of_gpio.h> +#include <linux/gpio/driver.h> +#include <linux/of_gpio.h> /* For of_mm_gpio_chip */ #include <linux/platform_device.h> #define ALTERA_GPIO_MAX_NGPIO 32 diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 30ad7d7c1678..fdcebe59510d 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -28,7 +28,7 @@ #include <linux/ioport.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/pci.h> #include <linux/spinlock.h> diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index d4e6ba0301bc..ba51ea15f379 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -15,7 +15,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/seq_file.h> diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 6b3ca6601af2..77e485557498 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -60,6 +60,7 @@ struct aspeed_gpio_bank { uint16_t val_regs; uint16_t irq_regs; uint16_t debounce_regs; + uint16_t tolerance_regs; const char names[4][3]; }; @@ -70,48 +71,56 @@ static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { .val_regs = 0x0000, .irq_regs = 0x0008, .debounce_regs = 0x0040, + .tolerance_regs = 0x001c, .names = { "A", "B", "C", "D" }, }, { .val_regs = 0x0020, .irq_regs = 0x0028, .debounce_regs = 0x0048, + .tolerance_regs = 0x003c, .names = { "E", "F", "G", "H" }, }, { .val_regs = 0x0070, .irq_regs = 0x0098, .debounce_regs = 0x00b0, + .tolerance_regs = 0x00ac, .names = { "I", "J", "K", "L" }, }, { .val_regs = 0x0078, .irq_regs = 0x00e8, .debounce_regs = 0x0100, + .tolerance_regs = 0x00fc, .names = { "M", "N", "O", "P" }, }, { .val_regs = 0x0080, .irq_regs = 0x0118, .debounce_regs = 0x0130, + .tolerance_regs = 0x012c, .names = { "Q", "R", "S", "T" }, }, { .val_regs = 0x0088, .irq_regs = 0x0148, .debounce_regs = 0x0160, + .tolerance_regs = 0x015c, .names = { "U", "V", "W", "X" }, }, { .val_regs = 0x01E0, .irq_regs = 0x0178, .debounce_regs = 0x0190, + .tolerance_regs = 0x018c, .names = { "Y", "Z", "AA", "AB" }, }, { - .val_regs = 0x01E8, - .irq_regs = 0x01A8, + .val_regs = 0x01e8, + .irq_regs = 0x01a8, .debounce_regs = 0x01c0, + .tolerance_regs = 0x01bc, .names = { "AC", "", "", "" }, }, }; @@ -140,7 +149,7 @@ static const struct aspeed_gpio_bank *to_bank(unsigned int offset) { unsigned int bank = GPIO_BANK(offset); - WARN_ON(bank > ARRAY_SIZE(aspeed_gpio_banks)); + WARN_ON(bank >= ARRAY_SIZE(aspeed_gpio_banks)); return &aspeed_gpio_banks[bank]; } @@ -534,6 +543,30 @@ static int aspeed_gpio_setup_irqs(struct aspeed_gpio *gpio, return 0; } +static int aspeed_gpio_reset_tolerance(struct gpio_chip *chip, + unsigned int offset, bool enable) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(chip); + const struct aspeed_gpio_bank *bank; + unsigned long flags; + u32 val; + + bank = to_bank(offset); + + spin_lock_irqsave(&gpio->lock, flags); + val = readl(gpio->base + bank->tolerance_regs); + + if (enable) + val |= GPIO_BIT(offset); + else + val &= ~GPIO_BIT(offset); + + writel(val, gpio->base + bank->tolerance_regs); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + static int aspeed_gpio_request(struct gpio_chip *chip, unsigned int offset) { if (!have_gpio(gpiochip_get_data(chip), offset)) @@ -771,6 +804,8 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, param == PIN_CONFIG_DRIVE_OPEN_SOURCE) /* Return -ENOTSUPP to trigger emulation, as per datasheet */ return -ENOTSUPP; + else if (param == PIN_CONFIG_PERSIST_STATE) + return aspeed_gpio_reset_tolerance(chip, offset, arg); return -ENOTSUPP; } diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index 5fad89dfab7e..3ae7c1876bf4 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -324,3 +324,6 @@ static struct platform_driver ath79_gpio_driver = { }; module_platform_driver(ath79_gpio_driver); + +MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X GPIO API support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-axp209.c b/drivers/gpio/gpio-axp209.c deleted file mode 100644 index 4a346b7b4172..000000000000 --- a/drivers/gpio/gpio-axp209.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * AXP20x GPIO driver - * - * Copyright (C) 2016 Maxime Ripard <maxime.ripard@free-electrons.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include <linux/bitops.h> -#include <linux/device.h> -#include <linux/gpio/driver.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/mfd/axp20x.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/regmap.h> -#include <linux/slab.h> - -#define AXP20X_GPIO_FUNCTIONS 0x7 -#define AXP20X_GPIO_FUNCTION_OUT_LOW 0 -#define AXP20X_GPIO_FUNCTION_OUT_HIGH 1 -#define AXP20X_GPIO_FUNCTION_INPUT 2 - -struct axp20x_gpio { - struct gpio_chip chip; - struct regmap *regmap; -}; - -static int axp20x_gpio_get_reg(unsigned offset) -{ - switch (offset) { - case 0: - return AXP20X_GPIO0_CTRL; - case 1: - return AXP20X_GPIO1_CTRL; - case 2: - return AXP20X_GPIO2_CTRL; - } - - return -EINVAL; -} - -static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - int reg; - - reg = axp20x_gpio_get_reg(offset); - if (reg < 0) - return reg; - - return regmap_update_bits(gpio->regmap, reg, - AXP20X_GPIO_FUNCTIONS, - AXP20X_GPIO_FUNCTION_INPUT); -} - -static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - unsigned int val; - int ret; - - ret = regmap_read(gpio->regmap, AXP20X_GPIO20_SS, &val); - if (ret) - return ret; - - return !!(val & BIT(offset + 4)); -} - -static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - unsigned int val; - int reg, ret; - - reg = axp20x_gpio_get_reg(offset); - if (reg < 0) - return reg; - - ret = regmap_read(gpio->regmap, reg, &val); - if (ret) - return ret; - - /* - * This shouldn't really happen if the pin is in use already, - * or if it's not in use yet, it doesn't matter since we're - * going to change the value soon anyway. Default to output. - */ - if ((val & AXP20X_GPIO_FUNCTIONS) > 2) - return 0; - - /* - * The GPIO directions are the three lowest values. - * 2 is input, 0 and 1 are output - */ - return val & 2; -} - -static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct axp20x_gpio *gpio = gpiochip_get_data(chip); - int reg; - - reg = axp20x_gpio_get_reg(offset); - if (reg < 0) - return reg; - - return regmap_update_bits(gpio->regmap, reg, - AXP20X_GPIO_FUNCTIONS, - value ? AXP20X_GPIO_FUNCTION_OUT_HIGH - : AXP20X_GPIO_FUNCTION_OUT_LOW); -} - -static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset, - int value) -{ - axp20x_gpio_output(chip, offset, value); -} - -static int axp20x_gpio_probe(struct platform_device *pdev) -{ - struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); - struct axp20x_gpio *gpio; - int ret; - - if (!of_device_is_available(pdev->dev.of_node)) - return -ENODEV; - - if (!axp20x) { - dev_err(&pdev->dev, "Parent drvdata not set\n"); - return -EINVAL; - } - - gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) - return -ENOMEM; - - gpio->chip.base = -1; - gpio->chip.can_sleep = true; - gpio->chip.parent = &pdev->dev; - gpio->chip.label = dev_name(&pdev->dev); - gpio->chip.owner = THIS_MODULE; - gpio->chip.get = axp20x_gpio_get; - gpio->chip.get_direction = axp20x_gpio_get_direction; - gpio->chip.set = axp20x_gpio_set; - gpio->chip.direction_input = axp20x_gpio_input; - gpio->chip.direction_output = axp20x_gpio_output; - gpio->chip.ngpio = 3; - - gpio->regmap = axp20x->regmap; - - ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); - if (ret) { - dev_err(&pdev->dev, "Failed to register GPIO chip\n"); - return ret; - } - - dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n"); - - return 0; -} - -static const struct of_device_id axp20x_gpio_match[] = { - { .compatible = "x-powers,axp209-gpio" }, - { } -}; -MODULE_DEVICE_TABLE(of, axp20x_gpio_match); - -static struct platform_driver axp20x_gpio_driver = { - .probe = axp20x_gpio_probe, - .driver = { - .name = "axp20x-gpio", - .of_match_table = axp20x_gpio_match, - }, -}; - -module_platform_driver(axp20x_gpio_driver); - -MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); -MODULE_DESCRIPTION("AXP20x PMIC GPIO driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 76861a00bb92..eb8369b21e90 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -17,7 +17,7 @@ #include <linux/bitops.h> #include <linux/err.h> #include <linux/io.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/init.h> @@ -127,7 +127,7 @@ static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio) u32 val; val = readl(reg_base + GPIO_CONTROL(gpio)) & GPIO_GPCTR0_IOTR_MASK; - return val ? GPIOF_DIR_IN : GPIOF_DIR_OUT; + return !!val; } static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) @@ -144,7 +144,7 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) raw_spin_lock_irqsave(&kona_gpio->lock, flags); /* this function only applies to output pin */ - if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN) + if (bcm_kona_gpio_get_dir(chip, gpio) == 1) goto out; reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); @@ -170,7 +170,7 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) reg_base = kona_gpio->reg_base; raw_spin_lock_irqsave(&kona_gpio->lock, flags); - if (bcm_kona_gpio_get_dir(chip, gpio) == GPIOF_DIR_IN) + if (bcm_kona_gpio_get_dir(chip, gpio) == 1) reg_offset = GPIO_IN_STATUS(bank_id); else reg_offset = GPIO_OUT_STATUS(bank_id); diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index bb4f8cf18bd9..16c7f9f49416 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -19,7 +19,6 @@ #include <linux/irqdomain.h> #include <linux/irqchip/chained_irq.h> #include <linux/interrupt.h> -#include <linux/bitops.h> enum gio_reg_index { GIO_REG_ODEN = 0, diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index acefb25e8eca..b8ec75cbd4b5 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -46,7 +46,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/spinlock.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/slab.h> /* Steal the hardware definitions from the bttv driver. */ diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index b6f0f729656c..58531d8b8c6e 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -18,7 +18,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/seq_file.h> #include <linux/bitops.h> #include <linux/regmap.h> diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c index 90278b19aa0e..8814c8f47e57 100644 --- a/drivers/gpio/gpio-cs5535.c +++ b/drivers/gpio/gpio-cs5535.c @@ -12,7 +12,7 @@ #include <linux/spinlock.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/io.h> #include <linux/cs5535.h> #include <asm/msr.h> diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c index dd8977cf3e85..b6d3e997eb26 100644 --- a/drivers/gpio/gpio-da9052.c +++ b/drivers/gpio/gpio-da9052.c @@ -15,7 +15,7 @@ #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/syscalls.h> #include <linux/seq_file.h> diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c index 82053b52cba0..2f1b5d23b10c 100644 --- a/drivers/gpio/gpio-da9055.c +++ b/drivers/gpio/gpio-da9055.c @@ -13,7 +13,7 @@ */ #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/mfd/da9055/core.h> #include <linux/mfd/da9055/reg.h> diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index e4b3d7db68c9..0b951ca78ec4 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -9,7 +9,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/clk.h> diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index 7b3394fdc624..b7a3a2db699b 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -176,8 +176,8 @@ static int ftgpio_gpio_probe(struct platform_device *pdev) return PTR_ERR(g->base); irq = platform_get_irq(pdev, 0); - if (!irq) - return -EINVAL; + if (irq <= 0) + return irq ? irq : -EINVAL; ret = bgpio_init(&g->gc, dev, 4, g->base + GPIO_DATA_IN, diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 98c7ff2a76e7..8d62db447ec1 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -58,3 +58,7 @@ static int __init iop3xx_gpio_init(void) return platform_driver_register(&iop3xx_gpio_driver); } arch_initcall(iop3xx_gpio_init); + +MODULE_DESCRIPTION("GPIO handling for Intel IOP3xx processors"); +MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index d43d0a2cc4c5..efb46edff81f 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -414,6 +414,6 @@ static void __exit it87_gpio_exit(void) module_init(it87_gpio_init); module_exit(it87_gpio_exit); -MODULE_AUTHOR("Diego Elio Pettenò <flameeyes@flameeyes.eu>"); +MODULE_AUTHOR("Diego Elio Pettenò <flameeyes@flameeyes.eu>"); MODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index c04fae1ba32a..9d8bcc69f245 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -709,8 +709,7 @@ static int max732x_probe(struct i2c_client *client, return 0; out_failed: - if (chip->client_dummy) - i2c_unregister_device(chip->client_dummy); + i2c_unregister_device(chip->client_dummy); return ret; } @@ -734,8 +733,7 @@ static int max732x_remove(struct i2c_client *client) gpiochip_remove(&chip->gpio_chip); /* unregister any dummy i2c_client */ - if (chip->client_dummy) - i2c_unregister_device(chip->client_dummy); + i2c_unregister_device(chip->client_dummy); return 0; } diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 9532d86a82f7..3a545ad17817 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -27,13 +27,15 @@ #include "gpiolib.h" #define GPIO_MOCKUP_NAME "gpio-mockup" -#define GPIO_MOCKUP_MAX_GC 10 +#define GPIO_MOCKUP_MAX_GC 10 /* * We're storing two values per chip: the GPIO base and the number * of GPIO lines. */ #define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2) +#define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__) + enum { GPIO_MOCKUP_DIR_OUT = 0, GPIO_MOCKUP_DIR_IN = 1, @@ -46,7 +48,7 @@ enum { */ struct gpio_mockup_line_status { int dir; - bool value; + int value; }; struct gpio_mockup_chip { @@ -62,17 +64,33 @@ struct gpio_mockup_dbgfs_private { int offset; }; +struct gpio_mockup_platform_data { + int base; + int ngpio; + int index; + bool named_lines; +}; + static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; -static int gpio_mockup_params_nr; -module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); +static int gpio_mockup_num_ranges; +module_param_array(gpio_mockup_ranges, int, &gpio_mockup_num_ranges, 0400); static bool gpio_mockup_named_lines; module_param_named(gpio_mockup_named_lines, gpio_mockup_named_lines, bool, 0400); -static const char gpio_mockup_name_start = 'A'; static struct dentry *gpio_mockup_dbg_dir; +static int gpio_mockup_range_base(unsigned int index) +{ + return gpio_mockup_ranges[index * 2]; +} + +static int gpio_mockup_range_ngpio(unsigned int index) +{ + return gpio_mockup_ranges[index * 2 + 1]; +} + static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); @@ -80,16 +98,26 @@ static int gpio_mockup_get(struct gpio_chip *gc, unsigned int offset) return chip->lines[offset].value; } -static void gpio_mockup_set(struct gpio_chip *gc, unsigned int offset, - int value) +static void gpio_mockup_set(struct gpio_chip *gc, + unsigned int offset, int value) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); chip->lines[offset].value = !!value; } -static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, - int value) +static void gpio_mockup_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) +{ + unsigned int bit; + + for_each_set_bit(bit, mask, gc->ngpio) + gpio_mockup_set(gc, bit, test_bit(bit, bits)); + +} + +static int gpio_mockup_dirout(struct gpio_chip *gc, + unsigned int offset, int value) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); @@ -115,29 +143,6 @@ static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) return chip->lines[offset].dir; } -static int gpio_mockup_name_lines(struct device *dev, - struct gpio_mockup_chip *chip) -{ - struct gpio_chip *gc = &chip->gc; - char **names; - int i; - - names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL); - if (!names) - return -ENOMEM; - - for (i = 0; i < gc->ngpio; i++) { - names[i] = devm_kasprintf(dev, GFP_KERNEL, - "%s-%d", gc->label, i); - if (!names[i]) - return -ENOMEM; - } - - gc->names = (const char *const *)names; - - return 0; -} - static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); @@ -188,15 +193,21 @@ static void gpio_mockup_debugfs_setup(struct device *dev, struct gpio_mockup_chip *chip) { struct gpio_mockup_dbgfs_private *priv; - struct dentry *evfile; + struct dentry *evfile, *link; struct gpio_chip *gc; + const char *devname; char *name; int i; gc = &chip->gc; + devname = dev_name(&gc->gpiodev->dev); - chip->dbg_dir = debugfs_create_dir(gc->label, gpio_mockup_dbg_dir); - if (!chip->dbg_dir) + chip->dbg_dir = debugfs_create_dir(devname, gpio_mockup_dbg_dir); + if (IS_ERR_OR_NULL(chip->dbg_dir)) + goto err; + + link = debugfs_create_symlink(gc->label, gpio_mockup_dbg_dir, devname); + if (IS_ERR_OR_NULL(link)) goto err; for (i = 0; i < gc->ngpio; i++) { @@ -214,23 +225,63 @@ static void gpio_mockup_debugfs_setup(struct device *dev, evfile = debugfs_create_file(name, 0200, chip->dbg_dir, priv, &gpio_mockup_event_ops); - if (!evfile) + if (IS_ERR_OR_NULL(evfile)) goto err; } return; err: - dev_err(dev, "error creating debugfs directory\n"); + dev_err(dev, "error creating debugfs event files\n"); } -static int gpio_mockup_add(struct device *dev, - struct gpio_mockup_chip *chip, - const char *name, int base, int ngpio) +static int gpio_mockup_name_lines(struct device *dev, + struct gpio_mockup_chip *chip) { struct gpio_chip *gc = &chip->gc; - int ret; + char **names; + int i; + + names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL); + if (!names) + return -ENOMEM; + + for (i = 0; i < gc->ngpio; i++) { + names[i] = devm_kasprintf(dev, GFP_KERNEL, + "%s-%d", gc->label, i); + if (!names[i]) + return -ENOMEM; + } + + gc->names = (const char *const *)names; + + return 0; +} + +static int gpio_mockup_probe(struct platform_device *pdev) +{ + struct gpio_mockup_platform_data *pdata; + struct gpio_mockup_chip *chip; + struct gpio_chip *gc; + int rv, base, ngpio; + struct device *dev; + char *name; + + dev = &pdev->dev; + pdata = dev_get_platdata(dev); + base = pdata->base; + ngpio = pdata->ngpio; + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + name = devm_kasprintf(dev, GFP_KERNEL, "%s-%c", + pdev->name, pdata->index); + if (!name) + return -ENOMEM; + + gc = &chip->gc; gc->base = base; gc->ngpio = ngpio; gc->label = name; @@ -238,6 +289,7 @@ static int gpio_mockup_add(struct device *dev, gc->parent = dev; gc->get = gpio_mockup_get; gc->set = gpio_mockup_set; + gc->set_multiple = gpio_mockup_set_multiple; gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; @@ -248,19 +300,19 @@ static int gpio_mockup_add(struct device *dev, if (!chip->lines) return -ENOMEM; - if (gpio_mockup_named_lines) { - ret = gpio_mockup_name_lines(dev, chip); - if (ret) - return ret; + if (pdata->named_lines) { + rv = gpio_mockup_name_lines(dev, chip); + if (rv) + return rv; } - ret = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio); - if (ret) - return ret; + rv = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio); + if (rv < 0) + return rv; - ret = devm_gpiochip_add_data(dev, &chip->gc, chip); - if (ret) - return ret; + rv = devm_gpiochip_add_data(dev, &chip->gc, chip); + if (rv) + return rv; if (gpio_mockup_dbg_dir) gpio_mockup_debugfs_setup(dev, chip); @@ -268,58 +320,6 @@ static int gpio_mockup_add(struct device *dev, return 0; } -static int gpio_mockup_probe(struct platform_device *pdev) -{ - int ret, i, base, ngpio, num_chips; - struct device *dev = &pdev->dev; - struct gpio_mockup_chip *chips; - char *chip_name; - - if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2)) - return -EINVAL; - - /* Each chip is described by two values. */ - num_chips = gpio_mockup_params_nr / 2; - - chips = devm_kcalloc(dev, num_chips, sizeof(*chips), GFP_KERNEL); - if (!chips) - return -ENOMEM; - - platform_set_drvdata(pdev, chips); - - for (i = 0; i < num_chips; i++) { - base = gpio_mockup_ranges[i * 2]; - - if (base == -1) - ngpio = gpio_mockup_ranges[i * 2 + 1]; - else - ngpio = gpio_mockup_ranges[i * 2 + 1] - base; - - if (ngpio >= 0) { - chip_name = devm_kasprintf(dev, GFP_KERNEL, - "%s-%c", GPIO_MOCKUP_NAME, - gpio_mockup_name_start + i); - if (!chip_name) - return -ENOMEM; - - ret = gpio_mockup_add(dev, &chips[i], - chip_name, base, ngpio); - } else { - ret = -EINVAL; - } - - if (ret) { - dev_err(dev, - "adding gpiochip failed: %d (base: %d, ngpio: %d)\n", - ret, base, base < 0 ? ngpio : base + ngpio); - - return ret; - } - } - - return 0; -} - static struct platform_driver gpio_mockup_driver = { .driver = { .name = GPIO_MOCKUP_NAME, @@ -327,44 +327,88 @@ static struct platform_driver gpio_mockup_driver = { .probe = gpio_mockup_probe, }; -static struct platform_device *pdev; -static int __init mock_device_init(void) +static struct platform_device *gpio_mockup_pdevs[GPIO_MOCKUP_MAX_GC]; + +static void gpio_mockup_unregister_pdevs(void) { - int err; + struct platform_device *pdev; + int i; - gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); - if (!gpio_mockup_dbg_dir) - pr_err("%s: error creating debugfs directory\n", - GPIO_MOCKUP_NAME); + for (i = 0; i < GPIO_MOCKUP_MAX_GC; i++) { + pdev = gpio_mockup_pdevs[i]; - pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1); - if (!pdev) - return -ENOMEM; + if (pdev) + platform_device_unregister(pdev); + } +} - err = platform_device_add(pdev); - if (err) { - platform_device_put(pdev); - return err; +static int __init gpio_mockup_init(void) +{ + int i, num_chips, err = 0, index = 'A'; + struct gpio_mockup_platform_data pdata; + struct platform_device *pdev; + + if ((gpio_mockup_num_ranges < 2) || + (gpio_mockup_num_ranges % 2) || + (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES)) + return -EINVAL; + + /* Each chip is described by two values. */ + num_chips = gpio_mockup_num_ranges / 2; + + /* + * The second value in the <base GPIO - number of GPIOS> pair must + * always be greater than 0. + */ + for (i = 0; i < num_chips; i++) { + if (gpio_mockup_range_ngpio(i) < 0) + return -EINVAL; } + gpio_mockup_dbg_dir = debugfs_create_dir("gpio-mockup-event", NULL); + if (IS_ERR_OR_NULL(gpio_mockup_dbg_dir)) + gpio_mockup_err("error creating debugfs directory\n"); + err = platform_driver_register(&gpio_mockup_driver); if (err) { - platform_device_unregister(pdev); + gpio_mockup_err("error registering platform driver\n"); return err; } + for (i = 0; i < num_chips; i++) { + pdata.index = index++; + pdata.base = gpio_mockup_range_base(i); + pdata.ngpio = pdata.base < 0 + ? gpio_mockup_range_ngpio(i) + : gpio_mockup_range_ngpio(i) - pdata.base; + pdata.named_lines = gpio_mockup_named_lines; + + pdev = platform_device_register_resndata(NULL, + GPIO_MOCKUP_NAME, + i, NULL, 0, &pdata, + sizeof(pdata)); + if (IS_ERR(pdev)) { + gpio_mockup_err("error registering device"); + platform_driver_unregister(&gpio_mockup_driver); + gpio_mockup_unregister_pdevs(); + return PTR_ERR(pdev); + } + + gpio_mockup_pdevs[i] = pdev; + } + return 0; } -static void __exit mock_device_exit(void) +static void __exit gpio_mockup_exit(void) { debugfs_remove_recursive(gpio_mockup_dbg_dir); platform_driver_unregister(&gpio_mockup_driver); - platform_device_unregister(pdev); + gpio_mockup_unregister_pdevs(); } -module_init(mock_device_init); -module_exit(mock_device_exit); +module_init(gpio_mockup_init); +module_exit(gpio_mockup_exit); MODULE_AUTHOR("Kamlakant Patel <kamlakant.patel@broadcom.com>"); MODULE_AUTHOR("Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>"); diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index e136d666f1e5..ab5035b96886 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1058,7 +1058,9 @@ static void omap_gpio_mod_init(struct gpio_bank *bank) static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) { + struct gpio_irq_chip *irq; static int gpio; + const char *label; int irq_base = 0; int ret; @@ -1080,21 +1082,15 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) bank->chip.parent = &omap_mpuio_device.dev; bank->chip.base = OMAP_MPUIO(0); } else { - bank->chip.label = "gpio"; + label = devm_kasprintf(bank->chip.parent, GFP_KERNEL, "gpio-%d-%d", + gpio, gpio + bank->width - 1); + if (!label) + return -ENOMEM; + bank->chip.label = label; bank->chip.base = gpio; } bank->chip.ngpio = bank->width; - ret = gpiochip_add_data(&bank->chip, bank); - if (ret) { - dev_err(bank->chip.parent, - "Could not register gpio chip %d\n", ret); - return ret; - } - - if (!bank->is_mpuio) - gpio += bank->width; - #ifdef CONFIG_ARCH_OMAP1 /* * REVISIT: Once we have OMAP1 supporting SPARSE_IRQ, we can drop @@ -1115,25 +1111,30 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) irqc->irq_set_wake = NULL; } - ret = gpiochip_irqchip_add(&bank->chip, irqc, - irq_base, handle_bad_irq, - IRQ_TYPE_NONE); + irq = &bank->chip.irq; + irq->chip = irqc; + irq->handler = handle_bad_irq; + irq->default_type = IRQ_TYPE_NONE; + irq->num_parents = 1; + irq->parents = &bank->irq; + irq->first = irq_base; + ret = gpiochip_add_data(&bank->chip, bank); if (ret) { dev_err(bank->chip.parent, - "Couldn't add irqchip to gpiochip %d\n", ret); - gpiochip_remove(&bank->chip); - return -ENODEV; + "Could not register gpio chip %d\n", ret); + return ret; } - gpiochip_set_chained_irqchip(&bank->chip, irqc, bank->irq, NULL); - ret = devm_request_irq(bank->chip.parent, bank->irq, omap_gpio_irq_handler, 0, dev_name(bank->chip.parent), bank); if (ret) gpiochip_remove(&bank->chip); + if (!bank->is_mpuio) + gpio += bank->width; + return ret; } diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c new file mode 100644 index 000000000000..f666e2e69074 --- /dev/null +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * GPIO driver for the ACCES PCIe-IDIO-24 family + * Copyright (C) 2018 William Breathitt Gray + * + * 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 in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * This driver supports the following ACCES devices: PCIe-IDIO-24, + * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12. + */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irqdesc.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +/** + * struct idio_24_gpio_reg - GPIO device registers structure + * @out0_7: Read: FET Outputs 0-7 + * Write: FET Outputs 0-7 + * @out8_15: Read: FET Outputs 8-15 + * Write: FET Outputs 8-15 + * @out16_23: Read: FET Outputs 16-23 + * Write: FET Outputs 16-23 + * @ttl_out0_7: Read: TTL/CMOS Outputs 0-7 + * Write: TTL/CMOS Outputs 0-7 + * @in0_7: Read: Isolated Inputs 0-7 + * Write: Reserved + * @in8_15: Read: Isolated Inputs 8-15 + * Write: Reserved + * @in16_23: Read: Isolated Inputs 16-23 + * Write: Reserved + * @ttl_in0_7: Read: TTL/CMOS Inputs 0-7 + * Write: Reserved + * @cos0_7: Read: COS Status Inputs 0-7 + * Write: COS Clear Inputs 0-7 + * @cos8_15: Read: COS Status Inputs 8-15 + * Write: COS Clear Inputs 8-15 + * @cos16_23: Read: COS Status Inputs 16-23 + * Write: COS Clear Inputs 16-23 + * @cos_ttl0_7: Read: COS Status TTL/CMOS 0-7 + * Write: COS Clear TTL/CMOS 0-7 + * @ctl: Read: Control Register + * Write: Control Register + * @reserved: Read: Reserved + * Write: Reserved + * @cos_enable: Read: COS Enable + * Write: COS Enable + * @soft_reset: Read: IRQ Output Pin Status + * Write: Software Board Reset + */ +struct idio_24_gpio_reg { + u8 out0_7; + u8 out8_15; + u8 out16_23; + u8 ttl_out0_7; + u8 in0_7; + u8 in8_15; + u8 in16_23; + u8 ttl_in0_7; + u8 cos0_7; + u8 cos8_15; + u8 cos16_23; + u8 cos_ttl0_7; + u8 ctl; + u8 reserved; + u8 cos_enable; + u8 soft_reset; +}; + +/** + * struct idio_24_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @lock: synchronization lock to prevent I/O race conditions + * @reg: I/O address offset for the GPIO device registers + * @irq_mask: I/O bits affected by interrupts + */ +struct idio_24_gpio { + struct gpio_chip chip; + raw_spinlock_t lock; + struct idio_24_gpio_reg __iomem *reg; + unsigned long irq_mask; +}; + +static int idio_24_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + const unsigned long out_mode_mask = BIT(1); + + /* FET Outputs */ + if (offset < 24) + return 0; + + /* Isolated Inputs */ + if (offset < 48) + return 1; + + /* TTL/CMOS I/O */ + /* OUT MODE = 1 when TTL/CMOS Output Mode is set */ + return !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask); +} + +static int idio_24_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + unsigned int ctl_state; + const unsigned long out_mode_mask = BIT(1); + + /* TTL/CMOS I/O */ + if (offset > 47) { + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + /* Clear TTL/CMOS Output Mode */ + ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask; + iowrite8(ctl_state, &idio24gpio->reg->ctl); + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); + } + + return 0; +} + +static int idio_24_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + unsigned int ctl_state; + const unsigned long out_mode_mask = BIT(1); + + /* TTL/CMOS I/O */ + if (offset > 47) { + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + /* Set TTL/CMOS Output Mode */ + ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask; + iowrite8(ctl_state, &idio24gpio->reg->ctl); + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); + } + + chip->set(chip, offset, value); + return 0; +} + +static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + const unsigned long offset_mask = BIT(offset % 8); + const unsigned long out_mode_mask = BIT(1); + + /* FET Outputs */ + if (offset < 8) + return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask); + + if (offset < 16) + return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask); + + if (offset < 24) + return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask); + + /* Isolated Inputs */ + if (offset < 32) + return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask); + + if (offset < 40) + return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask); + + if (offset < 48) + return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask); + + /* TTL/CMOS Outputs */ + if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) + return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask); + + /* TTL/CMOS Inputs */ + return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask); +} + +static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + const unsigned long out_mode_mask = BIT(1); + void __iomem *base; + const unsigned int mask = BIT(offset % 8); + unsigned long flags; + unsigned int out_state; + + /* Isolated Inputs */ + if (offset > 23 && offset < 48) + return; + + /* TTL/CMOS Inputs */ + if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask)) + return; + + /* TTL/CMOS Outputs */ + if (offset > 47) + base = &idio24gpio->reg->ttl_out0_7; + /* FET Outputs */ + else if (offset > 15) + base = &idio24gpio->reg->out16_23; + else if (offset > 7) + base = &idio24gpio->reg->out8_15; + else + base = &idio24gpio->reg->out0_7; + + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + if (value) + out_state = ioread8(base) | mask; + else + out_state = ioread8(base) & ~mask; + + iowrite8(out_state, base); + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); +} + +static void idio_24_irq_ack(struct irq_data *data) +{ +} + +static void idio_24_irq_mask(struct irq_data *data) +{ + struct gpio_chip *const chip = irq_data_get_irq_chip_data(data); + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + const unsigned long bit_offset = irqd_to_hwirq(data) - 24; + unsigned char new_irq_mask; + const unsigned long bank_offset = bit_offset/8 * 8; + unsigned char cos_enable_state; + + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + idio24gpio->irq_mask &= BIT(bit_offset); + new_irq_mask = idio24gpio->irq_mask >> bank_offset; + + if (!new_irq_mask) { + cos_enable_state = ioread8(&idio24gpio->reg->cos_enable); + + /* Disable Rising Edge detection */ + cos_enable_state &= ~BIT(bank_offset); + /* Disable Falling Edge detection */ + cos_enable_state &= ~BIT(bank_offset + 4); + + iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable); + } + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); +} + +static void idio_24_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *const chip = irq_data_get_irq_chip_data(data); + struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip); + unsigned long flags; + unsigned char prev_irq_mask; + const unsigned long bit_offset = irqd_to_hwirq(data) - 24; + const unsigned long bank_offset = bit_offset/8 * 8; + unsigned char cos_enable_state; + + raw_spin_lock_irqsave(&idio24gpio->lock, flags); + + prev_irq_mask = idio24gpio->irq_mask >> bank_offset; + idio24gpio->irq_mask |= BIT(bit_offset); + + if (!prev_irq_mask) { + cos_enable_state = ioread8(&idio24gpio->reg->cos_enable); + + /* Enable Rising Edge detection */ + cos_enable_state |= BIT(bank_offset); + /* Enable Falling Edge detection */ + cos_enable_state |= BIT(bank_offset + 4); + + iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable); + } + + raw_spin_unlock_irqrestore(&idio24gpio->lock, flags); +} + +static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + /* The only valid irq types are none and both-edges */ + if (flow_type != IRQ_TYPE_NONE && + (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) + return -EINVAL; + + return 0; +} + +static struct irq_chip idio_24_irqchip = { + .name = "pcie-idio-24", + .irq_ack = idio_24_irq_ack, + .irq_mask = idio_24_irq_mask, + .irq_unmask = idio_24_irq_unmask, + .irq_set_type = idio_24_irq_set_type +}; + +static irqreturn_t idio_24_irq_handler(int irq, void *dev_id) +{ + struct idio_24_gpio *const idio24gpio = dev_id; + unsigned long irq_status; + struct gpio_chip *const chip = &idio24gpio->chip; + unsigned long irq_mask; + int gpio; + + raw_spin_lock(&idio24gpio->lock); + + /* Read Change-Of-State status */ + irq_status = ioread32(&idio24gpio->reg->cos0_7); + + raw_spin_unlock(&idio24gpio->lock); + + /* Make sure our device generated IRQ */ + if (!irq_status) + return IRQ_NONE; + + /* Handle only unmasked IRQ */ + irq_mask = idio24gpio->irq_mask & irq_status; + + for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24) + generic_handle_irq(irq_find_mapping(chip->irq.domain, + gpio + 24)); + + raw_spin_lock(&idio24gpio->lock); + + /* Clear Change-Of-State status */ + iowrite32(irq_status, &idio24gpio->reg->cos0_7); + + raw_spin_unlock(&idio24gpio->lock); + + return IRQ_HANDLED; +} + +#define IDIO_24_NGPIO 56 +static const char *idio_24_names[IDIO_24_NGPIO] = { + "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", + "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", + "OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23", + "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", + "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15", + "IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23", + "TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7" +}; + +static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *const dev = &pdev->dev; + struct idio_24_gpio *idio24gpio; + int err; + const size_t pci_bar_index = 2; + const char *const name = pci_name(pdev); + + idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL); + if (!idio24gpio) + return -ENOMEM; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device (%d)\n", err); + return err; + } + + err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name); + if (err) { + dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err); + return err; + } + + idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; + + idio24gpio->chip.label = name; + idio24gpio->chip.parent = dev; + idio24gpio->chip.owner = THIS_MODULE; + idio24gpio->chip.base = -1; + idio24gpio->chip.ngpio = IDIO_24_NGPIO; + idio24gpio->chip.names = idio_24_names; + idio24gpio->chip.get_direction = idio_24_gpio_get_direction; + idio24gpio->chip.direction_input = idio_24_gpio_direction_input; + idio24gpio->chip.direction_output = idio_24_gpio_direction_output; + idio24gpio->chip.get = idio_24_gpio_get; + idio24gpio->chip.set = idio_24_gpio_set; + + raw_spin_lock_init(&idio24gpio->lock); + + /* Software board reset */ + iowrite8(0, &idio24gpio->reg->soft_reset); + + err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0, + handle_edge_irq, IRQ_TYPE_NONE); + if (err) { + dev_err(dev, "Could not add irqchip (%d)\n", err); + return err; + } + + err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED, + name, idio24gpio); + if (err) { + dev_err(dev, "IRQ handler registering failed (%d)\n", err); + return err; + } + + return 0; +} + +static const struct pci_device_id idio_24_pci_dev_id[] = { + { PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) }, + { PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id); + +static struct pci_driver idio_24_driver = { + .name = "pcie-idio-24", + .id_table = idio_24_pci_dev_id, + .probe = idio_24_probe +}; + +module_pci_driver(idio_24_driver); + +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); +MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index e6e5cca624a7..f8d7d1cd8488 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -190,6 +190,16 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) }; int i, j; + /* + * STMPE1600: to be able to get IRQ from pins, + * a read must be done on GPMR register, or a write in + * GPSR or GPCR registers + */ + if (stmpe->partnum == STMPE1600) { + stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_LSB]); + stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_GPMR_CSB]); + } + for (i = 0; i < CACHE_NR_REGS; i++) { /* STMPE801 and STMPE1600 don't have RE and FE registers */ if ((stmpe->partnum == STMPE801 || @@ -227,21 +237,11 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); - struct stmpe *stmpe = stmpe_gpio->stmpe; int offset = d->hwirq; int regoffset = offset / 8; int mask = BIT(offset % 8); stmpe_gpio->regs[REG_IE][regoffset] |= mask; - - /* - * STMPE1600 workaround: to be able to get IRQ from pins, - * a read must be done on GPMR register, or a write in - * GPSR or GPCR registers - */ - if (stmpe->partnum == STMPE1600) - stmpe_reg_read(stmpe, - stmpe->regs[STMPE_IDX_GPMR_LSB + regoffset]); } static void stmpe_dbg_show_one(struct seq_file *s, @@ -273,15 +273,21 @@ static void stmpe_dbg_show_one(struct seq_file *s, u8 fall_reg; u8 irqen_reg; - char *edge_det_values[] = {"edge-inactive", - "edge-asserted", - "not-supported"}; - char *rise_values[] = {"no-rising-edge-detection", - "rising-edge-detection", - "not-supported"}; - char *fall_values[] = {"no-falling-edge-detection", - "falling-edge-detection", - "not-supported"}; + static const char * const edge_det_values[] = { + "edge-inactive", + "edge-asserted", + "not-supported" + }; + static const char * const rise_values[] = { + "no-rising-edge-detection", + "rising-edge-detection", + "not-supported" + }; + static const char * const fall_values[] = { + "no-falling-edge-detection", + "falling-edge-detection", + "not-supported" + }; #define NOT_SUPPORTED_IDX 2 u8 edge_det = NOT_SUPPORTED_IDX; u8 rise = NOT_SUPPORTED_IDX; @@ -344,7 +350,7 @@ static void stmpe_dbg_show(struct seq_file *s, struct gpio_chip *gc) for (i = 0; i < gc->ngpio; i++, gpio++) { stmpe_dbg_show_one(s, gc, i, gpio); - seq_printf(s, "\n"); + seq_putc(s, '\n'); } } @@ -426,12 +432,9 @@ static int stmpe_gpio_probe(struct platform_device *pdev) struct stmpe *stmpe = dev_get_drvdata(pdev->dev.parent); struct device_node *np = pdev->dev.of_node; struct stmpe_gpio *stmpe_gpio; - int ret; - int irq = 0; - - irq = platform_get_irq(pdev, 0); + int ret, irq; - stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL); + stmpe_gpio = kzalloc(sizeof(*stmpe_gpio), GFP_KERNEL); if (!stmpe_gpio) return -ENOMEM; @@ -453,6 +456,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) if (stmpe_gpio->norequest_mask) stmpe_gpio->chip.irq.need_valid_mask = true; + irq = platform_get_irq(pdev, 0); if (irq < 0) dev_info(&pdev->dev, "device configured in no-irq mode: " diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index b5adb79a631a..d16e9d4a129b 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -553,8 +553,10 @@ static int thunderx_gpio_probe(struct pci_dev *pdev, txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, 0, 0, of_node_to_fwnode(dev->of_node), &thunderx_gpio_irqd_ops, txgpio); - if (!txgpio->irqd) + if (!txgpio->irqd) { + err = -ENOMEM; goto out; + } /* Push on irq_data and the domain for each line. */ for (i = 0; i < ngpio; i++) { diff --git a/drivers/gpio/gpio-winbond.c b/drivers/gpio/gpio-winbond.c new file mode 100644 index 000000000000..7f8f5b02e31d --- /dev/null +++ b/drivers/gpio/gpio-winbond.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * GPIO interface for Winbond Super I/O chips + * Currently, only W83627UHG (Nuvoton NCT6627UD) is supported. + * + * Author: Maciej S. Szmigiero <mail@maciej.szmigiero.name> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/gpio/driver.h> +#include <linux/ioport.h> +#include <linux/isa.h> +#include <linux/module.h> + +#define WB_GPIO_DRIVER_NAME KBUILD_MODNAME + +#define WB_SIO_BASE 0x2e +#define WB_SIO_BASE_HIGH 0x4e + +#define WB_SIO_EXT_ENTER_KEY 0x87 +#define WB_SIO_EXT_EXIT_KEY 0xaa + +/* global chip registers */ + +#define WB_SIO_REG_LOGICAL 0x07 + +#define WB_SIO_REG_CHIP_MSB 0x20 +#define WB_SIO_REG_CHIP_LSB 0x21 + +#define WB_SIO_CHIP_ID_W83627UHG 0xa230 +#define WB_SIO_CHIP_ID_W83627UHG_MASK GENMASK(15, 4) + +#define WB_SIO_REG_DPD 0x22 +#define WB_SIO_REG_DPD_UARTA 4 +#define WB_SIO_REG_DPD_UARTB 5 + +#define WB_SIO_REG_IDPD 0x23 +#define WB_SIO_REG_IDPD_UARTC 4 +#define WB_SIO_REG_IDPD_UARTD 5 +#define WB_SIO_REG_IDPD_UARTE 6 +#define WB_SIO_REG_IDPD_UARTF 7 + +#define WB_SIO_REG_GLOBAL_OPT 0x24 +#define WB_SIO_REG_GO_ENFDC 1 + +#define WB_SIO_REG_OVTGPIO3456 0x29 +#define WB_SIO_REG_OG3456_G3PP 3 +#define WB_SIO_REG_OG3456_G4PP 4 +#define WB_SIO_REG_OG3456_G5PP 5 +#define WB_SIO_REG_OG3456_G6PP 7 + +#define WB_SIO_REG_I2C_PS 0x2a +#define WB_SIO_REG_I2CPS_I2CFS 1 + +#define WB_SIO_REG_GPIO1_MF 0x2c +#define WB_SIO_REG_G1MF_G1PP 6 +#define WB_SIO_REG_G1MF_G2PP 7 +#define WB_SIO_REG_G1MF_FS_MASK GENMASK(1, 0) +#define WB_SIO_REG_G1MF_FS_IR_OFF 0 +#define WB_SIO_REG_G1MF_FS_IR 1 +#define WB_SIO_REG_G1MF_FS_GPIO1 2 +#define WB_SIO_REG_G1MF_FS_UARTB 3 + +/* not an actual device number, just a value meaning 'no device' */ +#define WB_SIO_DEV_NONE 0xff + +/* registers with offsets >= 0x30 are specific for a particular device */ + +/* UART B logical device */ +#define WB_SIO_DEV_UARTB 0x03 +#define WB_SIO_UARTB_REG_ENABLE 0x30 +#define WB_SIO_UARTB_ENABLE_ON 0 + +/* UART C logical device */ +#define WB_SIO_DEV_UARTC 0x06 +#define WB_SIO_UARTC_REG_ENABLE 0x30 +#define WB_SIO_UARTC_ENABLE_ON 0 + +/* GPIO3, GPIO4 logical device */ +#define WB_SIO_DEV_GPIO34 0x07 +#define WB_SIO_GPIO34_REG_ENABLE 0x30 +#define WB_SIO_GPIO34_ENABLE_3 0 +#define WB_SIO_GPIO34_ENABLE_4 1 +#define WB_SIO_GPIO34_REG_IO3 0xe0 +#define WB_SIO_GPIO34_REG_DATA3 0xe1 +#define WB_SIO_GPIO34_REG_INV3 0xe2 +#define WB_SIO_GPIO34_REG_IO4 0xe4 +#define WB_SIO_GPIO34_REG_DATA4 0xe5 +#define WB_SIO_GPIO34_REG_INV4 0xe6 + +/* WDTO, PLED, GPIO5, GPIO6 logical device */ +#define WB_SIO_DEV_WDGPIO56 0x08 +#define WB_SIO_WDGPIO56_REG_ENABLE 0x30 +#define WB_SIO_WDGPIO56_ENABLE_5 1 +#define WB_SIO_WDGPIO56_ENABLE_6 2 +#define WB_SIO_WDGPIO56_REG_IO5 0xe0 +#define WB_SIO_WDGPIO56_REG_DATA5 0xe1 +#define WB_SIO_WDGPIO56_REG_INV5 0xe2 +#define WB_SIO_WDGPIO56_REG_IO6 0xe4 +#define WB_SIO_WDGPIO56_REG_DATA6 0xe5 +#define WB_SIO_WDGPIO56_REG_INV6 0xe6 + +/* GPIO1, GPIO2, SUSLED logical device */ +#define WB_SIO_DEV_GPIO12 0x09 +#define WB_SIO_GPIO12_REG_ENABLE 0x30 +#define WB_SIO_GPIO12_ENABLE_1 0 +#define WB_SIO_GPIO12_ENABLE_2 1 +#define WB_SIO_GPIO12_REG_IO1 0xe0 +#define WB_SIO_GPIO12_REG_DATA1 0xe1 +#define WB_SIO_GPIO12_REG_INV1 0xe2 +#define WB_SIO_GPIO12_REG_IO2 0xe4 +#define WB_SIO_GPIO12_REG_DATA2 0xe5 +#define WB_SIO_GPIO12_REG_INV2 0xe6 + +/* UART D logical device */ +#define WB_SIO_DEV_UARTD 0x0d +#define WB_SIO_UARTD_REG_ENABLE 0x30 +#define WB_SIO_UARTD_ENABLE_ON 0 + +/* UART E logical device */ +#define WB_SIO_DEV_UARTE 0x0e +#define WB_SIO_UARTE_REG_ENABLE 0x30 +#define WB_SIO_UARTE_ENABLE_ON 0 + +/* + * for a description what a particular field of this struct means please see + * a description of the relevant module parameter at the bottom of this file + */ +struct winbond_gpio_params { + unsigned long base; + unsigned long gpios; + unsigned long ppgpios; + unsigned long odgpios; + bool pledgpio; + bool beepgpio; + bool i2cgpio; +}; + +static struct winbond_gpio_params params; + +static int winbond_sio_enter(unsigned long base) +{ + if (!request_muxed_region(base, 2, WB_GPIO_DRIVER_NAME)) + return -EBUSY; + + /* + * datasheet says two successive writes of the "key" value are needed + * in order for chip to enter the "Extended Function Mode" + */ + outb(WB_SIO_EXT_ENTER_KEY, base); + outb(WB_SIO_EXT_ENTER_KEY, base); + + return 0; +} + +static void winbond_sio_select_logical(unsigned long base, u8 dev) +{ + outb(WB_SIO_REG_LOGICAL, base); + outb(dev, base + 1); +} + +static void winbond_sio_leave(unsigned long base) +{ + outb(WB_SIO_EXT_EXIT_KEY, base); + + release_region(base, 2); +} + +static void winbond_sio_reg_write(unsigned long base, u8 reg, u8 data) +{ + outb(reg, base); + outb(data, base + 1); +} + +static u8 winbond_sio_reg_read(unsigned long base, u8 reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static void winbond_sio_reg_bset(unsigned long base, u8 reg, u8 bit) +{ + u8 val; + + val = winbond_sio_reg_read(base, reg); + val |= BIT(bit); + winbond_sio_reg_write(base, reg, val); +} + +static void winbond_sio_reg_bclear(unsigned long base, u8 reg, u8 bit) +{ + u8 val; + + val = winbond_sio_reg_read(base, reg); + val &= ~BIT(bit); + winbond_sio_reg_write(base, reg, val); +} + +static bool winbond_sio_reg_btest(unsigned long base, u8 reg, u8 bit) +{ + return winbond_sio_reg_read(base, reg) & BIT(bit); +} + +/** + * struct winbond_gpio_port_conflict - possibly conflicting device information + * @name: device name (NULL means no conflicting device defined) + * @dev: Super I/O logical device number where the testreg register + * is located (or WB_SIO_DEV_NONE - don't select any + * logical device) + * @testreg: register number where the testbit bit is located + * @testbit: index of a bit to check whether an actual conflict exists + * @warnonly: if set then a conflict isn't fatal (just warn about it), + * otherwise disable the particular GPIO port if a conflict + * is detected + */ +struct winbond_gpio_port_conflict { + const char *name; + u8 dev; + u8 testreg; + u8 testbit; + bool warnonly; +}; + +/** + * struct winbond_gpio_info - information about a particular GPIO port (device) + * @dev: Super I/O logical device number of the registers + * specified below + * @enablereg: port enable bit register number + * @enablebit: index of a port enable bit + * @outputreg: output driver mode bit register number + * @outputppbit: index of a push-pull output driver mode bit + * @ioreg: data direction register number + * @invreg: pin data inversion register number + * @datareg: pin data register number + * @conflict: description of a device that possibly conflicts with + * this port + */ +struct winbond_gpio_info { + u8 dev; + u8 enablereg; + u8 enablebit; + u8 outputreg; + u8 outputppbit; + u8 ioreg; + u8 invreg; + u8 datareg; + struct winbond_gpio_port_conflict conflict; +}; + +static const struct winbond_gpio_info winbond_gpio_infos[6] = { + { /* 0 */ + .dev = WB_SIO_DEV_GPIO12, + .enablereg = WB_SIO_GPIO12_REG_ENABLE, + .enablebit = WB_SIO_GPIO12_ENABLE_1, + .outputreg = WB_SIO_REG_GPIO1_MF, + .outputppbit = WB_SIO_REG_G1MF_G1PP, + .ioreg = WB_SIO_GPIO12_REG_IO1, + .invreg = WB_SIO_GPIO12_REG_INV1, + .datareg = WB_SIO_GPIO12_REG_DATA1, + .conflict = { + .name = "UARTB", + .dev = WB_SIO_DEV_UARTB, + .testreg = WB_SIO_UARTB_REG_ENABLE, + .testbit = WB_SIO_UARTB_ENABLE_ON, + .warnonly = true + } + }, + { /* 1 */ + .dev = WB_SIO_DEV_GPIO12, + .enablereg = WB_SIO_GPIO12_REG_ENABLE, + .enablebit = WB_SIO_GPIO12_ENABLE_2, + .outputreg = WB_SIO_REG_GPIO1_MF, + .outputppbit = WB_SIO_REG_G1MF_G2PP, + .ioreg = WB_SIO_GPIO12_REG_IO2, + .invreg = WB_SIO_GPIO12_REG_INV2, + .datareg = WB_SIO_GPIO12_REG_DATA2 + /* special conflict handling so doesn't use conflict data */ + }, + { /* 2 */ + .dev = WB_SIO_DEV_GPIO34, + .enablereg = WB_SIO_GPIO34_REG_ENABLE, + .enablebit = WB_SIO_GPIO34_ENABLE_3, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G3PP, + .ioreg = WB_SIO_GPIO34_REG_IO3, + .invreg = WB_SIO_GPIO34_REG_INV3, + .datareg = WB_SIO_GPIO34_REG_DATA3, + .conflict = { + .name = "UARTC", + .dev = WB_SIO_DEV_UARTC, + .testreg = WB_SIO_UARTC_REG_ENABLE, + .testbit = WB_SIO_UARTC_ENABLE_ON, + .warnonly = true + } + }, + { /* 3 */ + .dev = WB_SIO_DEV_GPIO34, + .enablereg = WB_SIO_GPIO34_REG_ENABLE, + .enablebit = WB_SIO_GPIO34_ENABLE_4, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G4PP, + .ioreg = WB_SIO_GPIO34_REG_IO4, + .invreg = WB_SIO_GPIO34_REG_INV4, + .datareg = WB_SIO_GPIO34_REG_DATA4, + .conflict = { + .name = "UARTD", + .dev = WB_SIO_DEV_UARTD, + .testreg = WB_SIO_UARTD_REG_ENABLE, + .testbit = WB_SIO_UARTD_ENABLE_ON, + .warnonly = true + } + }, + { /* 4 */ + .dev = WB_SIO_DEV_WDGPIO56, + .enablereg = WB_SIO_WDGPIO56_REG_ENABLE, + .enablebit = WB_SIO_WDGPIO56_ENABLE_5, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G5PP, + .ioreg = WB_SIO_WDGPIO56_REG_IO5, + .invreg = WB_SIO_WDGPIO56_REG_INV5, + .datareg = WB_SIO_WDGPIO56_REG_DATA5, + .conflict = { + .name = "UARTE", + .dev = WB_SIO_DEV_UARTE, + .testreg = WB_SIO_UARTE_REG_ENABLE, + .testbit = WB_SIO_UARTE_ENABLE_ON, + .warnonly = true + } + }, + { /* 5 */ + .dev = WB_SIO_DEV_WDGPIO56, + .enablereg = WB_SIO_WDGPIO56_REG_ENABLE, + .enablebit = WB_SIO_WDGPIO56_ENABLE_6, + .outputreg = WB_SIO_REG_OVTGPIO3456, + .outputppbit = WB_SIO_REG_OG3456_G6PP, + .ioreg = WB_SIO_WDGPIO56_REG_IO6, + .invreg = WB_SIO_WDGPIO56_REG_INV6, + .datareg = WB_SIO_WDGPIO56_REG_DATA6, + .conflict = { + .name = "FDC", + .dev = WB_SIO_DEV_NONE, + .testreg = WB_SIO_REG_GLOBAL_OPT, + .testbit = WB_SIO_REG_GO_ENFDC, + .warnonly = false + } + } +}; + +/* returns whether changing a pin is allowed */ +static bool winbond_gpio_get_info(unsigned int *gpio_num, + const struct winbond_gpio_info **info) +{ + bool allow_changing = true; + unsigned long i; + + for_each_set_bit(i, ¶ms.gpios, BITS_PER_LONG) { + if (*gpio_num < 8) + break; + + *gpio_num -= 8; + } + + *info = &winbond_gpio_infos[i]; + + /* + * GPIO2 (the second port) shares some pins with a basic PC + * functionality, which is very likely controlled by the firmware. + * Don't allow changing these pins by default. + */ + if (i == 1) { + if (*gpio_num == 0 && !params.pledgpio) + allow_changing = false; + else if (*gpio_num == 1 && !params.beepgpio) + allow_changing = false; + else if ((*gpio_num == 5 || *gpio_num == 6) && !params.i2cgpio) + allow_changing = false; + } + + return allow_changing; +} + +static int winbond_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + bool val; + + winbond_gpio_get_info(&offset, &info); + + val = winbond_sio_enter(*base); + if (val) + return val; + + winbond_sio_select_logical(*base, info->dev); + + val = winbond_sio_reg_btest(*base, info->datareg, offset); + if (winbond_sio_reg_btest(*base, info->invreg, offset)) + val = !val; + + winbond_sio_leave(*base); + + return val; +} + +static int winbond_gpio_direction_in(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + int ret; + + if (!winbond_gpio_get_info(&offset, &info)) + return -EACCES; + + ret = winbond_sio_enter(*base); + if (ret) + return ret; + + winbond_sio_select_logical(*base, info->dev); + + winbond_sio_reg_bset(*base, info->ioreg, offset); + + winbond_sio_leave(*base); + + return 0; +} + +static int winbond_gpio_direction_out(struct gpio_chip *gc, + unsigned int offset, + int val) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + int ret; + + if (!winbond_gpio_get_info(&offset, &info)) + return -EACCES; + + ret = winbond_sio_enter(*base); + if (ret) + return ret; + + winbond_sio_select_logical(*base, info->dev); + + winbond_sio_reg_bclear(*base, info->ioreg, offset); + + if (winbond_sio_reg_btest(*base, info->invreg, offset)) + val = !val; + + if (val) + winbond_sio_reg_bset(*base, info->datareg, offset); + else + winbond_sio_reg_bclear(*base, info->datareg, offset); + + winbond_sio_leave(*base); + + return 0; +} + +static void winbond_gpio_set(struct gpio_chip *gc, unsigned int offset, + int val) +{ + unsigned long *base = gpiochip_get_data(gc); + const struct winbond_gpio_info *info; + + if (!winbond_gpio_get_info(&offset, &info)) + return; + + if (winbond_sio_enter(*base) != 0) + return; + + winbond_sio_select_logical(*base, info->dev); + + if (winbond_sio_reg_btest(*base, info->invreg, offset)) + val = !val; + + if (val) + winbond_sio_reg_bset(*base, info->datareg, offset); + else + winbond_sio_reg_bclear(*base, info->datareg, offset); + + winbond_sio_leave(*base); +} + +static struct gpio_chip winbond_gpio_chip = { + .base = -1, + .label = WB_GPIO_DRIVER_NAME, + .owner = THIS_MODULE, + .can_sleep = true, + .get = winbond_gpio_get, + .direction_input = winbond_gpio_direction_in, + .set = winbond_gpio_set, + .direction_output = winbond_gpio_direction_out, +}; + +static void winbond_gpio_configure_port0_pins(unsigned long base) +{ + unsigned int val; + + val = winbond_sio_reg_read(base, WB_SIO_REG_GPIO1_MF); + if ((val & WB_SIO_REG_G1MF_FS_MASK) == WB_SIO_REG_G1MF_FS_GPIO1) + return; + + pr_warn("GPIO1 pins were connected to something else (%.2x), fixing\n", + val); + + val &= ~WB_SIO_REG_G1MF_FS_MASK; + val |= WB_SIO_REG_G1MF_FS_GPIO1; + + winbond_sio_reg_write(base, WB_SIO_REG_GPIO1_MF, val); +} + +static void winbond_gpio_configure_port1_check_i2c(unsigned long base) +{ + params.i2cgpio = !winbond_sio_reg_btest(base, WB_SIO_REG_I2C_PS, + WB_SIO_REG_I2CPS_I2CFS); + if (!params.i2cgpio) + pr_warn("disabling GPIO2.5 and GPIO2.6 as I2C is enabled\n"); +} + +static bool winbond_gpio_configure_port(unsigned long base, unsigned int idx) +{ + const struct winbond_gpio_info *info = &winbond_gpio_infos[idx]; + const struct winbond_gpio_port_conflict *conflict = &info->conflict; + + /* is there a possible conflicting device defined? */ + if (conflict->name != NULL) { + if (conflict->dev != WB_SIO_DEV_NONE) + winbond_sio_select_logical(base, conflict->dev); + + if (winbond_sio_reg_btest(base, conflict->testreg, + conflict->testbit)) { + if (conflict->warnonly) + pr_warn("enabled GPIO%u share pins with active %s\n", + idx + 1, conflict->name); + else { + pr_warn("disabling GPIO%u as %s is enabled\n", + idx + 1, conflict->name); + return false; + } + } + } + + /* GPIO1 and GPIO2 need some (additional) special handling */ + if (idx == 0) + winbond_gpio_configure_port0_pins(base); + else if (idx == 1) + winbond_gpio_configure_port1_check_i2c(base); + + winbond_sio_select_logical(base, info->dev); + + winbond_sio_reg_bset(base, info->enablereg, info->enablebit); + + if (params.ppgpios & BIT(idx)) + winbond_sio_reg_bset(base, info->outputreg, + info->outputppbit); + else if (params.odgpios & BIT(idx)) + winbond_sio_reg_bclear(base, info->outputreg, + info->outputppbit); + else + pr_notice("GPIO%u pins are %s\n", idx + 1, + winbond_sio_reg_btest(base, info->outputreg, + info->outputppbit) ? + "push-pull" : + "open drain"); + + return true; +} + +static int winbond_gpio_configure(unsigned long base) +{ + unsigned long i; + + for_each_set_bit(i, ¶ms.gpios, BITS_PER_LONG) + if (!winbond_gpio_configure_port(base, i)) + __clear_bit(i, ¶ms.gpios); + + if (!params.gpios) { + pr_err("please use 'gpios' module parameter to select some active GPIO ports to enable\n"); + return -EINVAL; + } + + return 0; +} + +static int winbond_gpio_check_chip(unsigned long base) +{ + int ret; + unsigned int chip; + + ret = winbond_sio_enter(base); + if (ret) + return ret; + + chip = winbond_sio_reg_read(base, WB_SIO_REG_CHIP_MSB) << 8; + chip |= winbond_sio_reg_read(base, WB_SIO_REG_CHIP_LSB); + + pr_notice("chip ID at %lx is %.4x\n", base, chip); + + if ((chip & WB_SIO_CHIP_ID_W83627UHG_MASK) != + WB_SIO_CHIP_ID_W83627UHG) { + pr_err("not an our chip\n"); + ret = -ENODEV; + } + + winbond_sio_leave(base); + + return ret; +} + +static int winbond_gpio_imatch(struct device *dev, unsigned int id) +{ + unsigned long gpios_rem; + int ret; + + gpios_rem = params.gpios & ~GENMASK(ARRAY_SIZE(winbond_gpio_infos) - 1, + 0); + if (gpios_rem) { + pr_warn("unknown ports (%lx) enabled in GPIO ports bitmask\n", + gpios_rem); + params.gpios &= ~gpios_rem; + } + + if (params.ppgpios & params.odgpios) { + pr_err("some GPIO ports are set both to push-pull and open drain mode at the same time\n"); + return 0; + } + + if (params.base != 0) + return winbond_gpio_check_chip(params.base) == 0; + + /* + * if the 'base' module parameter is unset probe two chip default + * I/O port bases + */ + params.base = WB_SIO_BASE; + ret = winbond_gpio_check_chip(params.base); + if (ret == 0) + return 1; + if (ret != -ENODEV && ret != -EBUSY) + return 0; + + params.base = WB_SIO_BASE_HIGH; + return winbond_gpio_check_chip(params.base) == 0; +} + +static int winbond_gpio_iprobe(struct device *dev, unsigned int id) +{ + int ret; + + if (params.base == 0) + return -EINVAL; + + ret = winbond_sio_enter(params.base); + if (ret) + return ret; + + ret = winbond_gpio_configure(params.base); + + winbond_sio_leave(params.base); + + if (ret) + return ret; + + /* + * Add 8 gpios for every GPIO port that was enabled in gpios + * module parameter (that wasn't disabled earlier in + * winbond_gpio_configure() & co. due to, for example, a pin conflict). + */ + winbond_gpio_chip.ngpio = hweight_long(params.gpios) * 8; + + /* + * GPIO6 port has only 5 pins, so if it is enabled we have to adjust + * the total count appropriately + */ + if (params.gpios & BIT(5)) + winbond_gpio_chip.ngpio -= (8 - 5); + + winbond_gpio_chip.parent = dev; + + return devm_gpiochip_add_data(dev, &winbond_gpio_chip, ¶ms.base); +} + +static struct isa_driver winbond_gpio_idriver = { + .driver = { + .name = WB_GPIO_DRIVER_NAME, + }, + .match = winbond_gpio_imatch, + .probe = winbond_gpio_iprobe, +}; + +module_isa_driver(winbond_gpio_idriver, 1); + +module_param_named(base, params.base, ulong, 0444); +MODULE_PARM_DESC(base, + "I/O port base (when unset - probe chip default ones)"); + +/* This parameter sets which GPIO devices (ports) we enable */ +module_param_named(gpios, params.gpios, ulong, 0444); +MODULE_PARM_DESC(gpios, + "bitmask of GPIO ports to enable (bit 0 - GPIO1, bit 1 - GPIO2, etc."); + +/* + * These two parameters below set how we configure GPIO ports output drivers. + * It can't be a one bitmask since we need three values per port: push-pull, + * open-drain and keep as-is (this is the default). + */ +module_param_named(ppgpios, params.ppgpios, ulong, 0444); +MODULE_PARM_DESC(ppgpios, + "bitmask of GPIO ports to set to push-pull mode (bit 0 - GPIO1, bit 1 - GPIO2, etc."); + +module_param_named(odgpios, params.odgpios, ulong, 0444); +MODULE_PARM_DESC(odgpios, + "bitmask of GPIO ports to set to open drain mode (bit 0 - GPIO1, bit 1 - GPIO2, etc."); + +/* + * GPIO2.0 and GPIO2.1 control a basic PC functionality that we + * don't allow tinkering with by default (it is very likely that the + * firmware owns these pins). + * These two parameters below allow overriding these prohibitions. + */ +module_param_named(pledgpio, params.pledgpio, bool, 0644); +MODULE_PARM_DESC(pledgpio, + "enable changing value of GPIO2.0 bit (Power LED), default no."); + +module_param_named(beepgpio, params.beepgpio, bool, 0644); +MODULE_PARM_DESC(beepgpio, + "enable changing value of GPIO2.1 bit (BEEP), default no."); + +MODULE_AUTHOR("Maciej S. Szmigiero <mail@maciej.szmigiero.name>"); +MODULE_DESCRIPTION("GPIO interface for Winbond Super I/O chips"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index d6f3d9ee1350..0ecffd172a80 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -414,7 +414,8 @@ EXPORT_SYMBOL_GPL(devm_acpi_dev_remove_driver_gpios); static bool acpi_get_driver_gpio_data(struct acpi_device *adev, const char *name, int index, - struct acpi_reference_args *args) + struct acpi_reference_args *args, + unsigned int *quirks) { const struct acpi_gpio_mapping *gm; @@ -430,6 +431,8 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev, args->args[1] = par->line_index; args->args[2] = par->active_low; args->nargs = 3; + + *quirks = gm->quirks; return true; } @@ -461,8 +464,8 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) } } -int -acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +static int +__acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) { int ret = 0; @@ -489,12 +492,31 @@ acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) return ret; } +int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) +{ + struct device *dev = &info->adev->dev; + enum gpiod_flags old = *flags; + int ret; + + ret = __acpi_gpio_update_gpiod_flags(&old, info->flags); + if (info->quirks & ACPI_GPIO_QUIRK_NO_IO_RESTRICTION) { + if (ret) + dev_warn(dev, FW_BUG "GPIO not in correct mode, fixing\n"); + } else { + if (ret) + dev_dbg(dev, "Override GPIO initialization flags\n"); + *flags = old; + } + + return ret; +} + struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; int pin_index; bool active_low; - struct acpi_device *adev; struct gpio_desc *desc; int n; }; @@ -531,8 +553,8 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) lookup->info.triggering = agpio->triggering; } else { lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio); + lookup->info.polarity = lookup->active_low; } - } return 1; @@ -541,12 +563,13 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, struct acpi_gpio_info *info) { + struct acpi_device *adev = lookup->info.adev; struct list_head res_list; int ret; INIT_LIST_HEAD(&res_list); - ret = acpi_dev_get_resources(lookup->adev, &res_list, + ret = acpi_dev_get_resources(adev, &res_list, acpi_populate_gpio_lookup, lookup); if (ret < 0) @@ -557,11 +580,8 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, if (!lookup->desc) return -ENOENT; - if (info) { + if (info) *info = lookup->info; - if (lookup->active_low) - info->polarity = lookup->active_low; - } return 0; } @@ -570,6 +590,7 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, struct acpi_gpio_lookup *lookup) { struct acpi_reference_args args; + unsigned int quirks = 0; int ret; memset(&args, 0, sizeof(args)); @@ -581,14 +602,14 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, if (!adev) return ret; - if (!acpi_get_driver_gpio_data(adev, propname, index, &args)) + if (!acpi_get_driver_gpio_data(adev, propname, index, &args, + &quirks)) return ret; } /* * The property was found and resolved, so need to lookup the GPIO based * on returned args. */ - lookup->adev = args.adev; if (args.nargs != 3) return -EPROTO; @@ -596,6 +617,8 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, lookup->pin_index = args.args[1]; lookup->active_low = !!args.args[2]; + lookup->info.adev = args.adev; + lookup->info.quirks = quirks; return 0; } @@ -643,11 +666,11 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, return ERR_PTR(ret); dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %d %u\n", - dev_name(&lookup.adev->dev), lookup.index, + dev_name(&lookup.info.adev->dev), lookup.index, lookup.pin_index, lookup.active_low); } else { dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); - lookup.adev = adev; + lookup.info.adev = adev; } ret = acpi_gpio_resource_lookup(&lookup, info); @@ -664,7 +687,6 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, struct acpi_gpio_info info; struct gpio_desc *desc; char propname[32]; - int err; int i; /* Try first from _DSD */ @@ -703,10 +725,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, if (info.polarity == GPIO_ACTIVE_LOW) *lookupflags |= GPIO_ACTIVE_LOW; - err = acpi_gpio_update_gpiod_flags(dflags, info.flags); - if (err) - dev_dbg(dev, "Override GPIO initialization flags\n"); - + acpi_gpio_update_gpiod_flags(dflags, &info); return desc; } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 72a0695d2ac3..564bb7a31da4 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -56,6 +56,42 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, return gpiochip_get_desc(chip, ret); } +static void of_gpio_flags_quirks(struct device_node *np, + enum of_gpio_flags *flags) +{ + /* + * Some GPIO fixed regulator quirks. + * Note that active low is the default. + */ + if (IS_ENABLED(CONFIG_REGULATOR) && + (of_device_is_compatible(np, "reg-fixed-voltage") || + of_device_is_compatible(np, "regulator-gpio"))) { + /* + * The regulator GPIO handles are specified such that the + * presence or absence of "enable-active-high" solely controls + * the polarity of the GPIO line. Any phandle flags must + * be actively ignored. + */ + if (*flags & OF_GPIO_ACTIVE_LOW) { + pr_warn("%s GPIO handle specifies active low - ignored\n", + of_node_full_name(np)); + *flags &= ~OF_GPIO_ACTIVE_LOW; + } + if (!of_property_read_bool(np, "enable-active-high")) + *flags |= OF_GPIO_ACTIVE_LOW; + } + /* + * Legacy open drain handling for fixed voltage regulators. + */ + if (IS_ENABLED(CONFIG_REGULATOR) && + of_device_is_compatible(np, "reg-fixed-voltage") && + of_property_read_bool(np, "gpio-open-drain")) { + *flags |= (OF_GPIO_SINGLE_ENDED | OF_GPIO_OPEN_DRAIN); + pr_info("%s uses legacy open drain flag - update the DTS if you can\n", + of_node_full_name(np)); + } +} + /** * of_get_named_gpiod_flags() - Get a GPIO descriptor and flags for GPIO API * @np: device node to get GPIO from @@ -93,6 +129,9 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, if (IS_ERR(desc)) goto out; + if (flags) + of_gpio_flags_quirks(np, flags); + pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n", __func__, propname, np, index, PTR_ERR_OR_ZERO(desc)); @@ -117,6 +156,71 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name, } EXPORT_SYMBOL(of_get_named_gpio_flags); +/* + * The SPI GPIO bindings happened before we managed to establish that GPIO + * properties should be named "foo-gpios" so we have this special kludge for + * them. + */ +static struct gpio_desc *of_find_spi_gpio(struct device *dev, const char *con_id, + enum of_gpio_flags *of_flags) +{ + char prop_name[32]; /* 32 is max size of property name */ + struct device_node *np = dev->of_node; + struct gpio_desc *desc; + + /* + * Hopefully the compiler stubs the rest of the function if this + * is false. + */ + if (!IS_ENABLED(CONFIG_SPI_MASTER)) + return ERR_PTR(-ENOENT); + + /* Allow this specifically for "spi-gpio" devices */ + if (!of_device_is_compatible(np, "spi-gpio") || !con_id) + return ERR_PTR(-ENOENT); + + /* Will be "gpio-sck", "gpio-mosi" or "gpio-miso" */ + snprintf(prop_name, sizeof(prop_name), "%s-%s", "gpio", con_id); + + desc = of_get_named_gpiod_flags(np, prop_name, 0, of_flags); + return desc; +} + +/* + * Some regulator bindings happened before we managed to establish that GPIO + * properties should be named "foo-gpios" so we have this special kludge for + * them. + */ +static struct gpio_desc *of_find_regulator_gpio(struct device *dev, const char *con_id, + enum of_gpio_flags *of_flags) +{ + /* These are the connection IDs we accept as legacy GPIO phandles */ + const char *whitelist[] = { + "wlf,ldoena", /* Arizona */ + "wlf,ldo1ena", /* WM8994 */ + "wlf,ldo2ena", /* WM8994 */ + }; + struct device_node *np = dev->of_node; + struct gpio_desc *desc; + int i; + + if (!IS_ENABLED(CONFIG_REGULATOR)) + return ERR_PTR(-ENOENT); + + if (!con_id) + return ERR_PTR(-ENOENT); + + for (i = 0; i < ARRAY_SIZE(whitelist); i++) + if (!strcmp(con_id, whitelist[i])) + break; + + if (i == ARRAY_SIZE(whitelist)) + return ERR_PTR(-ENOENT); + + desc = of_get_named_gpiod_flags(np, con_id, 0, of_flags); + return desc; +} + struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags) @@ -126,6 +230,7 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, struct gpio_desc *desc; unsigned int i; + /* Try GPIO property "foo-gpios" and "foo-gpio" */ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { if (con_id) snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id, @@ -140,6 +245,14 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, break; } + /* Special handling for SPI GPIOs if used */ + if (IS_ERR(desc)) + desc = of_find_spi_gpio(dev, con_id, &of_flags); + + /* Special handling for regulator GPIOs if used */ + if (IS_ERR(desc)) + desc = of_find_regulator_gpio(dev, con_id, &of_flags); + if (IS_ERR(desc)) return desc; @@ -153,8 +266,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *flags |= GPIO_OPEN_SOURCE; } - if (of_flags & OF_GPIO_SLEEP_MAY_LOSE_VALUE) - *flags |= GPIO_SLEEP_MAY_LOSE_VALUE; + if (of_flags & OF_GPIO_TRANSITORY) + *flags |= GPIO_TRANSITORY; return desc; } @@ -214,6 +327,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, if (xlate_flags & OF_GPIO_ACTIVE_LOW) *lflags |= GPIO_ACTIVE_LOW; + if (xlate_flags & OF_GPIO_TRANSITORY) + *lflags |= GPIO_TRANSITORY; if (of_property_read_bool(np, "input")) *dflags |= GPIOD_IN; diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 3f454eaf2101..3dbaf489a8a5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -8,6 +8,7 @@ #include <linux/interrupt.h> #include <linux/kdev_t.h> #include <linux/slab.h> +#include <linux/ctype.h> #include "gpiolib.h" @@ -106,8 +107,14 @@ static ssize_t value_show(struct device *dev, mutex_lock(&data->mutex); - status = sprintf(buf, "%d\n", gpiod_get_value_cansleep(desc)); + status = gpiod_get_value_cansleep(desc); + if (status < 0) + goto err; + buf[0] = '0' + status; + buf[1] = '\n'; + status = 2; +err: mutex_unlock(&data->mutex); return status; @@ -118,7 +125,7 @@ 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; + ssize_t status = 0; mutex_lock(&data->mutex); @@ -127,7 +134,11 @@ static ssize_t value_store(struct device *dev, } else { long value; - status = kstrtol(buf, 0, &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; @@ -138,7 +149,7 @@ static ssize_t value_store(struct device *dev, return status; } -static DEVICE_ATTR_RW(value); +static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { @@ -474,11 +485,15 @@ static ssize_t export_store(struct class *class, status = -ENODEV; goto done; } - status = gpiod_export(desc, true); - if (status < 0) - gpiod_free(desc); - else - set_bit(FLAG_SYSFS, &desc->flags); + + status = gpiod_set_transitory(desc, false); + if (!status) { + status = gpiod_export(desc, true); + if (status < 0) + gpiod_free(desc); + else + set_bit(FLAG_SYSFS, &desc->flags); + } done: if (status) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5d6e8bb38ac7..36ca5064486e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -162,7 +162,7 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { - if (!desc || !desc->gdev || !desc->gdev->chip) + if (!desc || !desc->gdev) return NULL; return desc->gdev->chip; } @@ -196,7 +196,7 @@ static int gpiochip_find_base(int ngpio) * gpiod_get_direction - return the current direction of a GPIO * @desc: GPIO to get the direction of * - * Return GPIOF_DIR_IN or GPIOF_DIR_OUT, or an error code in case of error. + * Returns 0 for output, 1 for input, or an error code in case of error. * * This function may sleep if gpiod_cansleep() is true. */ @@ -460,6 +460,15 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) 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 ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && + (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ if (!(lflags & GPIOHANDLE_REQUEST_OUTPUT) && ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || @@ -506,6 +515,10 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &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". @@ -588,6 +601,9 @@ out_free_lh: * @events: KFIFO for the GPIO events * @read_lock: mutex lock to protect reads from colliding with adding * new events to the FIFO + * @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; @@ -598,6 +614,7 @@ struct lineevent_state { wait_queue_head_t wait; DECLARE_KFIFO(events, struct gpioevent_data, 16); struct mutex read_lock; + u64 timestamp; }; #define GPIOEVENT_REQUEST_VALID_FLAGS \ @@ -732,7 +749,10 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) struct gpioevent_data ge; int ret, level; - ge.timestamp = ktime_get_real_ns(); + /* Do not leak kernel stack to userspace */ + memset(&ge, 0, sizeof(ge)); + + ge.timestamp = le->timestamp; level = gpiod_get_value_cansleep(le->desc); if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE @@ -760,6 +780,19 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) 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_real_ns(); + + return IRQ_WAKE_THREAD; +} + static int lineevent_create(struct gpio_device *gdev, void __user *ip) { struct gpioevent_request eventreq; @@ -852,7 +885,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) /* Request a thread to read the events */ ret = request_threaded_irq(le->irq, - NULL, + lineevent_irq_handler, lineevent_irq_thread, irqflags, le->label, @@ -1050,7 +1083,7 @@ static void gpiodevice_release(struct device *dev) list_del(&gdev->list); ida_simple_remove(&gpio_ida, gdev->id); - kfree(gdev->label); + kfree_const(gdev->label); kfree(gdev->descs); kfree(gdev); } @@ -1159,10 +1192,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, goto err_free_descs; } - if (chip->label) - gdev->label = kstrdup(chip->label, GFP_KERNEL); - else - gdev->label = kstrdup("unknown", GFP_KERNEL); + gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL); if (!gdev->label) { status = -ENOMEM; goto err_free_descs; @@ -1209,31 +1239,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, struct gpio_desc *desc = &gdev->descs[i]; desc->gdev = gdev; - /* - * REVISIT: most hardware initializes GPIOs as inputs - * (often with pullups enabled) so power usage is - * minimized. Linux code should set the gpio direction - * first thing; but until it does, and in case - * chip->get_direction is not set, we may expose the - * wrong direction in sysfs. - */ - if (chip->get_direction) { - /* - * If we have .get_direction, set up the initial - * direction flag from the hardware. - */ - int dir = chip->get_direction(chip, i); - - if (!dir) - set_bit(FLAG_IS_OUT, &desc->flags); - } else if (!chip->direction_input) { - /* - * If the chip lacks the .direction_input callback - * we logically assume all lines are outputs. - */ - set_bit(FLAG_IS_OUT, &desc->flags); - } + /* REVISIT: most hardware initializes GPIOs as inputs (often + * with pullups enabled) so power usage is minimized. Linux + * code should set the gpio direction first thing; but until + * it does, and in case chip->get_direction is not set, we may + * expose the wrong direction in sysfs. + */ + desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } #ifdef CONFIG_PINCTRL @@ -1283,7 +1296,7 @@ err_remove_from_list: list_del(&gdev->list); spin_unlock_irqrestore(&gpio_lock, flags); err_free_label: - kfree(gdev->label); + kfree_const(gdev->label); err_free_descs: kfree(gdev->descs); err_free_gdev: @@ -1383,7 +1396,7 @@ static int devm_gpio_chip_match(struct device *dev, void *res, void *data) } /** - * devm_gpiochip_add_data() - Resource manager piochip_add_data() + * devm_gpiochip_add_data() - Resource manager gpiochip_add_data() * @dev: the device pointer on which irq_chip belongs to. * @chip: the chip to register, with chip->base initialized * @data: driver-private data associated with this chip @@ -1510,14 +1523,15 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) gpiochip->irq.valid_mask = NULL; } -static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, - unsigned int offset) +bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, + unsigned int offset) { /* No mask means all valid */ if (likely(!gpiochip->irq.valid_mask)) return true; return test_bit(offset, gpiochip->irq.valid_mask); } +EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); /** * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip @@ -2174,40 +2188,37 @@ done: * macro to avoid endless duplication. If the desc is NULL it is an * optional GPIO and calls should just bail out. */ +static int validate_desc(const struct gpio_desc *desc, const char *func) +{ + if (!desc) + return 0; + if (IS_ERR(desc)) { + pr_warn("%s: invalid GPIO (errorpointer)\n", func); + return PTR_ERR(desc); + } + if (!desc->gdev) { + pr_warn("%s: invalid GPIO (no device)\n", func); + return -EINVAL; + } + if (!desc->gdev->chip) { + dev_warn(&desc->gdev->dev, + "%s: backing chip is gone\n", func); + return 0; + } + return 1; +} + #define VALIDATE_DESC(desc) do { \ - if (!desc) \ - return 0; \ - if (IS_ERR(desc)) { \ - pr_warn("%s: invalid GPIO (errorpointer)\n", __func__); \ - return PTR_ERR(desc); \ - } \ - if (!desc->gdev) { \ - pr_warn("%s: invalid GPIO (no device)\n", __func__); \ - return -EINVAL; \ - } \ - if ( !desc->gdev->chip ) { \ - dev_warn(&desc->gdev->dev, \ - "%s: backing chip is gone\n", __func__); \ - return 0; \ - } } while (0) + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return __valid; \ + } while (0) #define VALIDATE_DESC_VOID(desc) do { \ - if (!desc) \ - return; \ - if (IS_ERR(desc)) { \ - pr_warn("%s: invalid GPIO (errorpointer)\n", __func__); \ - return; \ - } \ - if (!desc->gdev) { \ - pr_warn("%s: invalid GPIO (no device)\n", __func__); \ - return; \ - } \ - if (!desc->gdev->chip) { \ - dev_warn(&desc->gdev->dev, \ - "%s: backing chip is gone\n", __func__); \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ return; \ - } } while (0) - + } while (0) int gpiod_request(struct gpio_desc *desc, const char *label) { @@ -2456,7 +2467,7 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw); */ int gpiod_direction_output(struct gpio_desc *desc, int value) { - struct gpio_chip *gc = desc->gdev->chip; + struct gpio_chip *gc; int ret; VALIDATE_DESC(desc); @@ -2473,6 +2484,7 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) return -EIO; } + gc = desc->gdev->chip; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { /* First see if we can enable open drain in hardware */ ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), @@ -2530,6 +2542,50 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) EXPORT_SYMBOL_GPL(gpiod_set_debounce); /** + * gpiod_set_transitory - Lose or retain GPIO state on suspend or reset + * @desc: descriptor of the GPIO for which to configure persistence + * @transitory: True to lose state on suspend or reset, false for persistence + * + * Returns: + * 0 on success, otherwise a negative error code. + */ +int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) +{ + struct gpio_chip *chip; + unsigned long packed; + int gpio; + int rc; + + VALIDATE_DESC(desc); + /* + * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for + * persistence state. + */ + if (transitory) + set_bit(FLAG_TRANSITORY, &desc->flags); + else + clear_bit(FLAG_TRANSITORY, &desc->flags); + + /* If the driver supports it, set the persistence state now */ + chip = desc->gdev->chip; + if (!chip->set_config) + return 0; + + packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, + !transitory); + gpio = gpio_chip_hwgpio(desc); + rc = chip->set_config(chip, gpio, packed); + if (rc == -ENOTSUPP) { + dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", + gpio); + return 0; + } + + return rc; +} +EXPORT_SYMBOL_GPL(gpiod_set_transitory); + +/** * gpiod_is_active_low - test whether a GPIO is active-low or not * @desc: the gpio descriptor to test * @@ -3129,8 +3185,7 @@ bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset) if (offset >= chip->ngpio) return false; - return !test_bit(FLAG_SLEEP_MAY_LOSE_VALUE, - &chip->gpiodev->descs[offset].flags); + return !test_bit(FLAG_TRANSITORY, &chip->gpiodev->descs[offset].flags); } EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); @@ -3565,8 +3620,10 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if (lflags & GPIO_SLEEP_MAY_LOSE_VALUE) - set_bit(FLAG_SLEEP_MAY_LOSE_VALUE, &desc->flags); + + status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); + if (status < 0) + return status; /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { @@ -3606,6 +3663,8 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, struct gpio_desc *desc = NULL; int status; enum gpio_lookup_flags lookupflags = 0; + /* Maybe we have a device name, maybe not */ + const char *devname = dev ? dev_name(dev) : "?"; dev_dbg(dev, "GPIO lookup for consumer %s\n", con_id); @@ -3634,7 +3693,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, return desc; } - status = gpiod_request(desc, con_id); + /* + * If a connection label was passed use that, else attempt to use + * the device name as label + */ + status = gpiod_request(desc, con_id ? con_id : devname); if (status < 0) return ERR_PTR(status); @@ -3650,17 +3713,88 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, EXPORT_SYMBOL_GPL(gpiod_get_index); /** + * gpiod_get_from_of_node() - obtain a GPIO from an OF node + * @node: handle of the OF node + * @propname: name of the DT property representing the GPIO + * @index: index of the GPIO to obtain for the consumer + * @dflags: GPIO initialization flags + * @label: label to attach to the requested GPIO + * + * Returns: + * On successful request the GPIO pin is configured in accordance with + * provided @dflags. If the node does not have the requested GPIO + * property, NULL is returned. + * + * In case of error an ERR_PTR() is returned. + */ +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label) +{ + struct gpio_desc *desc; + unsigned long lflags = 0; + enum of_gpio_flags flags; + bool active_low = false; + bool single_ended = false; + bool open_drain = false; + bool transitory = false; + int ret; + + desc = of_get_named_gpiod_flags(node, propname, + index, &flags); + + if (!desc || IS_ERR(desc)) { + /* If it is not there, just return NULL */ + if (PTR_ERR(desc) == -ENOENT) + return NULL; + return desc; + } + + active_low = flags & OF_GPIO_ACTIVE_LOW; + single_ended = flags & OF_GPIO_SINGLE_ENDED; + open_drain = flags & OF_GPIO_OPEN_DRAIN; + transitory = flags & OF_GPIO_TRANSITORY; + + ret = gpiod_request(desc, label); + if (ret) + return ERR_PTR(ret); + + if (active_low) + lflags |= GPIO_ACTIVE_LOW; + + if (single_ended) { + if (open_drain) + lflags |= GPIO_OPEN_DRAIN; + else + lflags |= GPIO_OPEN_SOURCE; + } + + if (transitory) + lflags |= GPIO_TRANSITORY; + + ret = gpiod_configure_flags(desc, propname, lflags, dflags); + if (ret < 0) { + gpiod_put(desc); + return ERR_PTR(ret); + } + + return desc; +} +EXPORT_SYMBOL(gpiod_get_from_of_node); + +/** * fwnode_get_named_gpiod - obtain a GPIO from firmware node * @fwnode: handle of the firmware node * @propname: name of the firmware property representing the GPIO - * @index: index of the GPIO to obtain in the consumer + * @index: index of the GPIO to obtain for the consumer * @dflags: GPIO initialization flags * @label: label to attach to the requested GPIO * * This function can be used for drivers that get their configuration - * from firmware. + * from opaque firmware. * - * Function properly finds the corresponding GPIO using whatever is the + * The function properly finds the corresponding GPIO using whatever is the * underlying firmware interface and then makes sure that the GPIO * descriptor is requested before it is returned to the caller. * @@ -3677,53 +3811,35 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, { struct gpio_desc *desc = ERR_PTR(-ENODEV); unsigned long lflags = 0; - bool active_low = false; - bool single_ended = false; - bool open_drain = false; int ret; if (!fwnode) return ERR_PTR(-EINVAL); if (is_of_node(fwnode)) { - enum of_gpio_flags flags; - - desc = of_get_named_gpiod_flags(to_of_node(fwnode), propname, - index, &flags); - if (!IS_ERR(desc)) { - active_low = flags & OF_GPIO_ACTIVE_LOW; - single_ended = flags & OF_GPIO_SINGLE_ENDED; - open_drain = flags & OF_GPIO_OPEN_DRAIN; - } + desc = gpiod_get_from_of_node(to_of_node(fwnode), + propname, index, + dflags, + label); + return desc; } else if (is_acpi_node(fwnode)) { struct acpi_gpio_info info; desc = acpi_node_get_gpiod(fwnode, propname, index, &info); - if (!IS_ERR(desc)) { - active_low = info.polarity == GPIO_ACTIVE_LOW; - ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags); - if (ret) - pr_debug("Override GPIO initialization flags\n"); - } - } + if (IS_ERR(desc)) + return desc; - if (IS_ERR(desc)) - return desc; + acpi_gpio_update_gpiod_flags(&dflags, &info); + if (info.polarity == GPIO_ACTIVE_LOW) + lflags |= GPIO_ACTIVE_LOW; + } + + /* Currently only ACPI takes this path */ ret = gpiod_request(desc, label); if (ret) return ERR_PTR(ret); - if (active_low) - lflags |= GPIO_ACTIVE_LOW; - - if (single_ended) { - if (open_drain) - lflags |= GPIO_OPEN_DRAIN; - else - lflags |= GPIO_OPEN_SOURCE; - } - ret = gpiod_configure_flags(desc, propname, lflags, dflags); if (ret < 0) { gpiod_put(desc); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 6c44d1652139..b17ec6795c81 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -58,7 +58,7 @@ struct gpio_device { struct gpio_desc *descs; int base; u16 ngpio; - char *label; + const char *label; void *data; struct list_head list; @@ -75,16 +75,20 @@ struct gpio_device { /** * struct acpi_gpio_info - ACPI GPIO specific information + * @adev: reference to ACPI device which consumes GPIO resource * @flags: GPIO initialization flags * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI + * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping */ struct acpi_gpio_info { + struct acpi_device *adev; enum gpiod_flags flags; bool gpioint; int polarity; int triggering; + unsigned int quirks; }; /* gpio suffixes used for ACPI and device tree lookup */ @@ -124,7 +128,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, - enum gpiod_flags update); + struct acpi_gpio_info *info); struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, @@ -149,7 +153,7 @@ static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } static inline int -acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, struct acpi_gpio_info *info) { return 0; } @@ -189,6 +193,12 @@ void gpiod_set_array_value_complex(bool raw, bool can_sleep, struct gpio_desc **desc_array, int *value_array); +/* This is just passed between gpiolib and devres */ +struct gpio_desc *gpiod_get_from_of_node(struct device_node *node, + const char *propname, int index, + enum gpiod_flags dflags, + const char *label); + extern struct spinlock gpio_lock; extern struct list_head gpio_devices; @@ -205,7 +215,7 @@ struct gpio_desc { #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ -#define FLAG_SLEEP_MAY_LOSE_VALUE 12 /* GPIO may lose value in sleep */ +#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ /* Connection label */ const char *label; |