From 7b0db5533608eb3864383cc211e56027d39a6cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 8 May 2017 10:41:48 +0200 Subject: [PATCH 01/70] gpio: fix description for gpio-ranges example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The length of the second entry is 20, so it affects GPIOs 10..29. Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index 84ede036f73d..2209c163f2a5 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -282,8 +282,8 @@ Example 1: }; Here, a single GPIO controller has GPIOs 0..9 routed to pin controller -pinctrl1's pins 20..29, and GPIOs 10..19 routed to pin controller pinctrl2's -pins 50..59. +pinctrl1's pins 20..29, and GPIOs 10..29 routed to pin controller pinctrl2's +pins 50..69. Example 2: From 226b2242d43d6d2d4a72e394c19fdd2f1b06f29e Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Thu, 20 Apr 2017 23:23:20 +0200 Subject: [PATCH 02/70] gpio: export add/remove lookup table functions For hot-pluggable devices adding GPIOs dynamically we need to assemble and add the gpio lookup tables at probe time in modules, so that requesting these GPIOs in attached drivers can work. Export lookup table functions for modules. Signed-off-by: Anatolij Gustschin Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5db44139cef8..995ca9cf7dbf 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3008,6 +3008,7 @@ void gpiod_add_lookup_table(struct gpiod_lookup_table *table) mutex_unlock(&gpio_lookup_lock); } +EXPORT_SYMBOL_GPL(gpiod_add_lookup_table); /** * gpiod_remove_lookup_table() - unregister GPIO device consumers @@ -3021,6 +3022,7 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) mutex_unlock(&gpio_lookup_lock); } +EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) { From 7808c42b4043f71daa91acdf7454d94ed5b7178e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 13 May 2017 01:18:45 +0900 Subject: [PATCH 03/70] gpio: zynq: remove unneeded (void *) casts in of_match_table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit of_device_id::data is an opaque pointer. No explicit cast is needed. Signed-off-by: Masahiro Yamada Acked-by: Sören Brinkmann Signed-off-by: Linus Walleij --- drivers/gpio/gpio-zynq.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 6b4d10d6e10f..ed87c9a6e0e6 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -651,9 +651,8 @@ static const struct zynq_platform_data zynq_gpio_def = { }; static const struct of_device_id zynq_gpio_of_match[] = { - { .compatible = "xlnx,zynq-gpio-1.0", .data = (void *)&zynq_gpio_def }, - { .compatible = "xlnx,zynqmp-gpio-1.0", - .data = (void *)&zynqmp_gpio_def }, + { .compatible = "xlnx,zynq-gpio-1.0", .data = &zynq_gpio_def }, + { .compatible = "xlnx,zynqmp-gpio-1.0", .data = &zynqmp_gpio_def }, { /* end of table */ } }; MODULE_DEVICE_TABLE(of, zynq_gpio_of_match); From 5704520d7880ee8b5d759e83df6090ea9f1a9b06 Mon Sep 17 00:00:00 2001 From: Nandor Han Date: Mon, 15 May 2017 08:58:25 +0300 Subject: [PATCH 04/70] gpio: xra1403: Add EXAR XRA1403 SPI GPIO expander driver This driver support basic XRA1403 functionalities: - set gpio direction - get gpio direction - set gpio high/low - get gpio status Signed-off-by: Nandor Han Signed-off-by: Semi Malinen Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 5 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-xra1403.c | 237 ++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/gpio/gpio-xra1403.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 23ca51ee6b28..dbde3ae03e74 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1225,6 +1225,11 @@ config GPIO_PISOSR GPIO driver for SPI compatible parallel-in/serial-out shift registers. These are input only devices. +config GPIO_XRA1403 + tristate "EXAR XRA1403 16-bit GPIO expander" + help + GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander. + endmenu menu "SPI or I2C GPIO expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 68b96277d9fa..4d9adc677ed6 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -141,6 +141,7 @@ obj-$(CONFIG_GPIO_XGENE) += gpio-xgene.o obj-$(CONFIG_GPIO_XGENE_SB) += gpio-xgene-sb.o obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o obj-$(CONFIG_GPIO_XLP) += gpio-xlp.o +obj-$(CONFIG_GPIO_XRA1403) += gpio-xra1403.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c new file mode 100644 index 000000000000..0230e4b7a2fb --- /dev/null +++ b/drivers/gpio/gpio-xra1403.c @@ -0,0 +1,237 @@ +/* + * GPIO driver for EXAR XRA1403 16-bit GPIO expander + * + * Copyright (c) 2017, General Electric Company + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* XRA1403 registers */ +#define XRA_GSR 0x00 /* GPIO State */ +#define XRA_OCR 0x02 /* Output Control */ +#define XRA_PIR 0x04 /* Input Polarity Inversion */ +#define XRA_GCR 0x06 /* GPIO Configuration */ +#define XRA_PUR 0x08 /* Input Internal Pull-up Resistor Enable/Disable */ +#define XRA_IER 0x0A /* Input Interrupt Enable */ +#define XRA_TSCR 0x0C /* Output Three-State Control */ +#define XRA_ISR 0x0E /* Input Interrupt Status */ +#define XRA_REIR 0x10 /* Input Rising Edge Interrupt Enable */ +#define XRA_FEIR 0x12 /* Input Falling Edge Interrupt Enable */ +#define XRA_IFR 0x14 /* Input Filter Enable/Disable */ + +struct xra1403 { + struct gpio_chip chip; + struct regmap *regmap; +}; + +static const struct regmap_config xra1403_regmap_cfg = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + + .max_register = XRA_IFR | 0x01, +}; + +static unsigned int to_reg(unsigned int reg, unsigned int offset) +{ + return reg + (offset > 7); +} + +static int xra1403_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct xra1403 *xra = gpiochip_get_data(chip); + + return regmap_update_bits(xra->regmap, to_reg(XRA_GCR, offset), + BIT(offset % 8), BIT(offset % 8)); +} + +static int xra1403_direction_output(struct gpio_chip *chip, unsigned int offset, + int value) +{ + int ret; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_update_bits(xra->regmap, to_reg(XRA_GCR, offset), + BIT(offset % 8), 0); + if (ret) + return ret; + + ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), + BIT(offset % 8), value ? BIT(offset % 8) : 0); + + return ret; +} + +static int xra1403_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + unsigned int val; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_read(xra->regmap, to_reg(XRA_GCR, offset), &val); + if (ret) + return ret; + + return !!(val & BIT(offset % 8)); +} + +static int xra1403_get(struct gpio_chip *chip, unsigned int offset) +{ + int ret; + unsigned int val; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_read(xra->regmap, to_reg(XRA_GSR, offset), &val); + if (ret) + return ret; + + return !!(val & BIT(offset % 8)); +} + +static void xra1403_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + int ret; + struct xra1403 *xra = gpiochip_get_data(chip); + + ret = regmap_update_bits(xra->regmap, to_reg(XRA_OCR, offset), + BIT(offset % 8), value ? BIT(offset % 8) : 0); + if (ret) + dev_err(chip->parent, "Failed to set pin: %d, ret: %d\n", + offset, ret); +} + +#ifdef CONFIG_DEBUG_FS +static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + int reg; + struct xra1403 *xra = gpiochip_get_data(chip); + int value[xra1403_regmap_cfg.max_register]; + int i; + unsigned int gcr; + unsigned int gsr; + + seq_puts(s, "xra reg:"); + for (reg = 0; reg <= xra1403_regmap_cfg.max_register; reg++) + seq_printf(s, " %2.2x", reg); + seq_puts(s, "\n value:"); + for (reg = 0; reg < xra1403_regmap_cfg.max_register; reg++) { + regmap_read(xra->regmap, reg, &value[reg]); + seq_printf(s, " %2.2x", value[reg]); + } + seq_puts(s, "\n"); + + gcr = value[XRA_GCR + 1] << 8 | value[XRA_GCR]; + gsr = value[XRA_GSR + 1] << 8 | value[XRA_GSR]; + for (i = 0; i < chip->ngpio; i++) { + const char *label = gpiochip_is_requested(chip, i); + + if (!label) + continue; + + seq_printf(s, " gpio-%-3d (%-12s) %s %s\n", + chip->base + i, label, + (gcr & BIT(i)) ? "in" : "out", + (gsr & BIT(i)) ? "hi" : "lo"); + } +} +#else +#define xra1403_dbg_show NULL +#endif + +static int xra1403_probe(struct spi_device *spi) +{ + struct xra1403 *xra; + struct gpio_desc *reset_gpio; + int ret; + + xra = devm_kzalloc(&spi->dev, sizeof(*xra), GFP_KERNEL); + if (!xra) + return -ENOMEM; + + /* bring the chip out of reset if reset pin is provided*/ + reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + dev_warn(&spi->dev, "Could not get reset-gpios\n"); + + xra->chip.direction_input = xra1403_direction_input; + xra->chip.direction_output = xra1403_direction_output; + xra->chip.get_direction = xra1403_get_direction; + xra->chip.get = xra1403_get; + xra->chip.set = xra1403_set; + + xra->chip.dbg_show = xra1403_dbg_show; + + xra->chip.ngpio = 16; + xra->chip.label = "xra1403"; + + xra->chip.base = -1; + xra->chip.can_sleep = true; + xra->chip.parent = &spi->dev; + xra->chip.owner = THIS_MODULE; + + xra->regmap = devm_regmap_init_spi(spi, &xra1403_regmap_cfg); + if (IS_ERR(xra->regmap)) { + ret = PTR_ERR(xra->regmap); + dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + ret = devm_gpiochip_add_data(&spi->dev, &xra->chip, xra); + if (ret < 0) { + dev_err(&spi->dev, "Unable to register gpiochip\n"); + return ret; + } + + spi_set_drvdata(spi, xra); + + return 0; +} + +static const struct spi_device_id xra1403_ids[] = { + { "xra1403" }, + {}, +}; +MODULE_DEVICE_TABLE(spi, xra1403_ids); + +static const struct of_device_id xra1403_spi_of_match[] = { + { .compatible = "exar,xra1403" }, + {}, +}; +MODULE_DEVICE_TABLE(of, xra1403_spi_of_match); + +static struct spi_driver xra1403_driver = { + .probe = xra1403_probe, + .id_table = xra1403_ids, + .driver = { + .name = "xra1403", + .of_match_table = of_match_ptr(xra1403_spi_of_match), + }, +}; + +module_spi_driver(xra1403_driver); + +MODULE_AUTHOR("Nandor Han "); +MODULE_AUTHOR("Semi Malinen "); +MODULE_DESCRIPTION("GPIO expander driver for EXAR XRA1403"); +MODULE_LICENSE("GPL v2"); From 39d2675a8bd48362577dfbe84b7b66024dd5176e Mon Sep 17 00:00:00 2001 From: Nandor Han Date: Mon, 15 May 2017 08:58:26 +0300 Subject: [PATCH 05/70] gpio: xra1403: Add XRA1403 support to MAINTAINERS file Add XRA1403 support to MAINTAINERS list. Signed-off-by: Nandor Han Signed-off-by: Linus Walleij --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9e984645c4b0..67b192be4825 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14075,6 +14075,14 @@ L: linux-kernel@vger.kernel.org S: Supported F: drivers/char/xillybus/ +XRA1403 GPIO EXPANDER +M: Nandor Han +M: Semi Malinen +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-xra1403.c +F: Documentation/devicetree/bindings/gpio/gpio-xra1403.txt + XTENSA XTFPGA PLATFORM SUPPORT M: Max Filippov L: linux-xtensa@linux-xtensa.org From 6ec015d6130283fe63bd2f374a34c44d23442286 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 19 May 2017 18:09:20 +0200 Subject: [PATCH 06/70] gpio: mvebu: sort header include This commit sorts alphabetically the header files. Reviewed-by: Thomas Petazzoni Signed-off-by: Gregory CLEMENT Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mvebu.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 19a92efabbef..a9e564f3410b 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -33,21 +33,21 @@ * interrupts. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "gpiolib.h" From 2233bf7a92e784f20d1a4a1d39438dcf51e75161 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 19 May 2017 18:09:21 +0200 Subject: [PATCH 07/70] gpio: mvebu: switch to regmap for register access In order to be able to use this driver with the Armada 7K/8K SoCs, we need to use the regmap to access the registers. Indeed for these new SoCs, the gpio node will be part of a syscon. [gregory.clement@free-electrons.com: - fixed merge conflcit from 4.10 to 4.12-rc1 - added a commit log] Signed-off-by: Thomas Petazzoni Signed-off-by: Gregory CLEMENT Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mvebu.c | 438 +++++++++++++++++++++----------------- 1 file changed, 246 insertions(+), 192 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index a9e564f3410b..3d03740a20e7 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include "gpiolib.h" @@ -106,9 +107,8 @@ struct mvebu_pwm { struct mvebu_gpio_chip { struct gpio_chip chip; - spinlock_t lock; - void __iomem *membase; - void __iomem *percpu_membase; + struct regmap *regs; + struct regmap *percpu_regs; int irqbase; struct irq_domain *domain; int soc_variant; @@ -130,92 +130,149 @@ struct mvebu_gpio_chip { * Functions returning addresses of individual registers for a given * GPIO controller. */ -static void __iomem *mvebu_gpioreg_out(struct mvebu_gpio_chip *mvchip) -{ - return mvchip->membase + GPIO_OUT_OFF; -} -static void __iomem *mvebu_gpioreg_blink(struct mvebu_gpio_chip *mvchip) -{ - return mvchip->membase + GPIO_BLINK_EN_OFF; -} - -static void __iomem *mvebu_gpioreg_blink_counter_select(struct mvebu_gpio_chip - *mvchip) -{ - return mvchip->membase + GPIO_BLINK_CNT_SELECT_OFF; -} - -static void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip) -{ - return mvchip->membase + GPIO_IO_CONF_OFF; -} - -static void __iomem *mvebu_gpioreg_in_pol(struct mvebu_gpio_chip *mvchip) -{ - return mvchip->membase + GPIO_IN_POL_OFF; -} - -static void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip) -{ - return mvchip->membase + GPIO_DATA_IN_OFF; -} - -static void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) +static void mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip, + struct regmap **map, unsigned int *offset) { int cpu; switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: case MVEBU_GPIO_SOC_VARIANT_MV78200: - return mvchip->membase + GPIO_EDGE_CAUSE_OFF; + *map = mvchip->regs; + *offset = GPIO_EDGE_CAUSE_OFF; + break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + - GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu); + *map = mvchip->percpu_regs; + *offset = GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu); + break; default: BUG(); } } -static void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip) +static u32 +mvebu_gpio_read_edge_cause(struct mvebu_gpio_chip *mvchip) +{ + struct regmap *map; + unsigned int offset; + u32 val; + + mvebu_gpioreg_edge_cause(mvchip, &map, &offset); + regmap_read(map, offset, &val); + + return val; +} + +static void +mvebu_gpio_write_edge_cause(struct mvebu_gpio_chip *mvchip, u32 val) +{ + struct regmap *map; + unsigned int offset; + + mvebu_gpioreg_edge_cause(mvchip, &map, &offset); + regmap_write(map, offset, val); +} + +static inline void +mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip, + struct regmap **map, unsigned int *offset) { int cpu; switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - return mvchip->membase + GPIO_EDGE_MASK_OFF; + *map = mvchip->regs; + *offset = GPIO_EDGE_MASK_OFF; + break; case MVEBU_GPIO_SOC_VARIANT_MV78200: cpu = smp_processor_id(); - return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu); + *map = mvchip->regs; + *offset = GPIO_EDGE_MASK_MV78200_OFF(cpu); + break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(cpu); + *map = mvchip->percpu_regs; + *offset = GPIO_EDGE_MASK_ARMADAXP_OFF(cpu); + break; default: BUG(); } } -static void __iomem *mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip) +static u32 +mvebu_gpio_read_edge_mask(struct mvebu_gpio_chip *mvchip) +{ + struct regmap *map; + unsigned int offset; + u32 val; + + mvebu_gpioreg_edge_mask(mvchip, &map, &offset); + regmap_read(map, offset, &val); + + return val; +} + +static void +mvebu_gpio_write_edge_mask(struct mvebu_gpio_chip *mvchip, u32 val) +{ + struct regmap *map; + unsigned int offset; + + mvebu_gpioreg_edge_mask(mvchip, &map, &offset); + regmap_write(map, offset, val); +} + +static void +mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip, + struct regmap **map, unsigned int *offset) { int cpu; switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - return mvchip->membase + GPIO_LEVEL_MASK_OFF; + *map = mvchip->regs; + *offset = GPIO_LEVEL_MASK_OFF; + break; case MVEBU_GPIO_SOC_VARIANT_MV78200: cpu = smp_processor_id(); - return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu); + *map = mvchip->regs; + *offset = GPIO_LEVEL_MASK_MV78200_OFF(cpu); + break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu); + *map = mvchip->percpu_regs; + *offset = GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu); + break; default: BUG(); } } +static u32 +mvebu_gpio_read_level_mask(struct mvebu_gpio_chip *mvchip) +{ + struct regmap *map; + unsigned int offset; + u32 val; + + mvebu_gpioreg_level_mask(mvchip, &map, &offset); + regmap_read(map, offset, &val); + + return val; +} + +static void +mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val) +{ + struct regmap *map; + unsigned int offset; + + mvebu_gpioreg_level_mask(mvchip, &map, &offset); + regmap_write(map, offset, val); +} + /* * Functions returning addresses of individual registers for a given * PWM controller. @@ -236,17 +293,9 @@ static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; - u32 u; - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_out(mvchip)); - if (value) - u |= BIT(pin); - else - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_out(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_OUT_OFF, + BIT(pin), value ? BIT(pin) : 0); } static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin) @@ -254,11 +303,16 @@ static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin) struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); u32 u; - if (readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin)) { - u = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) ^ - readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &u); + + if (u & BIT(pin)) { + u32 data_in, in_pol; + + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol); + u = data_in ^ in_pol; } else { - u = readl_relaxed(mvebu_gpioreg_out(mvchip)); + regmap_read(mvchip->regs, GPIO_OUT_OFF, &u); } return (u >> pin) & 1; @@ -268,25 +322,15 @@ static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; - u32 u; - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_blink(mvchip)); - if (value) - u |= BIT(pin); - else - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_blink(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF, + BIT(pin), value ? BIT(pin) : 0); } static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; int ret; - u32 u; /* * Check with the pinctrl driver whether this pin is usable as @@ -296,11 +340,8 @@ static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) if (ret) return ret; - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - u |= BIT(pin); - writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF, + BIT(pin), 1); return 0; } @@ -309,9 +350,7 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - unsigned long flags; int ret; - u32 u; /* * Check with the pinctrl driver whether this pin is usable as @@ -324,11 +363,8 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, mvebu_gpio_blink(chip, pin, 0); mvebu_gpio_set(chip, pin, value); - spin_lock_irqsave(&mvchip->lock, flags); - u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_io_conf(mvchip)); - spin_unlock_irqrestore(&mvchip->lock, flags); + regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF, + BIT(pin), 0); return 0; } @@ -350,7 +386,7 @@ static void mvebu_gpio_irq_ack(struct irq_data *d) u32 mask = d->mask; irq_gc_lock(gc); - writel_relaxed(~mask, mvebu_gpioreg_edge_cause(mvchip)); + mvebu_gpio_write_edge_cause(mvchip, ~mask); irq_gc_unlock(gc); } @@ -363,8 +399,7 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv &= ~mask; - - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); + mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -377,7 +412,7 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv |= mask; - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); + mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -390,7 +425,7 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv &= ~mask; - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); + mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -403,7 +438,7 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d) irq_gc_lock(gc); ct->mask_cache_priv |= mask; - writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); + mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); irq_gc_unlock(gc); } @@ -443,8 +478,8 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) pin = d->hwirq; - u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & BIT(pin); - if (!u) + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &u); + if ((u & BIT(pin)) == 0) return -EINVAL; type &= IRQ_TYPE_SENSE_MASK; @@ -462,31 +497,30 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) switch (type) { case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: - u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - u &= ~BIT(pin); - writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF, + BIT(pin), 0); break; case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: - u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - u |= BIT(pin); - writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF, + BIT(pin), 1); break; case IRQ_TYPE_EDGE_BOTH: { - u32 v; + u32 data_in, in_pol, val; - v = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)) ^ - readl_relaxed(mvebu_gpioreg_data_in(mvchip)); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); /* * set initial polarity based on current input level */ - u = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - if (v & BIT(pin)) - u |= BIT(pin); /* falling */ + if ((data_in ^ in_pol) & BIT(pin)) + val = BIT(pin); /* falling */ else - u &= ~BIT(pin); /* rising */ - writel_relaxed(u, mvebu_gpioreg_in_pol(mvchip)); + val = 0; /* raising */ + + regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF, + BIT(pin), val); break; } } @@ -497,7 +531,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) { struct mvebu_gpio_chip *mvchip = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); - u32 cause, type; + u32 cause, type, data_in, level_mask, edge_cause, edge_mask; int i; if (mvchip == NULL) @@ -505,10 +539,12 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) chained_irq_enter(chip, desc); - cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) & - readl_relaxed(mvebu_gpioreg_level_mask(mvchip)); - cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) & - readl_relaxed(mvebu_gpioreg_edge_mask(mvchip)); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); + level_mask = mvebu_gpio_read_level_mask(mvchip); + edge_cause = mvebu_gpio_read_edge_cause(mvchip); + edge_mask = mvebu_gpio_read_edge_mask(mvchip); + + cause = (data_in ^ level_mask) | (edge_cause & edge_mask); for (i = 0; i < mvchip->chip.ngpio; i++) { int irq; @@ -523,9 +559,9 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) /* Swap polarity (race with GPIO line) */ u32 polarity; - polarity = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &polarity); polarity ^= BIT(i); - writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip)); + regmap_write(mvchip->regs, GPIO_IN_POL_OFF, polarity); } generic_handle_irq(irq); @@ -628,7 +664,7 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, state->period = 1; } - u = readl_relaxed(mvebu_gpioreg_blink(mvchip)); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &u); if (u) state->enabled = true; else @@ -691,8 +727,8 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) { struct mvebu_pwm *mvpwm = mvchip->mvpwm; - mvpwm->blink_select = - readl_relaxed(mvebu_gpioreg_blink_counter_select(mvchip)); + regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF, + &mvpwm->blink_select); mvpwm->blink_on_duration = readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); mvpwm->blink_off_duration = @@ -703,8 +739,8 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) { struct mvebu_pwm *mvpwm = mvchip->mvpwm; - writel_relaxed(mvpwm->blink_select, - mvebu_gpioreg_blink_counter_select(mvchip)); + regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF, + mvpwm->blink_select); writel_relaxed(mvpwm->blink_on_duration, mvebu_pwmreg_blink_on_duration(mvpwm)); writel_relaxed(mvpwm->blink_off_duration, @@ -747,7 +783,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev, set = U32_MAX; else return -EINVAL; - writel_relaxed(0, mvebu_gpioreg_blink_counter_select(mvchip)); + regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF, 0); mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); if (!mvpwm) @@ -783,14 +819,14 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; int i; - out = readl_relaxed(mvebu_gpioreg_out(mvchip)); - io_conf = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)); - blink = readl_relaxed(mvebu_gpioreg_blink(mvchip)); - in_pol = readl_relaxed(mvebu_gpioreg_in_pol(mvchip)); - data_in = readl_relaxed(mvebu_gpioreg_data_in(mvchip)); - cause = readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)); - edg_msk = readl_relaxed(mvebu_gpioreg_edge_mask(mvchip)); - lvl_msk = readl_relaxed(mvebu_gpioreg_level_mask(mvchip)); + regmap_read(mvchip->regs, GPIO_OUT_OFF, &out); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &io_conf); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &blink); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); + cause = mvebu_gpio_read_edge_cause(mvchip); + edg_msk = mvebu_gpio_read_edge_mask(mvchip); + lvl_msk = mvebu_gpio_read_level_mask(mvchip); for (i = 0; i < chip->ngpio; i++) { const char *label; @@ -858,36 +894,36 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); int i; - mvchip->out_reg = readl(mvebu_gpioreg_out(mvchip)); - mvchip->io_conf_reg = readl(mvebu_gpioreg_io_conf(mvchip)); - mvchip->blink_en_reg = readl(mvebu_gpioreg_blink(mvchip)); - mvchip->in_pol_reg = readl(mvebu_gpioreg_in_pol(mvchip)); + regmap_read(mvchip->regs, GPIO_OUT_OFF, &mvchip->out_reg); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &mvchip->io_conf_reg); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &mvchip->blink_en_reg); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &mvchip->in_pol_reg); switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - mvchip->edge_mask_regs[0] = - readl(mvchip->membase + GPIO_EDGE_MASK_OFF); - mvchip->level_mask_regs[0] = - readl(mvchip->membase + GPIO_LEVEL_MASK_OFF); + regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF, + &mvchip->edge_mask_regs[0]); + regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF, + &mvchip->level_mask_regs[0]); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: for (i = 0; i < 2; i++) { - mvchip->edge_mask_regs[i] = - readl(mvchip->membase + - GPIO_EDGE_MASK_MV78200_OFF(i)); - mvchip->level_mask_regs[i] = - readl(mvchip->membase + - GPIO_LEVEL_MASK_MV78200_OFF(i)); + regmap_read(mvchip->regs, + GPIO_EDGE_MASK_MV78200_OFF(i), + &mvchip->edge_mask_regs[i]); + regmap_read(mvchip->regs, + GPIO_LEVEL_MASK_MV78200_OFF(i), + &mvchip->level_mask_regs[i]); } break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: for (i = 0; i < 4; i++) { - mvchip->edge_mask_regs[i] = - readl(mvchip->membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(i)); - mvchip->level_mask_regs[i] = - readl(mvchip->membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + regmap_read(mvchip->regs, + GPIO_EDGE_MASK_ARMADAXP_OFF(i), + &mvchip->edge_mask_regs[i]); + regmap_read(mvchip->regs, + GPIO_LEVEL_MASK_ARMADAXP_OFF(i), + &mvchip->level_mask_regs[i]); } break; default: @@ -905,35 +941,36 @@ static int mvebu_gpio_resume(struct platform_device *pdev) struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); int i; - writel(mvchip->out_reg, mvebu_gpioreg_out(mvchip)); - writel(mvchip->io_conf_reg, mvebu_gpioreg_io_conf(mvchip)); - writel(mvchip->blink_en_reg, mvebu_gpioreg_blink(mvchip)); - writel(mvchip->in_pol_reg, mvebu_gpioreg_in_pol(mvchip)); + regmap_write(mvchip->regs, GPIO_OUT_OFF, mvchip->out_reg); + regmap_write(mvchip->regs, GPIO_IO_CONF_OFF, mvchip->io_conf_reg); + regmap_write(mvchip->regs, GPIO_BLINK_EN_OFF, mvchip->blink_en_reg); + regmap_write(mvchip->regs, GPIO_IN_POL_OFF, mvchip->in_pol_reg); switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - writel(mvchip->edge_mask_regs[0], - mvchip->membase + GPIO_EDGE_MASK_OFF); - writel(mvchip->level_mask_regs[0], - mvchip->membase + GPIO_LEVEL_MASK_OFF); + regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, + mvchip->edge_mask_regs[0]); + regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, + mvchip->level_mask_regs[0]); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: for (i = 0; i < 2; i++) { - writel(mvchip->edge_mask_regs[i], - mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(i)); - writel(mvchip->level_mask_regs[i], - mvchip->membase + - GPIO_LEVEL_MASK_MV78200_OFF(i)); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_MV78200_OFF(i), + mvchip->edge_mask_regs[i]); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_MV78200_OFF(i), + mvchip->level_mask_regs[i]); } break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: for (i = 0; i < 4; i++) { - writel(mvchip->edge_mask_regs[i], - mvchip->membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(i)); - writel(mvchip->level_mask_regs[i], - mvchip->membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_ARMADAXP_OFF(i), + mvchip->edge_mask_regs[i]); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_ARMADAXP_OFF(i), + mvchip->level_mask_regs[i]); } break; default: @@ -946,6 +983,13 @@ static int mvebu_gpio_resume(struct platform_device *pdev) return 0; } +static const struct regmap_config mvebu_gpio_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + static int mvebu_gpio_probe(struct platform_device *pdev) { struct mvebu_gpio_chip *mvchip; @@ -954,6 +998,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) struct resource *res; struct irq_chip_generic *gc; struct irq_chip_type *ct; + void __iomem *base; unsigned int ngpios; bool have_irqs; int soc_variant; @@ -1009,11 +1054,15 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.of_node = np; mvchip->chip.dbg_show = mvebu_gpio_dbg_show; - spin_lock_init(&mvchip->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mvchip->membase = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mvchip->membase)) - return PTR_ERR(mvchip->membase); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mvchip->regs = devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvchip->regs)) + return PTR_ERR(mvchip->regs); /* * The Armada XP has a second range of registers for the @@ -1021,10 +1070,15 @@ static int mvebu_gpio_probe(struct platform_device *pdev) */ if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) { res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - mvchip->percpu_membase = devm_ioremap_resource(&pdev->dev, - res); - if (IS_ERR(mvchip->percpu_membase)) - return PTR_ERR(mvchip->percpu_membase); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mvchip->percpu_regs = + devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvchip->percpu_regs)) + return PTR_ERR(mvchip->percpu_regs); } /* @@ -1032,30 +1086,30 @@ static int mvebu_gpio_probe(struct platform_device *pdev) */ switch (soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); - writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF); - writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF); + regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0); + regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, 0); + regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, 0); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: - writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); + regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0); for (cpu = 0; cpu < 2; cpu++) { - writel_relaxed(0, mvchip->membase + - GPIO_EDGE_MASK_MV78200_OFF(cpu)); - writel_relaxed(0, mvchip->membase + - GPIO_LEVEL_MASK_MV78200_OFF(cpu)); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_MV78200_OFF(cpu), 0); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_MV78200_OFF(cpu), 0); } break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: - writel_relaxed(0, mvchip->membase + GPIO_EDGE_CAUSE_OFF); - writel_relaxed(0, mvchip->membase + GPIO_EDGE_MASK_OFF); - writel_relaxed(0, mvchip->membase + GPIO_LEVEL_MASK_OFF); + regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0); + regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, 0); + regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, 0); for (cpu = 0; cpu < 4; cpu++) { - writel_relaxed(0, mvchip->percpu_membase + - GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu)); - writel_relaxed(0, mvchip->percpu_membase + - GPIO_EDGE_MASK_ARMADAXP_OFF(cpu)); - writel_relaxed(0, mvchip->percpu_membase + - GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu)); + regmap_write(mvchip->percpu_regs, + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu), 0); + regmap_write(mvchip->percpu_regs, + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu), 0); + regmap_write(mvchip->percpu_regs, + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu), 0); } break; default: From 3b4c94bbacb06aa7ff595041efec82604e44fc59 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 19 May 2017 18:09:23 +0200 Subject: [PATCH 08/70] gpio: mvebu: allow building driver for Armada 7K/8K The mvebu gpio driver can also be used on arm64 mvebu SoC such as the Armada 7K/8K. This commit allows to build the driver for them (when only ARCH_MVEBU is defined) Reviewed-by: Thomas Petazzoni Signed-off-by: Gregory CLEMENT Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 75e64607b209..805754401845 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -326,7 +326,7 @@ config GPIO_MPC8XXX config GPIO_MVEBU def_bool y - depends on PLAT_ORION + depends on PLAT_ORION || ARCH_MVEBU depends on OF_GPIO select GENERIC_IRQ_CHIP From 0a848d638a25b4f2767b260ed83c271854e93cce Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 May 2017 23:57:25 +0200 Subject: [PATCH 09/70] gpio: max732x: move header file out of I2C realm include/linux/i2c is not for client devices. Move the header file to a more appropriate location. Signed-off-by: Wolfram Sang Signed-off-by: Linus Walleij --- arch/arm/mach-pxa/littleton.c | 2 +- drivers/gpio/gpio-max732x.c | 2 +- include/linux/{i2c => platform_data}/max732x.h | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename include/linux/{i2c => platform_data}/max732x.h (100%) diff --git a/arch/arm/mach-pxa/littleton.c b/arch/arm/mach-pxa/littleton.c index 051c554776a6..fae38fdc8d8e 100644 --- a/arch/arm/mach-pxa/littleton.c +++ b/arch/arm/mach-pxa/littleton.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 4ea4c6a1313b..7f4d26ce5f23 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include diff --git a/include/linux/i2c/max732x.h b/include/linux/platform_data/max732x.h similarity index 100% rename from include/linux/i2c/max732x.h rename to include/linux/platform_data/max732x.h From b6480faeee234829b315168aebcb281ecf95f178 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 May 2017 23:57:26 +0200 Subject: [PATCH 10/70] gpio: pcf857x: move header file out of I2C realm include/linux/i2c is not for client devices. Move the header file to a more appropriate location. Signed-off-by: Wolfram Sang Signed-off-by: Linus Walleij --- arch/arm/mach-davinci/board-da830-evm.c | 2 +- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/board-dm646x-evm.c | 2 +- arch/arm/mach-pxa/balloon3.c | 2 +- arch/arm/mach-pxa/stargate2.c | 2 +- arch/mips/ath79/mach-pb44.c | 2 +- drivers/gpio/gpio-pcf857x.c | 2 +- include/linux/{i2c => platform_data}/pcf857x.h | 0 8 files changed, 7 insertions(+), 7 deletions(-) rename include/linux/{i2c => platform_data}/pcf857x.h (100%) diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index 58075627c6df..f673cd7a6766 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 20f1874a5657..70e00dbeec96 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index cb176826d1cb..ca69d0b96a4f 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-pxa/balloon3.c b/arch/arm/mach-pxa/balloon3.c index d452a49c0396..1467c1d1e541 100644 --- a/arch/arm/mach-pxa/balloon3.c +++ b/arch/arm/mach-pxa/balloon3.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c index 7b6610e9dae4..2d45d18b1a5e 100644 --- a/arch/arm/mach-pxa/stargate2.c +++ b/arch/arm/mach-pxa/stargate2.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/mips/ath79/mach-pb44.c b/arch/mips/ath79/mach-pb44.c index 67b980d94fb7..be78298dffb4 100644 --- a/arch/mips/ath79/mach-pb44.c +++ b/arch/mips/ath79/mach-pb44.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include "machtypes.h" #include "dev-gpio-buttons.h" diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 8ddf9302ce3b..a4fd78b9c0e4 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include #include diff --git a/include/linux/i2c/pcf857x.h b/include/linux/platform_data/pcf857x.h similarity index 100% rename from include/linux/i2c/pcf857x.h rename to include/linux/platform_data/pcf857x.h From 240d3d5b2a7a32630f1fadd6b145d48978882824 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Mon, 22 May 2017 08:58:31 +0000 Subject: [PATCH 11/70] gpio: xlp: update GPIO_XLP dependency Broadcom Vulcan (ARCH_VULCAN) has been discontinued and will be deleted soon. So, update the GPIO_XLP Kconfig entry to remove the ARCH_VULCAN dependency. Also update the documentation to note that Cavium ThunderX2 uses this driver. Signed-off-by: Jayachandran C Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 805754401845..f5e1b3d8baed 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -504,12 +504,13 @@ config GPIO_XILINX config GPIO_XLP tristate "Netlogic XLP GPIO support" - depends on OF_GPIO && (CPU_XLP || ARCH_VULCAN || ARCH_THUNDER2 || COMPILE_TEST) + depends on OF_GPIO && (CPU_XLP || ARCH_THUNDER2 || COMPILE_TEST) select GPIOLIB_IRQCHIP help This driver provides support for GPIO interface on Netlogic XLP MIPS64 SoCs. Currently supported XLP variants are XLP8XX, XLP3XX, XLP2XX, - XLP9XX and XLP5XX. + XLP9XX and XLP5XX. The same GPIO controller block is also present in + Cavium's ThunderX2 CN99XX SoCs. If unsure, say N. From f37e335f925968942c6f613fa17c57f625042f87 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Wed, 24 May 2017 16:32:06 +0200 Subject: [PATCH 12/70] gpio: mvebu: Select REGMAP_MMIO now that regmap is used Since the commit "gpio: mvebu: switch to regmap for register access" the driver use the regmap. Explicitly select the REGMAP_MMIO symbol to fix build error. Reported-by: kbuild test robot Signed-off-by: Gregory CLEMENT Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f5e1b3d8baed..89e805fc5eba 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -329,6 +329,7 @@ config GPIO_MVEBU depends on PLAT_ORION || ARCH_MVEBU depends on OF_GPIO select GENERIC_IRQ_CHIP + select REGMAP_MMIO config GPIO_MXC def_bool y From 6dc0048cff988858254fcc26becfc1e9753efa79 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Tue, 23 May 2017 14:48:57 +0530 Subject: [PATCH 13/70] gpio: davinci: Handle return value of clk_prepare_enable clk_prepare_enable() can fail here and we must check its return value. Signed-off-by: Arvind Yadav Signed-off-by: Linus Walleij --- drivers/gpio/gpio-davinci.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index ac173575d3f6..65cb359308e3 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -437,6 +437,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) { unsigned gpio, bank; int irq; + int ret; struct clk *clk; u32 binten = 0; unsigned ngpio, bank_irq; @@ -480,12 +481,15 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) PTR_ERR(clk)); return PTR_ERR(clk); } - clk_prepare_enable(clk); + ret = clk_prepare_enable(clk); + if (ret) + return ret; if (!pdata->gpio_unbanked) { irq = devm_irq_alloc_descs(dev, -1, 0, ngpio, 0); if (irq < 0) { dev_err(dev, "Couldn't allocate IRQ numbers\n"); + clk_disable_unprepare(clk); return irq; } @@ -494,6 +498,7 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) chips); if (!irq_domain) { dev_err(dev, "Couldn't register an IRQ domain\n"); + clk_disable_unprepare(clk); return -ENODEV; } } @@ -562,8 +567,10 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) sizeof(struct davinci_gpio_irq_data), GFP_KERNEL); - if (!irqdata) + if (!irqdata) { + clk_disable_unprepare(clk); return -ENOMEM; + } irqdata->regs = g; irqdata->bank_num = bank; From f9e3a419aa53d77a9ba348f587775aea726fcafa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 May 2017 15:47:28 +0100 Subject: [PATCH 14/70] gpio: of: Reflect decoupling of open collector and active low/high Commit 4c0facddb7d8 ("gpio: core: Decouple open drain/source flag with active low/high") decoupled the open collector outputs from active low/high but did not update the documentation. Update the device tree documentation to correctly reflect this new separation between the two concepts. Signed-off-by: Charles Keepax Acked-by: Laxman Dewangan Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index 2209c163f2a5..bccb862398db 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -74,11 +74,12 @@ GPIO pin number, and GPIO flags as accepted by the "qe_pio_e" gpio-controller. Optional standard bitfield specifiers for the last cell: - Bit 0: 0 means active high, 1 means active low -- Bit 1: 1 means single-ended wiring, see: +- Bit 1: 0 mean push-pull wiring, see: + https://en.wikipedia.org/wiki/Push-pull_output + 1 means single-ended wiring, see: https://en.wikipedia.org/wiki/Single-ended_triode - When used with active-low, this means open drain/collector, see: +- Bit 2: 0 means open-source, 1 means open drain, see: https://en.wikipedia.org/wiki/Open_collector - When used with active-high, this means open source/emitter 1.1) GPIO specifier best practices ---------------------------------- From 05f479bf7d239f01ff6546f2bdeb14ad0fe65601 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 May 2017 15:47:29 +0100 Subject: [PATCH 15/70] gpio: Add new flags to control sleep status of GPIOs Add new flags to allow users to specify that they are not concerned with the status of GPIOs whilst in a sleep/low power state. Signed-off-by: Charles Keepax Acked-by: Rob Herring Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 3 +++ drivers/gpio/gpiolib.c | 12 ++++++++++++ drivers/gpio/gpiolib.h | 1 + include/dt-bindings/gpio/gpio.h | 4 ++++ include/linux/gpio/driver.h | 3 +++ include/linux/gpio/machine.h | 2 ++ include/linux/of_gpio.h | 1 + 7 files changed, 26 insertions(+) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index b13b7c7c335f..e2abf0eabaf8 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -153,6 +153,9 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *flags |= GPIO_OPEN_SOURCE; } + if (of_flags & OF_GPIO_SLEEP_MAY_LOOSE_VALUE) + *flags |= GPIO_SLEEP_MAY_LOOSE_VALUE; + return desc; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 995ca9cf7dbf..c81c42093ff6 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2869,6 +2869,16 @@ bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset) } EXPORT_SYMBOL_GPL(gpiochip_line_is_open_source); +bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset) +{ + if (offset >= chip->ngpio) + return false; + + return !test_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, + &chip->gpiodev->descs[offset].flags); +} +EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); + /** * gpiod_get_raw_value_cansleep() - return a gpio's raw value * @desc: gpio whose value will be returned @@ -3225,6 +3235,8 @@ static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if (lflags & GPIO_SLEEP_MAY_LOOSE_VALUE) + set_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, &desc->flags); /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 2495b7ee1b42..f74b8a763242 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -190,6 +190,7 @@ struct gpio_desc { #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ +#define FLAG_SLEEP_MAY_LOOSE_VALUE 12 /* GPIO may loose value in sleep */ /* Connection label */ const char *label; diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h index b4f54da694eb..c5074584561d 100644 --- a/include/dt-bindings/gpio/gpio.h +++ b/include/dt-bindings/gpio/gpio.h @@ -28,4 +28,8 @@ #define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) #define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE) +/* Bit 3 express GPIO suspend/resume persistence */ +#define GPIO_SLEEP_MAINTAIN_VALUE 0 +#define GPIO_SLEEP_MAY_LOOSE_VALUE 8 + #endif diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 393582867afd..af20369ec8e7 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -213,6 +213,9 @@ bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset); bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset); bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset); +/* Sleep persistence inquiry for drivers */ +bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset); + /* get driver data */ void *gpiochip_get_data(struct gpio_chip *chip); diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index c0d712d22b07..13adadf53c09 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -9,6 +9,8 @@ enum gpio_lookup_flags { GPIO_ACTIVE_LOW = (1 << 0), GPIO_OPEN_DRAIN = (1 << 1), GPIO_OPEN_SOURCE = (1 << 2), + GPIO_SLEEP_MAINTAIN_VALUE = (0 << 3), + GPIO_SLEEP_MAY_LOOSE_VALUE = (1 << 3), }; /** diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 1e089d5a182b..ca10f43564de 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -31,6 +31,7 @@ enum of_gpio_flags { OF_GPIO_ACTIVE_LOW = 0x1, OF_GPIO_SINGLE_ENDED = 0x2, OF_GPIO_OPEN_DRAIN = 0x4, + OF_GPIO_SLEEP_MAY_LOOSE_VALUE = 0x8, }; #ifdef CONFIG_OF_GPIO From 27a49ed17e2245cacbdea88ad78b8494653e0338 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 May 2017 15:47:30 +0100 Subject: [PATCH 16/70] gpio: arizona: Add support for GPIOs that need to be maintained The Arizona devices only maintain the state of output GPIOs whilst the CODEC is active, this can cause issues if the CODEC suspends whilst something is relying on the state of one of its GPIOs. However, in many systems the CODEC GPIOs are used for audio related features and thus the state of the GPIOs is unimportant whilst the CODEC is suspended. Often keeping the CODEC resumed in such a system would incur a power impact that is unacceptable. Allow the user to select whether a GPIO output should keep the CODEC resumed, by adding a flag through the second cell of the GPIO specifier in device tree. Signed-off-by: Charles Keepax Signed-off-by: Linus Walleij --- drivers/gpio/gpio-arizona.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index cd23fd727f95..d4e6ba0301bc 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -33,9 +33,23 @@ static int arizona_gpio_direction_in(struct gpio_chip *chip, unsigned offset) { struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; + bool persistent = gpiochip_line_is_persistent(chip, offset); + bool change; + int ret; - return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, - ARIZONA_GPN_DIR, ARIZONA_GPN_DIR); + ret = regmap_update_bits_check(arizona->regmap, + ARIZONA_GPIO1_CTRL + offset, + ARIZONA_GPN_DIR, ARIZONA_GPN_DIR, + &change); + if (ret < 0) + return ret; + + if (change && persistent) { + pm_runtime_mark_last_busy(chip->parent); + pm_runtime_put_autosuspend(chip->parent); + } + + return 0; } static int arizona_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -85,6 +99,21 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip, { struct arizona_gpio *arizona_gpio = gpiochip_get_data(chip); struct arizona *arizona = arizona_gpio->arizona; + bool persistent = gpiochip_line_is_persistent(chip, offset); + unsigned int val; + int ret; + + ret = regmap_read(arizona->regmap, ARIZONA_GPIO1_CTRL + offset, &val); + if (ret < 0) + return ret; + + if ((val & ARIZONA_GPN_DIR) && persistent) { + ret = pm_runtime_get_sync(chip->parent); + if (ret < 0) { + dev_err(chip->parent, "Failed to resume: %d\n", ret); + return ret; + } + } if (value) value = ARIZONA_GPN_LVL; @@ -158,6 +187,8 @@ static int arizona_gpio_probe(struct platform_device *pdev) else arizona_gpio->gpio_chip.base = -1; + pm_runtime_enable(&pdev->dev); + ret = devm_gpiochip_add_data(&pdev->dev, &arizona_gpio->gpio_chip, arizona_gpio); if (ret < 0) { From ca37978497194d55dedd28022403b053ad0d11b2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 23 May 2017 15:47:31 +0100 Subject: [PATCH 17/70] gpio: of: Add documentation of new sleep standard GPIO specifiers Add documentation of new GPIO specifiers indicating if the state of an output pin should be maintained during sleep/low-power mode. Signed-off-by: Charles Keepax Acked-by: Laxman Dewangan Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt index bccb862398db..802402f6cc5d 100644 --- a/Documentation/devicetree/bindings/gpio/gpio.txt +++ b/Documentation/devicetree/bindings/gpio/gpio.txt @@ -80,6 +80,8 @@ Optional standard bitfield specifiers for the last cell: https://en.wikipedia.org/wiki/Single-ended_triode - Bit 2: 0 means open-source, 1 means open drain, see: https://en.wikipedia.org/wiki/Open_collector +- Bit 3: 0 means the output should be maintained during sleep/low-power mode + 1 means the output state can be lost during sleep/low-power mode 1.1) GPIO specifier best practices ---------------------------------- From c29fd9ebb2b9d4bab16dfdce88547f9867ea6153 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:16 +0300 Subject: [PATCH 18/70] gpiolib: Export gpiod_configure_flags() to internal users This is preparatory patch for enabling GPIO ACPI to configure a pin accordingly. Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 +- drivers/gpio/gpiolib.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c81c42093ff6..28629b2b2510 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3224,7 +3224,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_optional); * requested function and/or index, or another IS_ERR() code if an error * occurred while trying to acquire the GPIO. */ -static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, +int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags) { int status; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index f74b8a763242..2aec8be7f444 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -200,6 +200,8 @@ struct gpio_desc { int gpiod_request(struct gpio_desc *desc, const char *label); void gpiod_free(struct gpio_desc *desc); +int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, + unsigned long lflags, enum gpiod_flags dflags); int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags); From 9e66504a919439448dfc57052ddf01b96964bdf7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:17 +0300 Subject: [PATCH 19/70] gpio: acpi: Align acpi_find_gpio() with DT version By some reason acpi_find_gpio() and acpi_gpio_count() have compared connection ID to "gpios" when tries to check if suffix is needed or not. Don't do any assumptions about what connection ID can be and, when defined, use it only with suffix as it's done in the device tree version. Reviewed-by: Dmitry Torokhov Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 2185232da823..055a8a255a40 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -599,7 +599,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, /* Try first from _DSD */ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id && strcmp(con_id, "gpios")) { + if (con_id) { snprintf(propname, sizeof(propname), "%s-%s", con_id, gpio_suffixes[i]); } else { @@ -1089,7 +1089,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id) /* Try first from _DSD */ for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id && strcmp(con_id, "gpios")) + if (con_id) snprintf(propname, sizeof(propname), "%s-%s", con_id, gpio_suffixes[i]); else From fe06b56cbf9fd6392f31ba1c782a3134daef7c80 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:18 +0300 Subject: [PATCH 20/70] gpio: acpi: Do sanity check for GpioInt in acpi_find_gpio() Check that we don't ask for output direction on GpioInt resource in cases with or without _DSD defined. Reviewed-by: Dmitry Torokhov Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 055a8a255a40..28f35a9de86b 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -622,12 +622,12 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info); if (IS_ERR(desc)) return desc; + } - if ((flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH) && - info.gpioint) { - dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); - return ERR_PTR(-ENOENT); - } + if (info.gpioint && + (flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH)) { + dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); + return ERR_PTR(-ENOENT); } if (info.polarity == GPIO_ACTIVE_LOW) From f10e4bf6632b5be11cea875b66ba959833a69258 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:19 +0300 Subject: [PATCH 21/70] gpio: acpi: Even more tighten up ACPI GPIO lookups The commit 10cf4899f8af ("gpiolib: tighten up ACPI legacy gpio lookups") prevents to getting same resource twice if the driver asks twice using different connection ID. But the whole idea of fallback might bring some problems. Imagine the case when we have two versions of BIOS/hardware where in one _DSD is introduced along with GPIO resources, but the other one uses just plain GPIO resource for another purpose Case 1: Device (DEVX) { ... Name (_CRS, ResourceTemplate () { GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly, "\\_SB.GPO0", 0, ResourceConsumer) {15} }) Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { Package () {"some-gpios", Package() {^DEVX, 0, 0, 0 }}, } }) } Case 2: Device (DEVX) { ... Name (_CRS, ResourceTemplate () { GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly, "\\_SB.GPO0", 0, ResourceConsumer) {27} }) } To prevent the possible misconfiguration tighten up even more GPIO ACPI lookups for case without connection ID provided. In the past the issue had been triggered by "use mctrl_gpio helpers" series [1,2]. [1] commit 4ef03d328769 ("tty/serial/8250: use mctrl_gpio helpers") [2] https://patchwork.kernel.org/patch/9283745/ Cc: Dmitry Torokhov Cc: Bastien Nocera Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 28f35a9de86b..0392d8ed332f 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1129,45 +1129,11 @@ int acpi_gpio_count(struct device *dev, const char *con_id) return count ? count : -ENOENT; } -struct acpi_crs_lookup { - struct list_head node; - struct acpi_device *adev; - const char *con_id; -}; - -static DEFINE_MUTEX(acpi_crs_lookup_lock); -static LIST_HEAD(acpi_crs_lookup_list); - bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id) { - struct acpi_crs_lookup *l, *lookup = NULL; - /* Never allow fallback if the device has properties */ if (adev->data.properties || adev->driver_gpios) return false; - mutex_lock(&acpi_crs_lookup_lock); - - list_for_each_entry(l, &acpi_crs_lookup_list, node) { - if (l->adev == adev) { - lookup = l; - break; - } - } - - if (!lookup) { - lookup = kmalloc(sizeof(*lookup), GFP_KERNEL); - if (lookup) { - lookup->adev = adev; - lookup->con_id = kstrdup(con_id, GFP_KERNEL); - list_add_tail(&lookup->node, &acpi_crs_lookup_list); - } - } - - mutex_unlock(&acpi_crs_lookup_lock); - - return lookup && - ((!lookup->con_id && !con_id) || - (lookup->con_id && con_id && - strcmp(lookup->con_id, con_id) == 0)); + return con_id == NULL; } From 6fe9da42f1d98fdb4be1598e230aca97e66cf35d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:20 +0300 Subject: [PATCH 22/70] gpio: acpi: Synchronize acpi_find_gpio() and acpi_gpio_count() If we pass connection ID to the both functions and at the same time acpi_can_fallback_to_crs() returns false we will get different results, i.e. the number of GPIO resources returned by acpi_gpio_count() might be not correct. Fix this by calling acpi_can_fallback_to_crs() in acpi_gpio_count() before trying to fallback. Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 0392d8ed332f..740df0e9dcb3 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1119,6 +1119,9 @@ int acpi_gpio_count(struct device *dev, const char *con_id) struct list_head resource_list; unsigned int crs_count = 0; + if (!acpi_can_fallback_to_crs(adev, con_id)) + return count; + INIT_LIST_HEAD(&resource_list); acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio_count, &crs_count); From ed7fcf1ed5ea4ea01243995ae085757a77cf0f3e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:21 +0300 Subject: [PATCH 23/70] gpio: acpi: Explain how to get GPIO descriptors in ACPI case Documentation lacks of explanation how we actually use device properties for GPIO resources. Add a section to the documentation about that. Suggested-by: Mika Westerberg Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- Documentation/acpi/gpio-properties.txt | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Documentation/acpi/gpio-properties.txt b/Documentation/acpi/gpio-properties.txt index 2aff0349facd..88c65cb5bf0a 100644 --- a/Documentation/acpi/gpio-properties.txt +++ b/Documentation/acpi/gpio-properties.txt @@ -156,3 +156,68 @@ pointed to by its first argument. That should be done in the driver's .probe() routine. On removal, the driver should unregister its GPIO mapping table by calling acpi_dev_remove_driver_gpios() on the ACPI device object where that table was previously registered. + +Using the _CRS fallback +----------------------- + +If a device does not have _DSD or the driver does not create ACPI GPIO +mapping, the Linux GPIO framework refuses to return any GPIOs. This is +because the driver does not know what it actually gets. For example if we +have a device like below: + + Device (BTH) + { + Name (_HID, ...) + + Name (_CRS, ResourceTemplate () { + GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone, + "\\_SB.GPO0", 0, ResourceConsumer) {15} + GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone, + "\\_SB.GPO0", 0, ResourceConsumer) {27} + }) + } + +The driver might expect to get the right GPIO when it does: + + desc = gpiod_get(dev, "reset", GPIOD_OUT_LOW); + +but since there is no way to know the mapping between "reset" and +the GpioIo() in _CRS desc will hold ERR_PTR(-ENOENT). + +The driver author can solve this by passing the mapping explictly +(the recommended way and documented in the above chapter). + +The ACPI GPIO mapping tables should not contaminate drivers that are not +knowing about which exact device they are servicing on. It implies that +the ACPI GPIO mapping tables are hardly linked to ACPI ID and certain +objects, as listed in the above chapter, of the device in question. + +Getting GPIO descriptor +----------------------- + +There are two main approaches to get GPIO resource from ACPI: + desc = gpiod_get(dev, connection_id, flags); + desc = gpiod_get_index(dev, connection_id, index, flags); + +We may consider two different cases here, i.e. when connection ID is +provided and otherwise. + +Case 1: + desc = gpiod_get(dev, "non-null-connection-id", flags); + desc = gpiod_get_index(dev, "non-null-connection-id", index, flags); + +Case 2: + desc = gpiod_get(dev, NULL, flags); + desc = gpiod_get_index(dev, NULL, index, flags); + +Case 1 assumes that corresponding ACPI device description must have +defined device properties and will prevent to getting any GPIO resources +otherwise. + +Case 2 explicitly tells GPIO core to look for resources in _CRS. + +Be aware that gpiod_get_index() in cases 1 and 2, assuming that there +are two versions of ACPI device description provided and no mapping is +present in the driver, will return different resources. That's why a +certain driver has to handle them carefully as explained in previous +chapter. From 2eca25af05481c7e462cbc077328e0eb3394d06b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:22 +0300 Subject: [PATCH 24/70] gpio: acpi: Factor out acpi_gpio_to_gpiod_flags() helper The helper function acpi_gpio_to_gpiod_flags() will be used later to configure pin properly whenever it's requested. While here, introduce a checking error code returned by gpiod_configure_flags() and bail out if it's not okay. Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 61 ++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 740df0e9dcb3..cb61e11558a5 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -423,6 +423,31 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev, return false; } +static enum gpiod_flags +acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) +{ + bool pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP; + + switch (agpio->io_restriction) { + case ACPI_IO_RESTRICT_INPUT: + return GPIOD_IN; + case ACPI_IO_RESTRICT_OUTPUT: + /* + * ACPI GPIO resources don't contain an initial value for the + * GPIO. Therefore we deduce that value from the pull field + * instead. If the pin is pulled up we assume default to be + * high, otherwise low. + */ + return pull_up ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + default: + /* + * Assume that the BIOS has configured the direction and pull + * accordingly. + */ + return GPIOD_ASIS; + } +} + struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; @@ -740,7 +765,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct acpi_resource *ares; int pin_index = (int)address; acpi_status status; - bool pull_up; int length; int i; @@ -755,7 +779,6 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, } agpio = &ares->data.gpio; - pull_up = agpio->pin_config == ACPI_PIN_CONFIG_PULLUP; if (WARN_ON(agpio->io_restriction == ACPI_IO_RESTRICT_INPUT && function == ACPI_WRITE)) { @@ -806,35 +829,23 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, } if (!found) { - desc = gpiochip_request_own_desc(chip, pin, - "ACPI:OpRegion"); + enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio); + const char *label = "ACPI:OpRegion"; + int err; + + desc = gpiochip_request_own_desc(chip, pin, label); if (IS_ERR(desc)) { status = AE_ERROR; mutex_unlock(&achip->conn_lock); goto out; } - switch (agpio->io_restriction) { - case ACPI_IO_RESTRICT_INPUT: - gpiod_direction_input(desc); - break; - case ACPI_IO_RESTRICT_OUTPUT: - /* - * ACPI GPIO resources don't contain an - * initial value for the GPIO. Therefore we - * deduce that value from the pull field - * instead. If the pin is pulled up we - * assume default to be high, otherwise - * low. - */ - gpiod_direction_output(desc, pull_up); - break; - default: - /* - * Assume that the BIOS has configured the - * direction and pull accordingly. - */ - break; + err = gpiod_configure_flags(desc, label, 0, flags); + if (err < 0) { + status = AE_NOT_CONFIGURED; + gpiochip_free_own_desc(desc); + mutex_unlock(&achip->conn_lock); + goto out; } conn = kzalloc(sizeof(*conn), GFP_KERNEL); From a31f5c3a68520ee6a2adee7d5f13f2e6ef209875 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:23 +0300 Subject: [PATCH 25/70] gpio: acpi: Override GPIO initialization flags This allows ACPI GPIO code to modify flags based on ACPI GpioIo() / GpioInt() resources. Signed-off-by: Andy Shevchenko Tested-by: Jarkko Nikula Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 50 +++++++++++++++++++++++++++++++++++-- drivers/gpio/gpiolib.c | 8 ++++-- drivers/gpio/gpiolib.h | 15 +++++++++-- 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index cb61e11558a5..e431222edc2b 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -448,6 +448,34 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) } } +int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +{ + int ret = 0; + + /* + * Check if the BIOS has IoRestriction with explicitly set direction + * and update @flags accordingly. Otherwise use whatever caller asked + * for. + */ + if (update & GPIOD_FLAGS_BIT_DIR_SET) { + enum gpiod_flags diff = *flags ^ update; + + /* + * Check if caller supplied incompatible GPIO initialization + * flags. + * + * Return %-EINVAL to notify that firmware has different + * settings and we are going to use them. + */ + if (((*flags & GPIOD_FLAGS_BIT_DIR_SET) && (diff & GPIOD_FLAGS_BIT_DIR_OUT)) || + ((*flags & GPIOD_FLAGS_BIT_DIR_OUT) && (diff & GPIOD_FLAGS_BIT_DIR_VAL))) + ret = -EINVAL; + *flags = update; + } + return ret; +} + struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; @@ -485,8 +513,11 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH */ if (lookup->info.gpioint) { + lookup->info.flags = GPIOD_IN; lookup->info.polarity = agpio->polarity; lookup->info.triggering = agpio->triggering; + } else { + lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio); } } @@ -613,13 +644,14 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, - enum gpiod_flags flags, + enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags) { struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_gpio_info info; struct gpio_desc *desc; char propname[32]; + int err; int i; /* Try first from _DSD */ @@ -650,7 +682,7 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, } if (info.gpioint && - (flags == GPIOD_OUT_LOW || flags == GPIOD_OUT_HIGH)) { + (*dflags == GPIOD_OUT_LOW || *dflags == GPIOD_OUT_HIGH)) { dev_dbg(dev, "refusing GpioInt() entry when doing GPIOD_OUT_* lookup\n"); return ERR_PTR(-ENOENT); } @@ -658,6 +690,10 @@ struct gpio_desc *acpi_find_gpio(struct device *dev, if (info.polarity == GPIO_ACTIVE_LOW) *lookupflags |= GPIO_ACTIVE_LOW; + err = acpi_gpio_update_gpiod_flags(dflags, info.flags); + if (err) + dev_dbg(dev, "Override GPIO initialization flags\n"); + return desc; } @@ -711,12 +747,16 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, * used to translate from the GPIO offset in the resource to the Linux IRQ * number. * + * The function is idempotent, though each time it runs it will configure GPIO + * pin direction according to the flags in GpioInt resource. + * * Return: Linux IRQ number (>%0) on success, negative errno on failure. */ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) { int idx, i; unsigned int irq_flags; + int ret; for (i = 0, idx = 0; idx <= index; i++) { struct acpi_gpio_info info; @@ -729,6 +769,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) return PTR_ERR(desc); if (info.gpioint && idx++ == index) { + char label[32]; int irq; if (IS_ERR(desc)) @@ -738,6 +779,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) if (irq < 0) return irq; + snprintf(label, sizeof(label), "GpioInt() %d", index); + ret = gpiod_configure_flags(desc, label, 0, info.flags); + if (ret < 0) + return ret; + irq_flags = acpi_dev_get_irq_type(info.triggering, info.polarity); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 28629b2b2510..300b1ff4513b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3286,7 +3286,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, desc = of_find_gpio(dev, con_id, idx, &lookupflags); } else if (ACPI_COMPANION(dev)) { dev_dbg(dev, "using ACPI for GPIO lookup\n"); - desc = acpi_find_gpio(dev, con_id, idx, flags, &lookupflags); + desc = acpi_find_gpio(dev, con_id, idx, &flags, &lookupflags); } } @@ -3367,8 +3367,12 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode, struct acpi_gpio_info info; desc = acpi_node_get_gpiod(fwnode, propname, index, &info); - if (!IS_ERR(desc)) + if (!IS_ERR(desc)) { active_low = info.polarity == GPIO_ACTIVE_LOW; + ret = acpi_gpio_update_gpiod_flags(&dflags, info.flags); + if (ret) + pr_debug("Override GPIO initialization flags\n"); + } } if (IS_ERR(desc)) diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 2aec8be7f444..a8be286eff86 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -75,11 +75,13 @@ struct gpio_device { /** * struct acpi_gpio_info - ACPI GPIO specific information + * @flags: GPIO initialization flags * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI */ struct acpi_gpio_info { + enum gpiod_flags flags; bool gpioint; int polarity; int triggering; @@ -121,10 +123,13 @@ void acpi_gpiochip_remove(struct gpio_chip *chip); void acpi_gpiochip_request_interrupts(struct gpio_chip *chip); void acpi_gpiochip_free_interrupts(struct gpio_chip *chip); +int acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, + enum gpiod_flags update); + struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, - enum gpiod_flags flags, + enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags); struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, int index, @@ -143,9 +148,15 @@ acpi_gpiochip_request_interrupts(struct gpio_chip *chip) { } static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } +static inline int +acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) +{ + return 0; +} + static inline struct gpio_desc * acpi_find_gpio(struct device *dev, const char *con_id, - unsigned int idx, enum gpiod_flags flags, + unsigned int idx, enum gpiod_flags *dflags, enum gpio_lookup_flags *lookupflags) { return ERR_PTR(-ENOENT); From 25e3ef894eef419ee239da42edc6c1f8a4f1cfb5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:24 +0300 Subject: [PATCH 26/70] gpio: acpi: Split out acpi_gpio_get_irq_resource() helper The helper does retrieve pointer to struct acpi_resource_gpio from struct acpi_resource if it represents GpioInt() resource. It will be used by PNP code later on. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-acpi.c | 23 ++++++++++++++++++----- include/linux/acpi.h | 7 +++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index e431222edc2b..6bea176b066c 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -165,6 +165,23 @@ static void acpi_gpio_chip_dh(acpi_handle handle, void *data) /* The address of this function is used as a key. */ } +bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio) +{ + struct acpi_resource_gpio *gpio; + + if (ares->type != ACPI_RESOURCE_TYPE_GPIO) + return false; + + gpio = &ares->data.gpio; + if (gpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) + return false; + + *agpio = gpio; + return true; +} +EXPORT_SYMBOL_GPL(acpi_gpio_get_irq_resource); + static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, void *context) { @@ -178,11 +195,7 @@ static acpi_status acpi_gpiochip_request_interrupt(struct acpi_resource *ares, unsigned long irqflags; int ret, pin, irq; - if (ares->type != ACPI_RESOURCE_TYPE_GPIO) - return AE_OK; - - agpio = &ares->data.gpio; - if (agpio->connection_type != ACPI_RESOURCE_GPIO_TYPE_INT) + if (!acpi_gpio_get_irq_resource(ares, &agpio)) return AE_OK; handle = ACPI_HANDLE(chip->parent); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 137e4a3d89c5..d5aa3c42f64d 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -964,6 +964,8 @@ int devm_acpi_dev_add_driver_gpios(struct device *dev, const struct acpi_gpio_mapping *gpios); void devm_acpi_dev_remove_driver_gpios(struct device *dev); +bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio); int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index); #else static inline int acpi_dev_add_driver_gpios(struct acpi_device *adev, @@ -980,6 +982,11 @@ static inline int devm_acpi_dev_add_driver_gpios(struct device *dev, } static inline void devm_acpi_dev_remove_driver_gpios(struct device *dev) {} +static inline bool acpi_gpio_get_irq_resource(struct acpi_resource *ares, + struct acpi_resource_gpio **agpio) +{ + return false; +} static inline int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) { return -ENXIO; From 2a56e9195dfb59548cc8b32e8e8d0eef4b65f7eb Mon Sep 17 00:00:00 2001 From: Jagadish Krishnamoorthy Date: Tue, 23 May 2017 20:03:25 +0300 Subject: [PATCH 27/70] PNP / ACPI: add support for GpioInt resource type The PNP ACPI driver parses ACPI interrupt resource but not GpioInt resource. When the firmware passes GpioInt resource for IRQ the PNP ACPI driver ignores it and hence the interrupt for the particular driver will not work. One such example is 8042 keyboard which uses PNP driver for obtaining the interrupt resource. On Intel Braswell project GpioInt is used instead of interrupt resource and the keyboard driver fails to register interrupt. Fix the issue by parsing GpioInt resource type. Signed-off-by: Jagadish Krishnamoorthy Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg [Fixed a parenthesis coding style thing] Signed-off-by: Linus Walleij --- drivers/pnp/pnpacpi/rsparser.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 4b717c699313..d0358cfb316e 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -180,6 +180,7 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, struct pnp_dev *dev = data; struct acpi_resource_dma *dma; struct acpi_resource_vendor_typed *vendor_typed; + struct acpi_resource_gpio *gpio; struct resource_win win = {{0}, 0}; struct resource *r = &win.res; int i, flags; @@ -210,6 +211,21 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, } } return AE_OK; + } else if (acpi_gpio_get_irq_resource(res, &gpio)) { + /* + * If the resource is GpioInt() type then extract the IRQ + * from GPIO resource and fill it into IRQ resource type. + */ + i = acpi_dev_gpio_irq_get(dev->data, 0); + if (i >= 0) { + flags = acpi_dev_irq_flags(gpio->triggering, + gpio->polarity, + gpio->sharable); + } else { + flags = IORESOURCE_DISABLED; + } + pnp_add_irq_resource(dev, i, flags); + return AE_OK; } else if (r->flags & IORESOURCE_DISABLED) { pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED); return AE_OK; From 1d7f2cdd56d45254be73301d08c65efa8041d3f3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:26 +0300 Subject: [PATCH 28/70] PNP / ACPI: join strings back for better maintenance Simply join string literals back for better maintenance and debugging. No functional changes intended. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/pnp/pnpacpi/rsparser.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index d0358cfb316e..39af2a0a270e 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -149,8 +149,8 @@ static int vendor_resource_matches(struct pnp_dev *dev, uuid_len == sizeof(match->data) && memcmp(uuid, match->data, uuid_len) == 0) { if (expected_len && expected_len != actual_len) { - dev_err(&dev->dev, "wrong vendor descriptor size; " - "expected %d, found %d bytes\n", + dev_err(&dev->dev, + "wrong vendor descriptor size; expected %d, found %d bytes\n", expected_len, actual_len); return 0; } @@ -204,9 +204,8 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, * one interrupt, we won't be able to re-encode it. */ if (pnp_can_write(dev)) { - dev_warn(&dev->dev, "multiple interrupts in " - "_CRS descriptor; configuration can't " - "be changed\n"); + dev_warn(&dev->dev, + "multiple interrupts in _CRS descriptor; configuration can't be changed\n"); dev->capabilities &= ~PNP_WRITE; } } @@ -347,8 +346,8 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev, if (p->interrupts[i] < PNP_IRQ_NR) __set_bit(p->interrupts[i], map.bits); else - dev_err(&dev->dev, "ignoring IRQ %d option " - "(too large for %d entry bitmap)\n", + dev_err(&dev->dev, + "ignoring IRQ %d option (too large for %d entry bitmap)\n", p->interrupts[i], PNP_IRQ_NR); } } @@ -949,8 +948,9 @@ int pnpacpi_encode_resources(struct pnp_dev *dev, struct acpi_buffer *buffer) case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: default: /* other type */ - dev_warn(&dev->dev, "can't encode unknown resource " - "type %d\n", resource->type); + dev_warn(&dev->dev, + "can't encode unknown resource type %d\n", + resource->type); return -EINVAL; } resource++; From 193d092939dd8a8de7474c888ccab3c2eccbc223 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 23 May 2017 20:03:27 +0300 Subject: [PATCH 29/70] PNP / ACPI: remove FSF address There is no point in keeping an address in the file since it's subject to change. Signed-off-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij --- drivers/pnp/pnpacpi/rsparser.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 39af2a0a270e..43d8ed577e70 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -15,10 +15,6 @@ * 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 #include From 923a654c186c43734cfac5d7af6940093051bcbc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 May 2017 16:08:38 +0300 Subject: [PATCH 30/70] gpiolib: Re-use bitmap_fill() instead of open coded loop Re-use bitmap_fill() instead of open coded loop for setting an area of bits in a bitmap. Signed-off-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 300b1ff4513b..be8097097326 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -1482,8 +1482,7 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) return -ENOMEM; /* Assume by default all GPIOs are valid */ - for (i = 0; i < gpiochip->ngpio; i++) - set_bit(i, gpiochip->irq_valid_mask); + bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); return 0; } From c9546cf141798a648c3053067a42936c36d626a3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 May 2017 10:33:38 +0200 Subject: [PATCH 31/70] gpio: mockup: fix direction values The comment in linux/gpio/driver.h says: @get_direction: returns direction for signal "offset", 0=out, 1=in We got those switched at some point. Fix the values. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index c6dadac70593..c18d011770c8 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -29,8 +29,8 @@ #define GPIO_MOCKUP_MAX_GC 10 enum { - DIR_IN = 0, - DIR_OUT, + DIR_OUT = 0, + DIR_IN = 1, }; /* From c650c00c102b2bcafb3b1f3f45e5b1e9e6f72e56 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 May 2017 10:33:39 +0200 Subject: [PATCH 32/70] gpio: mockup: add prefixes to the direction enum All internal symbols except for the direction enum follow the same convention and use the gpio_mockup prefix. Add the prefix to the DIR_IN and DIR_OUT definitions as well for consistency across the file. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index c18d011770c8..b1ee45cd4302 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -29,8 +29,8 @@ #define GPIO_MOCKUP_MAX_GC 10 enum { - DIR_OUT = 0, - DIR_IN = 1, + GPIO_MOCKUP_DIR_OUT = 0, + GPIO_MOCKUP_DIR_IN = 1, }; /* @@ -93,7 +93,7 @@ static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, struct gpio_mockup_chip *chip = gpiochip_get_data(gc); gpio_mockup_set(gc, offset, value); - chip->lines[offset].dir = DIR_OUT; + chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT; return 0; } @@ -102,7 +102,7 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); - chip->lines[offset].dir = DIR_IN; + chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN; return 0; } From 01a3f23c166dc30f002e17c1ba9412a0390048d3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 May 2017 10:33:40 +0200 Subject: [PATCH 33/70] gpio: mockup: be quiet unless something goes wrong When inserting and removing the module repeatedly (e.g. when running the libgpiod test-suite) the kernel log gets clobbered with messages reporting successful creation of dummy gpiochips. Remove this message and only emit logs when something bad happens. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index b1ee45cd4302..c17578e3bd6f 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -364,9 +364,6 @@ static int gpio_mockup_probe(struct platform_device *pdev) return ret; } - - dev_info(dev, "gpio<%d..%d> add successful!", - base, base + ngpio); } return 0; From c7f5326fb433d826de215e894918edfc4e45cc70 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 May 2017 10:33:41 +0200 Subject: [PATCH 34/70] gpio: mockup: support irqmask and irqunmask Even though this is a testing module, be nice and actually implement these functions. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index c17578e3bd6f..ba8d62aa801a 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -41,6 +41,7 @@ enum { struct gpio_mockup_line_status { int dir; bool value; + bool irq_enabled; }; struct gpio_mockup_irq_context { @@ -142,12 +143,21 @@ static int gpio_mockup_to_irq(struct gpio_chip *chip, unsigned int offset) return chip->irq_base + offset; } -/* - * While we should generally support irqmask and irqunmask, this driver is - * for testing purposes only so we don't care. - */ -static void gpio_mockup_irqmask(struct irq_data *d) { } -static void gpio_mockup_irqunmask(struct irq_data *d) { } +static void gpio_mockup_irqmask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + chip->lines[data->irq - gc->irq_base].irq_enabled = false; +} + +static void gpio_mockup_irqunmask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + chip->lines[data->irq - gc->irq_base].irq_enabled = true; +} static struct irq_chip gpio_mockup_irqchip = { .name = GPIO_MOCKUP_NAME, @@ -178,6 +188,7 @@ static int gpio_mockup_irqchip_setup(struct device *dev, for (i = 0; i < gc->ngpio; i++) { irq_set_chip(irq_base + i, gc->irqchip); + irq_set_chip_data(irq_base + i, gc); irq_set_handler(irq_base + i, &handle_simple_irq); irq_modify_status(irq_base + i, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); @@ -206,6 +217,9 @@ static ssize_t gpio_mockup_event_write(struct file *file, chip = priv->chip; gc = &chip->gc; + if (!chip->lines[priv->offset].irq_enabled) + return size; + if (copy_from_user(&buf, usr_buf, 1)) return -EFAULT; From 09445a103eb4e6a0b881056c1dc5f4ca8a186671 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 May 2017 10:37:36 +0200 Subject: [PATCH 35/70] gpio: pch: check the return value of irq_alloc_generic_chip() This function can fail, so check the return value before dereferencing the returned pointer. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-pch.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 71bc6da11337..f6600f8ada52 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -331,14 +331,18 @@ static irqreturn_t pch_gpio_handler(int irq, void *dev_id) return ret; } -static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip, - unsigned int irq_start, unsigned int num) +static int pch_gpio_alloc_generic_chip(struct pch_gpio *chip, + unsigned int irq_start, + unsigned int num) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("pch_gpio", 1, irq_start, chip->base, handle_simple_irq); + if (!gc) + return -ENOMEM; + gc->private = chip; ct = gc->chip_types; @@ -349,6 +353,8 @@ static void pch_gpio_alloc_generic_chip(struct pch_gpio *chip, irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST | IRQ_NOPROBE, 0); + + return 0; } static int pch_gpio_probe(struct pci_dev *pdev, @@ -425,7 +431,10 @@ static int pch_gpio_probe(struct pci_dev *pdev, goto err_request_irq; } - pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); + ret = pch_gpio_alloc_generic_chip(chip, irq_base, + gpio_pins[chip->ioh]); + if (ret) + goto err_request_irq; end: return 0; From d76e8babe420edbf9f9f9c78e42ef01f0e62dc07 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 May 2017 10:37:37 +0200 Subject: [PATCH 36/70] gpio: sta2x11: check the return value of irq_alloc_generic_chip() This function can fail, so check the return value before dereferencing the returned pointer. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-sta2x11.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c index 39df0620fa38..9e705162da8d 100644 --- a/drivers/gpio/gpio-sta2x11.c +++ b/drivers/gpio/gpio-sta2x11.c @@ -320,13 +320,16 @@ static irqreturn_t gsta_gpio_handler(int irq, void *dev_id) return ret; } -static void gsta_alloc_irq_chip(struct gsta_gpio *chip) +static int gsta_alloc_irq_chip(struct gsta_gpio *chip) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip(KBUILD_MODNAME, 1, chip->irq_base, chip->reg_base, handle_simple_irq); + if (!gc) + return -ENOMEM; + gc->private = chip; ct = gc->chip_types; @@ -350,6 +353,8 @@ static void gsta_alloc_irq_chip(struct gsta_gpio *chip) } gc->irq_cnt = i - gc->irq_base; } + + return 0; } /* The platform device used here is instantiated by the MFD device */ @@ -400,7 +405,10 @@ static int gsta_probe(struct platform_device *dev) return err; } chip->irq_base = err; - gsta_alloc_irq_chip(chip); + + err = gsta_alloc_irq_chip(chip); + if (err) + return err; err = devm_request_irq(&dev->dev, pdev->irq, gsta_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); From e3fe07e03e61c8483e8ffe804108787192177a76 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 May 2017 10:37:38 +0200 Subject: [PATCH 37/70] gpio: ml-ioh: check the return value of irq_alloc_generic_chip() This function can fail, so check the return value before dereferencing the returned pointer. Signed-off-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- drivers/gpio/gpio-ml-ioh.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 78896a869fd9..74fdce096c26 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -385,14 +385,18 @@ static irqreturn_t ioh_gpio_handler(int irq, void *dev_id) return ret; } -static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, - unsigned int irq_start, unsigned int num) +static int ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, + unsigned int irq_start, + unsigned int num) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("ioh_gpio", 1, irq_start, chip->base, handle_simple_irq); + if (!gc) + return -ENOMEM; + gc->private = chip; ct = gc->chip_types; @@ -404,6 +408,8 @@ static void ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST | IRQ_NOPROBE, 0); + + return 0; } static int ioh_gpio_probe(struct pci_dev *pdev, @@ -468,7 +474,11 @@ static int ioh_gpio_probe(struct pci_dev *pdev, goto err_gpiochip_add; } chip->irq_base = irq_base; - ioh_gpio_alloc_generic_chip(chip, irq_base, num_ports[j]); + + ret = ioh_gpio_alloc_generic_chip(chip, + irq_base, num_ports[j]); + if (ret) + goto err_gpiochip_add; } chip = chip_save; From c1a4634013141b96324c647b45356e16f1fff781 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 May 2017 23:57:27 +0200 Subject: [PATCH 38/70] gpio: adp5588: move header file out of I2C realm include/linux/i2c is not for client devices. Move the header file to a more appropriate location. Signed-off-by: Wolfram Sang Acked-by: Dmitry Torokhov Signed-off-by: Linus Walleij --- arch/blackfin/mach-bf537/boards/stamp.c | 2 +- drivers/gpio/gpio-adp5588.c | 2 +- drivers/input/keyboard/adp5588-keys.c | 2 +- include/linux/{i2c => platform_data}/adp5588.h | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename include/linux/{i2c => platform_data}/adp5588.h (100%) diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c index eaec7b4832a2..24985e658c19 100644 --- a/arch/blackfin/mach-bf537/boards/stamp.c +++ b/arch/blackfin/mach-bf537/boards/stamp.c @@ -22,7 +22,7 @@ #include #endif #include -#include +#include #include #include #include diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index c0f718b12317..e717f8dc3966 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -16,7 +16,7 @@ #include #include -#include +#include #define DRV_NAME "adp5588-gpio" diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 53fe9a3fb620..f9d273c8b306 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -20,7 +20,7 @@ #include #include -#include +#include /* Key Event Register xy */ #define KEY_EV_PRESSED (1 << 7) diff --git a/include/linux/i2c/adp5588.h b/include/linux/platform_data/adp5588.h similarity index 100% rename from include/linux/i2c/adp5588.h rename to include/linux/platform_data/adp5588.h From 073570dcb5687f6dd0bbc541f68c03c08a57ec58 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 30 May 2017 11:21:05 +0200 Subject: [PATCH 39/70] gpiolib: remove unused variable This was left behind by a cleanup patch: drivers/gpio/gpiolib.c: In function 'gpiochip_irqchip_init_valid_mask': drivers/gpio/gpiolib.c:1474:6: error: unused variable 'i' [-Werror=unused-variable] Fixes: 923a654c186c ("gpiolib: Re-use bitmap_fill() instead of open coded loop") Reported-by: kbuild test robot Reported-by: Stephen Rothwell Reported-by: Colin King Signed-off-by: Arnd Bergmann Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index be8097097326..62ffb4e293d2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1471,8 +1471,6 @@ static struct gpio_chip *find_chip_by_name(const char *name) static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) { - int i; - if (!gpiochip->irq_need_valid_mask) return 0; From 665dff997e166c67663f2e0fce16392c70d31511 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 30 May 2017 13:13:17 +0200 Subject: [PATCH 40/70] gpio: xra1403: select REGMAP_SPI Without the regmap code, we get a link error: drivers/gpio/built-in.o: In function `xra1403_probe': (.text+0x132e0): undefined reference to `__devm_regmap_init_spi' Fixes: 5704520d7880 ("gpio: xra1403: Add EXAR XRA1403 SPI GPIO expander driver") Signed-off-by: Arnd Bergmann Reviewed-by: Nandor Han Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 89e805fc5eba..ee0bb47bf02e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1229,6 +1229,7 @@ config GPIO_PISOSR config GPIO_XRA1403 tristate "EXAR XRA1403 16-bit GPIO expander" + select REGMAP_SPI help GPIO driver for EXAR XRA1403 16-bit SPI-based GPIO expander. From ead066e682af74d8bca000da90e05b5dafa6fbeb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 1 Jun 2017 12:30:01 +0200 Subject: [PATCH 41/70] gpio: of: Spelling: s/retures/returns/ Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index e2abf0eabaf8..54ce8dc58ad0 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -239,7 +239,7 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, * * This is only used by of_gpiochip_add to request/set GPIO initial * configuration. - * It retures error if it fails otherwise 0 on success. + * It returns error if it fails otherwise 0 on success. */ static int of_gpiochip_scan_gpios(struct gpio_chip *chip) { From ff8c474d1567bb311a2292efb02d362d3e3cf60e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 6 Jun 2017 17:28:51 +0300 Subject: [PATCH 42/70] gpio: merrifield: Remove unused header I don't remember how linux/gpio.h made the source, now it seems unused. Remove it. Signed-off-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-merrifield.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 9dbdc3672f5e..ec8560298805 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include From 5117f2b05588749b7c7a6ecd3c986bf9e8b581c0 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 8 Jun 2017 09:19:00 +0530 Subject: [PATCH 43/70] MAINTAINERS: gpio: gpio-davinci: Add entry for gpio-davinci driver Add an entry for the gpio-davinci driver and add myself as a maintainer for the same. Signed-off-by: Keerthy Signed-off-by: Linus Walleij --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 67b192be4825..1519e54d1073 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11789,6 +11789,13 @@ S: Maintained F: drivers/media/platform/davinci/ F: include/media/davinci/ +TI DAVINCI SERIES GPIO DRIVER +M: Keerthy +L: linux-gpio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/gpio/gpio-davinci.txt +F: drivers/gpio/gpio-davinci.c + TI AM437X VPFE DRIVER M: "Lad, Prabhakar" L: linux-media@vger.kernel.org From 3638bd4a066c14256bad0b99a60821709d3807b4 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Thu, 8 Jun 2017 10:32:07 -0700 Subject: [PATCH 44/70] gpio: zynq: Clarify quirk and provide helper function The one quirk used in the zynq GPIO driver was called FOO which is not very descriptive. Rename the quirk to IS_ZYNQ as it indicates whether the HW is a zynq or zynqmp device to allow handling of device-specific differences of the HW. Also provide a helper function to test whether the HW is zynq or zynqmp. Signed-off-by: Soren Brinkmann Signed-off-by: Linus Walleij --- drivers/gpio/gpio-zynq.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index ed87c9a6e0e6..df0851464006 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -96,8 +96,8 @@ /* GPIO upper 16 bit mask */ #define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000 -/* For GPIO quirks */ -#define ZYNQ_GPIO_QUIRK_FOO BIT(0) +/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */ +#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0) /** * struct zynq_gpio - gpio device private data structure @@ -135,6 +135,17 @@ struct zynq_platform_data { static struct irq_chip zynq_gpio_level_irqchip; static struct irq_chip zynq_gpio_edge_irqchip; +/** + * zynq_gpio_is_zynq - test if HW is zynq or zynqmp + * @gpio: Pointer to driver data struct + * + * Return: 0 if zynqmp, 1 if zynq. + */ +static int zynq_gpio_is_zynq(struct zynq_gpio *gpio) +{ + return !!(gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_IS_ZYNQ); +} + /** * zynq_gpio_get_bank_pin - Get the bank number and pin number within that bank * for a given pin in the GPIO device @@ -242,18 +253,16 @@ static void zynq_gpio_set_value(struct gpio_chip *chip, unsigned int pin, static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin) { u32 reg; - bool is_zynq_gpio; unsigned int bank_num, bank_pin_num; struct zynq_gpio *gpio = gpiochip_get_data(chip); - is_zynq_gpio = gpio->p_data->quirks & ZYNQ_GPIO_QUIRK_FOO; zynq_gpio_get_bank_pin(pin, &bank_num, &bank_pin_num, gpio); /* * On zynq bank 0 pins 7 and 8 are special and cannot be used * as inputs. */ - if (is_zynq_gpio && bank_num == 0 && + if (zynq_gpio_is_zynq(gpio) && bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8)) return -EINVAL; @@ -637,7 +646,7 @@ static const struct zynq_platform_data zynqmp_gpio_def = { static const struct zynq_platform_data zynq_gpio_def = { .label = "zynq_gpio", - .quirks = ZYNQ_GPIO_QUIRK_FOO, + .quirks = ZYNQ_GPIO_QUIRK_IS_ZYNQ, .ngpio = ZYNQ_GPIO_NR_GPIOS, .max_bank = ZYNQ_GPIO_MAX_BANK, .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(), From 43a2dcecd8ae16c76026d6728e072a6c0aa2d8ac Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Fri, 9 Jun 2017 12:09:17 +0200 Subject: [PATCH 45/70] gpio: mvebu: fix regmap_update_bits usage In some place in the driver regmap_update_bits was misused. Indeed the last argument is not the value of the bit (or group of bits) itself but the mask value inside the register. So when setting the bit N, then the value must be BIT(N) and not 1. CC: Ralph Sennhauser Signed-off-by: Gregory CLEMENT Reviewed-by: Thomas Petazzoni Tested-by: Ralph Sennhauser Tested-by: Chris Packham Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mvebu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 3d03740a20e7..877a3edffa47 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -341,7 +341,7 @@ static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) return ret; regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF, - BIT(pin), 1); + BIT(pin), BIT(pin)); return 0; } @@ -503,7 +503,7 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF, - BIT(pin), 1); + BIT(pin), BIT(pin)); break; case IRQ_TYPE_EDGE_BOTH: { u32 data_in, in_pol, val; From b6730b20837e7402292d35d3cb910fff4dac7099 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 12 Jun 2017 17:34:59 +0200 Subject: [PATCH 46/70] gpio: mvebu: Add support for the Armada 7K/8K SoCs The Armada 7K and 8K SoCs use the same gpio controller as most of the other mvebu SoCs. However, the main difference is that the GPIO controller is part of a bigger system controller, and a syscon is used to control the overall system controller. Therefore, the driver needs to be adjusted to retrieve the regmap of the syscon to access registers, and account for the fact that registers are located at a certain offset within the regmap. This commit add the support of the syscon and introduce a new variant for this case. It was based on the preliminary work of Thomas Petazzoni. Tested-by: Thomas Petazzoni Signed-off-by: Gregory CLEMENT Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mvebu.c | 212 +++++++++++++++++++++++++------------- 1 file changed, 141 insertions(+), 71 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 877a3edffa47..7a4ce0fb0ded 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -88,6 +89,7 @@ #define MVEBU_GPIO_SOC_VARIANT_ORION 0x1 #define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2 #define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3 +#define MVEBU_GPIO_SOC_VARIANT_A8K 0x4 #define MVEBU_MAX_GPIO_PER_BANK 32 @@ -108,6 +110,7 @@ struct mvebu_pwm { struct mvebu_gpio_chip { struct gpio_chip chip; struct regmap *regs; + u32 offset; struct regmap *percpu_regs; int irqbase; struct irq_domain *domain; @@ -139,8 +142,9 @@ static void mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip, switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: case MVEBU_GPIO_SOC_VARIANT_MV78200: + case MVEBU_GPIO_SOC_VARIANT_A8K: *map = mvchip->regs; - *offset = GPIO_EDGE_CAUSE_OFF; + *offset = GPIO_EDGE_CAUSE_OFF + mvchip->offset; break; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); @@ -183,8 +187,9 @@ mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip, switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: + case MVEBU_GPIO_SOC_VARIANT_A8K: *map = mvchip->regs; - *offset = GPIO_EDGE_MASK_OFF; + *offset = GPIO_EDGE_MASK_OFF + mvchip->offset; break; case MVEBU_GPIO_SOC_VARIANT_MV78200: cpu = smp_processor_id(); @@ -232,8 +237,9 @@ mvebu_gpioreg_level_mask(struct mvebu_gpio_chip *mvchip, switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: + case MVEBU_GPIO_SOC_VARIANT_A8K: *map = mvchip->regs; - *offset = GPIO_LEVEL_MASK_OFF; + *offset = GPIO_LEVEL_MASK_OFF + mvchip->offset; break; case MVEBU_GPIO_SOC_VARIANT_MV78200: cpu = smp_processor_id(); @@ -294,7 +300,7 @@ static void mvebu_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - regmap_update_bits(mvchip->regs, GPIO_OUT_OFF, + regmap_update_bits(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, BIT(pin), value ? BIT(pin) : 0); } @@ -303,16 +309,18 @@ static int mvebu_gpio_get(struct gpio_chip *chip, unsigned int pin) struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); u32 u; - regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &u); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); if (u & BIT(pin)) { u32 data_in, in_pol; - regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); - regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, + &data_in); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, + &in_pol); u = data_in ^ in_pol; } else { - regmap_read(mvchip->regs, GPIO_OUT_OFF, &u); + regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &u); } return (u >> pin) & 1; @@ -323,7 +331,7 @@ static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned int pin, { struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip); - regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF, + regmap_update_bits(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, BIT(pin), value ? BIT(pin) : 0); } @@ -340,7 +348,7 @@ static int mvebu_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) if (ret) return ret; - regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF, + regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, BIT(pin), BIT(pin)); return 0; @@ -363,7 +371,7 @@ static int mvebu_gpio_direction_output(struct gpio_chip *chip, unsigned int pin, mvebu_gpio_blink(chip, pin, 0); mvebu_gpio_set(chip, pin, value); - regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF, + regmap_update_bits(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, BIT(pin), 0); return 0; @@ -478,7 +486,7 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) pin = d->hwirq; - regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &u); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); if ((u & BIT(pin)) == 0) return -EINVAL; @@ -497,19 +505,23 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) switch (type) { case IRQ_TYPE_EDGE_RISING: case IRQ_TYPE_LEVEL_HIGH: - regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF, + regmap_update_bits(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, BIT(pin), 0); break; case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_LEVEL_LOW: - regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF, + regmap_update_bits(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, BIT(pin), BIT(pin)); break; case IRQ_TYPE_EDGE_BOTH: { u32 data_in, in_pol, val; - regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol); - regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); + regmap_read(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, &in_pol); + regmap_read(mvchip->regs, + GPIO_DATA_IN_OFF + mvchip->offset, &data_in); /* * set initial polarity based on current input level @@ -519,7 +531,8 @@ static int mvebu_gpio_irq_set_type(struct irq_data *d, unsigned int type) else val = 0; /* raising */ - regmap_update_bits(mvchip->regs, GPIO_IN_POL_OFF, + regmap_update_bits(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, BIT(pin), val); break; } @@ -539,7 +552,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) chained_irq_enter(chip, desc); - regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in); level_mask = mvebu_gpio_read_level_mask(mvchip); edge_cause = mvebu_gpio_read_edge_cause(mvchip); edge_mask = mvebu_gpio_read_edge_mask(mvchip); @@ -559,9 +572,13 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) /* Swap polarity (race with GPIO line) */ u32 polarity; - regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &polarity); + regmap_read(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, + &polarity); polarity ^= BIT(i); - regmap_write(mvchip->regs, GPIO_IN_POL_OFF, polarity); + regmap_write(mvchip->regs, + GPIO_IN_POL_OFF + mvchip->offset, + polarity); } generic_handle_irq(irq); @@ -664,7 +681,7 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, state->period = 1; } - regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &u); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u); if (u) state->enabled = true; else @@ -727,7 +744,7 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) { struct mvebu_pwm *mvpwm = mvchip->mvpwm; - regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF, + regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, &mvpwm->blink_select); mvpwm->blink_on_duration = readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); @@ -739,7 +756,7 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) { struct mvebu_pwm *mvpwm = mvchip->mvpwm; - regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF, + regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, mvpwm->blink_select); writel_relaxed(mvpwm->blink_on_duration, mvebu_pwmreg_blink_on_duration(mvpwm)); @@ -783,7 +800,8 @@ static int mvebu_pwm_probe(struct platform_device *pdev, set = U32_MAX; else return -EINVAL; - regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF, 0); + regmap_write(mvchip->regs, + GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, 0); mvpwm = devm_kzalloc(dev, sizeof(struct mvebu_pwm), GFP_KERNEL); if (!mvpwm) @@ -819,11 +837,11 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) u32 out, io_conf, blink, in_pol, data_in, cause, edg_msk, lvl_msk; int i; - regmap_read(mvchip->regs, GPIO_OUT_OFF, &out); - regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &io_conf); - regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &blink); - regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &in_pol); - regmap_read(mvchip->regs, GPIO_DATA_IN_OFF, &data_in); + regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, &out); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &io_conf); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &blink); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, &in_pol); + regmap_read(mvchip->regs, GPIO_DATA_IN_OFF + mvchip->offset, &data_in); cause = mvebu_gpio_read_edge_cause(mvchip); edg_msk = mvebu_gpio_read_edge_mask(mvchip); lvl_msk = mvebu_gpio_read_level_mask(mvchip); @@ -884,6 +902,10 @@ static const struct of_device_id mvebu_gpio_of_match[] = { .compatible = "marvell,armada-370-xp-gpio", .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION, }, + { + .compatible = "marvell,armada-8k-gpio", + .data = (void *) MVEBU_GPIO_SOC_VARIANT_A8K, + }, { /* sentinel */ }, @@ -894,16 +916,21 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); int i; - regmap_read(mvchip->regs, GPIO_OUT_OFF, &mvchip->out_reg); - regmap_read(mvchip->regs, GPIO_IO_CONF_OFF, &mvchip->io_conf_reg); - regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF, &mvchip->blink_en_reg); - regmap_read(mvchip->regs, GPIO_IN_POL_OFF, &mvchip->in_pol_reg); + regmap_read(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, + &mvchip->out_reg); + regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, + &mvchip->io_conf_reg); + regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, + &mvchip->blink_en_reg); + regmap_read(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, + &mvchip->in_pol_reg); switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF, + case MVEBU_GPIO_SOC_VARIANT_A8K: + regmap_read(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset, &mvchip->edge_mask_regs[0]); - regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF, + regmap_read(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset, &mvchip->level_mask_regs[0]); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: @@ -941,16 +968,21 @@ static int mvebu_gpio_resume(struct platform_device *pdev) struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); int i; - regmap_write(mvchip->regs, GPIO_OUT_OFF, mvchip->out_reg); - regmap_write(mvchip->regs, GPIO_IO_CONF_OFF, mvchip->io_conf_reg); - regmap_write(mvchip->regs, GPIO_BLINK_EN_OFF, mvchip->blink_en_reg); - regmap_write(mvchip->regs, GPIO_IN_POL_OFF, mvchip->in_pol_reg); + regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset, + mvchip->out_reg); + regmap_write(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, + mvchip->io_conf_reg); + regmap_write(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, + mvchip->blink_en_reg); + regmap_write(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset, + mvchip->in_pol_reg); switch (mvchip->soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, + case MVEBU_GPIO_SOC_VARIANT_A8K: + regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset, mvchip->edge_mask_regs[0]); - regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, + regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset, mvchip->level_mask_regs[0]); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: @@ -990,15 +1022,68 @@ static const struct regmap_config mvebu_gpio_regmap_config = { .fast_io = true, }; +static int mvebu_gpio_probe_raw(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip) +{ + struct resource *res; + void __iomem *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mvchip->regs = devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvchip->regs)) + return PTR_ERR(mvchip->regs); + + /* + * For the legacy SoCs, the regmap directly maps to the GPIO + * registers, so no offset is needed. + */ + mvchip->offset = 0; + + /* + * The Armada XP has a second range of registers for the + * per-CPU registers + */ + if (mvchip->soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + mvchip->percpu_regs = + devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvchip->percpu_regs)) + return PTR_ERR(mvchip->percpu_regs); + } + + return 0; +} + +static int mvebu_gpio_probe_syscon(struct platform_device *pdev, + struct mvebu_gpio_chip *mvchip) +{ + mvchip->regs = syscon_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(mvchip->regs)) + return PTR_ERR(mvchip->regs); + + if (of_property_read_u32(pdev->dev.of_node, "offset", &mvchip->offset)) + return -EINVAL; + + return 0; +} + static int mvebu_gpio_probe(struct platform_device *pdev) { struct mvebu_gpio_chip *mvchip; const struct of_device_id *match; struct device_node *np = pdev->dev.of_node; - struct resource *res; struct irq_chip_generic *gc; struct irq_chip_type *ct; - void __iomem *base; unsigned int ngpios; bool have_irqs; int soc_variant; @@ -1054,41 +1139,26 @@ static int mvebu_gpio_probe(struct platform_device *pdev) mvchip->chip.of_node = np; mvchip->chip.dbg_show = mvebu_gpio_dbg_show; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + if (soc_variant == MVEBU_GPIO_SOC_VARIANT_A8K) + err = mvebu_gpio_probe_syscon(pdev, mvchip); + else + err = mvebu_gpio_probe_raw(pdev, mvchip); - mvchip->regs = devm_regmap_init_mmio(&pdev->dev, base, - &mvebu_gpio_regmap_config); - if (IS_ERR(mvchip->regs)) - return PTR_ERR(mvchip->regs); - - /* - * The Armada XP has a second range of registers for the - * per-CPU registers - */ - if (soc_variant == MVEBU_GPIO_SOC_VARIANT_ARMADAXP) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - mvchip->percpu_regs = - devm_regmap_init_mmio(&pdev->dev, base, - &mvebu_gpio_regmap_config); - if (IS_ERR(mvchip->percpu_regs)) - return PTR_ERR(mvchip->percpu_regs); - } + if (err) + return err; /* * Mask and clear GPIO interrupts. */ switch (soc_variant) { case MVEBU_GPIO_SOC_VARIANT_ORION: - regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0); - regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF, 0); - regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF, 0); + case MVEBU_GPIO_SOC_VARIANT_A8K: + regmap_write(mvchip->regs, + GPIO_EDGE_CAUSE_OFF + mvchip->offset, 0); + regmap_write(mvchip->regs, + GPIO_EDGE_MASK_OFF + mvchip->offset, 0); + regmap_write(mvchip->regs, + GPIO_LEVEL_MASK_OFF + mvchip->offset, 0); break; case MVEBU_GPIO_SOC_VARIANT_MV78200: regmap_write(mvchip->regs, GPIO_EDGE_CAUSE_OFF, 0); From 6f9b3e776d171636ccafbcb39a8210b4fa37136b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:25 +0200 Subject: [PATCH 47/70] gpio: mockup: improve the debugfs input sanitization We're currently only checking the first character of the input to the debugfs event files, so a string like '0sdfdsf' is valid and indicates a falling edge event. Be more strict and only allow '0', '1', '0\n' & '1\n'. While we're at it: move the sanitization code before the irq_enabled check so that we indicate an error on invalid input even if nobody is waiting for events. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index ba8d62aa801a..da76267aa85c 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -208,8 +208,7 @@ static ssize_t gpio_mockup_event_write(struct file *file, struct seq_file *sfile; struct gpio_desc *desc; struct gpio_chip *gc; - int val; - char buf; + int rv, val; sfile = file->private_data; priv = sfile->private; @@ -217,19 +216,15 @@ static ssize_t gpio_mockup_event_write(struct file *file, chip = priv->chip; gc = &chip->gc; + rv = kstrtoint_from_user(usr_buf, size, 0, &val); + if (rv) + return rv; + if (val != 0 && val != 1) + return -EINVAL; + if (!chip->lines[priv->offset].irq_enabled) return size; - if (copy_from_user(&buf, usr_buf, 1)) - return -EFAULT; - - if (buf == '0') - val = 0; - else if (buf == '1') - val = 1; - else - return -EINVAL; - gpiod_set_value_cansleep(desc, val); priv->chip->irq_ctx.irq = gc->irq_base + priv->offset; irq_work_queue(&priv->chip->irq_ctx.work); From 650b57b083ef22208c1d10e554f964571221bca4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:26 +0200 Subject: [PATCH 48/70] gpio: mockup: tweak gpio_mockup_event_write() Invert the logic of the irq_enabled check and only access the private data after the input is sanitized. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index da76267aa85c..d78e8e081f5a 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -210,24 +210,23 @@ static ssize_t gpio_mockup_event_write(struct file *file, struct gpio_chip *gc; int rv, val; - sfile = file->private_data; - priv = sfile->private; - desc = priv->desc; - chip = priv->chip; - gc = &chip->gc; - rv = kstrtoint_from_user(usr_buf, size, 0, &val); if (rv) return rv; if (val != 0 && val != 1) return -EINVAL; - if (!chip->lines[priv->offset].irq_enabled) - return size; + sfile = file->private_data; + priv = sfile->private; + desc = priv->desc; + chip = priv->chip; + gc = &chip->gc; - gpiod_set_value_cansleep(desc, val); - priv->chip->irq_ctx.irq = gc->irq_base + priv->offset; - irq_work_queue(&priv->chip->irq_ctx.work); + if (chip->lines[priv->offset].irq_enabled) { + gpiod_set_value_cansleep(desc, val); + priv->chip->irq_ctx.irq = gc->irq_base + priv->offset; + irq_work_queue(&priv->chip->irq_ctx.work); + } return size; } From b6c2e77d34ff775e4cf4ca803877dafd74737551 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:27 +0200 Subject: [PATCH 49/70] gpio: mockup: refuse to accept an odd number of GPIO ranges Currently we ignore the last odd range value, since each chip is described by two values. Be more strict and require the user to pass an even number of ranges. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index d78e8e081f5a..d95d37aeb91f 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -334,7 +334,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) int ret, i, base, ngpio; char *chip_name; - if (gpio_mockup_params_nr < 2) + if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2)) return -EINVAL; chips = devm_kzalloc(dev, From b652336d3fb70b95a5b0a5ea3a32e7116b15be29 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:28 +0200 Subject: [PATCH 50/70] gpio: mockup: improve readability We currently shift bits here and there without actually explaining what we're doing. Add some helper variables with names indicating their purpose to improve the code readability. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index d95d37aeb91f..0cb6cbacc069 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -27,6 +27,11 @@ #define GPIO_MOCKUP_NAME "gpio-mockup" #define GPIO_MOCKUP_MAX_GC 10 +/* + * We're storing two values per chip: the GPIO base and the number + * of GPIO lines. + */ +#define GPIO_MOCKUP_MAX_RANGES (GPIO_MOCKUP_MAX_GC * 2) enum { GPIO_MOCKUP_DIR_OUT = 0, @@ -62,7 +67,7 @@ struct gpio_mockup_dbgfs_private { int offset; }; -static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_GC << 1]; +static int gpio_mockup_ranges[GPIO_MOCKUP_MAX_RANGES]; static int gpio_mockup_params_nr; module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); @@ -329,23 +334,24 @@ static int gpio_mockup_add(struct device *dev, static int gpio_mockup_probe(struct platform_device *pdev) { - struct gpio_mockup_chip *chips; + int ret, i, base, ngpio, num_chips; struct device *dev = &pdev->dev; - int ret, i, base, ngpio; + struct gpio_mockup_chip *chips; char *chip_name; if (gpio_mockup_params_nr < 2 || (gpio_mockup_params_nr % 2)) return -EINVAL; - chips = devm_kzalloc(dev, - sizeof(*chips) * (gpio_mockup_params_nr >> 1), - GFP_KERNEL); + /* Each chip is described by two values. */ + num_chips = gpio_mockup_params_nr / 2; + + chips = devm_kzalloc(dev, sizeof(*chips) * num_chips, GFP_KERNEL); if (!chips) return -ENOMEM; platform_set_drvdata(pdev, chips); - for (i = 0; i < gpio_mockup_params_nr >> 1; i++) { + for (i = 0; i < num_chips; i++) { base = gpio_mockup_ranges[i * 2]; if (base == -1) From 4dc9d76c983db100ec91b65f6cee804871e5102f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:29 +0200 Subject: [PATCH 51/70] gpio: mockup: don't return magic numbers from probe() When the requested number of GPIO lines is 0, return -EINVAL, not -1 which is -EPERM. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 0cb6cbacc069..ab2d38e491bc 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -369,7 +369,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) ret = gpio_mockup_add(dev, &chips[i], chip_name, base, ngpio); } else { - ret = -1; + ret = -EINVAL; } if (ret) { From ec604f151ec01c5c646e73a46620750395a46b60 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:30 +0200 Subject: [PATCH 52/70] gpio: mockup: improve the error message Indicate the error number and make the message a bit more elaborate. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index ab2d38e491bc..2f4fe4175dbf 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -373,8 +373,9 @@ static int gpio_mockup_probe(struct platform_device *pdev) } if (ret) { - dev_err(dev, "gpio<%d..%d> add failed\n", - base, base < 0 ? ngpio : base + ngpio); + dev_err(dev, + "adding gpiochip failed: %d (base: %d, ngpio: %d)\n", + ret, base, base < 0 ? ngpio : base + ngpio); return ret; } From c60c7f4c6b679822da0033b3b7c077b0a36fd8e9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:31 +0200 Subject: [PATCH 53/70] gpio: mockup: add myself as author Just taking credit for the recent changes and new features. :) Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 2f4fe4175dbf..536a229bde71 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -3,6 +3,7 @@ * * Copyright (C) 2014 Kamlakant Patel * Copyright (C) 2015-2016 Bamvor Jian Zhang + * Copyright (C) 2017 Bartosz Golaszewski * * 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 @@ -432,5 +433,6 @@ module_exit(mock_device_exit); MODULE_AUTHOR("Kamlakant Patel "); MODULE_AUTHOR("Bamvor Jian Zhang "); +MODULE_AUTHOR("Bartosz Golaszewski "); MODULE_DESCRIPTION("GPIO Testing driver"); MODULE_LICENSE("GPL v2"); From f6ac438e5e9d8052b07ebe43673b88f0496fed8d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Jun 2017 13:41:32 +0200 Subject: [PATCH 54/70] gpio: mockup: use devm_kcalloc() where applicable When allocating a zeroed array of objects use devm_kcalloc() instead of manually calculating the required size and using devm_kzalloc(). Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mockup.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 536a229bde71..a6565e128f9e 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -128,7 +128,7 @@ static int gpio_mockup_name_lines(struct device *dev, char **names; int i; - names = devm_kzalloc(dev, sizeof(char *) * gc->ngpio, GFP_KERNEL); + names = devm_kcalloc(dev, gc->ngpio, sizeof(char *), GFP_KERNEL); if (!names) return -ENOMEM; @@ -308,8 +308,8 @@ static int gpio_mockup_add(struct device *dev, gc->get_direction = gpio_mockup_get_direction; gc->to_irq = gpio_mockup_to_irq; - chip->lines = devm_kzalloc(dev, sizeof(*chip->lines) * gc->ngpio, - GFP_KERNEL); + chip->lines = devm_kcalloc(dev, gc->ngpio, + sizeof(*chip->lines), GFP_KERNEL); if (!chip->lines) return -ENOMEM; @@ -346,7 +346,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) /* Each chip is described by two values. */ num_chips = gpio_mockup_params_nr / 2; - chips = devm_kzalloc(dev, sizeof(*chips) * num_chips, GFP_KERNEL); + chips = devm_kcalloc(dev, num_chips, sizeof(*chips), GFP_KERNEL); if (!chips) return -ENOMEM; From d3936d7437e388f3a91995e8f07fb82affff2f0d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 9 Jun 2017 20:33:10 +0200 Subject: [PATCH 55/70] gpio-exar/8250-exar: Fix passing in of parent PCI device This fixes reloading of the GPIO driver for the same platform device instance as created by the exar UART driver: First of all, the driver sets drvdata to its own value during probing and does not restore the original value on exit. But this won't help anyway as the core clears drvdata after the driver left. Set the platform device parent instead. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij Acked-by: Greg Kroah-Hartman Signed-off-by: Linus Walleij --- drivers/gpio/gpio-exar.c | 2 +- drivers/tty/serial/8250/8250_exar.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 081076771217..85bf031d9772 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -119,7 +119,7 @@ static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) static int gpio_exar_probe(struct platform_device *pdev) { - struct pci_dev *pcidev = platform_get_drvdata(pdev); + struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent); struct exar_gpio_chip *exar_gpio; void __iomem *p; int index, ret; diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 1270ff163f63..2adc0f1a2196 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -196,7 +196,8 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) if (!pdev) return NULL; - platform_set_drvdata(pdev, pcidev); + pdev->dev.parent = &pcidev->dev; + if (platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; From 5dab5872e59390aa9cca26ee629b95f7179f6c77 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 9 Jun 2017 20:33:11 +0200 Subject: [PATCH 56/70] gpio: exar: Allocate resources on behalf of the platform device Do not allocate resources on behalf of the parent device but on our own. Otherwise, cleanup does not properly work if gpio-exar is removed but not the parent device. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/gpio/gpio-exar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 85bf031d9772..c5f5182d6cf0 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -139,7 +139,7 @@ static int gpio_exar_probe(struct platform_device *pdev) if (!p) return -ENOMEM; - exar_gpio = devm_kzalloc(&pcidev->dev, sizeof(*exar_gpio), GFP_KERNEL); + exar_gpio = devm_kzalloc(&pdev->dev, sizeof(*exar_gpio), GFP_KERNEL); if (!exar_gpio) return -ENOMEM; @@ -160,7 +160,7 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->regs = p; exar_gpio->index = index; - ret = devm_gpiochip_add_data(&pcidev->dev, + ret = devm_gpiochip_add_data(&pdev->dev, &exar_gpio->gpio_chip, exar_gpio); if (ret) goto err_destroy; From 7f45a875da46a112f781c25cfa9bcb93aaed4712 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 9 Jun 2017 20:33:13 +0200 Subject: [PATCH 57/70] gpio: exar: Fix reading of directions and values First, the logic for translating a register bit to the return code of exar_get_direction and exar_get_value were wrong. And second, there was a flip regarding the register bank in exar_get_direction. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij Signed-off-by: Linus Walleij --- drivers/gpio/gpio-exar.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index c5f5182d6cf0..4b46273ca545 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -68,7 +68,7 @@ static int exar_get(struct gpio_chip *chip, unsigned int reg) value = readb(exar_gpio->regs + reg); mutex_unlock(&exar_gpio->lock); - return !!value; + return value; } static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) @@ -78,7 +78,7 @@ static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) int val; addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - val = exar_get(chip, addr) >> (offset % 8); + val = exar_get(chip, addr) & BIT(offset % 8); return !!val; } @@ -89,8 +89,8 @@ static int exar_get_value(struct gpio_chip *chip, unsigned int offset) unsigned int addr; int val; - addr = bank ? EXAR_OFFSET_MPIOLVL_LO : EXAR_OFFSET_MPIOLVL_HI; - val = exar_get(chip, addr) >> (offset % 8); + addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + val = exar_get(chip, addr) & BIT(offset % 8); return !!val; } From eb2ec49b0d64475be6ada37bcd70a4230f07ae70 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Jun 2017 11:52:12 +0300 Subject: [PATCH 58/70] MAINTAINERS: Take maintainership for GPIO ACPI support We will help Linus to maintain GPIO ACPI support. Append a dedicated record to the MAINTAINERS data base. Signed-off-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Linus Walleij --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1519e54d1073..7eef51d84a2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5682,6 +5682,15 @@ F: include/asm-generic/gpio.h F: include/uapi/linux/gpio.h F: tools/gpio/ +GPIO ACPI SUPPORT +M: Mika Westerberg +M: Andy Shevchenko +L: linux-gpio@vger.kernel.org +L: linux-acpi@vger.kernel.org +S: Maintained +F: Documentation/acpi/gpio-properties.txt +F: drivers/gpio/gpiolib-acpi.c + GRE DEMULTIPLEXER DRIVER M: Dmitry Kozlov L: netdev@vger.kernel.org From edadced2bc7012108b05b47c0649c257ad28f03c Mon Sep 17 00:00:00 2001 From: Xiaoguang Chen Date: Fri, 2 Jun 2017 07:27:15 +0800 Subject: [PATCH 59/70] gpio: dwapb: fix missing first irq for edgeboth irq type dwapb_irq_set_type overwrites polarity register value for IRQ_TYPE_EDGE_BOTH case. If the polarity of one gpio is 0 by default, then it will set falling edge irq trigger. and the gpio may requires rising edge irq for the first time, and it will be missed. Do not overwrite polarity register for IRQ_TYPE_EDGE_BOTH case can solve this issue. Signed-off-by: Xiaoguang Chen Tested-by: Jisheng Zhang [Fix some really weird text encoding problem] Signed-off-by: Linus Walleij --- drivers/gpio/gpio-dwapb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index f051c4552af5..c07ada9c7af6 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -288,7 +288,8 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type) irq_setup_alt_chip(d, type); dwapb_write(gpio, GPIO_INTTYPE_LEVEL, level); - dwapb_write(gpio, GPIO_INT_POLARITY, polarity); + if (type != IRQ_TYPE_EDGE_BOTH) + dwapb_write(gpio, GPIO_INT_POLARITY, polarity); spin_unlock_irqrestore(&gc->bgpio_lock, flags); return 0; From 5c7f2c76cd6245d64afc09b1b5c8145adc2fd907 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Thu, 15 Jun 2017 12:03:11 +0530 Subject: [PATCH 60/70] gpio: lp87565: Add support for GPIO Add driver for lp87565 PMIC family GPIOs. Three GPIOs are supported and can be configured in Open-drain output or Push-pull output. Signed-off-by: Keerthy Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 10 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-lp87565.c | 190 ++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 drivers/gpio/gpio-lp87565.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ee0bb47bf02e..a7a10562498e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -954,6 +954,16 @@ config GPIO_LP873X This driver can also be built as a module. If so, the module will be called gpio-lp873x. +config GPIO_LP87565 + tristate "TI LP87565 GPIO" + depends on MFD_TI_LP87565 + help + This driver supports the GPIO on TI Lp873565 PMICs. 3 GPIOs are present + on LP87565 PMICs. + + This driver can also be built as a module. If so, the module will be + called gpio-lp87565. + config GPIO_MAX77620 tristate "GPIO support for PMIC MAX77620 and MAX20024" depends on MFD_MAX77620 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 43a326be2e57..3de054e1d8b4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_GPIO_LP3943) += gpio-lp3943.o obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o obj-$(CONFIG_ARCH_LPC32XX) += gpio-lpc32xx.o obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o +obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o obj-$(CONFIG_GPIO_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c new file mode 100644 index 000000000000..6313c50bb91b --- /dev/null +++ b/drivers/gpio/gpio-lp87565.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Keerthy + * + * 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 LP873X driver + */ + +#include +#include +#include +#include + +#include + +struct lp87565_gpio { + struct gpio_chip chip; + struct regmap *map; +}; + +static int lp87565_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + int ret, val; + + ret = regmap_read(gpio->map, LP87565_REG_GPIO_CONFIG, &val); + if (ret < 0) + return ret; + + return !(val & BIT(offset)); +} + +static int lp87565_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset), 0); +} + +static int lp87565_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset), !value ? BIT(offset) : 0); +} + +static int lp87565_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + int ret, val; + + ret = regmap_read(gpio->map, LP87565_REG_GPIO_IN, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(offset)); +} + +static void lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + + regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, + BIT(offset), value ? BIT(offset) : 0); +} + +static int lp87565_gpio_request(struct gpio_chip *gc, unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(gc); + int ret; + + switch (offset) { + case 0: + case 1: + case 2: + /* + * MUX can program the pin to be in EN1/2/3 pin mode + * Or GPIO1/2/3 mode. + * Setup the GPIO*_SEL MUX to GPIO mode + */ + ret = regmap_update_bits(gpio->map, + LP87565_REG_PIN_FUNCTION, + BIT(offset), BIT(offset)); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int lp87565_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(gc); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset + + __ffs(LP87565_GOIO1_OD)), + BIT(offset + + __ffs(LP87565_GOIO1_OD))); + case PIN_CONFIG_DRIVE_PUSH_PULL: + return regmap_update_bits(gpio->map, + LP87565_REG_GPIO_CONFIG, + BIT(offset + + __ffs(LP87565_GOIO1_OD)), 0); + default: + return -ENOTSUPP; + } +} + +static const struct gpio_chip template_chip = { + .label = "lp87565-gpio", + .owner = THIS_MODULE, + .request = lp87565_gpio_request, + .get_direction = lp87565_gpio_get_direction, + .direction_input = lp87565_gpio_direction_input, + .direction_output = lp87565_gpio_direction_output, + .get = lp87565_gpio_get, + .set = lp87565_gpio_set, + .set_config = lp87565_gpio_set_config, + .base = -1, + .ngpio = 3, + .can_sleep = true, +}; + +static int lp87565_gpio_probe(struct platform_device *pdev) +{ + struct lp87565_gpio *gpio; + struct lp87565 *lp87565; + int ret; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + lp87565 = dev_get_drvdata(pdev->dev.parent); + gpio->chip = template_chip; + gpio->chip.parent = lp87565->dev; + gpio->map = lp87565->regmap; + + ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + return 0; +} + +static const struct platform_device_id lp87565_gpio_id_table[] = { + { "lp87565-q1-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, lp87565_gpio_id_table); + +static struct platform_driver lp87565_gpio_driver = { + .driver = { + .name = "lp87565-gpio", + }, + .probe = lp87565_gpio_probe, + .id_table = lp87565_gpio_id_table, +}; +module_platform_driver(lp87565_gpio_driver); + +MODULE_AUTHOR("Keerthy "); +MODULE_DESCRIPTION("LP87565 GPIO driver"); +MODULE_LICENSE("GPL v2"); From 3a02dc974720cdfcea7fde462bcfc12a180e410f Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Mon, 26 Jun 2017 10:37:04 -0700 Subject: [PATCH 61/70] gpio: gpio-wcove: Fix GPIO control register offset calculation According to Whiskey Cove PMIC GPIO controller specification, for GPIO pins 0-12, GPIO input and output register control address range from, 0x4e44-0x4e50 for GPIO outputs control register 0x4e51-0x4e5d for GPIO input control register But, currently when calculating the GPIO register offsets in to_reg() function, all GPIO pins in the same bank uses the same GPIO control register address. This logic is incorrect. This patch fixes this issue. This patch also adds support to selectively skip register modification for virtual GPIOs. In case of Whiskey Cove PMIC, ACPI code may use up 94 virtual GPIOs. These virtual GPIOs are used by the ACPI code as means to access various non GPIO bits of PMIC. So for these virtual GPIOs, we don't need to manipulate the physical GPIO pin register. A similar patch has been merged recently by Hans for Crystal Cove PMIC GPIO driver. You can find more details about it in Commit 9a752b4c9ab9 ("gpio: crystalcove: Do not write regular gpio registers for virtual GPIOs") Signed-off-by: Kuppuswamy Sathyanarayanan Reported-by: Jukka Laitinen Signed-off-by: Linus Walleij --- drivers/gpio/gpio-wcove.c | 75 ++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 7b1bc20be209..37c103e50ebf 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -108,19 +108,14 @@ struct wcove_gpio { static inline unsigned int to_reg(int gpio, enum ctrl_register reg_type) { unsigned int reg; - int bank; - if (gpio < BANK0_NR_PINS) - bank = 0; - else if (gpio < BANK0_NR_PINS + BANK1_NR_PINS) - bank = 1; - else - bank = 2; + if (gpio >= WCOVE_GPIO_NUM) + return -EOPNOTSUPP; if (reg_type == CTRL_IN) - reg = GPIO_IN_CTRL_BASE + bank; + reg = GPIO_IN_CTRL_BASE + gpio; else - reg = GPIO_OUT_CTRL_BASE + bank; + reg = GPIO_OUT_CTRL_BASE + gpio; return reg; } @@ -145,7 +140,10 @@ static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) { - unsigned int reg = to_reg(gpio, CTRL_IN); + int reg = to_reg(gpio, CTRL_IN); + + if (reg < 0) + return; regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt); } @@ -153,27 +151,36 @@ static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) static int wcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); - return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_INPUT_SET); + if (reg < 0) + return 0; + + return regmap_write(wg->regmap, reg, CTLO_INPUT_SET); } static int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); - return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_OUTPUT_SET | value); + if (reg < 0) + return 0; + + return regmap_write(wg->regmap, reg, CTLO_OUTPUT_SET | value); } static int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio) { struct wcove_gpio *wg = gpiochip_get_data(chip); unsigned int val; - int ret; + int ret, reg = to_reg(gpio, CTRL_OUT); - ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &val); + if (reg < 0) + return 0; + + ret = regmap_read(wg->regmap, reg, &val); if (ret) return ret; @@ -184,9 +191,12 @@ static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) { struct wcove_gpio *wg = gpiochip_get_data(chip); unsigned int val; - int ret; + int ret, reg = to_reg(gpio, CTRL_IN); - ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &val); + if (reg < 0) + return 0; + + ret = regmap_read(wg->regmap, reg, &val); if (ret) return ret; @@ -197,25 +207,33 @@ static void wcove_gpio_set(struct gpio_chip *chip, unsigned int gpio, int value) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); + + if (reg < 0) + return; if (value) - regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 1); + regmap_update_bits(wg->regmap, reg, 1, 1); else - regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 0); + regmap_update_bits(wg->regmap, reg, 1, 0); } static int wcove_gpio_set_config(struct gpio_chip *chip, unsigned int gpio, unsigned long config) { struct wcove_gpio *wg = gpiochip_get_data(chip); + int reg = to_reg(gpio, CTRL_OUT); + + if (reg < 0) + return 0; switch (pinconf_to_config_param(config)) { case PIN_CONFIG_DRIVE_OPEN_DRAIN: - return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_DRV_MASK, CTLO_DRV_OD); + return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, + CTLO_DRV_OD); case PIN_CONFIG_DRIVE_PUSH_PULL: - return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), - CTLO_DRV_MASK, CTLO_DRV_CMOS); + return regmap_update_bits(wg->regmap, reg, CTLO_DRV_MASK, + CTLO_DRV_CMOS); default: break; } @@ -228,6 +246,9 @@ static int wcove_irq_type(struct irq_data *data, unsigned int type) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct wcove_gpio *wg = gpiochip_get_data(chip); + if (data->hwirq >= WCOVE_GPIO_NUM) + return 0; + switch (type) { case IRQ_TYPE_NONE: wg->intcnt = CTLI_INTCNT_DIS; @@ -278,6 +299,9 @@ static void wcove_irq_unmask(struct irq_data *data) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct wcove_gpio *wg = gpiochip_get_data(chip); + if (data->hwirq >= WCOVE_GPIO_NUM) + return; + wg->set_irq_mask = false; wg->update |= UPDATE_IRQ_MASK; } @@ -287,6 +311,9 @@ static void wcove_irq_mask(struct irq_data *data) struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct wcove_gpio *wg = gpiochip_get_data(chip); + if (data->hwirq >= WCOVE_GPIO_NUM) + return; + wg->set_irq_mask = true; wg->update |= UPDATE_IRQ_MASK; } From 85bb4646f8908eb786dfa19a6bb2ff1423dc8aa4 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Wed, 21 Jun 2017 15:27:09 +0100 Subject: [PATCH 62/70] gpio: rcar: Add R8A7743 (RZ/G1M) support Renesas RZ/G1M (R8A7743) SoC GPIO blocks are identical to the R-Car Gen2 family. Add support for its GPIO controllers. Signed-off-by: Biju Das Reviewed-by: Chris Paterson Acked-by: Geert Uytterhoeven Acked-by: Rob Herring Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt | 1 + drivers/gpio/gpio-rcar.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt index 7c1ab3b3254f..6826a371fb69 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt +++ b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt @@ -3,6 +3,7 @@ Required Properties: - compatible: should contain one of the following. + - "renesas,gpio-r8a7743": for R8A7743 (RZ/G1M) compatible GPIO controller. - "renesas,gpio-r8a7778": for R8A7778 (R-Mobile M1) compatible GPIO controller. - "renesas,gpio-r8a7779": for R8A7779 (R-Car H1) compatible GPIO controller. - "renesas,gpio-r8a7790": for R8A7790 (R-Car H2) compatible GPIO controller. diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 31ad288846af..4a1536a050bc 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -344,6 +344,10 @@ static const struct gpio_rcar_info gpio_rcar_info_gen2 = { static const struct of_device_id gpio_rcar_of_table[] = { { + .compatible = "renesas,gpio-r8a7743", + /* RZ/G1 GPIO is identical to R-Car Gen2. */ + .data = &gpio_rcar_info_gen2, + }, { .compatible = "renesas,gpio-r8a7790", .data = &gpio_rcar_info_gen2, }, { From 6697f1f82fba6d23b278222750ffb2de05abc13e Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sat, 13 May 2017 09:29:04 +0200 Subject: [PATCH 63/70] serial: uapi: Add support for bus termination The Siemens IOT2040 comes with a RS485 interface that allows to enable or disable bus termination via software. Add a bit to the flags field of serial_rs485 that applications can set in order to request this feature from the hardware. This seems generic enough to add it for everyone. Existing driver will simply ignore it when set. Signed-off-by: Sascha Weisenberger Signed-off-by: Jan Kiszka Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/serial.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index 5d59c3ebf459..d2667ecd54ac 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -122,6 +122,9 @@ struct serial_rs485 { #define SER_RS485_RTS_AFTER_SEND (1 << 2) /* Logical level for RTS pin after sent*/ #define SER_RS485_RX_DURING_TX (1 << 4) +#define SER_RS485_TERMINATE_BUS (1 << 5) /* Enable bus + termination + (if supported) */ __u32 delay_rts_before_send; /* Delay before send (milliseconds) */ __u32 delay_rts_after_send; /* Delay after send (milliseconds) */ __u32 padding[5]; /* Memory is cheap, new structs From a39f2fe7165647c2cd7bdbebb3d04061035e520f Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 25 May 2017 08:25:19 +0200 Subject: [PATCH 64/70] gpio-exar/8250-exar: Do not even instantiate a GPIO device for Commtech cards Commtech adapters need the MPIOs for internal purposes, and the gpio-exar driver already refused to pick them up. But there is actually no point in even creating the underlying platform device. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij Acked-by: Greg Kroah-Hartman --- drivers/gpio/gpio-exar.c | 3 --- drivers/tty/serial/8250/8250_exar.c | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 4b46273ca545..139d54008ad0 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -124,9 +124,6 @@ static int gpio_exar_probe(struct platform_device *pdev) void __iomem *p; int index, ret; - if (pcidev->vendor != PCI_VENDOR_ID_EXAR) - return -ENODEV; - /* * Map the pci device to get the register addresses. * We will need to read and write those registers to control diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 2adc0f1a2196..36877c8ad652 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -239,7 +239,9 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, /* Setup Multipurpose Input/Output pins. */ setup_gpio(p); - port->port.private_data = xr17v35x_register_gpio(pcidev); + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + port->port.private_data = + xr17v35x_register_gpio(pcidev); } return 0; From 8847f5f9ef554269d2a06100b311d363b9727da6 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Tue, 2 May 2017 08:42:40 +0200 Subject: [PATCH 65/70] gpio: exar: Fix iomap request The UART driver already maps the resource for us. Trying to do this here only fails and leaves us with a non-working device. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij --- drivers/gpio/gpio-exar.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 139d54008ad0..f3585a184f39 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -125,14 +125,10 @@ static int gpio_exar_probe(struct platform_device *pdev) int index, ret; /* - * Map the pci device to get the register addresses. - * We will need to read and write those registers to control - * the GPIO pins. - * Using managed functions will save us from unmaping on exit. - * As the device is enabled using managed functions by the - * UART driver we can also use managed functions here. + * The UART driver must have mapped region 0 prior to registering this + * device - use it. */ - p = pcim_iomap(pcidev, 0, 0); + p = pcim_iomap_table(pcidev)[0]; if (!p) return -ENOMEM; From 4076cf08ac7673aca7d4dd9ddf18045d08dbc292 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Sun, 21 May 2017 11:49:24 +0200 Subject: [PATCH 66/70] gpio-exar/8250-exar: Rearrange gpiochip parenthood Set the parent of the exar gpiochip to its platform device, like other gpiochips are doing it. In order to keep the relationship discoverable for ACPI systems, set the platform device companion to the PCI device. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Linus Walleij --- drivers/gpio/gpio-exar.c | 2 +- drivers/tty/serial/8250/8250_exar.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index f3585a184f39..1a629831d45b 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -142,7 +142,7 @@ static int gpio_exar_probe(struct platform_device *pdev) sprintf(exar_gpio->name, "exar_gpio%d", index); exar_gpio->gpio_chip.label = exar_gpio->name; - exar_gpio->gpio_chip.parent = &pcidev->dev; + exar_gpio->gpio_chip.parent = &pdev->dev; exar_gpio->gpio_chip.direction_output = exar_direction_output; exar_gpio->gpio_chip.direction_input = exar_direction_input; exar_gpio->gpio_chip.get_direction = exar_get_direction; diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 36877c8ad652..32e3cb58193f 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -9,6 +9,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ +#include #include #include #include @@ -197,6 +198,7 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) return NULL; pdev->dev.parent = &pcidev->dev; + ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); if (platform_device_add(pdev) < 0) { platform_device_put(pdev); From 0d963ebf57d4c6374b3a33050a18af23c1e2ede1 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 2 Jun 2017 07:27:59 +0200 Subject: [PATCH 67/70] serial: exar: Factor out platform hooks This prepares the addition of IOT2040 platform support by preparing the needed setup and rs485_config hooks. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 32 ++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 32e3cb58193f..575a252dc936 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -63,6 +63,11 @@ struct exar8250; +struct exar8250_platform { + int (*rs485_config)(struct uart_port *, struct serial_rs485 *); + int (*register_gpio)(struct pci_dev *, struct uart_8250_port *); +}; + /** * struct exar8250_board - board information * @num_ports: number of serial ports @@ -189,7 +194,7 @@ static void setup_gpio(u8 __iomem *p) } static void * -xr17v35x_register_gpio(struct pci_dev *pcidev) +__xr17v35x_register_gpio(struct pci_dev *pcidev) { struct platform_device *pdev; @@ -208,17 +213,36 @@ xr17v35x_register_gpio(struct pci_dev *pcidev) return pdev; } +static int xr17v35x_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + if (pcidev->vendor == PCI_VENDOR_ID_EXAR) + port->port.private_data = + __xr17v35x_register_gpio(pcidev); + + return 0; +} + +static const struct exar8250_platform exar8250_default_platform = { + .register_gpio = xr17v35x_register_gpio, +}; + static int pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) { const struct exar8250_board *board = priv->board; + const struct exar8250_platform *platform; unsigned int offset = idx * 0x400; unsigned int baud = 7812500; u8 __iomem *p; int ret; + platform = &exar8250_default_platform; + port->port.uartclk = baud * 16; + port->port.rs485_config = platform->rs485_config; + /* * Setup the uart clock for the devices on expansion slot to * half the clock speed of the main chip (which is 125MHz) @@ -241,12 +265,10 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, /* Setup Multipurpose Input/Output pins. */ setup_gpio(p); - if (pcidev->vendor == PCI_VENDOR_ID_EXAR) - port->port.private_data = - xr17v35x_register_gpio(pcidev); + ret = platform->register_gpio(pcidev, port); } - return 0; + return ret; } static void pci_xr17v35x_exit(struct pci_dev *pcidev) From 277036f05be242540b7bfe75f226107d04f51b06 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 2 Jun 2017 07:43:27 +0200 Subject: [PATCH 68/70] platform: Accept const properties Aligns us with device_add_properties, the function we call. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko --- drivers/base/platform.c | 2 +- include/linux/platform_device.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a102152301c8..71ea6f4d33c2 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -344,7 +344,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data); * platform device is released. */ int platform_device_add_properties(struct platform_device *pdev, - struct property_entry *properties) + const struct property_entry *properties) { return device_add_properties(&pdev->dev, properties); } diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 98c2a7c7108e..49f634d96118 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -172,7 +172,7 @@ extern int platform_device_add_resources(struct platform_device *pdev, extern int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size); extern int platform_device_add_properties(struct platform_device *pdev, - struct property_entry *properties); + const struct property_entry *properties); extern int platform_device_add(struct platform_device *pdev); extern void platform_device_del(struct platform_device *pdev); extern void platform_device_put(struct platform_device *pdev); From 380b1e2f3a2f32bfe9c0aa85a68629eb99b043c0 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 22 May 2017 12:43:18 +0200 Subject: [PATCH 69/70] gpio-exar/8250-exar: Make set of exported GPIOs configurable On the SIMATIC, IOT2040 only a single pin is exportable as GPIO, the rest is required to operate the UART. To allow modeling this case, expand the platform device data structure to specify a (consecutive) pin subset for exporting by the gpio-exar driver. Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 56 +++++++++++++++++------------ drivers/tty/serial/8250/8250_exar.c | 15 ++++++-- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 1a629831d45b..fb8d304cfa17 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -31,6 +31,7 @@ struct exar_gpio_chip { int index; void __iomem *regs; char name[20]; + unsigned int first_pin; }; static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, @@ -51,11 +52,12 @@ static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, static int exar_set_direction(struct gpio_chip *chip, int direction, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - exar_update(chip, addr, direction, offset % 8); + exar_update(chip, addr, direction, bit); return 0; } @@ -73,36 +75,33 @@ static int exar_get(struct gpio_chip *chip, unsigned int reg) static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; - int val; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - val = exar_get(chip, addr) & BIT(offset % 8); - - return !!val; + return !!(exar_get(chip, addr) & BIT(bit)); } static int exar_get_value(struct gpio_chip *chip, unsigned int offset) { - unsigned int bank = offset / 8; - unsigned int addr; - int val; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; - val = exar_get(chip, addr) & BIT(offset % 8); - - return !!val; + return !!(exar_get(chip, addr) & BIT(bit)); } static void exar_set_value(struct gpio_chip *chip, unsigned int offset, int value) { - unsigned int bank = offset / 8; - unsigned int addr; + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? + EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; + unsigned int bit = (offset + exar_gpio->first_pin) % 8; - addr = bank ? EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; - exar_update(chip, addr, value, offset % 8); + exar_update(chip, addr, value, bit); } static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, @@ -121,6 +120,7 @@ static int gpio_exar_probe(struct platform_device *pdev) { struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent); struct exar_gpio_chip *exar_gpio; + u32 first_pin, ngpios; void __iomem *p; int index, ret; @@ -132,6 +132,15 @@ static int gpio_exar_probe(struct platform_device *pdev) if (!p) return -ENOMEM; + ret = device_property_read_u32(&pdev->dev, "linux,first-pin", + &first_pin); + if (ret) + return ret; + + ret = device_property_read_u32(&pdev->dev, "ngpios", &ngpios); + if (ret) + return ret; + exar_gpio = devm_kzalloc(&pdev->dev, sizeof(*exar_gpio), GFP_KERNEL); if (!exar_gpio) return -ENOMEM; @@ -149,9 +158,10 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->gpio_chip.get = exar_get_value; exar_gpio->gpio_chip.set = exar_set_value; exar_gpio->gpio_chip.base = -1; - exar_gpio->gpio_chip.ngpio = 16; + exar_gpio->gpio_chip.ngpio = ngpios; exar_gpio->regs = p; exar_gpio->index = index; + exar_gpio->first_pin = first_pin; ret = devm_gpiochip_add_data(&pdev->dev, &exar_gpio->gpio_chip, exar_gpio); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 575a252dc936..9931e26ca061 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -194,7 +195,8 @@ static void setup_gpio(u8 __iomem *p) } static void * -__xr17v35x_register_gpio(struct pci_dev *pcidev) +__xr17v35x_register_gpio(struct pci_dev *pcidev, + const struct property_entry *properties) { struct platform_device *pdev; @@ -205,7 +207,8 @@ __xr17v35x_register_gpio(struct pci_dev *pcidev) pdev->dev.parent = &pcidev->dev; ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&pcidev->dev)); - if (platform_device_add(pdev) < 0) { + if (platform_device_add_properties(pdev, properties) < 0 || + platform_device_add(pdev) < 0) { platform_device_put(pdev); return NULL; } @@ -213,12 +216,18 @@ __xr17v35x_register_gpio(struct pci_dev *pcidev) return pdev; } +static const struct property_entry exar_gpio_properties[] = { + PROPERTY_ENTRY_U32("linux,first-pin", 0), + PROPERTY_ENTRY_U32("ngpios", 16), + { } +}; + static int xr17v35x_register_gpio(struct pci_dev *pcidev, struct uart_8250_port *port) { if (pcidev->vendor == PCI_VENDOR_ID_EXAR) port->port.private_data = - __xr17v35x_register_gpio(pcidev); + __xr17v35x_register_gpio(pcidev, exar_gpio_properties); return 0; } From 413058df4331ce29f9934a5870d582c7e71fe15f Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Wed, 27 Jul 2016 16:32:20 +0200 Subject: [PATCH 70/70] serial: exar: Add support for IOT2040 device This implements the setup of RS232 and the switch-over to RS485 or RS422 for the Siemens IOT2040. That uses an EXAR XR17V352 with external logic to switch between the different modes. The external logic is controlled via MPIO pins of the EXAR controller. Only pin 10 can be exported as GPIO on the IOT2040. It is connected to an LED. As the XR17V352 used on the IOT2040 is not equipped with an external EEPROM, it cannot present itself as IOT2040-variant via subvendor/ subdevice IDs. Thus, we have to check via DMI for the target platform. Co-developed with Sascha Weisenberger. Signed-off-by: Sascha Weisenberger Signed-off-by: Jan Kiszka Reviewed-by: Andy Shevchenko Acked-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 129 +++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 9931e26ca061..b105f4cb51aa 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -10,6 +10,7 @@ * the Free Software Foundation; either version 2 of the License. */ #include +#include #include #include #include @@ -62,6 +63,43 @@ #define UART_EXAR_MPIOSEL_15_8 0x99 /* MPIOSEL[15:8] */ #define UART_EXAR_MPIOOD_15_8 0x9a /* MPIOOD[15:8] */ +#define UART_EXAR_RS485_DLY(x) ((x) << 4) + +/* + * IOT2040 MPIO wiring semantics: + * + * MPIO Port Function + * ---- ---- -------- + * 0 2 Mode bit 0 + * 1 2 Mode bit 1 + * 2 2 Terminate bus + * 3 - + * 4 3 Mode bit 0 + * 5 3 Mode bit 1 + * 6 3 Terminate bus + * 7 - + * 8 2 Enable + * 9 3 Enable + * 10 - Red LED + * 11..15 - + */ + +/* IOT2040 MPIOs 0..7 */ +#define IOT2040_UART_MODE_RS232 0x01 +#define IOT2040_UART_MODE_RS485 0x02 +#define IOT2040_UART_MODE_RS422 0x03 +#define IOT2040_UART_TERMINATE_BUS 0x04 + +#define IOT2040_UART1_MASK 0x0f +#define IOT2040_UART2_SHIFT 4 + +#define IOT2040_UARTS_DEFAULT_MODE 0x11 /* both RS232 */ +#define IOT2040_UARTS_GPIO_LO_MODE 0x88 /* reserved pins as input */ + +/* IOT2040 MPIOs 8..15 */ +#define IOT2040_UARTS_ENABLE 0x03 +#define IOT2040_UARTS_GPIO_HI_MODE 0xF8 /* enable & LED as outputs */ + struct exar8250; struct exar8250_platform { @@ -236,18 +274,107 @@ static const struct exar8250_platform exar8250_default_platform = { .register_gpio = xr17v35x_register_gpio, }; +static int iot2040_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 mask = IOT2040_UART1_MASK; + u8 mode, value; + + if (is_rs485) { + if (rs485->flags & SER_RS485_RX_DURING_TX) + mode = IOT2040_UART_MODE_RS422; + else + mode = IOT2040_UART_MODE_RS485; + + if (rs485->flags & SER_RS485_TERMINATE_BUS) + mode |= IOT2040_UART_TERMINATE_BUS; + } else { + mode = IOT2040_UART_MODE_RS232; + } + + if (port->line == 3) { + mask <<= IOT2040_UART2_SHIFT; + mode <<= IOT2040_UART2_SHIFT; + } + + value = readb(p + UART_EXAR_MPIOLVL_7_0); + value &= ~mask; + value |= mode; + writeb(value, p + UART_EXAR_MPIOLVL_7_0); + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + port->rs485 = *rs485; + + return 0; +} + +static const struct property_entry iot2040_gpio_properties[] = { + PROPERTY_ENTRY_U32("linux,first-pin", 10), + PROPERTY_ENTRY_U32("ngpios", 1), + { } +}; + +static int iot2040_register_gpio(struct pci_dev *pcidev, + struct uart_8250_port *port) +{ + u8 __iomem *p = port->port.membase; + + writeb(IOT2040_UARTS_DEFAULT_MODE, p + UART_EXAR_MPIOLVL_7_0); + writeb(IOT2040_UARTS_GPIO_LO_MODE, p + UART_EXAR_MPIOSEL_7_0); + writeb(IOT2040_UARTS_ENABLE, p + UART_EXAR_MPIOLVL_15_8); + writeb(IOT2040_UARTS_GPIO_HI_MODE, p + UART_EXAR_MPIOSEL_15_8); + + port->port.private_data = + __xr17v35x_register_gpio(pcidev, iot2040_gpio_properties); + + return 0; +} + +static const struct exar8250_platform iot2040_platform = { + .rs485_config = iot2040_rs485_config, + .register_gpio = iot2040_register_gpio, +}; + +static const struct dmi_system_id exar_platforms[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), + DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, + "6ES7647-0AA00-1YA2"), + }, + .driver_data = (void *)&iot2040_platform, + }, + {} +}; + static int pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) { const struct exar8250_board *board = priv->board; const struct exar8250_platform *platform; + const struct dmi_system_id *dmi_match; unsigned int offset = idx * 0x400; unsigned int baud = 7812500; u8 __iomem *p; int ret; - platform = &exar8250_default_platform; + dmi_match = dmi_first_match(exar_platforms); + if (dmi_match) + platform = dmi_match->driver_data; + else + platform = &exar8250_default_platform; port->port.uartclk = baud * 16; port->port.rs485_config = platform->rs485_config;