The is the bulk of GPIO changes for the v4.16 kernel cycle.

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.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJacIW6AAoJEEEQszewGV1z9b0P/jxWKaCAGFTTu/HZQ79RBAFq
 w33nIazzoh+88sN7A9xKexpr4ibOxiCvOwkTtrUBNaxGGy5fslj4+OY5BzunEfBK
 1vYxyEqtenvvZK03pOd6CSfHKV+vD5ngnVHGdtGzRvtmDDiSgtzqyEyUhQcXM+l7
 PrEh6qrd4TBZezlVR8kn5eqcmclkCBVSQCuLSq+ThMmCKRZuOdf1Im3D6eBzh1/N
 P81HdcglqbSsfUl1RcFiHs9Z+KcZOq83CNl2Ej1LePK2JBZbmkx9dR+WSJmV1u4P
 6wvzFcQDhfGEiiteg2BS5c+o6aAyShpuRNut+2MLre8icmdfpqUEqFotHbfQjW5y
 sqaejGsJ5aHcRBq7UUM+F9s1R0iN3tlafi3L0WEhl0Tn5huRQq3Uqcw6e5l+XrWd
 0h+b5PbKJZO/iqzRhSl+rhc0V2CFDJOCwvY+JX6356fvrcF0T6LhvKfDYtKU3Iyb
 HB0RG1OcYe228f96azvafCkFyBIYX9mqHBvOXpQQgrZQYXfN1rupLvpOhxC+Wbvn
 nsGE2bdD6HA1bytTbkxbL+QWP7faHf5YVcZpaN7UWbO3sOzL46fj8eHwHUim95Tr
 pR5kDZRhZd8+9SCNZ/ttpaEbis9MOqS/3Mlxrj4GXtfFFmR53hjFy2bG/Z7R2RB0
 MlSEJRc8iDIs+1j3D2RR
 =k5nL
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2018-01-31 12:25:27 -08:00
commit 9798f5178f
60 changed files with 2743 additions and 696 deletions

View file

@ -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

View file

@ -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

View file

@ -124,6 +124,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

View file

@ -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>

View file

@ -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;

View file

@ -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>

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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;
}

View file

@ -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");

View file

@ -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 <[email protected]>");
MODULE_DESCRIPTION("AXP20x PMIC GPIO driver");
MODULE_LICENSE("GPL");

View file

@ -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);

View file

@ -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,

View file

@ -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. */

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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,

View file

@ -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 <[email protected]>");
MODULE_LICENSE("GPL");

View file

@ -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ò <[email protected]>");
MODULE_AUTHOR("Diego Elio Pettenò <[email protected]>");
MODULE_DESCRIPTION("GPIO interface for IT87xx Super I/O chips");
MODULE_LICENSE("GPL");

View file

@ -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;
}

View file

@ -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;
for (i = 0; i < GPIO_MOCKUP_MAX_GC; i++) {
pdev = gpio_mockup_pdevs[i];
if (pdev)
platform_device_unregister(pdev);
}
}
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 (!gpio_mockup_dbg_dir)
pr_err("%s: error creating debugfs directory\n",
GPIO_MOCKUP_NAME);
pdev = platform_device_alloc(GPIO_MOCKUP_NAME, -1);
if (!pdev)
return -ENOMEM;
err = platform_device_add(pdev);
if (err) {
platform_device_put(pdev);
return err;
}
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 <[email protected]>");
MODULE_AUTHOR("Bamvor Jian Zhang <[email protected]>");

View file

@ -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;
}

View file

@ -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 <[email protected]>");
MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver");
MODULE_LICENSE("GPL v2");

View file

@ -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;
int ret, irq;
irq = platform_get_irq(pdev, 0);
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: "

View file

@ -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++) {

732
drivers/gpio/gpio-winbond.c Normal file
View file

@ -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, &params.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, &params.gpios, BITS_PER_LONG)
if (!winbond_gpio_configure_port(base, i))
__clear_bit(i, &params.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, &params.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 <[email protected]>");
MODULE_DESCRIPTION("GPIO interface for Winbond Super I/O chips");
MODULE_LICENSE("GPL");

View file

@ -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;
}

View file

@ -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;

View file

@ -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)

View file

@ -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.
/* 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);
}
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) \
int __valid = validate_desc(desc, __func__); \
if (__valid <= 0) \
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__); \
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),
@ -2529,6 +2541,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);
@ -3649,18 +3712,89 @@ 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;
acpi_gpio_update_gpiod_flags(&dflags, &info);
if (info.polarity == GPIO_ACTIVE_LOW)
lflags |= GPIO_ACTIVE_LOW;
}
if (IS_ERR(desc))
return desc;
/* 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);

View file

@ -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;