This is the bulk of GPIO changes for the v4.20 series:

Core changes:
 
 - A patch series from Hans Verkuil to make it possible to
   enable/disable IRQs on a GPIO line at runtime and drive GPIO
   lines as output without having to put/get them from scratch.
   The irqchip callbacks have been improved so that they can
   use only the fastpatch callbacks to enable/disable irqs
   like any normal irqchip, especially the gpiod_lock_as_irq()
   has been improved to be callable in fastpath context.
   A bunch of rework had to be done to achieve this but it is
   a big win since I never liked to restrict this to slowpath.
   The only call requireing slowpath was try_module_get() and
   this is kept at the .request_resources() slowpath callback.
   In the GPIO CEC driver this is a big win sine a single
   line is used for both outgoing and incoming traffic, and
   this needs to use IRQs for incoming traffic while actively
   driving the line for outgoing traffic.
 
 - Janusz Krzysztofik improved the GPIO array API to pass a
   "cookie" (struct gpio_array) and a bitmap for setting or
   getting multiple GPIO lines at once. This improvement
   orginated in a specific need to speed up an OMAP1 driver and
   has led to a much better API and real performance gains
   when the state of the array can be used to bypass a lot
   of checks and code when we want things to go really fast.
   The previous code would minimize the number of calls
   down to the driver callbacks assuming the CPU speed was
   orders of magnitude faster than the I/O latency, but this
   assumption was wrong on several platforms: what we needed
   to do was to profile and improve the speed on the hot
   path of the array functions and this change is now
   completed.
 
 - Clean out the painful and hard to grasp BNF experiments
   from the device tree bindings. Future approaches are looking
   into using JSON schema for this purpose. (Rob Herring
   is floating a patch series.)
 
 New drivers:
 
 - The RCAR driver now supports r8a774a1 (RZ/G2M).
 
 - Synopsys GPIO via CREGs driver.
 
 Major improvements:
 
 - Modernization of the EP93xx driver to use irqdomain and
   other contemporary concepts.
 
 - The ingenic driver has been merged into the Ingenic pin
   control driver and removed from the GPIO subsystem.
 
 - Debounce support in the ftgpio010 driver.
 -----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJbzdyOAAoJEEEQszewGV1zfYcP/0HBEAOPhHD/i5OQxfKs1msh
 mFT/t/IbTmRpCgbEv4CDx4Kc/InE0sUnQr1TL/1WvU6uObM6Ncxq5Z90MvyrgzYu
 BqQHq2k2tORvkVSNRxcfD/BAAoo1EerXts1kDhutvdKfepfS6DxpENwzvsFgkVlq
 2jj1cdZztjv8A+9cspHDpQP+jDvl1VSc10nR5fRu1TttSpUwzRJaB30NBNXJmMJc
 5KUr67lEbsQRPsBvFErU11bydPqhfT+pXmODcfIwS0EtATQ8WC5mkSb/Ooei0fvT
 oZ7uR3Os8tMf7isOKssEyFabKwhnfOEt6TBt9em0TfUtInOo0Dc7r8TfBcn57fyZ
 xg2R9DQEVRfac8bjhF/BI5KHuN9IMGDDvj6XApumQVliZbISRjMnh3jte6RpcV0A
 Ejqz8FeDY13qvEdOnW1EPpwmXdDVWiEAq0ebGLStKNls+/4gB2HmyxGUOzJf+og5
 hujsxcJzGQqjCe0moeY/1d7vsy0ZjbHoS+p5fy79U212y2O7onEzFU92AX89bxKC
 rx2eCNmiZxCUy1nqu8edO62VnH6QdnqG3o+a4DJfCSHPvFM/E/NX9zHemZubQQ4I
 rYXNy4bL4tEG9cqWMfBxWrpiDZw7H6l8kXwdZG8IMyRU9BcKu96amgZ+jBXwzoaB
 JZelAAUWB9APghJYFr7o
 =YosT
 -----END PGP SIGNATURE-----

Merge tag 'gpio-v4.20-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio

Pull GPIO updates from Linus Walleij:
 "This is the bulk of GPIO changes for the v4.20 series:

  Core changes:

   - A patch series from Hans Verkuil to make it possible to
     enable/disable IRQs on a GPIO line at runtime and drive GPIO lines
     as output without having to put/get them from scratch.

     The irqchip callbacks have been improved so that they can use only
     the fastpatch callbacks to enable/disable irqs like any normal
     irqchip, especially the gpiod_lock_as_irq() has been improved to be
     callable in fastpath context.

     A bunch of rework had to be done to achieve this but it is a big
     win since I never liked to restrict this to slowpath. The only call
     requireing slowpath was try_module_get() and this is kept at the
     .request_resources() slowpath callback. In the GPIO CEC driver this
     is a big win sine a single line is used for both outgoing and
     incoming traffic, and this needs to use IRQs for incoming traffic
     while actively driving the line for outgoing traffic.

   - Janusz Krzysztofik improved the GPIO array API to pass a "cookie"
     (struct gpio_array) and a bitmap for setting or getting multiple
     GPIO lines at once.

     This improvement orginated in a specific need to speed up an OMAP1
     driver and has led to a much better API and real performance gains
     when the state of the array can be used to bypass a lot of checks
     and code when we want things to go really fast.

     The previous code would minimize the number of calls down to the
     driver callbacks assuming the CPU speed was orders of magnitude
     faster than the I/O latency, but this assumption was wrong on
     several platforms: what we needed to do was to profile and improve
     the speed on the hot path of the array functions and this change is
     now completed.

   - Clean out the painful and hard to grasp BNF experiments from the
     device tree bindings. Future approaches are looking into using JSON
     schema for this purpose. (Rob Herring is floating a patch series.)

  New drivers:

   - The RCAR driver now supports r8a774a1 (RZ/G2M).

   - Synopsys GPIO via CREGs driver.

  Major improvements:

   - Modernization of the EP93xx driver to use irqdomain and other
     contemporary concepts.

   - The ingenic driver has been merged into the Ingenic pin control
     driver and removed from the GPIO subsystem.

   - Debounce support in the ftgpio010 driver"

* tag 'gpio-v4.20-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (116 commits)
  gpio: Clarify kerneldoc on gpiochip_set_chained_irqchip()
  gpio: Remove unused 'irqchip' argument to gpiochip_set_cascaded_irqchip()
  gpio: Drop parent irq assignment during cascade setup
  mmc: pwrseq_simple: Fix incorrect handling of GPIO bitmap
  gpio: fix SNPS_CREG kconfig dependency warning
  gpiolib: Initialize gdev field before is used
  gpio: fix kernel-doc after devres.c file rename
  gpio: fix doc string for devm_gpiochip_add_data() to not talk about irq_chip
  gpio: syscon: Fix possible NULL ptr usage
  gpiolib: Show correct direction from the beginning
  pinctrl: msm: Use init_valid_mask exported function
  gpiolib: Add init_valid_mask exported function
  GPIO: add single-register GPIO via CREG driver
  dt-bindings: Document the Synopsys GPIO via CREG bindings
  gpio: mockup: use device properties instead of platform_data
  gpio: Slightly more helpful debugfs
  gpio: omap: Remove set but not used variable 'dev'
  gpio: omap: drop omap_gpio_list
  Accept partial 'gpio-line-names' property.
  gpio: omap: get rid of the conditional PM runtime calls
  ...
This commit is contained in:
Linus Torvalds 2018-10-23 08:45:05 +01:00
commit 114b5f8f7e
77 changed files with 2293 additions and 1240 deletions

View file

@ -200,6 +200,7 @@ config GPIO_EP93XX
def_bool y
depends on ARCH_EP93XX
select GPIO_GENERIC
select GPIOLIB_IRQCHIP
config GPIO_EXAR
tristate "Support for GPIO pins on XR17V352/354/358"
@ -428,6 +429,24 @@ config GPIO_REG
A 32-bit single register GPIO fixed in/out implementation. This
can be used to represent any register as a set of GPIO signals.
config GPIO_SIOX
tristate "SIOX GPIO support"
depends on SIOX
select GPIOLIB_IRQCHIP
help
Say yes here to support SIOX I/O devices. These are units connected
via a SIOX bus and have a number of fixed-direction I/O lines.
config GPIO_SNPS_CREG
bool "Synopsys GPIO via CREG (Control REGisters) driver"
depends on ARC || COMPILE_TEST
depends on OF_GPIO
help
This driver supports GPIOs via CREG on various Synopsys SoCs.
This is a single-register MMIO GPIO driver for complex cases
where only several fields in register belong to GPIO lines and
each GPIO line owns a field with different length and on/off value.
config GPIO_SPEAR_SPICS
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
depends on PLAT_SPEAR
@ -469,6 +488,7 @@ config GPIO_SYSCON
config GPIO_TB10X
bool
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
select OF_GPIO

View file

@ -3,8 +3,8 @@
ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
obj-$(CONFIG_GPIOLIB) += devres.o
obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_GPIOLIB) += gpiolib-devres.o
obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o
obj-$(CONFIG_GPIOLIB) += gpiolib-devprop.o
obj-$(CONFIG_OF_GPIO) += gpiolib-of.o
@ -110,6 +110,7 @@ obj-$(CONFIG_GPIO_REG) += gpio-reg.o
obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
@ -124,6 +125,7 @@ obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o
obj-$(CONFIG_GPIO_TPS65086) += gpio-tps65086.o
obj-$(CONFIG_GPIO_TPS65218) += gpio-tps65218.o

View file

@ -172,7 +172,7 @@ static struct platform_driver adp5520_gpio_driver = {
module_platform_driver(adp5520_gpio_driver);
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
MODULE_DESCRIPTION("GPIO ADP5520 Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:adp5520-gpio");

View file

@ -494,6 +494,6 @@ static struct i2c_driver adp5588_gpio_driver = {
module_i2c_driver(adp5588_gpio_driver);
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
MODULE_LICENSE("GPL");

View file

@ -373,6 +373,7 @@ static void bcm_kona_gpio_irq_mask(struct irq_data *d)
val = readl(reg_base + GPIO_INT_MASK(bank_id));
val |= BIT(bit);
writel(val, reg_base + GPIO_INT_MASK(bank_id));
gpiochip_disable_irq(&kona_gpio->gpio_chip, gpio);
raw_spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
@ -394,6 +395,7 @@ static void bcm_kona_gpio_irq_unmask(struct irq_data *d)
val = readl(reg_base + GPIO_INT_MSKCLR(bank_id));
val |= BIT(bit);
writel(val, reg_base + GPIO_INT_MSKCLR(bank_id));
gpiochip_enable_irq(&kona_gpio->gpio_chip, gpio);
raw_spin_unlock_irqrestore(&kona_gpio->lock, flags);
}
@ -485,23 +487,15 @@ static void bcm_kona_gpio_irq_handler(struct irq_desc *desc)
static int bcm_kona_gpio_irq_reqres(struct irq_data *d)
{
struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
int ret;
ret = gpiochip_lock_as_irq(&kona_gpio->gpio_chip, d->hwirq);
if (ret) {
dev_err(kona_gpio->gpio_chip.parent,
"unable to lock HW IRQ %lu for IRQ\n",
d->hwirq);
return ret;
}
return 0;
return gpiochip_reqres_irq(&kona_gpio->gpio_chip, d->hwirq);
}
static void bcm_kona_gpio_irq_relres(struct irq_data *d)
{
struct bcm_kona_gpio *kona_gpio = irq_data_get_irq_chip_data(d);
gpiochip_unlock_as_irq(&kona_gpio->gpio_chip, d->hwirq);
gpiochip_relres_irq(&kona_gpio->gpio_chip, d->hwirq);
}
static struct irq_chip bcm_gpio_irq_chip = {

View file

@ -664,6 +664,18 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
struct brcmstb_gpio_bank *bank;
struct gpio_chip *gc;
/*
* If bank_width is 0, then there is an empty bank in the
* register block. Special handling for this case.
*/
if (bank_width == 0) {
dev_dbg(dev, "Width 0 found: Empty bank @ %d\n",
num_banks);
num_banks++;
gpio_base += MAX_GPIO_PER_BANK;
continue;
}
bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
if (!bank) {
err = -ENOMEM;
@ -740,9 +752,6 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
goto fail;
}
dev_info(dev, "Registered %d banks (GPIO(s): %d-%d)\n",
num_banks, priv->gpio_base, gpio_base - 1);
if (priv->parent_wake_irq && need_wakeup_event)
pm_wakeup_event(dev, 0);

View file

@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0+
//
// Synopsys CREG (Control REGisters) GPIO driver
//
// Copyright (C) 2018 Synopsys
// Author: Eugeniy Paltsev <[email protected]>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#define MAX_GPIO 32
struct creg_layout {
u8 ngpio;
u8 shift[MAX_GPIO];
u8 on[MAX_GPIO];
u8 off[MAX_GPIO];
u8 bit_per_gpio[MAX_GPIO];
};
struct creg_gpio {
struct gpio_chip gc;
void __iomem *regs;
spinlock_t lock;
const struct creg_layout *layout;
};
static void creg_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
{
struct creg_gpio *hcg = gpiochip_get_data(gc);
const struct creg_layout *layout = hcg->layout;
u32 reg, reg_shift, value;
unsigned long flags;
int i;
value = val ? hcg->layout->on[offset] : hcg->layout->off[offset];
reg_shift = layout->shift[offset];
for (i = 0; i < offset; i++)
reg_shift += layout->bit_per_gpio[i] + layout->shift[i];
spin_lock_irqsave(&hcg->lock, flags);
reg = readl(hcg->regs);
reg &= ~(GENMASK(layout->bit_per_gpio[i] - 1, 0) << reg_shift);
reg |= (value << reg_shift);
writel(reg, hcg->regs);
spin_unlock_irqrestore(&hcg->lock, flags);
}
static int creg_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int val)
{
creg_gpio_set(gc, offset, val);
return 0;
}
static int creg_gpio_validate_pg(struct device *dev, struct creg_gpio *hcg,
int i)
{
const struct creg_layout *layout = hcg->layout;
if (layout->bit_per_gpio[i] < 1 || layout->bit_per_gpio[i] > 8)
return -EINVAL;
/* Check that on valiue fits it's placeholder */
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->on[i])
return -EINVAL;
/* Check that off valiue fits it's placeholder */
if (GENMASK(31, layout->bit_per_gpio[i]) & layout->off[i])
return -EINVAL;
if (layout->on[i] == layout->off[i])
return -EINVAL;
return 0;
}
static int creg_gpio_validate(struct device *dev, struct creg_gpio *hcg,
u32 ngpios)
{
u32 reg_len = 0;
int i;
if (hcg->layout->ngpio < 1 || hcg->layout->ngpio > MAX_GPIO)
return -EINVAL;
if (ngpios < 1 || ngpios > hcg->layout->ngpio) {
dev_err(dev, "ngpios must be in [1:%u]\n", hcg->layout->ngpio);
return -EINVAL;
}
for (i = 0; i < hcg->layout->ngpio; i++) {
if (creg_gpio_validate_pg(dev, hcg, i))
return -EINVAL;
reg_len += hcg->layout->shift[i] + hcg->layout->bit_per_gpio[i];
}
/* Check that we fit in 32 bit register */
if (reg_len > 32)
return -EINVAL;
return 0;
}
static const struct creg_layout hsdk_cs_ctl = {
.ngpio = 10,
.shift = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
.off = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
.on = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
.bit_per_gpio = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }
};
static const struct creg_layout axs10x_flsh_cs_ctl = {
.ngpio = 1,
.shift = { 0 },
.off = { 1 },
.on = { 3 },
.bit_per_gpio = { 2 }
};
static const struct of_device_id creg_gpio_ids[] = {
{
.compatible = "snps,creg-gpio-axs10x",
.data = &axs10x_flsh_cs_ctl
}, {
.compatible = "snps,creg-gpio-hsdk",
.data = &hsdk_cs_ctl
}, { /* sentinel */ }
};
static int creg_gpio_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct creg_gpio *hcg;
struct resource *mem;
u32 ngpios;
int ret;
hcg = devm_kzalloc(dev, sizeof(struct creg_gpio), GFP_KERNEL);
if (!hcg)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hcg->regs = devm_ioremap_resource(dev, mem);
if (IS_ERR(hcg->regs))
return PTR_ERR(hcg->regs);
match = of_match_node(creg_gpio_ids, pdev->dev.of_node);
hcg->layout = match->data;
if (!hcg->layout)
return -EINVAL;
ret = of_property_read_u32(dev->of_node, "ngpios", &ngpios);
if (ret)
return ret;
ret = creg_gpio_validate(dev, hcg, ngpios);
if (ret)
return ret;
spin_lock_init(&hcg->lock);
hcg->gc.label = dev_name(dev);
hcg->gc.base = -1;
hcg->gc.ngpio = ngpios;
hcg->gc.set = creg_gpio_set;
hcg->gc.direction_output = creg_gpio_dir_out;
hcg->gc.of_node = dev->of_node;
ret = devm_gpiochip_add_data(dev, &hcg->gc, hcg);
if (ret)
return ret;
dev_info(dev, "GPIO controller with %d gpios probed\n", ngpios);
return 0;
}
static struct platform_driver creg_gpio_snps_driver = {
.driver = {
.name = "snps-creg-gpio",
.of_match_table = creg_gpio_ids,
},
.probe = creg_gpio_probe,
};
builtin_platform_driver(creg_gpio_snps_driver);

View file

@ -9,6 +9,7 @@
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/gpio/driver.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@ -24,6 +25,12 @@
#include <linux/platform_device.h>
#include <linux/platform_data/gpio-davinci.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/spinlock.h>
#include <asm-generic/gpio.h>
#define MAX_REGS_BANKS 5
#define MAX_INT_PER_BANK 32
struct davinci_gpio_regs {
u32 dir;
@ -41,11 +48,31 @@ struct davinci_gpio_regs {
typedef struct irq_chip *(*gpio_get_irq_chip_cb_t)(unsigned int irq);
#define BINTEN 0x8 /* GPIO Interrupt Per-Bank Enable Register */
#define MAX_LABEL_SIZE 20
static void __iomem *gpio_base;
static unsigned int offset_array[5] = {0x10, 0x38, 0x60, 0x88, 0xb0};
struct davinci_gpio_irq_data {
void __iomem *regs;
struct davinci_gpio_controller *chip;
int bank_num;
};
struct davinci_gpio_controller {
struct gpio_chip chip;
struct irq_domain *irq_domain;
/* Serialize access to GPIO registers */
spinlock_t lock;
void __iomem *regs[MAX_REGS_BANKS];
int gpio_unbanked;
int irqs[MAX_INT_PER_BANK];
};
static inline u32 __gpio_mask(unsigned gpio)
{
return 1 << (gpio % 32);
}
static inline struct davinci_gpio_regs __iomem *irq2regs(struct irq_data *d)
{
struct davinci_gpio_regs __iomem *g;
@ -166,14 +193,12 @@ of_err:
static int davinci_gpio_probe(struct platform_device *pdev)
{
static int ctrl_num, bank_base;
int gpio, bank, i, ret = 0;
int bank, i, ret = 0;
unsigned int ngpio, nbank, nirq;
struct davinci_gpio_controller *chips;
struct davinci_gpio_platform_data *pdata;
struct device *dev = &pdev->dev;
struct resource *res;
char label[MAX_LABEL_SIZE];
pdata = davinci_gpio_get_pdata(pdev);
if (!pdata) {
@ -207,10 +232,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
else
nirq = DIV_ROUND_UP(ngpio, 16);
nbank = DIV_ROUND_UP(ngpio, 32);
chips = devm_kcalloc(dev,
nbank, sizeof(struct davinci_gpio_controller),
GFP_KERNEL);
chips = devm_kzalloc(dev, sizeof(*chips), GFP_KERNEL);
if (!chips)
return -ENOMEM;
@ -228,10 +250,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
}
}
snprintf(label, MAX_LABEL_SIZE, "davinci_gpio.%d", ctrl_num++);
chips->chip.label = devm_kstrdup(dev, label, GFP_KERNEL);
if (!chips->chip.label)
return -ENOMEM;
chips->chip.label = dev_name(dev);
chips->chip.direction_input = davinci_direction_in;
chips->chip.get = davinci_gpio_get;
@ -239,7 +258,7 @@ static int davinci_gpio_probe(struct platform_device *pdev)
chips->chip.set = davinci_gpio_set;
chips->chip.ngpio = ngpio;
chips->chip.base = bank_base;
chips->chip.base = -1;
#ifdef CONFIG_OF_GPIO
chips->chip.of_gpio_n_cells = 2;
@ -252,28 +271,21 @@ static int davinci_gpio_probe(struct platform_device *pdev)
}
#endif
spin_lock_init(&chips->lock);
bank_base += ngpio;
for (gpio = 0, bank = 0; gpio < ngpio; gpio += 32, bank++)
nbank = DIV_ROUND_UP(ngpio, 32);
for (bank = 0; bank < nbank; bank++)
chips->regs[bank] = gpio_base + offset_array[bank];
ret = devm_gpiochip_add_data(dev, &chips->chip, chips);
if (ret)
goto err;
return ret;
platform_set_drvdata(pdev, chips);
ret = davinci_gpio_irq_setup(pdev);
if (ret)
goto err;
return ret;
return 0;
err:
/* Revert the static variable increments */
ctrl_num--;
bank_base -= ngpio;
return ret;
}
/*--------------------------------------------------------------------------*/

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Generic EP93xx GPIO handling
*
@ -6,10 +7,6 @@
*
* Based on code originally from:
* linux/arch/arm/mach-ep93xx/core.c
*
* 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.
*/
#include <linux/init.h>
@ -19,16 +16,26 @@
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
/* FIXME: this is here for gpio_to_irq() - get rid of this! */
#include <linux/gpio.h>
#include <linux/bitops.h>
#include <mach/hardware.h>
#include <mach/gpio-ep93xx.h>
#define EP93XX_GPIO_F_INT_STATUS 0x5c
#define EP93XX_GPIO_A_INT_STATUS 0xa0
#define EP93XX_GPIO_B_INT_STATUS 0xbc
#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0))
/* Maximum value for gpio line identifiers */
#define EP93XX_GPIO_LINE_MAX 63
/* Maximum value for irq capable line identifiers */
#define EP93XX_GPIO_LINE_MAX_IRQ 23
/*
* Static mapping of GPIO bank F IRQS:
* F0..F7 (16..24) to irq 80..87.
*/
#define EP93XX_GPIO_F_IRQ_BASE 80
struct ep93xx_gpio {
void __iomem *mmio_base;
void __iomem *base;
struct gpio_chip gc[8];
};
@ -48,27 +55,45 @@ static const u8 eoi_register_offset[3] = { 0x98, 0xb4, 0x54 };
static const u8 int_en_register_offset[3] = { 0x9c, 0xb8, 0x58 };
static const u8 int_debounce_register_offset[3] = { 0xa8, 0xc4, 0x64 };
static void ep93xx_gpio_update_int_params(unsigned port)
static void ep93xx_gpio_update_int_params(struct ep93xx_gpio *epg, unsigned port)
{
BUG_ON(port > 2);
writeb_relaxed(0, EP93XX_GPIO_REG(int_en_register_offset[port]));
writeb_relaxed(0, epg->base + int_en_register_offset[port]);
writeb_relaxed(gpio_int_type2[port],
EP93XX_GPIO_REG(int_type2_register_offset[port]));
epg->base + int_type2_register_offset[port]);
writeb_relaxed(gpio_int_type1[port],
EP93XX_GPIO_REG(int_type1_register_offset[port]));
epg->base + int_type1_register_offset[port]);
writeb(gpio_int_unmasked[port] & gpio_int_enabled[port],
EP93XX_GPIO_REG(int_en_register_offset[port]));
epg->base + int_en_register_offset[port]);
}
static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable)
static int ep93xx_gpio_port(struct gpio_chip *gc)
{
int line = irq_to_gpio(irq);
int port = line >> 3;
int port_mask = 1 << (line & 7);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
int port = 0;
while (port < ARRAY_SIZE(epg->gc) && gc != &epg->gc[port])
port++;
/* This should not happen but is there as a last safeguard */
if (port == ARRAY_SIZE(epg->gc)) {
pr_crit("can't find the GPIO port\n");
return 0;
}
return port;
}
static void ep93xx_gpio_int_debounce(struct gpio_chip *gc,
unsigned int offset, bool enable)
{
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
int port = ep93xx_gpio_port(gc);
int port_mask = BIT(offset);
if (enable)
gpio_int_debounce[port] |= port_mask;
@ -76,29 +101,36 @@ static void ep93xx_gpio_int_debounce(unsigned int irq, bool enable)
gpio_int_debounce[port] &= ~port_mask;
writeb(gpio_int_debounce[port],
EP93XX_GPIO_REG(int_debounce_register_offset[port]));
epg->base + int_debounce_register_offset[port]);
}
static void ep93xx_gpio_ab_irq_handler(struct irq_desc *desc)
{
unsigned char status;
int i;
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
unsigned long stat;
int offset;
status = readb(EP93XX_GPIO_A_INT_STATUS);
for (i = 0; i < 8; i++) {
if (status & (1 << i)) {
int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_A(0)) + i;
generic_handle_irq(gpio_irq);
}
}
chained_irq_enter(irqchip, desc);
status = readb(EP93XX_GPIO_B_INT_STATUS);
for (i = 0; i < 8; i++) {
if (status & (1 << i)) {
int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_B(0)) + i;
generic_handle_irq(gpio_irq);
}
}
/*
* Dispatch the IRQs to the irqdomain of each A and B
* gpiochip irqdomains depending on what has fired.
* The tricky part is that the IRQ line is shared
* between bank A and B and each has their own gpiochip.
*/
stat = readb(epg->base + EP93XX_GPIO_A_INT_STATUS);
for_each_set_bit(offset, &stat, 8)
generic_handle_irq(irq_find_mapping(epg->gc[0].irq.domain,
offset));
stat = readb(epg->base + EP93XX_GPIO_B_INT_STATUS);
for_each_set_bit(offset, &stat, 8)
generic_handle_irq(irq_find_mapping(epg->gc[1].irq.domain,
offset));
chained_irq_exit(irqchip, desc);
}
static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc)
@ -106,60 +138,67 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc)
/*
* map discontiguous hw irq range to continuous sw irq range:
*
* IRQ_EP93XX_GPIO{0..7}MUX -> gpio_to_irq(EP93XX_GPIO_LINE_F({0..7})
* IRQ_EP93XX_GPIO{0..7}MUX -> EP93XX_GPIO_LINE_F{0..7}
*/
struct irq_chip *irqchip = irq_desc_get_chip(desc);
unsigned int irq = irq_desc_get_irq(desc);
int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */
int gpio_irq = gpio_to_irq(EP93XX_GPIO_LINE_F(0)) + port_f_idx;
int gpio_irq = EP93XX_GPIO_F_IRQ_BASE + port_f_idx;
chained_irq_enter(irqchip, desc);
generic_handle_irq(gpio_irq);
chained_irq_exit(irqchip, desc);
}
static void ep93xx_gpio_irq_ack(struct irq_data *d)
{
int line = irq_to_gpio(d->irq);
int port = line >> 3;
int port_mask = 1 << (line & 7);
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
int port = ep93xx_gpio_port(gc);
int port_mask = BIT(d->irq & 7);
if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH) {
gpio_int_type2[port] ^= port_mask; /* switch edge direction */
ep93xx_gpio_update_int_params(port);
ep93xx_gpio_update_int_params(epg, port);
}
writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
writeb(port_mask, epg->base + eoi_register_offset[port]);
}
static void ep93xx_gpio_irq_mask_ack(struct irq_data *d)
{
int line = irq_to_gpio(d->irq);
int port = line >> 3;
int port_mask = 1 << (line & 7);
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
int port = ep93xx_gpio_port(gc);
int port_mask = BIT(d->irq & 7);
if (irqd_get_trigger_type(d) == IRQ_TYPE_EDGE_BOTH)
gpio_int_type2[port] ^= port_mask; /* switch edge direction */
gpio_int_unmasked[port] &= ~port_mask;
ep93xx_gpio_update_int_params(port);
ep93xx_gpio_update_int_params(epg, port);
writeb(port_mask, EP93XX_GPIO_REG(eoi_register_offset[port]));
writeb(port_mask, epg->base + eoi_register_offset[port]);
}
static void ep93xx_gpio_irq_mask(struct irq_data *d)
{
int line = irq_to_gpio(d->irq);
int port = line >> 3;
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
int port = ep93xx_gpio_port(gc);
gpio_int_unmasked[port] &= ~(1 << (line & 7));
ep93xx_gpio_update_int_params(port);
gpio_int_unmasked[port] &= ~BIT(d->irq & 7);
ep93xx_gpio_update_int_params(epg, port);
}
static void ep93xx_gpio_irq_unmask(struct irq_data *d)
{
int line = irq_to_gpio(d->irq);
int port = line >> 3;
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
int port = ep93xx_gpio_port(gc);
gpio_int_unmasked[port] |= 1 << (line & 7);
ep93xx_gpio_update_int_params(port);
gpio_int_unmasked[port] |= BIT(d->irq & 7);
ep93xx_gpio_update_int_params(epg, port);
}
/*
@ -169,12 +208,14 @@ static void ep93xx_gpio_irq_unmask(struct irq_data *d)
*/
static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
{
const int gpio = irq_to_gpio(d->irq);
const int port = gpio >> 3;
const int port_mask = 1 << (gpio & 7);
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
int port = ep93xx_gpio_port(gc);
int offset = d->irq & 7;
int port_mask = BIT(offset);
irq_flow_handler_t handler;
gpio_direction_input(gpio);
gc->direction_input(gc, offset);
switch (type) {
case IRQ_TYPE_EDGE_RISING:
@ -200,7 +241,7 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
case IRQ_TYPE_EDGE_BOTH:
gpio_int_type1[port] |= port_mask;
/* set initial polarity based on current input level */
if (gpio_get_value(gpio))
if (gc->get(gc, offset))
gpio_int_type2[port] &= ~port_mask; /* falling */
else
gpio_int_type2[port] |= port_mask; /* rising */
@ -214,7 +255,7 @@ static int ep93xx_gpio_irq_type(struct irq_data *d, unsigned int type)
gpio_int_enabled[port] |= port_mask;
ep93xx_gpio_update_int_params(port);
ep93xx_gpio_update_int_params(epg, port);
return 0;
}
@ -228,35 +269,53 @@ static struct irq_chip ep93xx_gpio_irq_chip = {
.irq_set_type = ep93xx_gpio_irq_type,
};
static void ep93xx_gpio_init_irq(void)
static int ep93xx_gpio_init_irq(struct platform_device *pdev,
struct ep93xx_gpio *epg)
{
int ab_parent_irq = platform_get_irq(pdev, 0);
struct device *dev = &pdev->dev;
int gpio_irq;
int ret;
int i;
for (gpio_irq = gpio_to_irq(0);
gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) {
/* The A bank */
ret = gpiochip_irqchip_add(&epg->gc[0], &ep93xx_gpio_irq_chip,
64, handle_level_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(dev, "Could not add irqchip 0\n");
return ret;
}
gpiochip_set_chained_irqchip(&epg->gc[0], &ep93xx_gpio_irq_chip,
ab_parent_irq,
ep93xx_gpio_ab_irq_handler);
/* The B bank */
ret = gpiochip_irqchip_add(&epg->gc[1], &ep93xx_gpio_irq_chip,
72, handle_level_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(dev, "Could not add irqchip 1\n");
return ret;
}
gpiochip_set_chained_irqchip(&epg->gc[1], &ep93xx_gpio_irq_chip,
ab_parent_irq,
ep93xx_gpio_ab_irq_handler);
/* The F bank */
for (i = 0; i < 8; i++) {
gpio_irq = EP93XX_GPIO_F_IRQ_BASE + i;
irq_set_chip_data(gpio_irq, &epg->gc[5]);
irq_set_chip_and_handler(gpio_irq, &ep93xx_gpio_irq_chip,
handle_level_irq);
irq_clear_status_flags(gpio_irq, IRQ_NOREQUEST);
}
irq_set_chained_handler(IRQ_EP93XX_GPIO_AB,
ep93xx_gpio_ab_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO0MUX,
ep93xx_gpio_f_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO1MUX,
ep93xx_gpio_f_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO2MUX,
ep93xx_gpio_f_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO3MUX,
ep93xx_gpio_f_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO4MUX,
ep93xx_gpio_f_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO5MUX,
ep93xx_gpio_f_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO6MUX,
ep93xx_gpio_f_irq_handler);
irq_set_chained_handler(IRQ_EP93XX_GPIO7MUX,
ep93xx_gpio_f_irq_handler);
for (i = 1; i <= 8; i++)
irq_set_chained_handler_and_data(platform_get_irq(pdev, i),
ep93xx_gpio_f_irq_handler,
&epg->gc[i]);
return 0;
}
@ -268,68 +327,54 @@ struct ep93xx_gpio_bank {
int data;
int dir;
int base;
bool has_debounce;
bool has_irq;
};
#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _debounce) \
#define EP93XX_GPIO_BANK(_label, _data, _dir, _base, _has_irq) \
{ \
.label = _label, \
.data = _data, \
.dir = _dir, \
.base = _base, \
.has_debounce = _debounce, \
.has_irq = _has_irq, \
}
static struct ep93xx_gpio_bank ep93xx_gpio_banks[] = {
EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true),
EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true),
EP93XX_GPIO_BANK("A", 0x00, 0x10, 0, true), /* Bank A has 8 IRQs */
EP93XX_GPIO_BANK("B", 0x04, 0x14, 8, true), /* Bank B has 8 IRQs */
EP93XX_GPIO_BANK("C", 0x08, 0x18, 40, false),
EP93XX_GPIO_BANK("D", 0x0c, 0x1c, 24, false),
EP93XX_GPIO_BANK("E", 0x20, 0x24, 32, false),
EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true),
EP93XX_GPIO_BANK("F", 0x30, 0x34, 16, true), /* Bank F has 8 IRQs */
EP93XX_GPIO_BANK("G", 0x38, 0x3c, 48, false),
EP93XX_GPIO_BANK("H", 0x40, 0x44, 56, false),
};
static int ep93xx_gpio_set_config(struct gpio_chip *chip, unsigned offset,
static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset,
unsigned long config)
{
int gpio = chip->base + offset;
int irq = gpio_to_irq(gpio);
u32 debounce;
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
return -ENOTSUPP;
if (irq < 0)
return -EINVAL;
debounce = pinconf_to_config_argument(config);
ep93xx_gpio_int_debounce(irq, debounce ? true : false);
ep93xx_gpio_int_debounce(gc, offset, debounce ? true : false);
return 0;
}
/*
* Map GPIO A0..A7 (0..7) to irq 64..71,
* B0..B7 (7..15) to irq 72..79, and
* F0..F7 (16..24) to irq 80..87.
*/
static int ep93xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
static int ep93xx_gpio_f_to_irq(struct gpio_chip *gc, unsigned offset)
{
int gpio = chip->base + offset;
if (gpio > EP93XX_GPIO_LINE_MAX_IRQ)
return -EINVAL;
return 64 + gpio;
return EP93XX_GPIO_F_IRQ_BASE + offset;
}
static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev,
void __iomem *mmio_base, struct ep93xx_gpio_bank *bank)
struct ep93xx_gpio *epg,
struct ep93xx_gpio_bank *bank)
{
void __iomem *data = mmio_base + bank->data;
void __iomem *dir = mmio_base + bank->dir;
void __iomem *data = epg->base + bank->data;
void __iomem *dir = epg->base + bank->dir;
int err;
err = bgpio_init(gc, dev, 1, data, NULL, NULL, dir, NULL, 0);
@ -339,41 +384,41 @@ static int ep93xx_gpio_add_bank(struct gpio_chip *gc, struct device *dev,
gc->label = bank->label;
gc->base = bank->base;
if (bank->has_debounce) {
if (bank->has_irq)
gc->set_config = ep93xx_gpio_set_config;
gc->to_irq = ep93xx_gpio_to_irq;
}
return devm_gpiochip_add_data(dev, gc, NULL);
return devm_gpiochip_add_data(dev, gc, epg);
}
static int ep93xx_gpio_probe(struct platform_device *pdev)
{
struct ep93xx_gpio *ep93xx_gpio;
struct ep93xx_gpio *epg;
struct resource *res;
int i;
struct device *dev = &pdev->dev;
ep93xx_gpio = devm_kzalloc(dev, sizeof(struct ep93xx_gpio), GFP_KERNEL);
if (!ep93xx_gpio)
epg = devm_kzalloc(dev, sizeof(*epg), GFP_KERNEL);
if (!epg)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ep93xx_gpio->mmio_base = devm_ioremap_resource(dev, res);
if (IS_ERR(ep93xx_gpio->mmio_base))
return PTR_ERR(ep93xx_gpio->mmio_base);
epg->base = devm_ioremap_resource(dev, res);
if (IS_ERR(epg->base))
return PTR_ERR(epg->base);
for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) {
struct gpio_chip *gc = &ep93xx_gpio->gc[i];
struct gpio_chip *gc = &epg->gc[i];
struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i];
if (ep93xx_gpio_add_bank(gc, &pdev->dev,
ep93xx_gpio->mmio_base, bank))
if (ep93xx_gpio_add_bank(gc, &pdev->dev, epg, bank))
dev_warn(&pdev->dev, "Unable to add gpio bank %s\n",
bank->label);
bank->label);
/* Only bank F has especially funky IRQ handling */
if (i == 5)
gc->to_irq = ep93xx_gpio_f_to_irq;
}
ep93xx_gpio_init_irq();
ep93xx_gpio_init_irq(pdev, epg);
return 0;
}

View file

@ -15,6 +15,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/clk.h>
/* GPIO registers definition */
#define GPIO_DATA_OUT 0x00
@ -40,11 +41,14 @@
* struct ftgpio_gpio - Gemini GPIO state container
* @dev: containing device for this instance
* @gc: gpiochip for this instance
* @base: remapped I/O-memory base
* @clk: silicon clock
*/
struct ftgpio_gpio {
struct device *dev;
struct gpio_chip gc;
void __iomem *base;
struct clk *clk;
};
static void ftgpio_gpio_ack_irq(struct irq_data *d)
@ -157,6 +161,73 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(irqchip, desc);
}
static int ftgpio_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
unsigned long config)
{
enum pin_config_param param = pinconf_to_config_param(config);
u32 arg = pinconf_to_config_argument(config);
struct ftgpio_gpio *g = gpiochip_get_data(gc);
unsigned long pclk_freq;
u32 deb_div;
u32 val;
if (param != PIN_CONFIG_INPUT_DEBOUNCE)
return -ENOTSUPP;
/*
* Debounce only works if interrupts are enabled. The manual
* states that if PCLK is 66 MHz, and this is set to 0x7D0, then
* PCLK is divided down to 33 kHz for the debounce timer. 0x7D0 is
* 2000 decimal, so what they mean is simply that the PCLK is
* divided by this value.
*
* As we get a debounce setting in microseconds, we calculate the
* desired period time and see if we can get a suitable debounce
* time.
*/
pclk_freq = clk_get_rate(g->clk);
deb_div = DIV_ROUND_CLOSEST(pclk_freq, arg);
/* This register is only 24 bits wide */
if (deb_div > (1 << 24))
return -ENOTSUPP;
dev_dbg(g->dev, "prescale divisor: %08x, resulting frequency %lu Hz\n",
deb_div, (pclk_freq/deb_div));
val = readl(g->base + GPIO_DEBOUNCE_PRESCALE);
if (val == deb_div) {
/*
* The debounce timer happens to already be set to the
* desireable value, what a coincidence! We can just enable
* debounce on this GPIO line and return. This happens more
* often than you think, for example when all GPIO keys
* on a system are requesting the same debounce interval.
*/
val = readl(g->base + GPIO_DEBOUNCE_EN);
val |= BIT(offset);
writel(val, g->base + GPIO_DEBOUNCE_EN);
return 0;
}
val = readl(g->base + GPIO_DEBOUNCE_EN);
if (val) {
/*
* Oh no! Someone is already using the debounce with
* another setting than what we need. Bummer.
*/
return -ENOTSUPP;
}
/* First come, first serve */
writel(deb_div, g->base + GPIO_DEBOUNCE_PRESCALE);
/* Enable debounce */
val |= BIT(offset);
writel(val, g->base + GPIO_DEBOUNCE_EN);
return 0;
}
static int ftgpio_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -180,6 +251,19 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
if (irq <= 0)
return irq ? irq : -EINVAL;
g->clk = devm_clk_get(dev, NULL);
if (!IS_ERR(g->clk)) {
ret = clk_prepare_enable(g->clk);
if (ret)
return ret;
} else if (PTR_ERR(g->clk) == -EPROBE_DEFER) {
/*
* Percolate deferrals, for anything else,
* just live without the clocking.
*/
return PTR_ERR(g->clk);
}
ret = bgpio_init(&g->gc, dev, 4,
g->base + GPIO_DATA_IN,
g->base + GPIO_DATA_SET,
@ -189,7 +273,7 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
0);
if (ret) {
dev_err(dev, "unable to init generic GPIO\n");
return ret;
goto dis_clk;
}
g->gc.label = "FTGPIO010";
g->gc.base = -1;
@ -197,28 +281,50 @@ static int ftgpio_gpio_probe(struct platform_device *pdev)
g->gc.owner = THIS_MODULE;
/* ngpio is set by bgpio_init() */
/* We need a silicon clock to do debounce */
if (!IS_ERR(g->clk))
g->gc.set_config = ftgpio_gpio_set_config;
ret = devm_gpiochip_add_data(dev, &g->gc, g);
if (ret)
return ret;
goto dis_clk;
/* Disable, unmask and clear all interrupts */
writel(0x0, g->base + GPIO_INT_EN);
writel(0x0, g->base + GPIO_INT_MASK);
writel(~0x0, g->base + GPIO_INT_CLR);
/* Clear any use of debounce */
writel(0x0, g->base + GPIO_DEBOUNCE_EN);
ret = gpiochip_irqchip_add(&g->gc, &ftgpio_gpio_irqchip,
0, handle_bad_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_info(dev, "could not add irqchip\n");
return ret;
goto dis_clk;
}
gpiochip_set_chained_irqchip(&g->gc, &ftgpio_gpio_irqchip,
irq, ftgpio_gpio_irq_handler);
platform_set_drvdata(pdev, g);
dev_info(dev, "FTGPIO010 @%p registered\n", g->base);
return 0;
dis_clk:
if (!IS_ERR(g->clk))
clk_disable_unprepare(g->clk);
return ret;
}
static int ftgpio_gpio_remove(struct platform_device *pdev)
{
struct ftgpio_gpio *g = platform_get_drvdata(pdev);
if (!IS_ERR(g->clk))
clk_disable_unprepare(g->clk);
return 0;
}
static const struct of_device_id ftgpio_gpio_of_match[] = {
@ -239,6 +345,7 @@ static struct platform_driver ftgpio_gpio_driver = {
.name = "ftgpio010-gpio",
.of_match_table = of_match_ptr(ftgpio_gpio_of_match),
},
.probe = ftgpio_gpio_probe,
.probe = ftgpio_gpio_probe,
.remove = ftgpio_gpio_remove,
};
builtin_platform_driver(ftgpio_gpio_driver);

View file

@ -189,7 +189,6 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
unsigned long flag;
struct egpio_chip *egpio;
struct egpio_info *ei;
unsigned bit;
int pos;
int reg;
int shift;
@ -199,7 +198,6 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
egpio = gpiochip_get_data(chip);
ei = dev_get_drvdata(egpio->dev);
bit = egpio_bit(ei, offset);
pos = egpio_pos(ei, offset);
reg = egpio->reg_start + pos;
shift = pos << ei->reg_shift;
@ -334,7 +332,13 @@ static int __init egpio_probe(struct platform_device *pdev)
ei->chip[i].is_out = pdata->chip[i].direction;
ei->chip[i].dev = &(pdev->dev);
chip = &(ei->chip[i].chip);
chip->label = "htc-egpio";
chip->label = devm_kasprintf(&pdev->dev, GFP_KERNEL,
"htc-egpio-%d",
i);
if (!chip->label) {
ret = -ENOMEM;
goto fail;
}
chip->parent = &pdev->dev;
chip->owner = THIS_MODULE;
chip->get = egpio_get;

View file

@ -313,18 +313,21 @@ static int max3191x_set_config(struct gpio_chip *gpio, unsigned int offset,
static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
struct gpio_desc **desc,
struct gpio_array *info,
int value)
{
int i, *values;
unsigned long *values;
values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
values = bitmap_alloc(ndescs, GFP_KERNEL);
if (!values)
return;
for (i = 0; i < ndescs; i++)
values[i] = value;
if (value)
bitmap_fill(values, ndescs);
else
bitmap_zero(values, ndescs);
gpiod_set_array_value_cansleep(ndescs, desc, values);
gpiod_set_array_value_cansleep(ndescs, desc, info, values);
kfree(values);
}
@ -397,7 +400,8 @@ static int max3191x_probe(struct spi_device *spi)
if (max3191x->modesel_pins)
gpiod_set_array_single_value_cansleep(
max3191x->modesel_pins->ndescs,
max3191x->modesel_pins->desc, max3191x->mode);
max3191x->modesel_pins->desc,
max3191x->modesel_pins->info, max3191x->mode);
max3191x->ignore_uv = device_property_read_bool(dev,
"maxim,ignore-undervoltage");

View file

@ -1,14 +1,10 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Generic driver for memory-mapped GPIO controllers.
*
* Copyright 2008 MontaVista Software, Inc.
* Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.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.
*
* ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`.......
* ...`` ```````..
* ..The simplest form of a GPIO controller that the driver supports is``

View file

@ -18,6 +18,7 @@
#include <linux/irq_sim.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/property.h>
#include "gpiolib.h"
@ -28,6 +29,8 @@
* of GPIO lines.
*/
#define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2)
/* Maximum of three properties + the sentinel. */
#define GPIO_MOCKUP_MAX_PROP 4
#define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__)
@ -59,13 +62,6 @@ 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_num_ranges;
module_param_array(gpio_mockup_ranges, int, &gpio_mockup_num_ranges, 0400);
@ -255,26 +251,37 @@ static int gpio_mockup_name_lines(struct device *dev,
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;
const char *name;
int rv, base;
u16 ngpio;
dev = &pdev->dev;
pdata = dev_get_platdata(dev);
base = pdata->base;
ngpio = pdata->ngpio;
rv = device_property_read_u32(dev, "gpio-base", &base);
if (rv)
base = -1;
rv = device_property_read_u16(dev, "nr-gpios", &ngpio);
if (rv)
return rv;
rv = device_property_read_string(dev, "chip-name", &name);
if (rv)
name = NULL;
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;
if (!name) {
name = devm_kasprintf(dev, GFP_KERNEL,
"%s-%c", pdev->name, pdev->id + 'A');
if (!name)
return -ENOMEM;
}
gc = &chip->gc;
gc->base = base;
@ -295,7 +302,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
if (!chip->lines)
return -ENOMEM;
if (pdata->named_lines) {
if (device_property_read_bool(dev, "named-gpio-lines")) {
rv = gpio_mockup_name_lines(dev, chip);
if (rv)
return rv;
@ -339,9 +346,11 @@ static void gpio_mockup_unregister_pdevs(void)
static int __init gpio_mockup_init(void)
{
int i, num_chips, err = 0, index = 'A';
struct gpio_mockup_platform_data pdata;
struct property_entry properties[GPIO_MOCKUP_MAX_PROP];
int i, prop, num_chips, err = 0, base;
struct platform_device_info pdevinfo;
struct platform_device *pdev;
u16 ngpio;
if ((gpio_mockup_num_ranges < 2) ||
(gpio_mockup_num_ranges % 2) ||
@ -371,17 +380,28 @@ static int __init gpio_mockup_init(void)
}
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;
memset(properties, 0, sizeof(properties));
memset(&pdevinfo, 0, sizeof(pdevinfo));
prop = 0;
pdev = platform_device_register_resndata(NULL,
GPIO_MOCKUP_NAME,
i, NULL, 0, &pdata,
sizeof(pdata));
base = gpio_mockup_range_base(i);
if (base >= 0)
properties[prop++] = PROPERTY_ENTRY_U32("gpio-base",
base);
ngpio = base < 0 ? gpio_mockup_range_ngpio(i)
: gpio_mockup_range_ngpio(i) - base;
properties[prop++] = PROPERTY_ENTRY_U16("nr-gpios", ngpio);
if (gpio_mockup_named_lines)
properties[prop++] = PROPERTY_ENTRY_BOOL(
"named-gpio-lines");
pdevinfo.name = GPIO_MOCKUP_NAME;
pdevinfo.id = i;
pdevinfo.properties = properties;
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev)) {
gpio_mockup_err("error registering device");
platform_driver_unregister(&gpio_mockup_driver);

View file

@ -18,8 +18,6 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
/* FIXME: for gpio_get_value(), replace this by direct register read */
#include <linux/gpio.h>
#include <linux/module.h>
#define MXS_SET 0x4
@ -86,7 +84,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
port->both_edges &= ~pin_mask;
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
val = gpio_get_value(port->gc.base + d->hwirq);
val = port->gc.get(&port->gc, d->hwirq);
if (val)
edge = GPIO_INT_FALL_EDGE;
else

View file

@ -19,6 +19,7 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/cpu_pm.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
#include <linux/pm.h>
@ -28,10 +29,10 @@
#include <linux/bitops.h>
#include <linux/platform_data/gpio-omap.h>
#define OFF_MODE 1
#define OMAP4_GPIO_DEBOUNCINGTIME_MASK 0xFF
static LIST_HEAD(omap_gpio_list);
#define OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER BIT(2)
#define OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN BIT(1)
struct gpio_regs {
u32 irqenable1;
@ -48,6 +49,13 @@ struct gpio_regs {
u32 debounce_en;
};
struct gpio_bank;
struct gpio_omap_funcs {
void (*idle_enable_level_quirk)(struct gpio_bank *bank);
void (*idle_disable_level_quirk)(struct gpio_bank *bank);
};
struct gpio_bank {
struct list_head node;
void __iomem *base;
@ -55,6 +63,7 @@ struct gpio_bank {
u32 non_wakeup_gpios;
u32 enabled_non_wakeup_gpios;
struct gpio_regs context;
struct gpio_omap_funcs funcs;
u32 saved_datain;
u32 level_mask;
u32 toggle_mask;
@ -62,6 +71,8 @@ struct gpio_bank {
raw_spinlock_t wa_lock;
struct gpio_chip chip;
struct clk *dbck;
struct notifier_block nb;
unsigned int is_suspended:1;
u32 mod_usage;
u32 irq_usage;
u32 dbck_enable_mask;
@ -73,8 +84,8 @@ struct gpio_bank {
int stride;
u32 width;
int context_loss_count;
int power_mode;
bool workaround_enabled;
u32 quirks;
void (*set_dataout)(struct gpio_bank *bank, unsigned gpio, int enable);
void (*set_dataout_multiple)(struct gpio_bank *bank,
@ -368,9 +379,18 @@ static inline void omap_set_gpio_trigger(struct gpio_bank *bank, int gpio,
readl_relaxed(bank->base + bank->regs->fallingdetect);
if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0);
bank->context.wake_en =
readl_relaxed(bank->base + bank->regs->wkup_en);
/* Defer wkup_en register update until we idle? */
if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) {
if (trigger)
bank->context.wake_en |= gpio_bit;
else
bank->context.wake_en &= ~gpio_bit;
} else {
omap_gpio_rmw(base, bank->regs->wkup_en, gpio_bit,
trigger != 0);
bank->context.wake_en =
readl_relaxed(bank->base + bank->regs->wkup_en);
}
}
/* This part needs to be executed always for OMAP{34xx, 44xx} */
@ -682,12 +702,7 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
struct gpio_bank *bank = gpiochip_get_data(chip);
unsigned long flags;
/*
* If this is the first gpio_request for the bank,
* enable the bank module.
*/
if (!BANK_USED(bank))
pm_runtime_get_sync(chip->parent);
pm_runtime_get_sync(chip->parent);
raw_spin_lock_irqsave(&bank->lock, flags);
omap_enable_gpio_module(bank, offset);
@ -711,12 +726,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
omap_disable_gpio_module(bank, offset);
raw_spin_unlock_irqrestore(&bank->lock, flags);
/*
* If this is the last gpio to be freed in the bank,
* disable the bank module.
*/
if (!BANK_USED(bank))
pm_runtime_put(chip->parent);
pm_runtime_put(chip->parent);
}
/*
@ -741,7 +751,9 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
if (WARN_ON(!isr_reg))
goto exit;
pm_runtime_get_sync(bank->chip.parent);
if (WARN_ONCE(!pm_runtime_active(bank->chip.parent),
"gpio irq%i while runtime suspended?\n", irq))
return IRQ_NONE;
while (1) {
raw_spin_lock_irqsave(&bank->lock, lock_flags);
@ -792,7 +804,6 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank)
}
}
exit:
pm_runtime_put(bank->chip.parent);
return IRQ_HANDLED;
}
@ -841,20 +852,14 @@ static void omap_gpio_irq_bus_lock(struct irq_data *data)
{
struct gpio_bank *bank = omap_irq_data_get_bank(data);
if (!BANK_USED(bank))
pm_runtime_get_sync(bank->chip.parent);
pm_runtime_get_sync(bank->chip.parent);
}
static void gpio_irq_bus_sync_unlock(struct irq_data *data)
{
struct gpio_bank *bank = omap_irq_data_get_bank(data);
/*
* If this is the last IRQ to be freed in the bank,
* disable the bank module.
*/
if (!BANK_USED(bank))
pm_runtime_put(bank->chip.parent);
pm_runtime_put(bank->chip.parent);
}
static void omap_gpio_ack_irq(struct irq_data *d)
@ -899,6 +904,82 @@ static void omap_gpio_unmask_irq(struct irq_data *d)
raw_spin_unlock_irqrestore(&bank->lock, flags);
}
/*
* Only edges can generate a wakeup event to the PRCM.
*
* Therefore, ensure any wake-up capable GPIOs have
* edge-detection enabled before going idle to ensure a wakeup
* to the PRCM is generated on a GPIO transition. (c.f. 34xx
* NDA TRM 25.5.3.1)
*
* The normal values will be restored upon ->runtime_resume()
* by writing back the values saved in bank->context.
*/
static void __maybe_unused
omap2_gpio_enable_level_quirk(struct gpio_bank *bank)
{
u32 wake_low, wake_hi;
/* Enable additional edge detection for level gpios for idle */
wake_low = bank->context.leveldetect0 & bank->context.wake_en;
if (wake_low)
writel_relaxed(wake_low | bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
if (wake_hi)
writel_relaxed(wake_hi | bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
}
static void __maybe_unused
omap2_gpio_disable_level_quirk(struct gpio_bank *bank)
{
/* Disable edge detection for level gpios after idle */
writel_relaxed(bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
writel_relaxed(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
}
/*
* On omap4 and later SoC variants a level interrupt with wkup_en
* enabled blocks the GPIO functional clock from idling until the GPIO
* instance has been reset. To avoid that, we must set wkup_en only for
* idle for level interrupts, and clear level registers for the duration
* of idle. The level interrupts will be still there on wakeup by their
* nature.
*/
static void __maybe_unused
omap4_gpio_enable_level_quirk(struct gpio_bank *bank)
{
/* Update wake register for idle, edge bits might be already set */
writel_relaxed(bank->context.wake_en,
bank->base + bank->regs->wkup_en);
/* Clear level registers for idle */
writel_relaxed(0, bank->base + bank->regs->leveldetect0);
writel_relaxed(0, bank->base + bank->regs->leveldetect1);
}
static void __maybe_unused
omap4_gpio_disable_level_quirk(struct gpio_bank *bank)
{
/* Restore level registers after idle */
writel_relaxed(bank->context.leveldetect0,
bank->base + bank->regs->leveldetect0);
writel_relaxed(bank->context.leveldetect1,
bank->base + bank->regs->leveldetect1);
/* Clear saved wkup_en for level, it will be set for next idle again */
bank->context.wake_en &= ~(bank->context.leveldetect0 |
bank->context.leveldetect1);
/* Update wake with only edge configuration */
writel_relaxed(bank->context.wake_en,
bank->base + bank->regs->wkup_en);
}
/*---------------------------------------------------------------------*/
static int omap_mpuio_suspend_noirq(struct device *dev)
@ -1218,6 +1299,36 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc)
return ret;
}
static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context);
static void omap_gpio_unidle(struct gpio_bank *bank);
static int gpio_omap_cpu_notifier(struct notifier_block *nb,
unsigned long cmd, void *v)
{
struct gpio_bank *bank;
unsigned long flags;
bank = container_of(nb, struct gpio_bank, nb);
raw_spin_lock_irqsave(&bank->lock, flags);
switch (cmd) {
case CPU_CLUSTER_PM_ENTER:
if (bank->is_suspended)
break;
omap_gpio_idle(bank, true);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
if (bank->is_suspended)
break;
omap_gpio_unidle(bank);
break;
}
raw_spin_unlock_irqrestore(&bank->lock, flags);
return NOTIFY_OK;
}
static const struct of_device_id omap_gpio_match[];
static int omap_gpio_probe(struct platform_device *pdev)
@ -1256,6 +1367,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
irqc->irq_bus_sync_unlock = gpio_irq_bus_sync_unlock,
irqc->name = dev_name(&pdev->dev);
irqc->flags = IRQCHIP_MASK_ON_SUSPEND;
irqc->parent_device = dev;
bank->irq = platform_get_irq(pdev, 0);
if (bank->irq <= 0) {
@ -1270,6 +1382,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
bank->chip.parent = dev;
bank->chip.owner = THIS_MODULE;
bank->dbck_flag = pdata->dbck_flag;
bank->quirks = pdata->quirks;
bank->stride = pdata->bank_stride;
bank->width = pdata->bank_width;
bank->is_mpuio = pdata->is_mpuio;
@ -1278,6 +1391,7 @@ static int omap_gpio_probe(struct platform_device *pdev)
#ifdef CONFIG_OF_GPIO
bank->chip.of_node = of_node_get(node);
#endif
if (node) {
if (!of_property_read_bool(node, "ti,gpio-always-on"))
bank->loses_context = true;
@ -1298,6 +1412,18 @@ static int omap_gpio_probe(struct platform_device *pdev)
omap_set_gpio_dataout_mask_multiple;
}
if (bank->quirks & OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN) {
bank->funcs.idle_enable_level_quirk =
omap4_gpio_enable_level_quirk;
bank->funcs.idle_disable_level_quirk =
omap4_gpio_disable_level_quirk;
} else if (bank->quirks & OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER) {
bank->funcs.idle_enable_level_quirk =
omap2_gpio_enable_level_quirk;
bank->funcs.idle_disable_level_quirk =
omap2_gpio_disable_level_quirk;
}
raw_spin_lock_init(&bank->lock);
raw_spin_lock_init(&bank->wa_lock);
@ -1322,7 +1448,6 @@ static int omap_gpio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bank);
pm_runtime_enable(dev);
pm_runtime_irq_safe(dev);
pm_runtime_get_sync(dev);
if (bank->is_mpuio)
@ -1341,9 +1466,13 @@ static int omap_gpio_probe(struct platform_device *pdev)
omap_gpio_show_rev(bank);
pm_runtime_put(dev);
if (bank->funcs.idle_enable_level_quirk &&
bank->funcs.idle_disable_level_quirk) {
bank->nb.notifier_call = gpio_omap_cpu_notifier;
cpu_pm_register_notifier(&bank->nb);
}
list_add_tail(&bank->node, &omap_gpio_list);
pm_runtime_put(dev);
return 0;
}
@ -1352,6 +1481,8 @@ static int omap_gpio_remove(struct platform_device *pdev)
{
struct gpio_bank *bank = platform_get_drvdata(pdev);
if (bank->nb.notifier_call)
cpu_pm_unregister_notifier(&bank->nb);
list_del(&bank->node);
gpiochip_remove(&bank->chip);
pm_runtime_disable(&pdev->dev);
@ -1361,48 +1492,22 @@ static int omap_gpio_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_ARCH_OMAP2PLUS
#if defined(CONFIG_PM)
static void omap_gpio_restore_context(struct gpio_bank *bank);
static int omap_gpio_runtime_suspend(struct device *dev)
static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
struct device *dev = bank->chip.parent;
u32 l1 = 0, l2 = 0;
unsigned long flags;
u32 wake_low, wake_hi;
raw_spin_lock_irqsave(&bank->lock, flags);
/*
* Only edges can generate a wakeup event to the PRCM.
*
* Therefore, ensure any wake-up capable GPIOs have
* edge-detection enabled before going idle to ensure a wakeup
* to the PRCM is generated on a GPIO transition. (c.f. 34xx
* NDA TRM 25.5.3.1)
*
* The normal values will be restored upon ->runtime_resume()
* by writing back the values saved in bank->context.
*/
wake_low = bank->context.leveldetect0 & bank->context.wake_en;
if (wake_low)
writel_relaxed(wake_low | bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
if (wake_hi)
writel_relaxed(wake_hi | bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
if (bank->funcs.idle_enable_level_quirk)
bank->funcs.idle_enable_level_quirk(bank);
if (!bank->enabled_non_wakeup_gpios)
goto update_gpio_context_count;
if (bank->power_mode != OFF_MODE) {
bank->power_mode = 0;
if (!may_lose_context)
goto update_gpio_context_count;
}
/*
* If going to OFF, remove triggering for all
* non-wakeup GPIOs. Otherwise spurious IRQs will be
@ -1427,23 +1532,16 @@ update_gpio_context_count:
bank->get_context_loss_count(dev);
omap_gpio_dbck_disable(bank);
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
static void omap_gpio_init_context(struct gpio_bank *p);
static int omap_gpio_runtime_resume(struct device *dev)
static void omap_gpio_unidle(struct gpio_bank *bank)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
struct device *dev = bank->chip.parent;
u32 l = 0, gen, gen0, gen1;
unsigned long flags;
int c;
raw_spin_lock_irqsave(&bank->lock, flags);
/*
* On the first resume during the probe, the context has not
* been initialised and so initialise it now. Also initialise
@ -1459,16 +1557,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
omap_gpio_dbck_enable(bank);
/*
* In ->runtime_suspend(), level-triggered, wakeup-enabled
* GPIOs were set to edge trigger also in order to be able to
* generate a PRCM wakeup. Here we restore the
* pre-runtime_suspend() values for edge triggering.
*/
writel_relaxed(bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
writel_relaxed(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);
if (bank->funcs.idle_disable_level_quirk)
bank->funcs.idle_disable_level_quirk(bank);
if (bank->loses_context) {
if (!bank->get_context_loss_count) {
@ -1478,16 +1568,13 @@ static int omap_gpio_runtime_resume(struct device *dev)
if (c != bank->context_loss_count) {
omap_gpio_restore_context(bank);
} else {
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
return;
}
}
}
if (!bank->workaround_enabled) {
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
if (!bank->workaround_enabled)
return;
l = readl_relaxed(bank->base + bank->regs->datain);
@ -1540,41 +1627,8 @@ static int omap_gpio_runtime_resume(struct device *dev)
}
bank->workaround_enabled = false;
raw_spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
#endif /* CONFIG_PM */
#if IS_BUILTIN(CONFIG_GPIO_OMAP)
void omap2_gpio_prepare_for_idle(int pwr_mode)
{
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
if (!BANK_USED(bank) || !bank->loses_context)
continue;
bank->power_mode = pwr_mode;
pm_runtime_put_sync_suspend(bank->chip.parent);
}
}
void omap2_gpio_resume_after_idle(void)
{
struct gpio_bank *bank;
list_for_each_entry(bank, &omap_gpio_list, node) {
if (!BANK_USED(bank) || !bank->loses_context)
continue;
pm_runtime_get_sync(bank->chip.parent);
}
}
#endif
#if defined(CONFIG_PM)
static void omap_gpio_init_context(struct gpio_bank *p)
{
struct omap_gpio_reg_offs *regs = p->regs;
@ -1631,17 +1685,57 @@ static void omap_gpio_restore_context(struct gpio_bank *bank)
writel_relaxed(bank->context.irqenable2,
bank->base + bank->regs->irqenable2);
}
#endif /* CONFIG_PM */
#else
#define omap_gpio_runtime_suspend NULL
#define omap_gpio_runtime_resume NULL
static inline void omap_gpio_init_context(struct gpio_bank *p) {}
#endif
static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
unsigned long flags;
int error = 0;
raw_spin_lock_irqsave(&bank->lock, flags);
/* Must be idled only by CPU_CLUSTER_PM_ENTER? */
if (bank->irq_usage) {
error = -EBUSY;
goto unlock;
}
omap_gpio_idle(bank, true);
bank->is_suspended = true;
unlock:
raw_spin_unlock_irqrestore(&bank->lock, flags);
return error;
}
static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gpio_bank *bank = platform_get_drvdata(pdev);
unsigned long flags;
int error = 0;
raw_spin_lock_irqsave(&bank->lock, flags);
/* Must be unidled only by CPU_CLUSTER_PM_ENTER? */
if (bank->irq_usage) {
error = -EBUSY;
goto unlock;
}
omap_gpio_unidle(bank);
bank->is_suspended = false;
unlock:
raw_spin_unlock_irqrestore(&bank->lock, flags);
return error;
}
#ifdef CONFIG_ARCH_OMAP2PLUS
static const struct dev_pm_ops gpio_pm_ops = {
SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
NULL)
};
#else
static const struct dev_pm_ops gpio_pm_ops;
#endif /* CONFIG_ARCH_OMAP2PLUS */
#if defined(CONFIG_OF)
static struct omap_gpio_reg_offs omap2_gpio_regs = {
@ -1690,6 +1784,11 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = {
.fallingdetect = OMAP4_GPIO_FALLINGDETECT,
};
/*
* Note that omap2 does not currently support idle modes with context loss so
* no need to add OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER quirk flag to save
* and restore context.
*/
static const struct omap_gpio_platform_data omap2_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
@ -1700,12 +1799,15 @@ static const struct omap_gpio_platform_data omap3_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
.quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER,
};
static const struct omap_gpio_platform_data omap4_pdata = {
.regs = &omap4_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
.quirks = OMAP_GPIO_QUIRK_IDLE_REMOVE_TRIGGER |
OMAP_GPIO_QUIRK_DEFERRED_WKUP_EN,
};
static const struct of_device_id omap_gpio_match[] = {

View file

@ -776,6 +776,9 @@ static int pxa_gpio_suspend(void)
struct pxa_gpio_bank *c;
int gpio;
if (!pchip)
return 0;
for_each_gpio_bank(gpio, c, pchip) {
c->saved_gplr = readl_relaxed(c->regbase + GPLR_OFFSET);
c->saved_gpdr = readl_relaxed(c->regbase + GPDR_OFFSET);
@ -794,6 +797,9 @@ static void pxa_gpio_resume(void)
struct pxa_gpio_bank *c;
int gpio;
if (!pchip)
return;
for_each_gpio_bank(gpio, c, pchip) {
/* restore level with set/clear */
writel_relaxed(c->saved_gplr, c->regbase + GPSR_OFFSET);

View file

@ -321,6 +321,9 @@ static void gpio_rcar_set_multiple(struct gpio_chip *chip, unsigned long *mask,
u32 val, bankmask;
bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0);
if (chip->valid_mask)
bankmask &= chip->valid_mask[0];
if (!bankmask)
return;
@ -558,6 +561,9 @@ static int gpio_rcar_resume(struct device *dev)
u32 mask;
for (offset = 0; offset < p->gpio_chip.ngpio; offset++) {
if (!gpiochip_line_is_valid(&p->gpio_chip, offset))
continue;
mask = BIT(offset);
/* I/O pin */
if (!(p->bank_info.iointsel & mask)) {

293
drivers/gpio/gpio-siox.c Normal file
View file

@ -0,0 +1,293 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015-2018 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
*/
#include <linux/module.h>
#include <linux/siox.h>
#include <linux/gpio/driver.h>
#include <linux/of.h>
struct gpio_siox_ddata {
struct gpio_chip gchip;
struct irq_chip ichip;
struct mutex lock;
u8 setdata[1];
u8 getdata[3];
spinlock_t irqlock;
u32 irq_enable;
u32 irq_status;
u32 irq_type[20];
};
/*
* Note that this callback only sets the value that is clocked out in the next
* cycle.
*/
static int gpio_siox_set_data(struct siox_device *sdevice, u8 status, u8 buf[])
{
struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
mutex_lock(&ddata->lock);
buf[0] = ddata->setdata[0];
mutex_unlock(&ddata->lock);
return 0;
}
static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
{
struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
size_t offset;
u32 trigger;
mutex_lock(&ddata->lock);
spin_lock_irq(&ddata->irqlock);
for (offset = 0; offset < 12; ++offset) {
unsigned int bitpos = 11 - offset;
unsigned int gpiolevel = buf[bitpos / 8] & (1 << bitpos % 8);
unsigned int prev_level =
ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
u32 irq_type = ddata->irq_type[offset];
if (gpiolevel) {
if ((irq_type & IRQ_TYPE_LEVEL_HIGH) ||
((irq_type & IRQ_TYPE_EDGE_RISING) && !prev_level))
ddata->irq_status |= 1 << offset;
} else {
if ((irq_type & IRQ_TYPE_LEVEL_LOW) ||
((irq_type & IRQ_TYPE_EDGE_FALLING) && prev_level))
ddata->irq_status |= 1 << offset;
}
}
trigger = ddata->irq_status & ddata->irq_enable;
spin_unlock_irq(&ddata->irqlock);
ddata->getdata[0] = buf[0];
ddata->getdata[1] = buf[1];
ddata->getdata[2] = buf[2];
mutex_unlock(&ddata->lock);
for (offset = 0; offset < 12; ++offset) {
if (trigger & (1 << offset)) {
struct irq_domain *irqdomain = ddata->gchip.irq.domain;
unsigned int irq = irq_find_mapping(irqdomain, offset);
/*
* Conceptually handle_nested_irq should call the flow
* handler of the irq chip. But it doesn't, so we have
* to clean the irq_status here.
*/
spin_lock_irq(&ddata->irqlock);
ddata->irq_status &= ~(1 << offset);
spin_unlock_irq(&ddata->irqlock);
handle_nested_irq(irq);
}
}
return 0;
}
static void gpio_siox_irq_ack(struct irq_data *d)
{
struct irq_chip *ic = irq_data_get_irq_chip(d);
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
ddata->irq_status &= ~(1 << d->hwirq);
spin_unlock_irq(&ddata->irqlock);
}
static void gpio_siox_irq_mask(struct irq_data *d)
{
struct irq_chip *ic = irq_data_get_irq_chip(d);
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
ddata->irq_enable &= ~(1 << d->hwirq);
spin_unlock_irq(&ddata->irqlock);
}
static void gpio_siox_irq_unmask(struct irq_data *d)
{
struct irq_chip *ic = irq_data_get_irq_chip(d);
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
ddata->irq_enable |= 1 << d->hwirq;
spin_unlock_irq(&ddata->irqlock);
}
static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
{
struct irq_chip *ic = irq_data_get_irq_chip(d);
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
ddata->irq_type[d->hwirq] = type;
spin_unlock_irq(&ddata->irqlock);
return 0;
}
static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset)
{
struct gpio_siox_ddata *ddata =
container_of(chip, struct gpio_siox_ddata, gchip);
int ret;
mutex_lock(&ddata->lock);
if (offset >= 12) {
unsigned int bitpos = 19 - offset;
ret = ddata->setdata[0] & (1 << bitpos);
} else {
unsigned int bitpos = 11 - offset;
ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
}
mutex_unlock(&ddata->lock);
return ret;
}
static void gpio_siox_set(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct gpio_siox_ddata *ddata =
container_of(chip, struct gpio_siox_ddata, gchip);
u8 mask = 1 << (19 - offset);
mutex_lock(&ddata->lock);
if (value)
ddata->setdata[0] |= mask;
else
ddata->setdata[0] &= ~mask;
mutex_unlock(&ddata->lock);
}
static int gpio_siox_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
if (offset >= 12)
return -EINVAL;
return 0;
}
static int gpio_siox_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
if (offset < 12)
return -EINVAL;
gpio_siox_set(chip, offset, value);
return 0;
}
static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset)
{
if (offset < 12)
return 1; /* input */
else
return 0; /* output */
}
static int gpio_siox_probe(struct siox_device *sdevice)
{
struct gpio_siox_ddata *ddata;
int ret;
ddata = devm_kzalloc(&sdevice->dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
dev_set_drvdata(&sdevice->dev, ddata);
mutex_init(&ddata->lock);
spin_lock_init(&ddata->irqlock);
ddata->gchip.base = -1;
ddata->gchip.can_sleep = 1;
ddata->gchip.parent = &sdevice->dev;
ddata->gchip.owner = THIS_MODULE;
ddata->gchip.get = gpio_siox_get;
ddata->gchip.set = gpio_siox_set;
ddata->gchip.direction_input = gpio_siox_direction_input;
ddata->gchip.direction_output = gpio_siox_direction_output;
ddata->gchip.get_direction = gpio_siox_get_direction;
ddata->gchip.ngpio = 20;
ddata->ichip.name = "siox-gpio";
ddata->ichip.irq_ack = gpio_siox_irq_ack;
ddata->ichip.irq_mask = gpio_siox_irq_mask;
ddata->ichip.irq_unmask = gpio_siox_irq_unmask;
ddata->ichip.irq_set_type = gpio_siox_irq_set_type;
ret = gpiochip_add(&ddata->gchip);
if (ret) {
dev_err(&sdevice->dev,
"Failed to register gpio chip (%d)\n", ret);
goto err_gpiochip;
}
ret = gpiochip_irqchip_add(&ddata->gchip, &ddata->ichip,
0, handle_level_irq, IRQ_TYPE_EDGE_RISING);
if (ret) {
dev_err(&sdevice->dev,
"Failed to register irq chip (%d)\n", ret);
err_gpiochip:
gpiochip_remove(&ddata->gchip);
}
return ret;
}
static int gpio_siox_remove(struct siox_device *sdevice)
{
struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
gpiochip_remove(&ddata->gchip);
return 0;
}
static struct siox_driver gpio_siox_driver = {
.probe = gpio_siox_probe,
.remove = gpio_siox_remove,
.set_data = gpio_siox_set_data,
.get_data = gpio_siox_get_data,
.driver = {
.name = "gpio-siox",
},
};
static int __init gpio_siox_init(void)
{
return siox_driver_register(&gpio_siox_driver);
}
module_init(gpio_siox_init);
static void __exit gpio_siox_exit(void)
{
siox_driver_unregister(&gpio_siox_driver);
}
module_exit(gpio_siox_exit);
MODULE_AUTHOR("Uwe Kleine-Koenig <[email protected]>");
MODULE_DESCRIPTION("SIOX gpio driver");
MODULE_LICENSE("GPL v2");

View file

@ -122,7 +122,7 @@ static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
BIT(offs % SYSCON_REG_BITS));
}
priv->data->set(chip, offset, val);
chip->set(chip, offset, val);
return 0;
}

View file

@ -45,14 +45,12 @@
/**
* @spinlock: used for atomic read/modify/write of registers
* @base: register base address
* @domain: IRQ domain of GPIO generated interrupts managed by this controller
* @irq: Interrupt line of parent interrupt controller
* @gc: gpio_chip structure associated to this GPIO controller
*/
struct tb10x_gpio {
spinlock_t spinlock;
void __iomem *base;
struct irq_domain *domain;
int irq;
@ -76,60 +74,14 @@ static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
u32 r;
unsigned long flags;
spin_lock_irqsave(&gpio->spinlock, flags);
spin_lock_irqsave(&gpio->gc.bgpio_lock, flags);
r = tb10x_reg_read(gpio, offs);
r = (r & ~mask) | (val & mask);
tb10x_reg_write(gpio, offs, r);
spin_unlock_irqrestore(&gpio->spinlock, flags);
}
static int tb10x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
int mask = BIT(offset);
int val = TB10X_GPIO_DIR_IN << offset;
tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
return 0;
}
static int tb10x_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
int val;
val = tb10x_reg_read(tb10x_gpio, OFFSET_TO_REG_DATA);
if (val & BIT(offset))
return 1;
else
return 0;
}
static void tb10x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
int mask = BIT(offset);
int val = value << offset;
tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DATA, mask, val);
}
static int tb10x_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
int mask = BIT(offset);
int val = TB10X_GPIO_DIR_OUT << offset;
tb10x_gpio_set(chip, offset, value);
tb10x_set_bits(tb10x_gpio, OFFSET_TO_REG_DDR, mask, val);
return 0;
spin_unlock_irqrestore(&gpio->gc.bgpio_lock, flags);
}
static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
@ -169,72 +121,85 @@ static int tb10x_gpio_probe(struct platform_device *pdev)
{
struct tb10x_gpio *tb10x_gpio;
struct resource *mem;
struct device_node *dn = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
int ret = -EBUSY;
u32 ngpio;
if (!dn)
if (!np)
return -EINVAL;
if (of_property_read_u32(dn, "abilis,ngpio", &ngpio))
if (of_property_read_u32(np, "abilis,ngpio", &ngpio))
return -EINVAL;
tb10x_gpio = devm_kzalloc(&pdev->dev, sizeof(*tb10x_gpio), GFP_KERNEL);
tb10x_gpio = devm_kzalloc(dev, sizeof(*tb10x_gpio), GFP_KERNEL);
if (tb10x_gpio == NULL)
return -ENOMEM;
spin_lock_init(&tb10x_gpio->spinlock);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
tb10x_gpio->base = devm_ioremap_resource(&pdev->dev, mem);
tb10x_gpio->base = devm_ioremap_resource(dev, mem);
if (IS_ERR(tb10x_gpio->base))
return PTR_ERR(tb10x_gpio->base);
tb10x_gpio->gc.label =
devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOF", pdev->dev.of_node);
tb10x_gpio->gc.label =
devm_kasprintf(dev, GFP_KERNEL, "%pOF", pdev->dev.of_node);
if (!tb10x_gpio->gc.label)
return -ENOMEM;
tb10x_gpio->gc.parent = &pdev->dev;
tb10x_gpio->gc.owner = THIS_MODULE;
tb10x_gpio->gc.direction_input = tb10x_gpio_direction_in;
tb10x_gpio->gc.get = tb10x_gpio_get;
tb10x_gpio->gc.direction_output = tb10x_gpio_direction_out;
tb10x_gpio->gc.set = tb10x_gpio_set;
tb10x_gpio->gc.request = gpiochip_generic_request;
tb10x_gpio->gc.free = gpiochip_generic_free;
tb10x_gpio->gc.base = -1;
tb10x_gpio->gc.ngpio = ngpio;
tb10x_gpio->gc.can_sleep = false;
/*
* Initialize generic GPIO with one single register for reading and setting
* the lines, no special set or clear registers and a data direction register
* wher 1 means "output".
*/
ret = bgpio_init(&tb10x_gpio->gc, dev, 4,
tb10x_gpio->base + OFFSET_TO_REG_DATA,
NULL,
NULL,
tb10x_gpio->base + OFFSET_TO_REG_DDR,
NULL,
0);
if (ret) {
dev_err(dev, "unable to init generic GPIO\n");
return ret;
}
tb10x_gpio->gc.base = -1;
tb10x_gpio->gc.parent = dev;
tb10x_gpio->gc.owner = THIS_MODULE;
/*
* ngpio is set by bgpio_init() but we override it, this .request()
* callback also overrides the one set up by generic GPIO.
*/
tb10x_gpio->gc.ngpio = ngpio;
tb10x_gpio->gc.request = gpiochip_generic_request;
tb10x_gpio->gc.free = gpiochip_generic_free;
ret = devm_gpiochip_add_data(&pdev->dev, &tb10x_gpio->gc, tb10x_gpio);
ret = devm_gpiochip_add_data(dev, &tb10x_gpio->gc, tb10x_gpio);
if (ret < 0) {
dev_err(&pdev->dev, "Could not add gpiochip.\n");
dev_err(dev, "Could not add gpiochip.\n");
return ret;
}
platform_set_drvdata(pdev, tb10x_gpio);
if (of_find_property(dn, "interrupt-controller", NULL)) {
if (of_find_property(np, "interrupt-controller", NULL)) {
struct irq_chip_generic *gc;
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "No interrupt specified.\n");
dev_err(dev, "No interrupt specified.\n");
return ret;
}
tb10x_gpio->gc.to_irq = tb10x_gpio_to_irq;
tb10x_gpio->irq = ret;
ret = devm_request_irq(&pdev->dev, ret, tb10x_gpio_irq_cascade,
ret = devm_request_irq(dev, ret, tb10x_gpio_irq_cascade,
IRQF_TRIGGER_NONE | IRQF_SHARED,
dev_name(&pdev->dev), tb10x_gpio);
dev_name(dev), tb10x_gpio);
if (ret != 0)
return ret;
tb10x_gpio->domain = irq_domain_add_linear(dn,
tb10x_gpio->domain = irq_domain_add_linear(np,
tb10x_gpio->gc.ngpio,
&irq_generic_chip_ops, NULL);
if (!tb10x_gpio->domain) {

View file

@ -1,20 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether expressed or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License version 2 for more details.
*
* Based on the TPS65912 driver
*/
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/platform_device.h>

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/*
* TI TPS6586x GPIO driver
*
@ -7,22 +8,10 @@
* Based on tps6586x.c
* Copyright (c) 2010 CompuLab Ltd.
* Mike Rapoport <mike@compulab.co.il>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mfd/tps6586x.h>

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* TI TPS6591x GPIO driver
*
@ -5,18 +6,12 @@
*
* Author: Graeme Gregory <gg@slimlogic.co.uk>
* Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/mfd/tps65910.h>

View file

@ -1,23 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* GPIO driver for TI TPS65912x PMICs
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether expressed or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License version 2 for more details.
*
* Based on the Arizona GPIO driver and the previous TPS65912 driver by
* Margarita Olaya Cabrera <magi@slimlogic.co.uk>
*/
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@ -40,9 +32,9 @@ static int tps65912_gpio_get_direction(struct gpio_chip *gc,
return ret;
if (val & GPIO_CFG_MASK)
return GPIOF_DIR_OUT;
return 0;
else
return GPIOF_DIR_IN;
return 1;
}
static int tps65912_gpio_direction_input(struct gpio_chip *gc, unsigned offset)

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Digital I/O driver for Technologic Systems TS-5500
*
@ -16,17 +17,12 @@
* TS-5600:
* Documentation: http://wiki.embeddedarm.com/wiki/TS-5600
* Blocks: LCD port (identical to TS-5500 LCD).
*
* 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.
*/
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_data/gpio-ts5500.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@ -318,7 +314,6 @@ static void ts5500_disable_irq(struct ts5500_priv *priv)
static int ts5500_dio_probe(struct platform_device *pdev)
{
enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
struct ts5500_dio_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device *dev = &pdev->dev;
const char *name = dev_name(dev);
struct ts5500_priv *priv;
@ -349,10 +344,6 @@ static int ts5500_dio_probe(struct platform_device *pdev)
priv->gpio_chip.set = ts5500_gpio_set;
priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
priv->gpio_chip.base = -1;
if (pdata) {
priv->gpio_chip.base = pdata->base;
priv->strap = pdata->strap;
}
switch (block) {
case TS5500_DIO1:

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Access to GPIOs on TWL4030/TPS659x0 chips
*
@ -9,20 +10,6 @@
*
* Initial Code:
* Andy Lowe / Nishanth Menon
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
@ -30,7 +17,7 @@
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/irqdomain.h>
@ -167,6 +154,23 @@ static int twl4030_set_gpio_direction(int gpio, int is_input)
return ret;
}
static int twl4030_get_gpio_direction(int gpio)
{
u8 d_bnk = gpio >> 3;
u8 d_msk = BIT(gpio & 0x7);
u8 base = REG_GPIODATADIR1 + d_bnk;
int ret = 0;
ret = gpio_twl4030_read(base);
if (ret < 0)
return ret;
/* 1 = output, but gpiolib semantics are inverse so invert */
ret = !(ret & d_msk);
return ret;
}
static int twl4030_set_gpio_dataout(int gpio, int enable)
{
u8 d_bnk = gpio >> 3;
@ -372,6 +376,28 @@ static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
return ret;
}
static int twl_get_direction(struct gpio_chip *chip, unsigned offset)
{
struct gpio_twl4030_priv *priv = gpiochip_get_data(chip);
/*
* Default 0 = output
* LED GPIOs >= TWL4030_GPIO_MAX are always output
*/
int ret = 0;
mutex_lock(&priv->mutex);
if (offset < TWL4030_GPIO_MAX) {
ret = twl4030_get_gpio_direction(offset);
if (ret) {
mutex_unlock(&priv->mutex);
return ret;
}
}
mutex_unlock(&priv->mutex);
return ret;
}
static int twl_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct gpio_twl4030_priv *priv = gpiochip_get_data(chip);
@ -387,8 +413,9 @@ static const struct gpio_chip template_chip = {
.request = twl_request,
.free = twl_free,
.direction_input = twl_direction_in,
.get = twl_get,
.direction_output = twl_direction_out,
.get_direction = twl_get_direction,
.get = twl_get,
.set = twl_set,
.to_irq = twl_to_irq,
.can_sleep = true,

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Access to GPOs on TWL6040 chip
*
@ -6,28 +7,15 @@
* Authors:
* Sergio Aguirre <saaguirre@ti.com>
* Peter Ujfalusi <peter.ujfalusi@ti.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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/mfd/twl6040.h>
@ -41,7 +29,13 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset)
if (ret < 0)
return ret;
return (ret >> offset) & 1;
return !!(ret & BIT(offset));
}
static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset)
{
/* This means "out" */
return 0;
}
static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
@ -62,9 +56,9 @@ static void twl6040gpo_set(struct gpio_chip *chip, unsigned offset, int value)
return;
if (value)
gpoctl = ret | (1 << offset);
gpoctl = ret | BIT(offset);
else
gpoctl = ret & ~(1 << offset);
gpoctl = ret & ~BIT(offset);
twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
}
@ -74,6 +68,7 @@ static struct gpio_chip twl6040gpo_chip = {
.owner = THIS_MODULE,
.get = twl6040gpo_get,
.direction_output = twl6040gpo_direction_out,
.get_direction = twl6040gpo_get_direction,
.set = twl6040gpo_set,
.can_sleep = true,
};

View file

@ -1,23 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Freescale vf610 GPIO support through PORT and GPIO
*
* Copyright (c) 2014 Toradex AG.
*
* Author: Stefan Agner <stefan@agner.ch>.
*
* 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.
* 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.
*/
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>

View file

@ -1,15 +1,10 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Nano River Technologies viperboard GPIO lib driver
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.de>
* All rights reserved.
*
* 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/kernel.h>
@ -19,9 +14,8 @@
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/usb.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/mfd/viperboard.h>

View file

@ -1,27 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for NEC VR4100 series General-purpose I/O Unit.
*
* Copyright (C) 2002 MontaVista Software Inc.
* Author: Yoichi Yuasa <source@mvista.com>
* Copyright (C) 2003-2009 Yoichi Yuasa <yuasa@linux-mips.org>
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@ -384,44 +371,6 @@ static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir)
return 0;
}
int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull)
{
u16 reg, mask;
unsigned long flags;
if ((giu_flags & GPIO_HAS_PULLUPDOWN_IO) != GPIO_HAS_PULLUPDOWN_IO)
return -EPERM;
if (pin >= 15)
return -EINVAL;
mask = 1 << pin;
spin_lock_irqsave(&giu_lock, flags);
if (pull == GPIO_PULL_UP || pull == GPIO_PULL_DOWN) {
reg = giu_read(GIUTERMUPDN);
if (pull == GPIO_PULL_UP)
reg |= mask;
else
reg &= ~mask;
giu_write(GIUTERMUPDN, reg);
reg = giu_read(GIUUSEUPDN);
reg |= mask;
giu_write(GIUUSEUPDN, reg);
} else {
reg = giu_read(GIUUSEUPDN);
reg &= ~mask;
giu_write(GIUUSEUPDN, reg);
}
spin_unlock_irqrestore(&giu_lock, flags);
return 0;
}
EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown);
static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin)
{
u16 reg, mask;

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO
*
@ -5,27 +6,10 @@
* Copyright (C) 2010 One Laptop per Child
* Author: Harald Welte <HaraldWelte@viatech.com>
* All rights reserved.
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* gpiolib support for Wolfson WM831x PMICs
*
@ -5,17 +6,12 @@
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/mfd/core.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* gpiolib support for Wolfson WM835x PMICs
*
@ -5,17 +6,12 @@
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/mfd/core.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* gpiolib support for Wolfson WM8994
*
@ -5,17 +6,12 @@
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.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/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/mfd/core.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>

View file

@ -1,18 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2003-2015 Broadcom Corporation
* All Rights Reserved
*
* 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.
*/
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/module.h>

View file

@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013 TangoTec Ltd.
* Author: Baruch Siach <baruch@tkos.co.il>
*
* 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.
*
* Driver for the Xtensa LX4 GPIO32 Option
*
* Documentation: Xtensa LX4 Microprocessor Data Book, Section 2.22
@ -30,7 +27,7 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>

View file

@ -16,7 +16,7 @@
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
/*
* Memory layout:

View file

@ -1,17 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ACPI helpers for GPIO API
*
* Copyright (C) 2012, Intel Corporation
* Authors: Mathias Nyman <mathias.nyman@linux.intel.com>
* Mika Westerberg <mika.westerberg@linux.intel.com>
*
* 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.
*/
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>

View file

@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Device property helpers for GPIO chips.
*
* Copyright (C) 2016, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*
* 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.
*/
#include <linux/property.h>
@ -32,32 +29,29 @@ void devprop_gpiochip_set_names(struct gpio_chip *chip,
struct gpio_device *gdev = chip->gpiodev;
const char **names;
int ret, i;
int count;
ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
NULL, 0);
if (ret < 0)
count = fwnode_property_read_string_array(fwnode, "gpio-line-names",
NULL, 0);
if (count < 0)
return;
if (ret != gdev->ngpio) {
dev_warn(&gdev->dev,
"names %d do not match number of GPIOs %d\n", ret,
gdev->ngpio);
return;
}
if (count > gdev->ngpio)
count = gdev->ngpio;
names = kcalloc(gdev->ngpio, sizeof(*names), GFP_KERNEL);
names = kcalloc(count, sizeof(*names), GFP_KERNEL);
if (!names)
return;
ret = fwnode_property_read_string_array(fwnode, "gpio-line-names",
names, gdev->ngpio);
names, count);
if (ret < 0) {
dev_warn(&gdev->dev, "failed to read GPIO line names\n");
kfree(names);
return;
}
for (i = 0; i < gdev->ngpio; i++)
for (i = 0; i < count; i++)
gdev->descs[i].name = names[i];
kfree(names);

View file

@ -1,14 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* drivers/gpio/devres.c - managed gpio resources
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* devres.c - managed gpio resources
* This file is based on kernel/irq/devres.c
*
* Copyright (c) 2011 John Crispin <john@phrozen.org>

View file

@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>

View file

@ -1,14 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
* OF helpers for the GPIO API
*
* Copyright (c) 2007-2008 MontaVista Software, Inc.
*
* Author: Anton Vorontsov <avorontsov@ru.mvista.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/device.h>
@ -58,7 +54,8 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
}
static void of_gpio_flags_quirks(struct device_node *np,
enum of_gpio_flags *flags)
enum of_gpio_flags *flags,
int index)
{
/*
* Some GPIO fixed regulator quirks.
@ -92,6 +89,51 @@ static void of_gpio_flags_quirks(struct device_node *np,
pr_info("%s uses legacy open drain flag - update the DTS if you can\n",
of_node_full_name(np));
}
/*
* Legacy handling of SPI active high chip select. If we have a
* property named "cs-gpios" we need to inspect the child node
* to determine if the flags should have inverted semantics.
*/
if (IS_ENABLED(CONFIG_SPI_MASTER) &&
of_property_read_bool(np, "cs-gpios")) {
struct device_node *child;
u32 cs;
int ret;
for_each_child_of_node(np, child) {
ret = of_property_read_u32(child, "reg", &cs);
if (!ret)
continue;
if (cs == index) {
/*
* SPI children have active low chip selects
* by default. This can be specified negatively
* by just omitting "spi-cs-high" in the
* device node, or actively by tagging on
* GPIO_ACTIVE_LOW as flag in the device
* tree. If the line is simultaneously
* tagged as active low in the device tree
* and has the "spi-cs-high" set, we get a
* conflict and the "spi-cs-high" flag will
* take precedence.
*/
if (of_property_read_bool(np, "spi-cs-high")) {
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;
}
} else {
if (!(*flags & OF_GPIO_ACTIVE_LOW))
pr_info("%s enforce active low on chipselect handle\n",
of_node_full_name(np));
*flags |= OF_GPIO_ACTIVE_LOW;
}
break;
}
}
}
}
/**
@ -132,7 +174,7 @@ struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
goto out;
if (flags)
of_gpio_flags_quirks(np, flags);
of_gpio_flags_quirks(np, flags, index);
pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
__func__, propname, np, index,
@ -349,8 +391,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
else if (of_property_read_bool(np, "output-high"))
*dflags |= GPIOD_OUT_HIGH;
else {
pr_warn("GPIO line %d (%s): no hogging state specified, bailing out\n",
desc_to_gpio(desc), np->name);
pr_warn("GPIO line %d (%pOFn): no hogging state specified, bailing out\n",
desc_to_gpio(desc), np);
return ERR_PTR(-EINVAL);
}

View file

@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
@ -444,11 +444,6 @@ static struct attribute *gpiochip_attrs[] = {
};
ATTRIBUTE_GROUPS(gpiochip);
static struct gpio_desc *gpio_to_valid_desc(int gpio)
{
return gpio_is_valid(gpio) ? gpio_to_desc(gpio) : NULL;
}
/*
* /sys/class/gpio/export ... write-only
* integer N ... number of GPIO to export (full access)
@ -467,7 +462,7 @@ static ssize_t export_store(struct class *class,
if (status < 0)
goto done;
desc = gpio_to_valid_desc(gpio);
desc = gpio_to_desc(gpio);
/* reject invalid GPIOs */
if (!desc) {
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
@ -514,7 +509,7 @@ static ssize_t unexport_store(struct class *class,
if (status < 0)
goto done;
desc = gpio_to_valid_desc(gpio);
desc = gpio_to_desc(gpio);
/* reject bogus commands (gpio_unexport ignores them) */
if (!desc) {
pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Internal GPIO functions.
*
* Copyright (C) 2013, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*
* 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.
*/
#ifndef GPIOLIB_H
@ -183,15 +180,26 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
}
#endif
struct gpio_array {
struct gpio_desc **desc;
unsigned int size;
struct gpio_chip *chip;
unsigned long *get_mask;
unsigned long *set_mask;
unsigned long invert_mask[];
};
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum);
int gpiod_get_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
struct gpio_array *array_info,
unsigned long *value_bitmap);
int gpiod_set_array_value_complex(bool raw, bool can_sleep,
unsigned int array_size,
struct gpio_desc **desc_array,
int *value_array);
unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap);
/* This is just passed between gpiolib and devres */
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
@ -214,6 +222,7 @@ struct gpio_desc {
#define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */
#define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */
#define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */
#define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */
#define FLAG_IS_HOGGED 11 /* GPIO is hogged */
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */