summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/auxdisplay/hd44780.c61
-rw-r--r--drivers/bus/ts-nbus.c20
-rw-r--r--drivers/gpio/Kconfig13
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-adp5520.c2
-rw-r--r--drivers/gpio/gpio-adp5588.c2
-rw-r--r--drivers/gpio/gpio-bcm-kona.c14
-rw-r--r--drivers/gpio/gpio-brcmstb.c15
-rw-r--r--drivers/gpio/gpio-davinci.c60
-rw-r--r--drivers/gpio/gpio-ep93xx.c297
-rw-r--r--drivers/gpio/gpio-ftgpio010.c115
-rw-r--r--drivers/gpio/gpio-htc-egpio.c8
-rw-r--r--drivers/gpio/gpio-ingenic.c392
-rw-r--r--drivers/gpio/gpio-max3191x.c16
-rw-r--r--drivers/gpio/gpio-mxs.c4
-rw-r--r--drivers/gpio/gpio-pxa.c6
-rw-r--r--drivers/gpio/gpio-rcar.c6
-rw-r--r--drivers/gpio/gpio-tb10x.c123
-rw-r--r--drivers/gpio/gpio-tps65086.c12
-rw-r--r--drivers/gpio/gpio-tps6586x.c15
-rw-r--r--drivers/gpio/gpio-tps65910.c9
-rw-r--r--drivers/gpio/gpio-tps65912.c16
-rw-r--r--drivers/gpio/gpio-ts5500.c13
-rw-r--r--drivers/gpio/gpio-twl4030.c59
-rw-r--r--drivers/gpio/gpio-twl6040.c31
-rw-r--r--drivers/gpio/gpio-vf610.c13
-rw-r--r--drivers/gpio/gpio-viperboard.c10
-rw-r--r--drivers/gpio/gpio-vr41xx.c55
-rw-r--r--drivers/gpio/gpio-vx855.c20
-rw-r--r--drivers/gpio/gpio-wm831x.c8
-rw-r--r--drivers/gpio/gpio-wm8350.c8
-rw-r--r--drivers/gpio/gpio-wm8994.c8
-rw-r--r--drivers/gpio/gpio-xlp.c12
-rw-r--r--drivers/gpio/gpio-xtensa.c7
-rw-r--r--drivers/gpio/gpio-zevio.c2
-rw-r--r--drivers/gpio/gpiolib-acpi.c1
-rw-r--r--drivers/gpio/gpiolib-of.c54
-rw-r--r--drivers/gpio/gpiolib-sysfs.c10
-rw-r--r--drivers/gpio/gpiolib.c466
-rw-r--r--drivers/gpio/gpiolib.h16
-rw-r--r--drivers/i2c/muxes/i2c-mux-gpio.c14
-rw-r--r--drivers/mmc/core/pwrseq_simple.c13
-rw-r--r--drivers/mux/gpio.c12
-rw-r--r--drivers/net/phy/mdio-mux-gpio.c11
-rw-r--r--drivers/pcmcia/soc_common.c9
-rw-r--r--drivers/phy/motorola/phy-mapphone-mdm6600.c19
-rw-r--r--drivers/pinctrl/Kconfig6
-rw-r--r--drivers/pinctrl/pinctrl-ingenic.c462
-rw-r--r--drivers/staging/iio/adc/ad7606.c8
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.c7
50 files changed, 1450 insertions, 1111 deletions
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
index f1a42f0f1ded..9ad93ea42fdc 100644
--- a/drivers/auxdisplay/hd44780.c
+++ b/drivers/auxdisplay/hd44780.c
@@ -62,20 +62,15 @@ static void hd44780_strobe_gpio(struct hd44780 *hd)
/* write to an LCD panel register in 8 bit GPIO mode */
static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW */
- unsigned int i, n;
-
- for (i = 0; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 9;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */
+ unsigned int n;
+
+ values[0] = val;
+ __assign_bit(8, values, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 10 : 9;
/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], NULL, values);
hd44780_strobe_gpio(hd);
}
@@ -83,32 +78,25 @@ static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
/* write to an LCD panel register in 4 bit GPIO mode */
static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
- unsigned int i, n;
+ DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
+ unsigned int n;
/* High nibble + RS, RW */
- for (i = 4; i < 8; i++)
- values[PIN_DATA0 + i] = !!(val & BIT(i));
- values[PIN_CTRL_RS] = rs;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ values[0] = val >> 4;
+ __assign_bit(4, values, rs);
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;
/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);
hd44780_strobe_gpio(hd);
/* Low nibble */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(val & BIT(i));
+ values[0] &= ~0x0fUL;
+ values[0] |= val & 0x0f;
/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);
hd44780_strobe_gpio(hd);
}
@@ -155,23 +143,16 @@ static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
{
- int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
+ DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */
struct hd44780 *hd = lcd->drvdata;
- unsigned int i, n;
+ unsigned int n;
/* Command nibble + RS, RW */
- for (i = 0; i < 4; i++)
- values[PIN_DATA4 + i] = !!(cmd & BIT(i));
- values[PIN_CTRL_RS] = 0;
- n = 5;
- if (hd->pins[PIN_CTRL_RW]) {
- values[PIN_CTRL_RW] = 0;
- n++;
- }
+ values[0] = cmd & 0x0f;
+ n = hd->pins[PIN_CTRL_RW] ? 6 : 5;
/* Present the data to the port */
- gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
- &values[PIN_DATA4]);
+ gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4], NULL, values);
hd44780_strobe_gpio(hd);
}
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 073fd9011154..9989ce904a37 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -110,13 +110,12 @@ static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction)
*/
static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus)
{
- int i;
- int values[8];
+ DECLARE_BITMAP(values, 8);
- for (i = 0; i < 8; i++)
- values[i] = 0;
+ values[0] = 0;
- gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values);
+ gpiod_set_array_value_cansleep(8, ts_nbus->data->desc,
+ ts_nbus->data->info, values);
gpiod_set_value_cansleep(ts_nbus->csn, 0);
gpiod_set_value_cansleep(ts_nbus->strobe, 0);
gpiod_set_value_cansleep(ts_nbus->ale, 0);
@@ -157,16 +156,11 @@ static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val)
static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte)
{
struct gpio_descs *gpios = ts_nbus->data;
- int i;
- int values[8];
+ DECLARE_BITMAP(values, 8);
- for (i = 0; i < 8; i++)
- if (byte & BIT(i))
- values[i] = 1;
- else
- values[i] = 0;
+ values[0] = byte;
- gpiod_set_array_value_cansleep(8, gpios->desc, values);
+ gpiod_set_array_value_cansleep(8, gpios->desc, gpios->info, values);
}
/*
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4f52c3a8ec99..b8cbf5081389 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -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"
@@ -267,17 +268,6 @@ config GPIO_ICH
If unsure, say N.
-config GPIO_INGENIC
- tristate "Ingenic JZ47xx SoCs GPIO support"
- depends on OF
- depends on MACH_INGENIC || COMPILE_TEST
- select GPIOLIB_IRQCHIP
- help
- Say yes here to support the GPIO functionality present on the
- JZ4740 and JZ4780 SoCs from Ingenic.
-
- If unsure, say N.
-
config GPIO_IOP
tristate "Intel IOP GPIO"
depends on ARCH_IOP32X || ARCH_IOP33X || COMPILE_TEST
@@ -480,6 +470,7 @@ config GPIO_SYSCON
config GPIO_TB10X
bool
+ select GPIO_GENERIC
select GENERIC_IRQ_CHIP
select OF_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index c256aff66a65..80d58c2de730 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -57,7 +57,6 @@ obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
-obj-$(CONFIG_GPIO_INGENIC) += gpio-ingenic.o
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o
diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c
index 21452622d954..e321955782a1 100644
--- a/drivers/gpio/gpio-adp5520.c
+++ b/drivers/gpio/gpio-adp5520.c
@@ -172,7 +172,7 @@ static struct platform_driver adp5520_gpio_driver = {
module_platform_driver(adp5520_gpio_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("GPIO ADP5520 Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:adp5520-gpio");
diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c
index 3530ccd17e04..054feb17d3a0 100644
--- a/drivers/gpio/gpio-adp5588.c
+++ b/drivers/gpio/gpio-adp5588.c
@@ -478,6 +478,6 @@ static struct i2c_driver adp5588_gpio_driver = {
module_i2c_driver(adp5588_gpio_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index d0707fc23afd..c5536a509b59 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -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 = {
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index 16c7f9f49416..af936dcca659 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -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);
diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c
index a5ece8ea79bc..5c1564fcc24e 100644
--- a/drivers/gpio/gpio-davinci.c
+++ b/drivers/gpio/gpio-davinci.c
@@ -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;
}
/*--------------------------------------------------------------------------*/
diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c
index 45d384039e9b..71728d6e0bca 100644
--- a/drivers/gpio/gpio-ep93xx.c
+++ b/drivers/gpio/gpio-ep93xx.c
@@ -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>
+
+#define EP93XX_GPIO_F_INT_STATUS 0x5c
+#define EP93XX_GPIO_A_INT_STATUS 0xa0
+#define EP93XX_GPIO_B_INT_STATUS 0xbc
+
+/* Maximum value for gpio line identifiers */
+#define EP93XX_GPIO_LINE_MAX 63
-#include <mach/hardware.h>
-#include <mach/gpio-ep93xx.h>
+/* Maximum value for irq capable line identifiers */
+#define EP93XX_GPIO_LINE_MAX_IRQ 23
-#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0))
+/*
+ * 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 int ep93xx_gpio_port(struct gpio_chip *gc)
+{
+ 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(unsigned int irq, bool enable)
+static void ep93xx_gpio_int_debounce(struct gpio_chip *gc,
+ unsigned int offset, bool enable)
{
- 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 = 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;
}
diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c
index 868bf8501560..95f578804b0e 100644
--- a/drivers/gpio/gpio-ftgpio010.c
+++ b/drivers/gpio/gpio-ftgpio010.c
@@ -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);
diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c
index ad6e5b518669..38be44dbd6e1 100644
--- a/drivers/gpio/gpio-htc-egpio.c
+++ b/drivers/gpio/gpio-htc-egpio.c
@@ -334,7 +334,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;
diff --git a/drivers/gpio/gpio-ingenic.c b/drivers/gpio/gpio-ingenic.c
deleted file mode 100644
index e738e384a5ca..000000000000
--- a/drivers/gpio/gpio-ingenic.c
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * Ingenic JZ47xx GPIO driver
- *
- * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net>
- *
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#include <linux/gpio/driver.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/regmap.h>
-
-#define GPIO_PIN 0x00
-#define GPIO_MSK 0x20
-
-#define JZ4740_GPIO_DATA 0x10
-#define JZ4740_GPIO_SELECT 0x50
-#define JZ4740_GPIO_DIR 0x60
-#define JZ4740_GPIO_TRIG 0x70
-#define JZ4740_GPIO_FLAG 0x80
-
-#define JZ4770_GPIO_INT 0x10
-#define JZ4770_GPIO_PAT1 0x30
-#define JZ4770_GPIO_PAT0 0x40
-#define JZ4770_GPIO_FLAG 0x50
-
-#define REG_SET(x) ((x) + 0x4)
-#define REG_CLEAR(x) ((x) + 0x8)
-
-enum jz_version {
- ID_JZ4740,
- ID_JZ4770,
- ID_JZ4780,
-};
-
-struct ingenic_gpio_chip {
- struct regmap *map;
- struct gpio_chip gc;
- struct irq_chip irq_chip;
- unsigned int irq, reg_base;
- enum jz_version version;
-};
-
-static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg)
-{
- unsigned int val;
-
- regmap_read(jzgc->map, jzgc->reg_base + reg, &val);
-
- return (u32) val;
-}
-
-static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc,
- u8 reg, u8 offset, bool set)
-{
- if (set)
- reg = REG_SET(reg);
- else
- reg = REG_CLEAR(reg);
-
- regmap_write(jzgc->map, jzgc->reg_base + reg, BIT(offset));
-}
-
-static inline bool gpio_get_value(struct ingenic_gpio_chip *jzgc, u8 offset)
-{
- unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN);
-
- return !!(val & BIT(offset));
-}
-
-static void gpio_set_value(struct ingenic_gpio_chip *jzgc, u8 offset, int value)
-{
- if (jzgc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value);
- else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value);
-}
-
-static void irq_set_type(struct ingenic_gpio_chip *jzgc,
- u8 offset, unsigned int type)
-{
- u8 reg1, reg2;
-
- if (jzgc->version >= ID_JZ4770) {
- reg1 = JZ4770_GPIO_PAT1;
- reg2 = JZ4770_GPIO_PAT0;
- } else {
- reg1 = JZ4740_GPIO_TRIG;
- reg2 = JZ4740_GPIO_DIR;
- }
-
- switch (type) {
- case IRQ_TYPE_EDGE_RISING:
- gpio_ingenic_set_bit(jzgc, reg2, offset, true);
- gpio_ingenic_set_bit(jzgc, reg1, offset, true);
- break;
- case IRQ_TYPE_EDGE_FALLING:
- gpio_ingenic_set_bit(jzgc, reg2, offset, false);
- gpio_ingenic_set_bit(jzgc, reg1, offset, true);
- break;
- case IRQ_TYPE_LEVEL_HIGH:
- gpio_ingenic_set_bit(jzgc, reg2, offset, true);
- gpio_ingenic_set_bit(jzgc, reg1, offset, false);
- break;
- case IRQ_TYPE_LEVEL_LOW:
- default:
- gpio_ingenic_set_bit(jzgc, reg2, offset, false);
- gpio_ingenic_set_bit(jzgc, reg1, offset, false);
- break;
- }
-}
-
-static void ingenic_gpio_irq_mask(struct irq_data *irqd)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
-
- gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true);
-}
-
-static void ingenic_gpio_irq_unmask(struct irq_data *irqd)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
-
- gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false);
-}
-
-static void ingenic_gpio_irq_enable(struct irq_data *irqd)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
- int irq = irqd->hwirq;
-
- if (jzgc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true);
- else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true);
-
- ingenic_gpio_irq_unmask(irqd);
-}
-
-static void ingenic_gpio_irq_disable(struct irq_data *irqd)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
- int irq = irqd->hwirq;
-
- ingenic_gpio_irq_mask(irqd);
-
- if (jzgc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false);
- else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false);
-}
-
-static void ingenic_gpio_irq_ack(struct irq_data *irqd)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
- int irq = irqd->hwirq;
- bool high;
-
- if (irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) {
- /*
- * Switch to an interrupt for the opposite edge to the one that
- * triggered the interrupt being ACKed.
- */
- high = gpio_get_value(jzgc, irq);
- if (high)
- irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_FALLING);
- else
- irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_RISING);
- }
-
- if (jzgc->version >= ID_JZ4770)
- gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false);
- else
- gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true);
-}
-
-static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
-
- switch (type) {
- case IRQ_TYPE_EDGE_BOTH:
- case IRQ_TYPE_EDGE_RISING:
- case IRQ_TYPE_EDGE_FALLING:
- irq_set_handler_locked(irqd, handle_edge_irq);
- break;
- case IRQ_TYPE_LEVEL_HIGH:
- case IRQ_TYPE_LEVEL_LOW:
- irq_set_handler_locked(irqd, handle_level_irq);
- break;
- default:
- irq_set_handler_locked(irqd, handle_bad_irq);
- }
-
- if (type == IRQ_TYPE_EDGE_BOTH) {
- /*
- * The hardware does not support interrupts on both edges. The
- * best we can do is to set up a single-edge interrupt and then
- * switch to the opposing edge when ACKing the interrupt.
- */
- bool high = gpio_get_value(jzgc, irqd->hwirq);
-
- type = high ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
- }
-
- irq_set_type(jzgc, irqd->hwirq, type);
- return 0;
-}
-
-static int ingenic_gpio_irq_set_wake(struct irq_data *irqd, unsigned int on)
-{
- struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
-
- return irq_set_irq_wake(jzgc->irq, on);
-}
-
-static void ingenic_gpio_irq_handler(struct irq_desc *desc)
-{
- struct gpio_chip *gc = irq_desc_get_handler_data(desc);
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
- struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data);
- unsigned long flag, i;
-
- chained_irq_enter(irq_chip, desc);
-
- if (jzgc->version >= ID_JZ4770)
- flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG);
- else
- flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG);
-
- for_each_set_bit(i, &flag, 32)
- generic_handle_irq(irq_linear_revmap(gc->irq.domain, i));
- chained_irq_exit(irq_chip, desc);
-}
-
-static void ingenic_gpio_set(struct gpio_chip *gc,
- unsigned int offset, int value)
-{
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
-
- gpio_set_value(jzgc, offset, value);
-}
-
-static int ingenic_gpio_get(struct gpio_chip *gc, unsigned int offset)
-{
- struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
-
- return (int) gpio_get_value(jzgc, offset);
-}
-
-static int ingenic_gpio_direction_input(struct gpio_chip *gc,
- unsigned int offset)
-{
- return pinctrl_gpio_direction_input(gc->base + offset);
-}
-
-static int ingenic_gpio_direction_output(struct gpio_chip *gc,
- unsigned int offset, int value)
-{
- ingenic_gpio_set(gc, offset, value);
- return pinctrl_gpio_direction_output(gc->base + offset);
-}
-
-static const struct of_device_id ingenic_gpio_of_match[] = {
- { .compatible = "ingenic,jz4740-gpio", .data = (void *)ID_JZ4740 },
- { .compatible = "ingenic,jz4770-gpio", .data = (void *)ID_JZ4770 },
- { .compatible = "ingenic,jz4780-gpio", .data = (void *)ID_JZ4780 },
- {},
-};
-MODULE_DEVICE_TABLE(of, ingenic_gpio_of_match);
-
-static int ingenic_gpio_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct ingenic_gpio_chip *jzgc;
- u32 bank;
- int err;
-
- jzgc = devm_kzalloc(dev, sizeof(*jzgc), GFP_KERNEL);
- if (!jzgc)
- return -ENOMEM;
-
- jzgc->map = dev_get_drvdata(dev->parent);
- if (!jzgc->map) {
- dev_err(dev, "Cannot get parent regmap\n");
- return -ENXIO;
- }
-
- err = of_property_read_u32(dev->of_node, "reg", &bank);
- if (err) {
- dev_err(dev, "Cannot read \"reg\" property: %i\n", err);
- return err;
- }
-
- jzgc->reg_base = bank * 0x100;
-
- jzgc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "GPIO%c", 'A' + bank);
- if (!jzgc->gc.label)
- return -ENOMEM;
-
- /* DO NOT EXPAND THIS: FOR BACKWARD GPIO NUMBERSPACE COMPATIBIBILITY
- * ONLY: WORK TO TRANSITION CONSUMERS TO USE THE GPIO DESCRIPTOR API IN
- * <linux/gpio/consumer.h> INSTEAD.
- */
- jzgc->gc.base = bank * 32;
-
- jzgc->gc.ngpio = 32;
- jzgc->gc.parent = dev;
- jzgc->gc.of_node = dev->of_node;
- jzgc->gc.owner = THIS_MODULE;
- jzgc->version = (enum jz_version)of_device_get_match_data(dev);
-
- jzgc->gc.set = ingenic_gpio_set;
- jzgc->gc.get = ingenic_gpio_get;
- jzgc->gc.direction_input = ingenic_gpio_direction_input;
- jzgc->gc.direction_output = ingenic_gpio_direction_output;
-
- if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
- jzgc->gc.request = gpiochip_generic_request;
- jzgc->gc.free = gpiochip_generic_free;
- }
-
- err = devm_gpiochip_add_data(dev, &jzgc->gc, jzgc);
- if (err)
- return err;
-
- jzgc->irq = irq_of_parse_and_map(dev->of_node, 0);
- if (!jzgc->irq)
- return -EINVAL;
-
- jzgc->irq_chip.name = jzgc->gc.label;
- jzgc->irq_chip.irq_enable = ingenic_gpio_irq_enable;
- jzgc->irq_chip.irq_disable = ingenic_gpio_irq_disable;
- jzgc->irq_chip.irq_unmask = ingenic_gpio_irq_unmask;
- jzgc->irq_chip.irq_mask = ingenic_gpio_irq_mask;
- jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack;
- jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type;
- jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake;
- jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND;
-
- err = gpiochip_irqchip_add(&jzgc->gc, &jzgc->irq_chip, 0,
- handle_level_irq, IRQ_TYPE_NONE);
- if (err)
- return err;
-
- gpiochip_set_chained_irqchip(&jzgc->gc, &jzgc->irq_chip,
- jzgc->irq, ingenic_gpio_irq_handler);
- return 0;
-}
-
-static int ingenic_gpio_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
-static struct platform_driver ingenic_gpio_driver = {
- .driver = {
- .name = "gpio-ingenic",
- .of_match_table = of_match_ptr(ingenic_gpio_of_match),
- },
- .probe = ingenic_gpio_probe,
- .remove = ingenic_gpio_remove,
-};
-
-static int __init ingenic_gpio_drv_register(void)
-{
- return platform_driver_register(&ingenic_gpio_driver);
-}
-subsys_initcall(ingenic_gpio_drv_register);
-
-static void __exit ingenic_gpio_drv_unregister(void)
-{
- platform_driver_unregister(&ingenic_gpio_driver);
-}
-module_exit(ingenic_gpio_drv_unregister);
-
-MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
-MODULE_DESCRIPTION("Ingenic JZ47xx GPIO driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c
index b5b9cb1fda50..9a8876abeb57 100644
--- a/drivers/gpio/gpio-max3191x.c
+++ b/drivers/gpio/gpio-max3191x.c
@@ -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");
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index df30490da820..ea874fd033a5 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -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
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index c18712dabf93..bfe4c5c9f41c 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -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);
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 55cc61086d99..3c82bb3c2030 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -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)) {
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
index a12cd0b5c972..d5e5d19f4c0a 100644
--- a/drivers/gpio/gpio-tb10x.c
+++ b/drivers/gpio/gpio-tb10x.c
@@ -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;
-
-
- ret = devm_gpiochip_add_data(&pdev->dev, &tb10x_gpio->gc, tb10x_gpio);
+ /*
+ * 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(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) {
diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c
index b23c4d2429be..2eea98ff4ea3 100644
--- a/drivers/gpio/gpio-tps65086.c
+++ b/drivers/gpio/gpio-tps65086.c
@@ -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>
diff --git a/drivers/gpio/gpio-tps6586x.c b/drivers/gpio/gpio-tps6586x.c
index 042b9a20781a..9b6cc74f47c8 100644
--- a/drivers/gpio/gpio-tps6586x.c
+++ b/drivers/gpio/gpio-tps6586x.c
@@ -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>
diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c
index e63d7dabf78b..0c785b0fd161 100644
--- a/drivers/gpio/gpio-tps65910.c
+++ b/drivers/gpio/gpio-tps65910.c
@@ -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>
diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c
index abc0798ef843..3ad68bd78282 100644
--- a/drivers/gpio/gpio-tps65912.c
+++ b/drivers/gpio/gpio-tps65912.c
@@ -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)
diff --git a/drivers/gpio/gpio-ts5500.c b/drivers/gpio/gpio-ts5500.c
index 6cfeba07f882..c91890488402 100644
--- a/drivers/gpio/gpio-ts5500.c
+++ b/drivers/gpio/gpio-ts5500.c
@@ -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:
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index 9b511df5450e..fbfb648d3502 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -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,
diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c
index dadeacf43e0c..c845b2ff1f43 100644
--- a/drivers/gpio/gpio-twl6040.c
+++ b/drivers/gpio/gpio-twl6040.c
@@ -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,
};
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index d4ad6d0e02a2..5960396c8d9a 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -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>
diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
index e6d1328dddfa..9b604f13e302 100644
--- a/drivers/gpio/gpio-viperboard.c
+++ b/drivers/gpio/gpio-viperboard.c
@@ -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>
diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c
index 027699cec911..b13a49c89cc1 100644
--- a/drivers/gpio/gpio-vr41xx.c
+++ b/drivers/gpio/gpio-vr41xx.c
@@ -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;
diff --git a/drivers/gpio/gpio-vx855.c b/drivers/gpio/gpio-vx855.c
index 98a6f1fcc561..4ff146ca32fe 100644
--- a/drivers/gpio/gpio-vx855.c
+++ b/drivers/gpio/gpio-vx855.c
@@ -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>
diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c
index 324813e8304e..a3a32a77041f 100644
--- a/drivers/gpio/gpio-wm831x.c
+++ b/drivers/gpio/gpio-wm831x.c
@@ -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>
diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c
index e46752e73dd9..460f0a4b04bd 100644
--- a/drivers/gpio/gpio-wm8350.c
+++ b/drivers/gpio/gpio-wm8350.c
@@ -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>
diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c
index 1e35756ac55b..9af89cf7f6bc 100644
--- a/drivers/gpio/gpio-wm8994.c
+++ b/drivers/gpio/gpio-wm8994.c
@@ -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>
diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c
index 8e4275eaa7d7..0a3607fd21af 100644
--- a/drivers/gpio/gpio-xlp.c
+++ b/drivers/gpio/gpio-xlp.c
@@ -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>
diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c
index f16c0427952e..43d3fa5f511a 100644
--- a/drivers/gpio/gpio-xtensa.c
+++ b/drivers/gpio/gpio-xtensa.c
@@ -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>
diff --git a/drivers/gpio/gpio-zevio.c b/drivers/gpio/gpio-zevio.c
index 3926ce9c2840..57432397e5e5 100644
--- a/drivers/gpio/gpio-zevio.c
+++ b/drivers/gpio/gpio-zevio.c
@@ -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:
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index c48ed9d89ff5..79271e08eea5 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -11,7 +11,6 @@
*/
#include <linux/errno.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index a4f1157d6aa0..9985b748b14d 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -57,7 +57,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.
@@ -91,6 +92,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;
+ }
+ }
+ }
}
/**
@@ -131,7 +177,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,
@@ -348,8 +394,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);
}
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 3dbaf489a8a5..3d7c90fe7189 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -2,7 +2,6 @@
#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 +443,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 +461,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 +508,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);
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index e8f8a1999393..03d15d84bdd8 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -210,15 +210,15 @@ static int gpiochip_find_base(int ngpio)
*/
int gpiod_get_direction(struct gpio_desc *desc)
{
- struct gpio_chip *chip;
- unsigned offset;
- int status = -EINVAL;
+ struct gpio_chip *chip;
+ unsigned offset;
+ int status;
chip = gpiod_to_chip(desc);
offset = gpio_chip_hwgpio(desc);
if (!chip->get_direction)
- return status;
+ return -ENOTSUPP;
status = chip->get_direction(chip, offset);
if (status > 0) {
@@ -427,7 +427,7 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
struct linehandle_state *lh = filep->private_data;
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- int vals[GPIOHANDLES_MAX];
+ DECLARE_BITMAP(vals, GPIOHANDLES_MAX);
int i;
if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) {
@@ -436,13 +436,14 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
true,
lh->numdescs,
lh->descs,
+ NULL,
vals);
if (ret)
return ret;
memset(&ghd, 0, sizeof(ghd));
for (i = 0; i < lh->numdescs; i++)
- ghd.values[i] = vals[i];
+ ghd.values[i] = test_bit(i, vals);
if (copy_to_user(ip, &ghd, sizeof(ghd)))
return -EFAULT;
@@ -461,13 +462,14 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
/* Clamp all values to [0,1] */
for (i = 0; i < lh->numdescs; i++)
- vals[i] = !!ghd.values[i];
+ __assign_bit(i, vals, ghd.values[i]);
/* Reuse the array setting function */
return gpiod_set_array_value_complex(false,
true,
lh->numdescs,
lh->descs,
+ NULL,
vals);
}
return -EINVAL;
@@ -812,26 +814,26 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
{
struct lineevent_state *le = p;
struct gpioevent_data ge;
- int ret, level;
+ int ret;
/* Do not leak kernel stack to userspace */
memset(&ge, 0, sizeof(ge));
ge.timestamp = le->timestamp;
- level = gpiod_get_value_cansleep(le->desc);
if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
&& le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
+ int level = gpiod_get_value_cansleep(le->desc);
if (level)
/* Emit low-to-high event */
ge.id = GPIOEVENT_EVENT_RISING_EDGE;
else
/* Emit high-to-low event */
ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
- } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE && level) {
+ } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) {
/* Emit low-to-high event */
ge.id = GPIOEVENT_EVENT_RISING_EDGE;
- } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE && !level) {
+ } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
/* Emit high-to-low event */
ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
} else {
@@ -942,7 +944,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
if (eflags & GPIOEVENT_REQUEST_FALLING_EDGE)
irqflags |= IRQF_TRIGGER_FALLING;
irqflags |= IRQF_ONESHOT;
- irqflags |= IRQF_SHARED;
INIT_KFIFO(le->events);
init_waitqueue_head(&le->wait);
@@ -1804,39 +1805,75 @@ static const struct irq_domain_ops gpiochip_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
+static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+ if (!gpiochip_irqchip_irq_valid(chip, offset))
+ return -ENXIO;
+
+ return irq_create_mapping(chip->irq.domain, offset);
+}
+
static int gpiochip_irq_reqres(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
- int ret;
- if (!try_module_get(chip->gpiodev->owner))
- return -ENODEV;
-
- ret = gpiochip_lock_as_irq(chip, d->hwirq);
- if (ret) {
- chip_err(chip,
- "unable to lock HW IRQ %lu for IRQ\n",
- d->hwirq);
- module_put(chip->gpiodev->owner);
- return ret;
- }
- return 0;
+ return gpiochip_reqres_irq(chip, d->hwirq);
}
static void gpiochip_irq_relres(struct irq_data *d)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
- gpiochip_unlock_as_irq(chip, d->hwirq);
- module_put(chip->gpiodev->owner);
+ gpiochip_relres_irq(chip, d->hwirq);
}
-static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset)
+static void gpiochip_irq_enable(struct irq_data *d)
{
- if (!gpiochip_irqchip_irq_valid(chip, offset))
- return -ENXIO;
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
- return irq_create_mapping(chip->irq.domain, offset);
+ gpiochip_enable_irq(chip, d->hwirq);
+ if (chip->irq.irq_enable)
+ chip->irq.irq_enable(d);
+ else
+ chip->irq.chip->irq_unmask(d);
+}
+
+static void gpiochip_irq_disable(struct irq_data *d)
+{
+ struct gpio_chip *chip = irq_data_get_irq_chip_data(d);
+
+ if (chip->irq.irq_disable)
+ chip->irq.irq_disable(d);
+ else
+ chip->irq.chip->irq_mask(d);
+ gpiochip_disable_irq(chip, d->hwirq);
+}
+
+static void gpiochip_set_irq_hooks(struct gpio_chip *gpiochip)
+{
+ struct irq_chip *irqchip = gpiochip->irq.chip;
+
+ if (!irqchip->irq_request_resources &&
+ !irqchip->irq_release_resources) {
+ irqchip->irq_request_resources = gpiochip_irq_reqres;
+ irqchip->irq_release_resources = gpiochip_irq_relres;
+ }
+ if (WARN_ON(gpiochip->irq.irq_enable))
+ return;
+ /* Check if the irqchip already has this hook... */
+ if (irqchip->irq_enable == gpiochip_irq_enable) {
+ /*
+ * ...and if so, give a gentle warning that this is bad
+ * practice.
+ */
+ chip_info(gpiochip,
+ "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
+ return;
+ }
+ gpiochip->irq.irq_enable = irqchip->irq_enable;
+ gpiochip->irq.irq_disable = irqchip->irq_disable;
+ irqchip->irq_enable = gpiochip_irq_enable;
+ irqchip->irq_disable = gpiochip_irq_disable;
}
/**
@@ -1897,16 +1934,6 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
if (!gpiochip->irq.domain)
return -EINVAL;
- /*
- * It is possible for a driver to override this, but only if the
- * alternative functions are both implemented.
- */
- if (!irqchip->irq_request_resources &&
- !irqchip->irq_release_resources) {
- irqchip->irq_request_resources = gpiochip_irq_reqres;
- irqchip->irq_release_resources = gpiochip_irq_relres;
- }
-
if (gpiochip->irq.parent_handler) {
void *data = gpiochip->irq.parent_handler_data ?: gpiochip;
@@ -1922,6 +1949,8 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
}
}
+ gpiochip_set_irq_hooks(gpiochip);
+
acpi_gpiochip_request_interrupts(gpiochip);
return 0;
@@ -1935,11 +1964,12 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip,
*/
static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
{
+ struct irq_chip *irqchip = gpiochip->irq.chip;
unsigned int offset;
acpi_gpiochip_free_interrupts(gpiochip);
- if (gpiochip->irq.chip && gpiochip->irq.parent_handler) {
+ if (irqchip && gpiochip->irq.parent_handler) {
struct gpio_irq_chip *irq = &gpiochip->irq;
unsigned int i;
@@ -1963,11 +1993,19 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip)
irq_domain_remove(gpiochip->irq.domain);
}
- if (gpiochip->irq.chip) {
- gpiochip->irq.chip->irq_request_resources = NULL;
- gpiochip->irq.chip->irq_release_resources = NULL;
- gpiochip->irq.chip = NULL;
+ if (irqchip) {
+ if (irqchip->irq_request_resources == gpiochip_irq_reqres) {
+ irqchip->irq_request_resources = NULL;
+ irqchip->irq_release_resources = NULL;
+ }
+ if (irqchip->irq_enable == gpiochip_irq_enable) {
+ irqchip->irq_enable = gpiochip->irq.irq_enable;
+ irqchip->irq_disable = gpiochip->irq.irq_disable;
+ }
}
+ gpiochip->irq.irq_enable = NULL;
+ gpiochip->irq.irq_disable = NULL;
+ gpiochip->irq.chip = NULL;
gpiochip_irqchip_free_valid_mask(gpiochip);
}
@@ -2056,15 +2094,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
return -EINVAL;
}
- /*
- * It is possible for a driver to override this, but only if the
- * alternative functions are both implemented.
- */
- if (!irqchip->irq_request_resources &&
- !irqchip->irq_release_resources) {
- irqchip->irq_request_resources = gpiochip_irq_reqres;
- irqchip->irq_release_resources = gpiochip_irq_relres;
- }
+ gpiochip_set_irq_hooks(gpiochip);
acpi_gpiochip_request_interrupts(gpiochip);
@@ -2604,8 +2634,9 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
else
value = !!value;
- /* GPIOs used for IRQs shall not be set as output */
- if (test_bit(FLAG_USED_AS_IRQ, &desc->flags)) {
+ /* GPIOs used for enabled IRQs shall not be set as output */
+ if (test_bit(FLAG_USED_AS_IRQ, &desc->flags) &&
+ test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) {
gpiod_err(desc,
"%s: tried to set a GPIO tied to an IRQ as output\n",
__func__);
@@ -2784,9 +2815,39 @@ static int gpio_chip_get_multiple(struct gpio_chip *chip,
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 i = 0;
+ int err, i = 0;
+
+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ err = gpio_chip_get_multiple(array_info->chip,
+ array_info->get_mask,
+ value_bitmap);
+ if (err)
+ return err;
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ if (bitmap_full(array_info->get_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->get_mask, array_size);
+ } else {
+ array_info = NULL;
+ }
while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
@@ -2817,7 +2878,12 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
int hwgpio = gpio_chip_hwgpio(desc);
__set_bit(hwgpio, mask);
- i++;
+
+ if (array_info)
+ i = find_next_zero_bit(array_info->get_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
@@ -2828,15 +2894,21 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
return ret;
}
- for (j = first; j < i; j++) {
+ for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j];
int hwgpio = gpio_chip_hwgpio(desc);
int value = test_bit(hwgpio, bits);
if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
- value_array[j] = value;
+ __assign_bit(j, value_bitmap, value);
trace_gpio_value(desc_to_gpio(desc), 1, value);
+
+ if (array_info)
+ j = find_next_zero_bit(array_info->get_mask, i,
+ j);
+ else
+ j++;
}
if (mask != fastpath)
@@ -2895,9 +2967,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
/**
* gpiod_get_raw_array_value() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -2907,20 +2980,24 @@ EXPORT_SYMBOL_GPL(gpiod_get_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
/**
* gpiod_get_array_value() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -2929,12 +3006,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value);
* and it will complain if the GPIO chip functions potentially sleep.
*/
int gpiod_get_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, false, array_size,
- desc_array, value_array);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value);
@@ -3027,10 +3107,37 @@ static void gpio_chip_set_multiple(struct gpio_chip *chip,
int gpiod_set_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 i = 0;
+ /*
+ * Validate array_info against desc_array and its size.
+ * It should immediately follow desc_array if both
+ * have been obtained from the same gpiod_get_array() call.
+ */
+ if (array_info && array_info->desc == desc_array &&
+ array_size <= array_info->size &&
+ (void *)array_info == desc_array + array_info->size) {
+ if (!can_sleep)
+ WARN_ON(array_info->chip->can_sleep);
+
+ if (!raw && !bitmap_empty(array_info->invert_mask, array_size))
+ bitmap_xor(value_bitmap, value_bitmap,
+ array_info->invert_mask, array_size);
+
+ gpio_chip_set_multiple(array_info->chip, array_info->set_mask,
+ value_bitmap);
+
+ if (bitmap_full(array_info->set_mask, array_size))
+ return 0;
+
+ i = find_first_zero_bit(array_info->set_mask, array_size);
+ } else {
+ array_info = NULL;
+ }
+
while (i < array_size) {
struct gpio_chip *chip = desc_array[i]->gdev->chip;
unsigned long fastpath[2 * BITS_TO_LONGS(FASTPATH_NGPIO)];
@@ -3056,9 +3163,16 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do {
struct gpio_desc *desc = desc_array[i];
int hwgpio = gpio_chip_hwgpio(desc);
- int value = value_array[i];
+ int value = test_bit(i, value_bitmap);
- if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags))
+ /*
+ * Pins applicable for fast input but not for
+ * fast output processing may have been already
+ * inverted inside the fast path, skip them.
+ */
+ if (!raw && !(array_info &&
+ test_bit(i, array_info->invert_mask)) &&
+ test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
trace_gpio_value(desc_to_gpio(desc), 0, value);
/*
@@ -3077,7 +3191,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
__clear_bit(hwgpio, bits);
count++;
}
- i++;
+
+ if (array_info)
+ i = find_next_zero_bit(array_info->set_mask,
+ array_size, i);
+ else
+ i++;
} while ((i < array_size) &&
(desc_array[i]->gdev->chip == chip));
/* push collected bits to outputs */
@@ -3152,9 +3271,10 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
/**
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3163,20 +3283,23 @@ EXPORT_SYMBOL_GPL(gpiod_set_value);
* complain if the GPIO chip functions potentially sleep.
*/
int gpiod_set_raw_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, false, array_size,
- desc_array, value_array);
+ desc_array, array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
/**
* gpiod_set_array_value() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3185,12 +3308,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value);
* complain if the GPIO chip functions potentially sleep.
*/
void gpiod_set_array_value(unsigned int array_size,
- struct gpio_desc **desc_array, int *value_array)
+ struct gpio_desc **desc_array,
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
if (!desc_array)
return;
gpiod_set_array_value_complex(false, false, array_size, desc_array,
- value_array);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value);
@@ -3292,6 +3417,7 @@ int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset)
}
set_bit(FLAG_USED_AS_IRQ, &desc->flags);
+ set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
/*
* If the consumer has not set up a label (such as when the
@@ -3322,6 +3448,7 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
return;
clear_bit(FLAG_USED_AS_IRQ, &desc->flags);
+ clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
/* If we only had this marking, erase it */
if (desc->label && !strcmp(desc->label, "interrupt"))
@@ -3329,6 +3456,28 @@ void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset)
}
EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq);
+void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpio_desc *desc = gpiochip_get_desc(chip, offset);
+
+ if (!IS_ERR(desc) &&
+ !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags)))
+ clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
+}
+EXPORT_SYMBOL_GPL(gpiochip_disable_irq);
+
+void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ struct gpio_desc *desc = gpiochip_get_desc(chip, offset);
+
+ if (!IS_ERR(desc) &&
+ !WARN_ON(!test_bit(FLAG_USED_AS_IRQ, &desc->flags))) {
+ WARN_ON(test_bit(FLAG_IS_OUT, &desc->flags));
+ set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags);
+ }
+}
+EXPORT_SYMBOL_GPL(gpiochip_enable_irq);
+
bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset)
{
if (offset >= chip->ngpio)
@@ -3338,6 +3487,30 @@ bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset)
}
EXPORT_SYMBOL_GPL(gpiochip_line_is_irq);
+int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ int ret;
+
+ if (!try_module_get(chip->gpiodev->owner))
+ return -ENODEV;
+
+ ret = gpiochip_lock_as_irq(chip, offset);
+ if (ret) {
+ chip_err(chip, "unable to lock HW IRQ %u for IRQ\n", offset);
+ module_put(chip->gpiodev->owner);
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gpiochip_reqres_irq);
+
+void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset)
+{
+ gpiochip_unlock_as_irq(chip, offset);
+ module_put(chip->gpiodev->owner);
+}
+EXPORT_SYMBOL_GPL(gpiochip_relres_irq);
+
bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset)
{
if (offset >= chip->ngpio)
@@ -3410,9 +3583,10 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
/**
* gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap to store the read values
*
* Read the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status. Return 0 in case of success,
@@ -3422,21 +3596,24 @@ EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep);
*/
int gpiod_get_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(true, true, array_size,
- desc_array, value_array);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
/**
* gpiod_get_array_value_cansleep() - read values from an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be read
- * @value_array: array to store the read values
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap to store the read values
*
* Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account. Return 0 in case of success, else an error code.
@@ -3445,13 +3622,15 @@ EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep);
*/
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_get_array_value_complex(false, true, array_size,
- desc_array, value_array);
+ desc_array, array_info,
+ value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep);
@@ -3493,9 +3672,10 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
/**
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap of values to assign
*
* Set the raw values of the GPIOs, i.e. the values of the physical lines
* without regard for their ACTIVE_LOW status.
@@ -3504,13 +3684,14 @@ EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
*/
int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return -EINVAL;
return gpiod_set_array_value_complex(true, true, array_size, desc_array,
- value_array);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_raw_array_value_cansleep);
@@ -3533,9 +3714,10 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
/**
* gpiod_set_array_value_cansleep() - assign values to an array of GPIOs
- * @array_size: number of elements in the descriptor / value arrays
+ * @array_size: number of elements in the descriptor array / value bitmap
* @desc_array: array of GPIO descriptors whose values will be assigned
- * @value_array: array of values to assign
+ * @array_info: information on applicability of fast bitmap processing path
+ * @value_bitmap: bitmap of values to assign
*
* Set the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status
* into account.
@@ -3544,13 +3726,14 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n)
*/
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
- int *value_array)
+ struct gpio_array *array_info,
+ unsigned long *value_bitmap)
{
might_sleep_if(extra_checks);
if (!desc_array)
return;
gpiod_set_array_value_complex(false, true, array_size, desc_array,
- value_array);
+ array_info, value_bitmap);
}
EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
@@ -4170,7 +4353,9 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
{
struct gpio_desc *desc;
struct gpio_descs *descs;
- int count;
+ struct gpio_array *array_info = NULL;
+ struct gpio_chip *chip;
+ int count, bitmap_size;
count = gpiod_count(dev, con_id);
if (count < 0)
@@ -4186,9 +4371,92 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
gpiod_put_array(descs);
return ERR_CAST(desc);
}
+
descs->desc[descs->ndescs] = desc;
+
+ chip = gpiod_to_chip(desc);
+ /*
+ * If pin hardware number of array member 0 is also 0, select
+ * its chip as a candidate for fast bitmap processing path.
+ */
+ if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) {
+ struct gpio_descs *array;
+
+ bitmap_size = BITS_TO_LONGS(chip->ngpio > count ?
+ chip->ngpio : count);
+
+ array = kzalloc(struct_size(descs, desc, count) +
+ struct_size(array_info, invert_mask,
+ 3 * bitmap_size), GFP_KERNEL);
+ if (!array) {
+ gpiod_put_array(descs);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ memcpy(array, descs,
+ struct_size(descs, desc, descs->ndescs + 1));
+ kfree(descs);
+
+ descs = array;
+ array_info = (void *)(descs->desc + count);
+ array_info->get_mask = array_info->invert_mask +
+ bitmap_size;
+ array_info->set_mask = array_info->get_mask +
+ bitmap_size;
+
+ array_info->desc = descs->desc;
+ array_info->size = count;
+ array_info->chip = chip;
+ bitmap_set(array_info->get_mask, descs->ndescs,
+ count - descs->ndescs);
+ bitmap_set(array_info->set_mask, descs->ndescs,
+ count - descs->ndescs);
+ descs->info = array_info;
+ }
+ /* Unmark array members which don't belong to the 'fast' chip */
+ if (array_info && array_info->chip != chip) {
+ __clear_bit(descs->ndescs, array_info->get_mask);
+ __clear_bit(descs->ndescs, array_info->set_mask);
+ }
+ /*
+ * Detect array members which belong to the 'fast' chip
+ * but their pins are not in hardware order.
+ */
+ else if (array_info &&
+ gpio_chip_hwgpio(desc) != descs->ndescs) {
+ /*
+ * Don't use fast path if all array members processed so
+ * far belong to the same chip as this one but its pin
+ * hardware number is different from its array index.
+ */
+ if (bitmap_full(array_info->get_mask, descs->ndescs)) {
+ array_info = NULL;
+ } else {
+ __clear_bit(descs->ndescs,
+ array_info->get_mask);
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ }
+ } else if (array_info) {
+ /* Exclude open drain or open source from fast output */
+ if (gpiochip_line_is_open_drain(chip, descs->ndescs) ||
+ gpiochip_line_is_open_source(chip, descs->ndescs))
+ __clear_bit(descs->ndescs,
+ array_info->set_mask);
+ /* Identify 'fast' pins which require invertion */
+ if (gpiod_is_active_low(desc))
+ __set_bit(descs->ndescs,
+ array_info->invert_mask);
+ }
+
descs->ndescs++;
}
+ if (array_info)
+ dev_dbg(dev,
+ "GPIO array info: chip=%s, size=%d, get_mask=%lx, set_mask=%lx, invert_mask=%lx\n",
+ array_info->chip->label, array_info->size,
+ *array_info->get_mask, *array_info->set_mask,
+ *array_info->invert_mask);
return descs;
}
EXPORT_SYMBOL_GPL(gpiod_get_array);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index a7e49fef73d4..f23a0c681494 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -183,15 +183,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);
+ 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 +225,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 */
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
index 401308e3d036..13882a2a4f60 100644
--- a/drivers/i2c/muxes/i2c-mux-gpio.c
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -22,18 +22,16 @@ struct gpiomux {
struct i2c_mux_gpio_platform_data data;
unsigned gpio_base;
struct gpio_desc **gpios;
- int *values;
};
static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
{
- int i;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(val));
- for (i = 0; i < mux->data.n_gpios; i++)
- mux->values[i] = (val >> i) & 1;
+ values[0] = val;
- gpiod_set_array_value_cansleep(mux->data.n_gpios,
- mux->gpios, mux->values);
+ gpiod_set_array_value_cansleep(mux->data.n_gpios, mux->gpios, NULL,
+ values);
}
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
@@ -182,15 +180,13 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values,
- mux->data.n_gpios * sizeof(*mux->gpios) +
- mux->data.n_gpios * sizeof(*mux->values), 0,
+ mux->data.n_gpios * sizeof(*mux->gpios), 0,
i2c_mux_gpio_select, NULL);
if (!muxc) {
ret = -ENOMEM;
goto alloc_failed;
}
mux->gpios = muxc->priv;
- mux->values = (int *)(mux->gpios + mux->data.n_gpios);
muxc->priv = mux;
platform_set_drvdata(pdev, muxc);
diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c
index a8b9fee4d62a..7f882a2bb872 100644
--- a/drivers/mmc/core/pwrseq_simple.c
+++ b/drivers/mmc/core/pwrseq_simple.c
@@ -40,18 +40,13 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq,
struct gpio_descs *reset_gpios = pwrseq->reset_gpios;
if (!IS_ERR(reset_gpios)) {
- int i, *values;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(value));
int nvalues = reset_gpios->ndescs;
- values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL);
- if (!values)
- return;
+ values[0] = value;
- for (i = 0; i < nvalues; i++)
- values[i] = value;
-
- gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values);
- kfree(values);
+ gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc,
+ reset_gpios->info, values);
}
}
diff --git a/drivers/mux/gpio.c b/drivers/mux/gpio.c
index 6fdd9316db8b..02c1f2c014e8 100644
--- a/drivers/mux/gpio.c
+++ b/drivers/mux/gpio.c
@@ -17,20 +17,18 @@
struct mux_gpio {
struct gpio_descs *gpios;
- int *val;
};
static int mux_gpio_set(struct mux_control *mux, int state)
{
struct mux_gpio *mux_gpio = mux_chip_priv(mux->chip);
- int i;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(state));
- for (i = 0; i < mux_gpio->gpios->ndescs; i++)
- mux_gpio->val[i] = (state >> i) & 1;
+ values[0] = state;
gpiod_set_array_value_cansleep(mux_gpio->gpios->ndescs,
mux_gpio->gpios->desc,
- mux_gpio->val);
+ mux_gpio->gpios->info, values);
return 0;
}
@@ -58,13 +56,11 @@ static int mux_gpio_probe(struct platform_device *pdev)
if (pins < 0)
return pins;
- mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio) +
- pins * sizeof(*mux_gpio->val));
+ mux_chip = devm_mux_chip_alloc(dev, 1, sizeof(*mux_gpio));
if (IS_ERR(mux_chip))
return PTR_ERR(mux_chip);
mux_gpio = mux_chip_priv(mux_chip);
- mux_gpio->val = (int *)(mux_gpio + 1);
mux_chip->ops = &mux_gpio_ops;
mux_gpio->gpios = devm_gpiod_get_array(dev, "mux", GPIOD_OUT_LOW);
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index bc90764a8b8d..fe34576262bd 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -20,23 +20,21 @@
struct mdio_mux_gpio_state {
struct gpio_descs *gpios;
void *mux_handle;
- int values[];
};
static int mdio_mux_gpio_switch_fn(int current_child, int desired_child,
void *data)
{
struct mdio_mux_gpio_state *s = data;
- unsigned int n;
+ DECLARE_BITMAP(values, BITS_PER_TYPE(desired_child));
if (current_child == desired_child)
return 0;
- for (n = 0; n < s->gpios->ndescs; n++)
- s->values[n] = (desired_child >> n) & 1;
+ values[0] = desired_child;
gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc,
- s->values);
+ s->gpios->info, values);
return 0;
}
@@ -51,8 +49,7 @@ static int mdio_mux_gpio_probe(struct platform_device *pdev)
if (IS_ERR(gpios))
return PTR_ERR(gpios);
- s = devm_kzalloc(&pdev->dev, struct_size(s, values, gpios->ndescs),
- GFP_KERNEL);
+ s = devm_kzalloc(&pdev->dev, sizeof(*s), GFP_KERNEL);
if (!s) {
gpiod_put_array(gpios);
return -ENOMEM;
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index c5f2344c189b..3a8c84bb174d 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -351,19 +351,20 @@ static int soc_common_pcmcia_config_skt(
if (ret == 0) {
struct gpio_desc *descs[2];
- int values[2], n = 0;
+ DECLARE_BITMAP(values, 2);
+ int n = 0;
if (skt->gpio_reset) {
descs[n] = skt->gpio_reset;
- values[n++] = !!(state->flags & SS_RESET);
+ __assign_bit(n++, values, state->flags & SS_RESET);
}
if (skt->gpio_bus_enable) {
descs[n] = skt->gpio_bus_enable;
- values[n++] = !!(state->flags & SS_OUTPUT_ENA);
+ __assign_bit(n++, values, state->flags & SS_OUTPUT_ENA);
}
if (n)
- gpiod_set_array_value_cansleep(n, descs, values);
+ gpiod_set_array_value_cansleep(n, descs, NULL, values);
/*
* This really needs a better solution. The IRQ
diff --git a/drivers/phy/motorola/phy-mapphone-mdm6600.c b/drivers/phy/motorola/phy-mapphone-mdm6600.c
index 0075fb0bef8c..25d456a323c2 100644
--- a/drivers/phy/motorola/phy-mapphone-mdm6600.c
+++ b/drivers/phy/motorola/phy-mapphone-mdm6600.c
@@ -157,15 +157,13 @@ static const struct phy_ops gpio_usb_ops = {
*/
static void phy_mdm6600_cmd(struct phy_mdm6600 *ddata, int val)
{
- int values[PHY_MDM6600_NR_CMD_LINES];
- int i;
+ DECLARE_BITMAP(values, PHY_MDM6600_NR_CMD_LINES);
- val &= (1 << PHY_MDM6600_NR_CMD_LINES) - 1;
- for (i = 0; i < PHY_MDM6600_NR_CMD_LINES; i++)
- values[i] = (val & BIT(i)) >> i;
+ values[0] = val;
gpiod_set_array_value_cansleep(PHY_MDM6600_NR_CMD_LINES,
- ddata->cmd_gpios->desc, values);
+ ddata->cmd_gpios->desc,
+ ddata->cmd_gpios->info, values);
}
/**
@@ -176,7 +174,7 @@ static void phy_mdm6600_status(struct work_struct *work)
{
struct phy_mdm6600 *ddata;
struct device *dev;
- int values[PHY_MDM6600_NR_STATUS_LINES];
+ DECLARE_BITMAP(values, PHY_MDM6600_NR_STATUS_LINES);
int error, i, val = 0;
ddata = container_of(work, struct phy_mdm6600, status_work.work);
@@ -184,16 +182,17 @@ static void phy_mdm6600_status(struct work_struct *work)
error = gpiod_get_array_value_cansleep(PHY_MDM6600_NR_STATUS_LINES,
ddata->status_gpios->desc,
+ ddata->status_gpios->info,
values);
if (error)
return;
for (i = 0; i < PHY_MDM6600_NR_STATUS_LINES; i++) {
- val |= values[i] << i;
+ val |= test_bit(i, values) << i;
dev_dbg(ddata->dev, "XXX %s: i: %i values[i]: %i val: %i\n",
- __func__, i, values[i], val);
+ __func__, i, test_bit(i, values), val);
}
- ddata->status = val;
+ ddata->status = values[0];
dev_info(dev, "modem status: %i %s\n",
ddata->status,
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index e86752be1f19..f7be9e1455b5 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -309,12 +309,14 @@ config PINCTRL_ZYNQ
config PINCTRL_INGENIC
bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
- default y
+ default MACH_INGENIC
depends on OF
- depends on MACH_INGENIC || COMPILE_TEST
+ depends on MIPS || COMPILE_TEST
select GENERIC_PINCONF
select GENERIC_PINCTRL_GROUPS
select GENERIC_PINMUX_FUNCTIONS
+ select GPIOLIB
+ select GPIOLIB_IRQCHIP
select REGMAP_MMIO
config PINCTRL_RK805
diff --git a/drivers/pinctrl/pinctrl-ingenic.c b/drivers/pinctrl/pinctrl-ingenic.c
index 6a1b6058b991..97821514baf5 100644
--- a/drivers/pinctrl/pinctrl-ingenic.c
+++ b/drivers/pinctrl/pinctrl-ingenic.c
@@ -7,10 +7,11 @@
*/
#include <linux/compiler.h>
-#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
@@ -24,6 +25,9 @@
#include "pinconf.h"
#include "pinmux.h"
+#define GPIO_PIN 0x00
+#define GPIO_MSK 0x20
+
#define JZ4740_GPIO_DATA 0x10
#define JZ4740_GPIO_PULL_DIS 0x30
#define JZ4740_GPIO_FUNC 0x40
@@ -33,7 +37,6 @@
#define JZ4740_GPIO_FLAG 0x80
#define JZ4770_GPIO_INT 0x10
-#define JZ4770_GPIO_MSK 0x20
#define JZ4770_GPIO_PAT1 0x30
#define JZ4770_GPIO_PAT0 0x40
#define JZ4770_GPIO_FLAG 0x50
@@ -46,6 +49,7 @@
enum jz_version {
ID_JZ4740,
+ ID_JZ4725B,
ID_JZ4770,
ID_JZ4780,
};
@@ -72,6 +76,13 @@ struct ingenic_pinctrl {
const struct ingenic_chip_info *info;
};
+struct ingenic_gpio_chip {
+ struct ingenic_pinctrl *jzpc;
+ struct gpio_chip gc;
+ struct irq_chip irq_chip;
+ unsigned int irq, reg_base;
+};
+
static const u32 jz4740_pull_ups[4] = {
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
};
@@ -205,6 +216,99 @@ static const struct ingenic_chip_info jz4740_chip_info = {
.pull_downs = jz4740_pull_downs,
};
+static int jz4725b_mmc0_1bit_pins[] = { 0x48, 0x49, 0x5c, };
+static int jz4725b_mmc0_4bit_pins[] = { 0x5d, 0x5b, 0x56, };
+static int jz4725b_mmc1_1bit_pins[] = { 0x7a, 0x7b, 0x7c, };
+static int jz4725b_mmc1_4bit_pins[] = { 0x7d, 0x7e, 0x7f, };
+static int jz4725b_uart_data_pins[] = { 0x4c, 0x4d, };
+static int jz4725b_nand_cs1_pins[] = { 0x55, };
+static int jz4725b_nand_cs2_pins[] = { 0x56, };
+static int jz4725b_nand_cs3_pins[] = { 0x57, };
+static int jz4725b_nand_cs4_pins[] = { 0x58, };
+static int jz4725b_nand_cle_ale_pins[] = { 0x48, 0x49 };
+static int jz4725b_nand_fre_fwe_pins[] = { 0x5c, 0x5d };
+static int jz4725b_pwm_pwm0_pins[] = { 0x4a, };
+static int jz4725b_pwm_pwm1_pins[] = { 0x4b, };
+static int jz4725b_pwm_pwm2_pins[] = { 0x4c, };
+static int jz4725b_pwm_pwm3_pins[] = { 0x4d, };
+static int jz4725b_pwm_pwm4_pins[] = { 0x4e, };
+static int jz4725b_pwm_pwm5_pins[] = { 0x4f, };
+
+static int jz4725b_mmc0_1bit_funcs[] = { 1, 1, 1, };
+static int jz4725b_mmc0_4bit_funcs[] = { 1, 0, 1, };
+static int jz4725b_mmc1_1bit_funcs[] = { 0, 0, 0, };
+static int jz4725b_mmc1_4bit_funcs[] = { 0, 0, 0, };
+static int jz4725b_uart_data_funcs[] = { 1, 1, };
+static int jz4725b_nand_cs1_funcs[] = { 0, };
+static int jz4725b_nand_cs2_funcs[] = { 0, };
+static int jz4725b_nand_cs3_funcs[] = { 0, };
+static int jz4725b_nand_cs4_funcs[] = { 0, };
+static int jz4725b_nand_cle_ale_funcs[] = { 0, 0, };
+static int jz4725b_nand_fre_fwe_funcs[] = { 0, 0, };
+static int jz4725b_pwm_pwm0_funcs[] = { 0, };
+static int jz4725b_pwm_pwm1_funcs[] = { 0, };
+static int jz4725b_pwm_pwm2_funcs[] = { 0, };
+static int jz4725b_pwm_pwm3_funcs[] = { 0, };
+static int jz4725b_pwm_pwm4_funcs[] = { 0, };
+static int jz4725b_pwm_pwm5_funcs[] = { 0, };
+
+static const struct group_desc jz4725b_groups[] = {
+ INGENIC_PIN_GROUP("mmc0-1bit", jz4725b_mmc0_1bit),
+ INGENIC_PIN_GROUP("mmc0-4bit", jz4725b_mmc0_4bit),
+ INGENIC_PIN_GROUP("mmc1-1bit", jz4725b_mmc1_1bit),
+ INGENIC_PIN_GROUP("mmc1-4bit", jz4725b_mmc1_4bit),
+ INGENIC_PIN_GROUP("uart-data", jz4725b_uart_data),
+ INGENIC_PIN_GROUP("nand-cs1", jz4725b_nand_cs1),
+ INGENIC_PIN_GROUP("nand-cs2", jz4725b_nand_cs2),
+ INGENIC_PIN_GROUP("nand-cs3", jz4725b_nand_cs3),
+ INGENIC_PIN_GROUP("nand-cs4", jz4725b_nand_cs4),
+ INGENIC_PIN_GROUP("nand-cle-ale", jz4725b_nand_cle_ale),
+ INGENIC_PIN_GROUP("nand-fre-fwe", jz4725b_nand_fre_fwe),
+ INGENIC_PIN_GROUP("pwm0", jz4725b_pwm_pwm0),
+ INGENIC_PIN_GROUP("pwm1", jz4725b_pwm_pwm1),
+ INGENIC_PIN_GROUP("pwm2", jz4725b_pwm_pwm2),
+ INGENIC_PIN_GROUP("pwm3", jz4725b_pwm_pwm3),
+ INGENIC_PIN_GROUP("pwm4", jz4725b_pwm_pwm4),
+ INGENIC_PIN_GROUP("pwm5", jz4725b_pwm_pwm5),
+};
+
+static const char *jz4725b_mmc0_groups[] = { "mmc0-1bit", "mmc0-4bit", };
+static const char *jz4725b_mmc1_groups[] = { "mmc1-1bit", "mmc1-4bit", };
+static const char *jz4725b_uart_groups[] = { "uart-data", };
+static const char *jz4725b_nand_groups[] = {
+ "nand-cs1", "nand-cs2", "nand-cs3", "nand-cs4",
+ "nand-cle-ale", "nand-fre-fwe",
+};
+static const char *jz4725b_pwm0_groups[] = { "pwm0", };
+static const char *jz4725b_pwm1_groups[] = { "pwm1", };
+static const char *jz4725b_pwm2_groups[] = { "pwm2", };
+static const char *jz4725b_pwm3_groups[] = { "pwm3", };
+static const char *jz4725b_pwm4_groups[] = { "pwm4", };
+static const char *jz4725b_pwm5_groups[] = { "pwm5", };
+
+static const struct function_desc jz4725b_functions[] = {
+ { "mmc0", jz4725b_mmc0_groups, ARRAY_SIZE(jz4725b_mmc0_groups), },
+ { "mmc1", jz4725b_mmc1_groups, ARRAY_SIZE(jz4725b_mmc1_groups), },
+ { "uart", jz4725b_uart_groups, ARRAY_SIZE(jz4725b_uart_groups), },
+ { "nand", jz4725b_nand_groups, ARRAY_SIZE(jz4725b_nand_groups), },
+ { "pwm0", jz4725b_pwm0_groups, ARRAY_SIZE(jz4725b_pwm0_groups), },
+ { "pwm1", jz4725b_pwm1_groups, ARRAY_SIZE(jz4725b_pwm1_groups), },
+ { "pwm2", jz4725b_pwm2_groups, ARRAY_SIZE(jz4725b_pwm2_groups), },
+ { "pwm3", jz4725b_pwm3_groups, ARRAY_SIZE(jz4725b_pwm3_groups), },
+ { "pwm4", jz4725b_pwm4_groups, ARRAY_SIZE(jz4725b_pwm4_groups), },
+ { "pwm5", jz4725b_pwm5_groups, ARRAY_SIZE(jz4725b_pwm5_groups), },
+};
+
+static const struct ingenic_chip_info jz4725b_chip_info = {
+ .num_chips = 4,
+ .groups = jz4725b_groups,
+ .num_groups = ARRAY_SIZE(jz4725b_groups),
+ .functions = jz4725b_functions,
+ .num_functions = ARRAY_SIZE(jz4725b_functions),
+ .pull_ups = jz4740_pull_ups,
+ .pull_downs = jz4740_pull_downs,
+};
+
static const u32 jz4770_pull_ups[6] = {
0x3fffffff, 0xfff0030c, 0xffffffff, 0xffff4fff, 0xfffffb7c, 0xffa7f00f,
};
@@ -438,6 +542,235 @@ static const struct ingenic_chip_info jz4770_chip_info = {
.pull_downs = jz4770_pull_downs,
};
+static u32 gpio_ingenic_read_reg(struct ingenic_gpio_chip *jzgc, u8 reg)
+{
+ unsigned int val;
+
+ regmap_read(jzgc->jzpc->map, jzgc->reg_base + reg, &val);
+
+ return (u32) val;
+}
+
+static void gpio_ingenic_set_bit(struct ingenic_gpio_chip *jzgc,
+ u8 reg, u8 offset, bool set)
+{
+ if (set)
+ reg = REG_SET(reg);
+ else
+ reg = REG_CLEAR(reg);
+
+ regmap_write(jzgc->jzpc->map, jzgc->reg_base + reg, BIT(offset));
+}
+
+static inline bool ingenic_gpio_get_value(struct ingenic_gpio_chip *jzgc,
+ u8 offset)
+{
+ unsigned int val = gpio_ingenic_read_reg(jzgc, GPIO_PIN);
+
+ return !!(val & BIT(offset));
+}
+
+static void ingenic_gpio_set_value(struct ingenic_gpio_chip *jzgc,
+ u8 offset, int value)
+{
+ if (jzgc->jzpc->version >= ID_JZ4770)
+ gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value);
+ else
+ gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value);
+}
+
+static void irq_set_type(struct ingenic_gpio_chip *jzgc,
+ u8 offset, unsigned int type)
+{
+ u8 reg1, reg2;
+
+ if (jzgc->jzpc->version >= ID_JZ4770) {
+ reg1 = JZ4770_GPIO_PAT1;
+ reg2 = JZ4770_GPIO_PAT0;
+ } else {
+ reg1 = JZ4740_GPIO_TRIG;
+ reg2 = JZ4740_GPIO_DIR;
+ }
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ gpio_ingenic_set_bit(jzgc, reg2, offset, true);
+ gpio_ingenic_set_bit(jzgc, reg1, offset, true);
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ gpio_ingenic_set_bit(jzgc, reg2, offset, false);
+ gpio_ingenic_set_bit(jzgc, reg1, offset, true);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ gpio_ingenic_set_bit(jzgc, reg2, offset, true);
+ gpio_ingenic_set_bit(jzgc, reg1, offset, false);
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ default:
+ gpio_ingenic_set_bit(jzgc, reg2, offset, false);
+ gpio_ingenic_set_bit(jzgc, reg1, offset, false);
+ break;
+ }
+}
+
+static void ingenic_gpio_irq_mask(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+ gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, true);
+}
+
+static void ingenic_gpio_irq_unmask(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+ gpio_ingenic_set_bit(jzgc, GPIO_MSK, irqd->hwirq, false);
+}
+
+static void ingenic_gpio_irq_enable(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+ int irq = irqd->hwirq;
+
+ if (jzgc->jzpc->version >= ID_JZ4770)
+ gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, true);
+ else
+ gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true);
+
+ ingenic_gpio_irq_unmask(irqd);
+}
+
+static void ingenic_gpio_irq_disable(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+ int irq = irqd->hwirq;
+
+ ingenic_gpio_irq_mask(irqd);
+
+ if (jzgc->jzpc->version >= ID_JZ4770)
+ gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_INT, irq, false);
+ else
+ gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false);
+}
+
+static void ingenic_gpio_irq_ack(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+ int irq = irqd->hwirq;
+ bool high;
+
+ if (irqd_get_trigger_type(irqd) == IRQ_TYPE_EDGE_BOTH) {
+ /*
+ * Switch to an interrupt for the opposite edge to the one that
+ * triggered the interrupt being ACKed.
+ */
+ high = ingenic_gpio_get_value(jzgc, irq);
+ if (high)
+ irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_FALLING);
+ else
+ irq_set_type(jzgc, irq, IRQ_TYPE_EDGE_RISING);
+ }
+
+ if (jzgc->jzpc->version >= ID_JZ4770)
+ gpio_ingenic_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false);
+ else
+ gpio_ingenic_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true);
+}
+
+static int ingenic_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_EDGE_FALLING:
+ irq_set_handler_locked(irqd, handle_edge_irq);
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ case IRQ_TYPE_LEVEL_LOW:
+ irq_set_handler_locked(irqd, handle_level_irq);
+ break;
+ default:
+ irq_set_handler_locked(irqd, handle_bad_irq);
+ }
+
+ if (type == IRQ_TYPE_EDGE_BOTH) {
+ /*
+ * The hardware does not support interrupts on both edges. The
+ * best we can do is to set up a single-edge interrupt and then
+ * switch to the opposing edge when ACKing the interrupt.
+ */
+ bool high = ingenic_gpio_get_value(jzgc, irqd->hwirq);
+
+ type = high ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
+ }
+
+ irq_set_type(jzgc, irqd->hwirq, type);
+ return 0;
+}
+
+static int ingenic_gpio_irq_set_wake(struct irq_data *irqd, unsigned int on)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+ return irq_set_irq_wake(jzgc->irq, on);
+}
+
+static void ingenic_gpio_irq_handler(struct irq_desc *desc)
+{
+ struct gpio_chip *gc = irq_desc_get_handler_data(desc);
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+ struct irq_chip *irq_chip = irq_data_get_irq_chip(&desc->irq_data);
+ unsigned long flag, i;
+
+ chained_irq_enter(irq_chip, desc);
+
+ if (jzgc->jzpc->version >= ID_JZ4770)
+ flag = gpio_ingenic_read_reg(jzgc, JZ4770_GPIO_FLAG);
+ else
+ flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG);
+
+ for_each_set_bit(i, &flag, 32)
+ generic_handle_irq(irq_linear_revmap(gc->irq.domain, i));
+ chained_irq_exit(irq_chip, desc);
+}
+
+static void ingenic_gpio_set(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+ ingenic_gpio_set_value(jzgc, offset, value);
+}
+
+static int ingenic_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+
+ return (int) ingenic_gpio_get_value(jzgc, offset);
+}
+
+static int ingenic_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return pinctrl_gpio_direction_input(gc->base + offset);
+}
+
+static int ingenic_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ ingenic_gpio_set(gc, offset, value);
+ return pinctrl_gpio_direction_output(gc->base + offset);
+}
+
static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc,
unsigned int pin, u8 reg, bool set)
{
@@ -460,6 +793,21 @@ static inline bool ingenic_get_pin_config(struct ingenic_pinctrl *jzpc,
return val & BIT(idx);
}
+static int ingenic_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
+ struct ingenic_pinctrl *jzpc = jzgc->jzpc;
+ unsigned int pin = gc->base + offset;
+
+ if (jzpc->version >= ID_JZ4770)
+ return ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_PAT1);
+
+ if (ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_SELECT))
+ return true;
+
+ return !ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_DIR);
+}
+
static const struct pinctrl_ops ingenic_pctlops = {
.get_groups_count = pinctrl_generic_get_group_count,
.get_group_name = pinctrl_generic_get_group_name,
@@ -479,7 +827,7 @@ static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc,
if (jzpc->version >= ID_JZ4770) {
ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
- ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, false);
+ ingenic_config_pin(jzpc, pin, GPIO_MSK, false);
ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, func & 0x2);
ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, func & 0x1);
} else {
@@ -532,7 +880,7 @@ static int ingenic_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
if (jzpc->version >= ID_JZ4770) {
ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
- ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, true);
+ ingenic_config_pin(jzpc, pin, GPIO_MSK, true);
ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, input);
} else {
ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, false);
@@ -712,12 +1060,95 @@ static const struct regmap_config ingenic_pinctrl_regmap_config = {
static const struct of_device_id ingenic_pinctrl_of_match[] = {
{ .compatible = "ingenic,jz4740-pinctrl", .data = (void *) ID_JZ4740 },
+ { .compatible = "ingenic,jz4725b-pinctrl", .data = (void *)ID_JZ4725B },
{ .compatible = "ingenic,jz4770-pinctrl", .data = (void *) ID_JZ4770 },
{ .compatible = "ingenic,jz4780-pinctrl", .data = (void *) ID_JZ4780 },
{},
};
-static int ingenic_pinctrl_probe(struct platform_device *pdev)
+static const struct of_device_id ingenic_gpio_of_match[] __initconst = {
+ { .compatible = "ingenic,jz4740-gpio", },
+ { .compatible = "ingenic,jz4770-gpio", },
+ { .compatible = "ingenic,jz4780-gpio", },
+ {},
+};
+
+static int __init ingenic_gpio_probe(struct ingenic_pinctrl *jzpc,
+ struct device_node *node)
+{
+ struct ingenic_gpio_chip *jzgc;
+ struct device *dev = jzpc->dev;
+ unsigned int bank;
+ int err;
+
+ err = of_property_read_u32(node, "reg", &bank);
+ if (err) {
+ dev_err(dev, "Cannot read \"reg\" property: %i\n", err);
+ return err;
+ }
+
+ jzgc = devm_kzalloc(dev, sizeof(*jzgc), GFP_KERNEL);
+ if (!jzgc)
+ return -ENOMEM;
+
+ jzgc->jzpc = jzpc;
+ jzgc->reg_base = bank * 0x100;
+
+ jzgc->gc.label = devm_kasprintf(dev, GFP_KERNEL, "GPIO%c", 'A' + bank);
+ if (!jzgc->gc.label)
+ return -ENOMEM;
+
+ /* DO NOT EXPAND THIS: FOR BACKWARD GPIO NUMBERSPACE COMPATIBIBILITY
+ * ONLY: WORK TO TRANSITION CONSUMERS TO USE THE GPIO DESCRIPTOR API IN
+ * <linux/gpio/consumer.h> INSTEAD.
+ */
+ jzgc->gc.base = bank * 32;
+
+ jzgc->gc.ngpio = 32;
+ jzgc->gc.parent = dev;
+ jzgc->gc.of_node = node;
+ jzgc->gc.owner = THIS_MODULE;
+
+ jzgc->gc.set = ingenic_gpio_set;
+ jzgc->gc.get = ingenic_gpio_get;
+ jzgc->gc.direction_input = ingenic_gpio_direction_input;
+ jzgc->gc.direction_output = ingenic_gpio_direction_output;
+ jzgc->gc.get_direction = ingenic_gpio_get_direction;
+
+ if (of_property_read_bool(node, "gpio-ranges")) {
+ jzgc->gc.request = gpiochip_generic_request;
+ jzgc->gc.free = gpiochip_generic_free;
+ }
+
+ err = devm_gpiochip_add_data(dev, &jzgc->gc, jzgc);
+ if (err)
+ return err;
+
+ jzgc->irq = irq_of_parse_and_map(node, 0);
+ if (!jzgc->irq)
+ return -EINVAL;
+
+ jzgc->irq_chip.name = jzgc->gc.label;
+ jzgc->irq_chip.irq_enable = ingenic_gpio_irq_enable;
+ jzgc->irq_chip.irq_disable = ingenic_gpio_irq_disable;
+ jzgc->irq_chip.irq_unmask = ingenic_gpio_irq_unmask;
+ jzgc->irq_chip.irq_mask = ingenic_gpio_irq_mask;
+ jzgc->irq_chip.irq_ack = ingenic_gpio_irq_ack;
+ jzgc->irq_chip.irq_set_type = ingenic_gpio_irq_set_type;
+ jzgc->irq_chip.irq_set_wake = ingenic_gpio_irq_set_wake;
+ jzgc->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
+ err = gpiochip_irqchip_add(&jzgc->gc, &jzgc->irq_chip, 0,
+ handle_level_irq, IRQ_TYPE_NONE);
+ if (err)
+ return err;
+
+ gpiochip_set_chained_irqchip(&jzgc->gc, &jzgc->irq_chip,
+ jzgc->irq, ingenic_gpio_irq_handler);
+ return 0;
+}
+
+static int __init ingenic_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ingenic_pinctrl *jzpc;
@@ -727,6 +1158,7 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev)
const struct of_device_id *of_id = of_match_device(
ingenic_pinctrl_of_match, dev);
const struct ingenic_chip_info *chip_info;
+ struct device_node *node;
unsigned int i;
int err;
@@ -755,6 +1187,8 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev)
if (jzpc->version >= ID_JZ4770)
chip_info = &jz4770_chip_info;
+ else if (jzpc->version >= ID_JZ4725B)
+ chip_info = &jz4725b_chip_info;
else
chip_info = &jz4740_chip_info;
jzpc->info = chip_info;
@@ -815,11 +1249,11 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev)
dev_set_drvdata(dev, jzpc->map);
- if (dev->of_node) {
- err = of_platform_populate(dev->of_node, NULL, NULL, dev);
- if (err) {
- dev_err(dev, "Failed to probe GPIO devices\n");
- return err;
+ for_each_child_of_node(dev->of_node, node) {
+ if (of_match_node(ingenic_gpio_of_match, node)) {
+ err = ingenic_gpio_probe(jzpc, node);
+ if (err)
+ return err;
}
}
@@ -828,6 +1262,7 @@ static int ingenic_pinctrl_probe(struct platform_device *pdev)
static const struct platform_device_id ingenic_pinctrl_ids[] = {
{ "jz4740-pinctrl", ID_JZ4740 },
+ { "jz4725b-pinctrl", ID_JZ4725B },
{ "jz4770-pinctrl", ID_JZ4770 },
{ "jz4780-pinctrl", ID_JZ4780 },
{},
@@ -837,14 +1272,13 @@ static struct platform_driver ingenic_pinctrl_driver = {
.driver = {
.name = "pinctrl-ingenic",
.of_match_table = of_match_ptr(ingenic_pinctrl_of_match),
- .suppress_bind_attrs = true,
},
- .probe = ingenic_pinctrl_probe,
.id_table = ingenic_pinctrl_ids,
};
static int __init ingenic_pinctrl_drv_register(void)
{
- return platform_driver_register(&ingenic_pinctrl_driver);
+ return platform_driver_probe(&ingenic_pinctrl_driver,
+ ingenic_pinctrl_probe);
}
-postcore_initcall(ingenic_pinctrl_drv_register);
+subsys_initcall(ingenic_pinctrl_drv_register);
diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c
index 25b9fcd5e3a4..b7810b1aad07 100644
--- a/drivers/staging/iio/adc/ad7606.c
+++ b/drivers/staging/iio/adc/ad7606.c
@@ -202,7 +202,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
- int values[3];
+ DECLARE_BITMAP(values, 3);
int ret, i;
switch (mask) {
@@ -227,12 +227,10 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- values[0] = (ret >> 0) & 1;
- values[1] = (ret >> 1) & 1;
- values[2] = (ret >> 2) & 1;
+ values[0] = ret;
mutex_lock(&st->lock);
- gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
+ gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
values);
st->oversampling = val;
mutex_unlock(&st->lock);
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 1c06325beaca..39ed56214cd3 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -40,7 +40,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
struct gpio_desc *desc_array[UART_GPIO_MAX];
- int value_array[UART_GPIO_MAX];
+ DECLARE_BITMAP(values, UART_GPIO_MAX);
unsigned int count = 0;
if (gpios == NULL)
@@ -49,10 +49,11 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
for (i = 0; i < UART_GPIO_MAX; i++)
if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
desc_array[count] = gpios->gpio[i];
- value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
+ __assign_bit(count, values,
+ mctrl & mctrl_gpios_desc[i].mctrl);
count++;
}
- gpiod_set_array_value(count, desc_array, value_array);
+ gpiod_set_array_value(count, desc_array, NULL, values);
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);