From da9a1c6767540a94a41f21cd13876890c1159e88 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Apr 2016 11:08:07 +0200 Subject: arm64: do away with ARCH_[WANT_OPTIONAL|REQUIRE]_GPIOLIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces: - "select ARCH_REQUIRE_GPIOLIB" with "select GPIOLIB" as this can now be selected directly. - "select ARCH_WANT_OPTIONAL_GPIOLIB" with no dependency: GPIOLIB is now selectable by everyone, so we need not declare our intent to select it. Cc: Michael Büsch Acked-by: Will Deacon Signed-off-by: Linus Walleij diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 69c8787..98d80e7 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -15,7 +15,6 @@ config ARM64 select ARCH_USE_CMPXCHG_LOCKREF select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_NUMA_BALANCING - select ARCH_WANT_OPTIONAL_GPIOLIB select ARCH_WANT_COMPAT_IPC_PARSE_VERSION select ARCH_WANT_FRAME_POINTERS select ARCH_HAS_UBSAN_SANITIZE_ALL diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index bb2616b..4fb0bef 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -15,8 +15,8 @@ config ARCH_ALPINE config ARCH_BCM2835 bool "Broadcom BCM2835 family" - select ARCH_REQUIRE_GPIOLIB select CLKSRC_OF + select GPIOLIB select PINCTRL select PINCTRL_BCM2835 select ARM_AMBA @@ -29,15 +29,15 @@ config ARCH_BCM2835 config ARCH_BCM_IPROC bool "Broadcom iProc SoC Family" select COMMON_CLK_IPROC + select GPIOLIB select PINCTRL - select ARCH_REQUIRE_GPIOLIB help This enables support for Broadcom iProc based SoCs config ARCH_BERLIN bool "Marvell Berlin SoC Family" - select ARCH_REQUIRE_GPIOLIB select DW_APB_ICTL + select GPIOLIB select PINCTRL help This enables support for Marvell Berlin SoC Family @@ -108,7 +108,7 @@ config ARCH_QCOM config ARCH_ROCKCHIP bool "Rockchip Platforms" select ARCH_HAS_RESET_CONTROLLER - select ARCH_REQUIRE_GPIOLIB + select GPIOLIB select PINCTRL select PINCTRL_ROCKCHIP select ROCKCHIP_TIMER @@ -154,11 +154,11 @@ config ARCH_STRATIX10 config ARCH_TEGRA bool "NVIDIA Tegra SoC Family" select ARCH_HAS_RESET_CONTROLLER - select ARCH_REQUIRE_GPIOLIB select CLKDEV_LOOKUP select CLKSRC_MMIO select CLKSRC_OF select GENERIC_CLOCKEVENTS + select GPIOLIB select HAVE_CLK select PINCTRL select RESET_CONTROLLER @@ -183,8 +183,8 @@ config ARCH_UNIPHIER config ARCH_VEXPRESS bool "ARMv8 software model (Versatile Express)" - select ARCH_REQUIRE_GPIOLIB select COMMON_CLK_VERSATILE + select GPIOLIB select PM select PM_GENERIC_DOMAINS select POWER_RESET_VEXPRESS -- cgit v0.10.2 From e8919e964d50a11649bb50e4a34a9ca99016b47e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Apr 2016 11:12:36 +0200 Subject: blackfin: do away with ARCH_REQUIRE_GPIOLIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace "select ARCH_REQUIRE_GPIOLIB" with "select GPIOLIB" as this can now be selected directly. Cc: Michael Büsch Cc: Steven Miao Cc: adi-buildroot-devel@lists.sourceforge.net Signed-off-by: Linus Walleij diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 28c63fe..3c1bd64 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -26,7 +26,7 @@ config BLACKFIN select HAVE_OPROFILE select HAVE_PERF_EVENTS select ARCH_HAVE_CUSTOM_GPIO_H - select ARCH_REQUIRE_GPIOLIB + select GPIOLIB select HAVE_UID16 select HAVE_UNDERSCORE_SYMBOL_PREFIX select VIRT_TO_BUS -- cgit v0.10.2 From 10e59a648812e380e6bee93f1218c9199fe84de2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 2 Aug 2016 09:42:45 +0200 Subject: ARM: do away with final ARCH_REQUIRE_GPIOLIB A new user of the Kconfig selection of ARCH_REQUIRE_GPIOLIB has appeared. Replace with just selecting GPIOLIB. Acked-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-clps711x/Kconfig b/arch/arm/mach-clps711x/Kconfig index dc7c6ed..61284b9 100644 --- a/arch/arm/mach-clps711x/Kconfig +++ b/arch/arm/mach-clps711x/Kconfig @@ -1,13 +1,13 @@ menuconfig ARCH_CLPS711X bool "Cirrus Logic EP721x/EP731x-based" depends on ARCH_MULTI_V4T - select ARCH_REQUIRE_GPIOLIB select AUTO_ZRELADDR select CLKSRC_OF select CLPS711X_TIMER select COMMON_CLK select CPU_ARM720T select GENERIC_CLOCKEVENTS + select GPIOLIB select MFD_SYSCON select OF_IRQ select USE_OF -- cgit v0.10.2 From 65053e1a7743e282c3dd08d3d435ac8b746f5359 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Apr 2016 13:40:17 +0200 Subject: gpio: delete ARCH_[WANTS_OPTIONAL|REQUIRE]_GPIOLIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GPIOLIB is now selectable explicitly, and always available for all archs. All archs that require GPIOLIB are switched to select GPIOLIB directly. Delete the hairy ARCH_REQUIRE_GPIOLIB and ARCH_WANTS_OPTIONAL_GPIOLIB Kconfig symbols. Cc: Michael Büsch Signed-off-by: Linus Walleij diff --git a/Documentation/gpio/board.txt b/Documentation/gpio/board.txt index 86d3fa9..40884c4 100644 --- a/Documentation/gpio/board.txt +++ b/Documentation/gpio/board.txt @@ -8,9 +8,9 @@ gpio-legacy.txt (actually, there is no real mapping possible with the old interface; you just fetch an integer from somewhere and request the corresponding GPIO. -Platforms that make use of GPIOs must select ARCH_REQUIRE_GPIOLIB (if GPIO usage -is mandatory) or ARCH_WANT_OPTIONAL_GPIOLIB (if GPIO support can be omitted) in -their Kconfig. Then, how GPIOs are mapped depends on what the platform uses to +All platforms can enable the GPIO library, but if the platform strictly +requires GPIO functionality to be present, it needs to select GPIOLIB from its +Kconfig. Then, how GPIOs are mapped depends on what the platform uses to describe its hardware layout. Currently, mappings can be defined through device tree, ACPI, and platform data. diff --git a/Documentation/gpio/gpio-legacy.txt b/Documentation/gpio/gpio-legacy.txt index 79ab564..b34fd94 100644 --- a/Documentation/gpio/gpio-legacy.txt +++ b/Documentation/gpio/gpio-legacy.txt @@ -72,8 +72,8 @@ in this document, but drivers acting as clients to the GPIO interface must not care how it's implemented.) That said, if the convention is supported on their platform, drivers should -use it when possible. Platforms must select ARCH_REQUIRE_GPIOLIB or -ARCH_WANT_OPTIONAL_GPIOLIB in their Kconfig. Drivers that can't work without +use it when possible. Platforms must select GPIOLIB if GPIO functionality +is strictly required. Drivers that can't work without standard GPIO calls should have Kconfig entries which depend on GPIOLIB. The GPIO calls are available, either as "real code" or as optimized-away stubs, when drivers use the include file: @@ -553,22 +553,14 @@ either NULL or the label associated with that GPIO when it was requested. Platform Support ---------------- -To support this framework, a platform's Kconfig will "select" either -ARCH_REQUIRE_GPIOLIB or ARCH_WANT_OPTIONAL_GPIOLIB -and arrange that its includes and defines -three functions: gpio_get_value(), gpio_set_value(), and gpio_cansleep(). +To force-enable this framework, a platform's Kconfig will "select" GPIOLIB, +else it is up to the user to configure support for GPIO. It may also provide a custom value for ARCH_NR_GPIOS, so that it better reflects the number of GPIOs in actual use on that platform, without wasting static table space. (It should count both built-in/SoC GPIOs and also ones on GPIO expanders. -ARCH_REQUIRE_GPIOLIB means that the gpiolib code will always get compiled -into the kernel on that architecture. - -ARCH_WANT_OPTIONAL_GPIOLIB means the gpiolib code defaults to off and the user -can enable it and build it into the kernel optionally. - If neither of these options are selected, the platform does not support GPIOs through GPIO-lib and the code cannot be enabled by the user. diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98dd47a..6de9062 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -10,27 +10,6 @@ config ARCH_HAVE_CUSTOM_GPIO_H overriding the default implementations. New uses of this are strongly discouraged. -config ARCH_WANT_OPTIONAL_GPIOLIB - bool - help - Select this config option from the architecture Kconfig, if - it is possible to use gpiolib on the architecture, but let the - user decide whether to actually build it or not. - Select this instead of ARCH_REQUIRE_GPIOLIB, if your architecture does - not depend on GPIOs being available, but rather let the user - decide whether he needs it or not. - -config ARCH_REQUIRE_GPIOLIB - bool - select GPIOLIB - help - Platforms select gpiolib if they use this infrastructure - for all their GPIOs, usually starting with ones integrated - into SOC processors. - Selecting this from the architecture code will cause the gpiolib - code to always get built in. - - menuconfig GPIOLIB bool "GPIO Support" select ANON_INODES -- cgit v0.10.2 From 0f4be8cf637ea4637faba8a0e4bf2270287c6ba0 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:06 +0200 Subject: mfd: stmpe: Add STMPE_IDX_SYS_CTRL/2 enum As STMPE1801/1601/24xx has a SYS_CTRL register and STMPE1601/2403 has even a SYS_CTRL2 register, add STMPE_IDX_SYS_CTRL/2 and update driver code accordingly This update prepares the ground for not yet supported STMPE1600 which share similar REG_SYS_CTRL register. Signed-off-by: Patrice Chotard Acked-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 94c7cc0..c053b2b 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -469,6 +469,8 @@ static const struct mfd_cell stmpe_ts_cell = { static const u8 stmpe811_regs[] = { [STMPE_IDX_CHIP_ID] = STMPE811_REG_CHIP_ID, + [STMPE_IDX_SYS_CTRL] = STMPE811_REG_SYS_CTRL, + [STMPE_IDX_SYS_CTRL2] = STMPE811_REG_SYS_CTRL2, [STMPE_IDX_ICR_LSB] = STMPE811_REG_INT_CTRL, [STMPE_IDX_IER_LSB] = STMPE811_REG_INT_EN, [STMPE_IDX_ISR_MSB] = STMPE811_REG_INT_STA, @@ -511,7 +513,7 @@ static int stmpe811_enable(struct stmpe *stmpe, unsigned int blocks, if (blocks & STMPE_BLOCK_TOUCHSCREEN) mask |= STMPE811_SYS_CTRL2_TSC_OFF; - return __stmpe_set_bits(stmpe, STMPE811_REG_SYS_CTRL2, mask, + return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL2], mask, enable ? 0 : mask); } @@ -556,6 +558,8 @@ static struct stmpe_variant_info stmpe610 = { static const u8 stmpe1601_regs[] = { [STMPE_IDX_CHIP_ID] = STMPE1601_REG_CHIP_ID, + [STMPE_IDX_SYS_CTRL] = STMPE1601_REG_SYS_CTRL, + [STMPE_IDX_SYS_CTRL2] = STMPE1601_REG_SYS_CTRL2, [STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB, [STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB, [STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB, @@ -640,13 +644,13 @@ static int stmpe1601_autosleep(struct stmpe *stmpe, return timeout; } - ret = __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2, + ret = __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL2], STMPE1601_AUTOSLEEP_TIMEOUT_MASK, timeout); if (ret < 0) return ret; - return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL2, + return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL2], STPME1601_AUTOSLEEP_ENABLE, STPME1601_AUTOSLEEP_ENABLE); } @@ -671,7 +675,7 @@ static int stmpe1601_enable(struct stmpe *stmpe, unsigned int blocks, else mask &= ~STMPE1601_SYS_CTRL_ENABLE_SPWM; - return __stmpe_set_bits(stmpe, STMPE1601_REG_SYS_CTRL, mask, + return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL], mask, enable ? mask : 0); } @@ -710,6 +714,7 @@ static struct stmpe_variant_info stmpe1601 = { */ static const u8 stmpe1801_regs[] = { [STMPE_IDX_CHIP_ID] = STMPE1801_REG_CHIP_ID, + [STMPE_IDX_SYS_CTRL] = STMPE1801_REG_SYS_CTRL, [STMPE_IDX_ICR_LSB] = STMPE1801_REG_INT_CTRL_LOW, [STMPE_IDX_IER_LSB] = STMPE1801_REG_INT_EN_MASK_LOW, [STMPE_IDX_ISR_LSB] = STMPE1801_REG_INT_STA_LOW, @@ -756,14 +761,14 @@ static int stmpe1801_reset(struct stmpe *stmpe) unsigned long timeout; int ret = 0; - ret = __stmpe_set_bits(stmpe, STMPE1801_REG_SYS_CTRL, + ret = __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL], STMPE1801_MSK_SYS_CTRL_RESET, STMPE1801_MSK_SYS_CTRL_RESET); if (ret < 0) return ret; timeout = jiffies + msecs_to_jiffies(100); while (time_before(jiffies, timeout)) { - ret = __stmpe_reg_read(stmpe, STMPE1801_REG_SYS_CTRL); + ret = __stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL]); if (ret < 0) return ret; if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET)) @@ -794,6 +799,8 @@ static struct stmpe_variant_info stmpe1801 = { static const u8 stmpe24xx_regs[] = { [STMPE_IDX_CHIP_ID] = STMPE24XX_REG_CHIP_ID, + [STMPE_IDX_SYS_CTRL] = STMPE24XX_REG_SYS_CTRL, + [STMPE_IDX_SYS_CTRL2] = STMPE24XX_REG_SYS_CTRL2, [STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB, [STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB, [STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB, @@ -840,7 +847,7 @@ static int stmpe24xx_enable(struct stmpe *stmpe, unsigned int blocks, if (blocks & STMPE_BLOCK_KEYPAD) mask |= STMPE24XX_SYS_CTRL_ENABLE_KPC; - return __stmpe_set_bits(stmpe, STMPE24XX_REG_SYS_CTRL, mask, + return __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL], mask, enable ? mask : 0); } diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 84adb46..406f9f2 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -138,6 +138,7 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE811_NR_INTERNAL_IRQS 8 #define STMPE811_REG_CHIP_ID 0x00 +#define STMPE811_REG_SYS_CTRL 0x03 #define STMPE811_REG_SYS_CTRL2 0x04 #define STMPE811_REG_SPI_CFG 0x08 #define STMPE811_REG_INT_CTRL 0x09 @@ -264,6 +265,7 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE24XX_NR_INTERNAL_IRQS 9 #define STMPE24XX_REG_SYS_CTRL 0x02 +#define STMPE24XX_REG_SYS_CTRL2 0x03 #define STMPE24XX_REG_ICR_LSB 0x11 #define STMPE24XX_REG_IER_LSB 0x13 #define STMPE24XX_REG_ISR_MSB 0x14 diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index de748bc..eb8b73b 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -39,6 +39,8 @@ enum stmpe_partnum { */ enum { STMPE_IDX_CHIP_ID, + STMPE_IDX_SYS_CTRL, + STMPE_IDX_SYS_CTRL2, STMPE_IDX_ICR_LSB, STMPE_IDX_IER_LSB, STMPE_IDX_ISR_LSB, -- cgit v0.10.2 From c4dd1ba355aae2bc3d1213da6c66c53e3c31e028 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:07 +0200 Subject: mfd: stmpe: Add reset support for all STMPE variant Reset was only implemented for STMPE1801 variant despite all variant have a SOFT_RESET bit. For STMPE2401/2403/801/1601/1801 SOFT_RESET bit is bit 7 of SYS_CTRL register. For STMPE610/811 (which have the same variant id) SOFT_RESET bit is bit 1 of SYS_CTRL register. Signed-off-by: Patrice Chotard Acked-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index c053b2b..8500525 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -756,13 +756,22 @@ static int stmpe1801_enable(struct stmpe *stmpe, unsigned int blocks, enable ? mask : 0); } -static int stmpe1801_reset(struct stmpe *stmpe) +static int stmpe_reset(struct stmpe *stmpe) { + u16 id_val = stmpe->variant->id_val; unsigned long timeout; int ret = 0; + u8 reset_bit; + + if (id_val == STMPE811_ID) + /* STMPE801 and STMPE610 use bit 1 of SYS_CTRL register */ + reset_bit = STMPE811_SYS_CTRL_RESET; + else + /* all other STMPE variant use bit 7 of SYS_CTRL register */ + reset_bit = STMPE_SYS_CTRL_RESET; ret = __stmpe_set_bits(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL], - STMPE1801_MSK_SYS_CTRL_RESET, STMPE1801_MSK_SYS_CTRL_RESET); + reset_bit, reset_bit); if (ret < 0) return ret; @@ -771,7 +780,7 @@ static int stmpe1801_reset(struct stmpe *stmpe) ret = __stmpe_reg_read(stmpe, stmpe->regs[STMPE_IDX_SYS_CTRL]); if (ret < 0) return ret; - if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET)) + if (!(ret & reset_bit)) return 0; usleep_range(100, 200); } @@ -1095,11 +1104,9 @@ static int stmpe_chip_init(struct stmpe *stmpe) if (ret) return ret; - if (id == STMPE1801_ID) { - ret = stmpe1801_reset(stmpe); - if (ret < 0) - return ret; - } + ret = stmpe_reset(stmpe); + if (ret < 0) + return ret; if (stmpe->irq >= 0) { if (id == STMPE801_ID) diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 406f9f2..4ae343d 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -104,6 +104,8 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE_ICR_LSB_EDGE (1 << 1) #define STMPE_ICR_LSB_GIM (1 << 0) +#define STMPE_SYS_CTRL_RESET (1 << 7) + /* * STMPE801 */ @@ -126,6 +128,7 @@ int stmpe_remove(struct stmpe *stmpe); /* * STMPE811 */ +#define STMPE811_ID 0x0811 #define STMPE811_IRQ_TOUCH_DET 0 #define STMPE811_IRQ_FIFO_TH 1 @@ -155,6 +158,8 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE811_REG_GPIO_FE 0x16 #define STMPE811_REG_GPIO_AF 0x17 +#define STMPE811_SYS_CTRL_RESET (1 << 1) + #define STMPE811_SYS_CTRL2_ADC_OFF (1 << 0) #define STMPE811_SYS_CTRL2_TSC_OFF (1 << 1) #define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2) @@ -244,8 +249,6 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE1801_REG_GPIO_PULL_UP_MID 0x23 #define STMPE1801_REG_GPIO_PULL_UP_HIGH 0x24 -#define STMPE1801_MSK_SYS_CTRL_RESET (1 << 7) - #define STMPE1801_MSK_INT_EN_KPC (1 << 1) #define STMPE1801_MSK_INT_EN_GPIO (1 << 3) -- cgit v0.10.2 From 287849cb3814a5077c79a2489bef055c9f5e656c Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:08 +0200 Subject: gpio: stmpe: Fix edge and rising/falling edge detection By cross-checking STMPE 610/801/811/1601/2401/2403 datasheets, it appears that edge detection and rising/falling edge detection is not supported by all STMPE variant: GPIO GPIO Edge detection rising/falling edge detection 610 | X | X | 801 | | | 811 | X | X | 1600 | | | 1601 | X | X | 1801 | | X | 2401 | X | X | 2403 | X | X | Rework stmpe_dbg_show_one() and stmpe_gpio_irq to correctly take these cases into account. Signed-off-by: Patrice Chotard Reviewed-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index f675132..fdac5d9 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -247,39 +247,74 @@ static void stmpe_dbg_show_one(struct seq_file *s, gpio, label ?: "(none)", val ? "hi" : "lo"); } else { - u8 edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_MSB] + num_banks - 1 - (offset / 8); - u8 rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB] - (offset / 8); - u8 fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB] - (offset / 8); - u8 irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB] - (offset / 8); - bool edge_det; - bool rise; - bool fall; + u8 edge_det_reg; + u8 rise_reg; + u8 fall_reg; + u8 irqen_reg; + + char *edge_det_values[] = {"edge-inactive", + "edge-asserted", + "not-supported"}; + char *rise_values[] = {"no-rising-edge-detection", + "rising-edge-detection", + "not-supported"}; + char *fall_values[] = {"no-falling-edge-detection", + "falling-edge-detection", + "not-supported"}; + #define NOT_SUPPORTED_IDX 2 + u8 edge_det = NOT_SUPPORTED_IDX; + u8 rise = NOT_SUPPORTED_IDX; + u8 fall = NOT_SUPPORTED_IDX; bool irqen; - ret = stmpe_reg_read(stmpe, edge_det_reg); - if (ret < 0) - return; - edge_det = !!(ret & mask); - ret = stmpe_reg_read(stmpe, rise_reg); - if (ret < 0) + switch (stmpe->partnum) { + case STMPE610: + case STMPE811: + case STMPE1601: + case STMPE2401: + case STMPE2403: + edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_MSB] + + num_banks - 1 - (offset / 8); + ret = stmpe_reg_read(stmpe, edge_det_reg); + if (ret < 0) + return; + edge_det = !!(ret & mask); + + case STMPE1801: + rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB] - + (offset / 8); + fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB] - + (offset / 8); + ret = stmpe_reg_read(stmpe, rise_reg); + if (ret < 0) + return; + rise = !!(ret & mask); + ret = stmpe_reg_read(stmpe, fall_reg); + if (ret < 0) + return; + fall = !!(ret & mask); + + case STMPE801: + irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB] - + (offset / 8); + break; + + default: return; - rise = !!(ret & mask); - ret = stmpe_reg_read(stmpe, fall_reg); - if (ret < 0) - return; - fall = !!(ret & mask); + } + ret = stmpe_reg_read(stmpe, irqen_reg); if (ret < 0) return; irqen = !!(ret & mask); - seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s %s%s%s", + seq_printf(s, " gpio-%-3d (%-20.20s) in %s %13s %13s %25s %25s", gpio, label ?: "(none)", val ? "hi" : "lo", - edge_det ? "edge-asserted" : "edge-inactive", - irqen ? "IRQ-enabled" : "", - rise ? " rising-edge-detection" : "", - fall ? " falling-edge-detection" : ""); + edge_det_values[edge_det], + irqen ? "IRQ-enabled" : "IRQ-disabled", + rise_values[rise], + fall_values[fall]); } } @@ -338,8 +373,8 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) stmpe_reg_write(stmpe, statmsbreg + i, status[i]); - /* Edge detect register is not present on 801 */ - if (stmpe->partnum != STMPE801) + /* Edge detect register is not present on 801 and 1801 */ + if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1801) stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i, status[i]); } -- cgit v0.10.2 From 6936e1f88d233444ac8010d9b6bfaac3ba698ee2 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:09 +0200 Subject: gpio: stmpe: Write int status register only when needed On STMPE801/1801 datasheets, it's mentionned writing in interrupt status register has no effect, bits are cleared when reading. Signed-off-by: Amelie DELAUNAY Signed-off-by: Patrice Chotard Reviewed-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index fdac5d9..5806af5 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -371,12 +371,16 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) stat &= ~(1 << bit); } - stmpe_reg_write(stmpe, statmsbreg + i, status[i]); - - /* Edge detect register is not present on 801 and 1801 */ - if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1801) + /* + * interrupt status register write has no effect on + * 801 and 1801, bits are cleared when read. + * Edge detect register is not present on 801 and 1801 + */ + if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1801) { + stmpe_reg_write(stmpe, statmsbreg + i, status[i]); stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] + i, status[i]); + } } return IRQ_HANDLED; -- cgit v0.10.2 From c16bee7897bffc3814390c9279bf01137a6bd595 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:10 +0200 Subject: mfd: stmpe: Use generic bit mask name In order to prepare the ground to STMPE1600, as STMPE1600's SYS_CTRL register has the same layout as STMPE801 variant, unify STMPExxx_REG_SYS_CTRL_RESET/INT_EN/INT_HI bit masks to more generic STMPE_SYS_CTRL_RESET/INT_EN/INT_HI Signed-off-by: Patrice Chotard Signed-off-by: Lee Jones diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 8500525..a070476 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -1110,7 +1110,7 @@ static int stmpe_chip_init(struct stmpe *stmpe) if (stmpe->irq >= 0) { if (id == STMPE801_ID) - icr = STMPE801_REG_SYS_CTRL_INT_EN; + icr = STMPE_SYS_CTRL_INT_EN; else icr = STMPE_ICR_LSB_GIM; @@ -1124,7 +1124,7 @@ static int stmpe_chip_init(struct stmpe *stmpe) if (irq_trigger == IRQF_TRIGGER_RISING || irq_trigger == IRQF_TRIGGER_HIGH) { if (id == STMPE801_ID) - icr |= STMPE801_REG_SYS_CTRL_INT_HI; + icr |= STMPE_SYS_CTRL_INT_HI; else icr |= STMPE_ICR_LSB_HIGH; } diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 4ae343d..4ba1123 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -105,6 +105,8 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE_ICR_LSB_GIM (1 << 0) #define STMPE_SYS_CTRL_RESET (1 << 7) +#define STMPE_SYS_CTRL_INT_EN (1 << 2) +#define STMPE_SYS_CTRL_INT_HI (1 << 0) /* * STMPE801 @@ -121,10 +123,6 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE801_REG_GPIO_SET_PIN 0x11 #define STMPE801_REG_GPIO_DIR 0x12 -#define STMPE801_REG_SYS_CTRL_RESET (1 << 7) -#define STMPE801_REG_SYS_CTRL_INT_EN (1 << 2) -#define STMPE801_REG_SYS_CTRL_INT_HI (1 << 0) - /* * STMPE811 */ -- cgit v0.10.2 From 897ac6674c64ca94df5b70ea5c6815a296e1d32a Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:11 +0200 Subject: mfd: stmpe: Rework registers access this update allows to use registers map as following : regs[reg_index + offset] instead of regs[reg_index] + offset This makes code clearer and will facilitate the addition of STMPE1600 on which LSB and MSB registers are respectively located at addr and addr + 1. Despite for all others STMPE variant, LSB and MSB registers are respectively located in reverse order at addr + 1 and addr. For variant which have 3 registers's bank, we use LSB,CSB and MSB indexes which contains respectively LSB (or LOW), CSB (or MID) and MSB (or HIGH) register addresses (STMPE1801/STMPE24xx). For variant which have 2 registers's bank, we use LSB and CSB indexes only. In this case the CSB index contains the MSB regs address (STMPE 1601). Signed-off-by: Patrice Chotard Reviewed-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index a070476..1877d1e 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -483,7 +483,7 @@ static const u8 stmpe811_regs[] = { [STMPE_IDX_GPAFR_U_MSB] = STMPE811_REG_GPIO_AF, [STMPE_IDX_IEGPIOR_LSB] = STMPE811_REG_GPIO_INT_EN, [STMPE_IDX_ISGPIOR_MSB] = STMPE811_REG_GPIO_INT_STA, - [STMPE_IDX_GPEDR_MSB] = STMPE811_REG_GPIO_ED, + [STMPE_IDX_GPEDR_LSB] = STMPE811_REG_GPIO_ED, }; static struct stmpe_variant_block stmpe811_blocks[] = { @@ -561,19 +561,28 @@ static const u8 stmpe1601_regs[] = { [STMPE_IDX_SYS_CTRL] = STMPE1601_REG_SYS_CTRL, [STMPE_IDX_SYS_CTRL2] = STMPE1601_REG_SYS_CTRL2, [STMPE_IDX_ICR_LSB] = STMPE1601_REG_ICR_LSB, + [STMPE_IDX_IER_MSB] = STMPE1601_REG_IER_MSB, [STMPE_IDX_IER_LSB] = STMPE1601_REG_IER_LSB, [STMPE_IDX_ISR_MSB] = STMPE1601_REG_ISR_MSB, [STMPE_IDX_GPMR_LSB] = STMPE1601_REG_GPIO_MP_LSB, + [STMPE_IDX_GPMR_CSB] = STMPE1601_REG_GPIO_MP_MSB, [STMPE_IDX_GPSR_LSB] = STMPE1601_REG_GPIO_SET_LSB, + [STMPE_IDX_GPSR_CSB] = STMPE1601_REG_GPIO_SET_MSB, [STMPE_IDX_GPCR_LSB] = STMPE1601_REG_GPIO_CLR_LSB, + [STMPE_IDX_GPCR_CSB] = STMPE1601_REG_GPIO_CLR_MSB, [STMPE_IDX_GPDR_LSB] = STMPE1601_REG_GPIO_SET_DIR_LSB, + [STMPE_IDX_GPDR_CSB] = STMPE1601_REG_GPIO_SET_DIR_MSB, + [STMPE_IDX_GPEDR_LSB] = STMPE1601_REG_GPIO_ED_LSB, + [STMPE_IDX_GPEDR_CSB] = STMPE1601_REG_GPIO_ED_MSB, [STMPE_IDX_GPRER_LSB] = STMPE1601_REG_GPIO_RE_LSB, + [STMPE_IDX_GPRER_CSB] = STMPE1601_REG_GPIO_RE_MSB, [STMPE_IDX_GPFER_LSB] = STMPE1601_REG_GPIO_FE_LSB, + [STMPE_IDX_GPFER_CSB] = STMPE1601_REG_GPIO_FE_MSB, [STMPE_IDX_GPPUR_LSB] = STMPE1601_REG_GPIO_PU_LSB, [STMPE_IDX_GPAFR_U_MSB] = STMPE1601_REG_GPIO_AF_U_MSB, [STMPE_IDX_IEGPIOR_LSB] = STMPE1601_REG_INT_EN_GPIO_MASK_LSB, + [STMPE_IDX_IEGPIOR_CSB] = STMPE1601_REG_INT_EN_GPIO_MASK_MSB, [STMPE_IDX_ISGPIOR_MSB] = STMPE1601_REG_INT_STA_GPIO_MSB, - [STMPE_IDX_GPEDR_MSB] = STMPE1601_REG_GPIO_ED_MSB, }; static struct stmpe_variant_block stmpe1601_blocks[] = { @@ -719,14 +728,28 @@ static const u8 stmpe1801_regs[] = { [STMPE_IDX_IER_LSB] = STMPE1801_REG_INT_EN_MASK_LOW, [STMPE_IDX_ISR_LSB] = STMPE1801_REG_INT_STA_LOW, [STMPE_IDX_GPMR_LSB] = STMPE1801_REG_GPIO_MP_LOW, + [STMPE_IDX_GPMR_CSB] = STMPE1801_REG_GPIO_MP_MID, + [STMPE_IDX_GPMR_MSB] = STMPE1801_REG_GPIO_MP_HIGH, [STMPE_IDX_GPSR_LSB] = STMPE1801_REG_GPIO_SET_LOW, + [STMPE_IDX_GPSR_CSB] = STMPE1801_REG_GPIO_SET_MID, + [STMPE_IDX_GPSR_MSB] = STMPE1801_REG_GPIO_SET_HIGH, [STMPE_IDX_GPCR_LSB] = STMPE1801_REG_GPIO_CLR_LOW, + [STMPE_IDX_GPCR_CSB] = STMPE1801_REG_GPIO_CLR_MID, + [STMPE_IDX_GPCR_MSB] = STMPE1801_REG_GPIO_CLR_HIGH, [STMPE_IDX_GPDR_LSB] = STMPE1801_REG_GPIO_SET_DIR_LOW, + [STMPE_IDX_GPDR_CSB] = STMPE1801_REG_GPIO_SET_DIR_MID, + [STMPE_IDX_GPDR_MSB] = STMPE1801_REG_GPIO_SET_DIR_HIGH, [STMPE_IDX_GPRER_LSB] = STMPE1801_REG_GPIO_RE_LOW, + [STMPE_IDX_GPRER_CSB] = STMPE1801_REG_GPIO_RE_MID, + [STMPE_IDX_GPRER_MSB] = STMPE1801_REG_GPIO_RE_HIGH, [STMPE_IDX_GPFER_LSB] = STMPE1801_REG_GPIO_FE_LOW, + [STMPE_IDX_GPFER_CSB] = STMPE1801_REG_GPIO_FE_MID, + [STMPE_IDX_GPFER_MSB] = STMPE1801_REG_GPIO_FE_HIGH, [STMPE_IDX_GPPUR_LSB] = STMPE1801_REG_GPIO_PULL_UP_LOW, [STMPE_IDX_IEGPIOR_LSB] = STMPE1801_REG_INT_EN_GPIO_MASK_LOW, - [STMPE_IDX_ISGPIOR_LSB] = STMPE1801_REG_INT_STA_GPIO_LOW, + [STMPE_IDX_IEGPIOR_CSB] = STMPE1801_REG_INT_EN_GPIO_MASK_MID, + [STMPE_IDX_IEGPIOR_MSB] = STMPE1801_REG_INT_EN_GPIO_MASK_HIGH, + [STMPE_IDX_ISGPIOR_MSB] = STMPE1801_REG_INT_STA_GPIO_HIGH, }; static struct stmpe_variant_block stmpe1801_blocks[] = { @@ -811,19 +834,36 @@ static const u8 stmpe24xx_regs[] = { [STMPE_IDX_SYS_CTRL] = STMPE24XX_REG_SYS_CTRL, [STMPE_IDX_SYS_CTRL2] = STMPE24XX_REG_SYS_CTRL2, [STMPE_IDX_ICR_LSB] = STMPE24XX_REG_ICR_LSB, + [STMPE_IDX_IER_MSB] = STMPE24XX_REG_IER_MSB, [STMPE_IDX_IER_LSB] = STMPE24XX_REG_IER_LSB, [STMPE_IDX_ISR_MSB] = STMPE24XX_REG_ISR_MSB, [STMPE_IDX_GPMR_LSB] = STMPE24XX_REG_GPMR_LSB, + [STMPE_IDX_GPMR_CSB] = STMPE24XX_REG_GPMR_CSB, + [STMPE_IDX_GPMR_MSB] = STMPE24XX_REG_GPMR_MSB, [STMPE_IDX_GPSR_LSB] = STMPE24XX_REG_GPSR_LSB, + [STMPE_IDX_GPSR_CSB] = STMPE24XX_REG_GPSR_CSB, + [STMPE_IDX_GPSR_MSB] = STMPE24XX_REG_GPSR_MSB, [STMPE_IDX_GPCR_LSB] = STMPE24XX_REG_GPCR_LSB, + [STMPE_IDX_GPCR_CSB] = STMPE24XX_REG_GPCR_CSB, + [STMPE_IDX_GPCR_MSB] = STMPE24XX_REG_GPCR_MSB, [STMPE_IDX_GPDR_LSB] = STMPE24XX_REG_GPDR_LSB, + [STMPE_IDX_GPDR_CSB] = STMPE24XX_REG_GPDR_CSB, + [STMPE_IDX_GPDR_MSB] = STMPE24XX_REG_GPDR_MSB, [STMPE_IDX_GPRER_LSB] = STMPE24XX_REG_GPRER_LSB, + [STMPE_IDX_GPRER_CSB] = STMPE24XX_REG_GPRER_CSB, + [STMPE_IDX_GPRER_MSB] = STMPE24XX_REG_GPRER_MSB, [STMPE_IDX_GPFER_LSB] = STMPE24XX_REG_GPFER_LSB, + [STMPE_IDX_GPFER_CSB] = STMPE24XX_REG_GPFER_CSB, + [STMPE_IDX_GPFER_MSB] = STMPE24XX_REG_GPFER_MSB, [STMPE_IDX_GPPUR_LSB] = STMPE24XX_REG_GPPUR_LSB, [STMPE_IDX_GPPDR_LSB] = STMPE24XX_REG_GPPDR_LSB, [STMPE_IDX_GPAFR_U_MSB] = STMPE24XX_REG_GPAFR_U_MSB, [STMPE_IDX_IEGPIOR_LSB] = STMPE24XX_REG_IEGPIOR_LSB, + [STMPE_IDX_IEGPIOR_CSB] = STMPE24XX_REG_IEGPIOR_CSB, + [STMPE_IDX_IEGPIOR_MSB] = STMPE24XX_REG_IEGPIOR_MSB, [STMPE_IDX_ISGPIOR_MSB] = STMPE24XX_REG_ISGPIOR_MSB, + [STMPE_IDX_GPEDR_LSB] = STMPE24XX_REG_GPEDR_LSB, + [STMPE_IDX_GPEDR_CSB] = STMPE24XX_REG_GPEDR_CSB, [STMPE_IDX_GPEDR_MSB] = STMPE24XX_REG_GPEDR_MSB, }; @@ -998,7 +1038,7 @@ static void stmpe_irq_sync_unlock(struct irq_data *data) continue; stmpe->oldier[i] = new; - stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB] - i, new); + stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_IER_LSB + i], new); } mutex_unlock(&stmpe->irq_lock); diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 4ba1123..f127342 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -179,19 +179,32 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE1601_REG_SYS_CTRL 0x02 #define STMPE1601_REG_SYS_CTRL2 0x03 +#define STMPE1601_REG_ICR_MSB 0x10 #define STMPE1601_REG_ICR_LSB 0x11 +#define STMPE1601_REG_IER_MSB 0x12 #define STMPE1601_REG_IER_LSB 0x13 #define STMPE1601_REG_ISR_MSB 0x14 -#define STMPE1601_REG_CHIP_ID 0x80 +#define STMPE1601_REG_ISR_LSB 0x15 +#define STMPE1601_REG_INT_EN_GPIO_MASK_MSB 0x16 #define STMPE1601_REG_INT_EN_GPIO_MASK_LSB 0x17 #define STMPE1601_REG_INT_STA_GPIO_MSB 0x18 -#define STMPE1601_REG_GPIO_MP_LSB 0x87 +#define STMPE1601_REG_INT_STA_GPIO_LSB 0x19 +#define STMPE1601_REG_CHIP_ID 0x80 +#define STMPE1601_REG_GPIO_SET_MSB 0x82 #define STMPE1601_REG_GPIO_SET_LSB 0x83 +#define STMPE1601_REG_GPIO_CLR_MSB 0x84 #define STMPE1601_REG_GPIO_CLR_LSB 0x85 +#define STMPE1601_REG_GPIO_MP_MSB 0x86 +#define STMPE1601_REG_GPIO_MP_LSB 0x87 +#define STMPE1601_REG_GPIO_SET_DIR_MSB 0x88 #define STMPE1601_REG_GPIO_SET_DIR_LSB 0x89 #define STMPE1601_REG_GPIO_ED_MSB 0x8A +#define STMPE1601_REG_GPIO_ED_LSB 0x8B +#define STMPE1601_REG_GPIO_RE_MSB 0x8C #define STMPE1601_REG_GPIO_RE_LSB 0x8D +#define STMPE1601_REG_GPIO_FE_MSB 0x8E #define STMPE1601_REG_GPIO_FE_LSB 0x8F +#define STMPE1601_REG_GPIO_PU_MSB 0x90 #define STMPE1601_REG_GPIO_PU_LSB 0x91 #define STMPE1601_REG_GPIO_AF_U_MSB 0x92 @@ -267,23 +280,47 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE24XX_REG_SYS_CTRL 0x02 #define STMPE24XX_REG_SYS_CTRL2 0x03 +#define STMPE24XX_REG_ICR_MSB 0x10 #define STMPE24XX_REG_ICR_LSB 0x11 +#define STMPE24XX_REG_IER_MSB 0x12 #define STMPE24XX_REG_IER_LSB 0x13 #define STMPE24XX_REG_ISR_MSB 0x14 -#define STMPE24XX_REG_CHIP_ID 0x80 +#define STMPE24XX_REG_ISR_LSB 0x15 +#define STMPE24XX_REG_IEGPIOR_MSB 0x16 +#define STMPE24XX_REG_IEGPIOR_CSB 0x17 #define STMPE24XX_REG_IEGPIOR_LSB 0x18 #define STMPE24XX_REG_ISGPIOR_MSB 0x19 -#define STMPE24XX_REG_GPMR_LSB 0xA4 +#define STMPE24XX_REG_ISGPIOR_CSB 0x1A +#define STMPE24XX_REG_ISGPIOR_LSB 0x1B +#define STMPE24XX_REG_CHIP_ID 0x80 +#define STMPE24XX_REG_GPSR_MSB 0x83 +#define STMPE24XX_REG_GPSR_CSB 0x84 #define STMPE24XX_REG_GPSR_LSB 0x85 +#define STMPE24XX_REG_GPCR_MSB 0x86 +#define STMPE24XX_REG_GPCR_CSB 0x87 #define STMPE24XX_REG_GPCR_LSB 0x88 +#define STMPE24XX_REG_GPDR_MSB 0x89 +#define STMPE24XX_REG_GPDR_CSB 0x8A #define STMPE24XX_REG_GPDR_LSB 0x8B #define STMPE24XX_REG_GPEDR_MSB 0x8C +#define STMPE24XX_REG_GPEDR_CSB 0x8D +#define STMPE24XX_REG_GPEDR_LSB 0x8E +#define STMPE24XX_REG_GPRER_MSB 0x8F +#define STMPE24XX_REG_GPRER_CSB 0x90 #define STMPE24XX_REG_GPRER_LSB 0x91 +#define STMPE24XX_REG_GPFER_MSB 0x92 +#define STMPE24XX_REG_GPFER_CSB 0x93 #define STMPE24XX_REG_GPFER_LSB 0x94 +#define STMPE24XX_REG_GPPUR_MSB 0x95 +#define STMPE24XX_REG_GPPUR_CSB 0x96 #define STMPE24XX_REG_GPPUR_LSB 0x97 -#define STMPE24XX_REG_GPPDR_LSB 0x9a +#define STMPE24XX_REG_GPPDR_MSB 0x98 +#define STMPE24XX_REG_GPPDR_CSB 0x99 +#define STMPE24XX_REG_GPPDR_LSB 0x9A #define STMPE24XX_REG_GPAFR_U_MSB 0x9B - +#define STMPE24XX_REG_GPMR_MSB 0xA2 +#define STMPE24XX_REG_GPMR_CSB 0xA3 +#define STMPE24XX_REG_GPMR_LSB 0xA4 #define STMPE24XX_SYS_CTRL_ENABLE_GPIO (1 << 3) #define STMPE24XX_SYSCON_ENABLE_PWM (1 << 2) #define STMPE24XX_SYS_CTRL_ENABLE_KPC (1 << 1) diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index eb8b73b..6b26661 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -43,20 +43,38 @@ enum { STMPE_IDX_SYS_CTRL2, STMPE_IDX_ICR_LSB, STMPE_IDX_IER_LSB, + STMPE_IDX_IER_MSB, STMPE_IDX_ISR_LSB, STMPE_IDX_ISR_MSB, STMPE_IDX_GPMR_LSB, + STMPE_IDX_GPMR_CSB, + STMPE_IDX_GPMR_MSB, STMPE_IDX_GPSR_LSB, + STMPE_IDX_GPSR_CSB, + STMPE_IDX_GPSR_MSB, STMPE_IDX_GPCR_LSB, + STMPE_IDX_GPCR_CSB, + STMPE_IDX_GPCR_MSB, STMPE_IDX_GPDR_LSB, + STMPE_IDX_GPDR_CSB, + STMPE_IDX_GPDR_MSB, + STMPE_IDX_GPEDR_LSB, + STMPE_IDX_GPEDR_CSB, STMPE_IDX_GPEDR_MSB, STMPE_IDX_GPRER_LSB, + STMPE_IDX_GPRER_CSB, + STMPE_IDX_GPRER_MSB, STMPE_IDX_GPFER_LSB, + STMPE_IDX_GPFER_CSB, + STMPE_IDX_GPFER_MSB, STMPE_IDX_GPPUR_LSB, STMPE_IDX_GPPDR_LSB, STMPE_IDX_GPAFR_U_MSB, STMPE_IDX_IEGPIOR_LSB, + STMPE_IDX_IEGPIOR_CSB, + STMPE_IDX_IEGPIOR_MSB, STMPE_IDX_ISGPIOR_LSB, + STMPE_IDX_ISGPIOR_CSB, STMPE_IDX_ISGPIOR_MSB, STMPE_IDX_MAX, }; -- cgit v0.10.2 From 43db289d00c66a2d21da50001510725f598bb6e7 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:12 +0200 Subject: gpio: stmpe: Rework registers access This update allows to use registers map as following : regs[reg_index + offset] instead of regs[reg_index] + offset This makes code clearer and will facilitate the addition of STMPE1600 on which LSB and MSB registers are respectively located at addr and addr + 1. Despite for all others STMPE variant, LSB and MSB registers are respectively located in reverse order at addr + 1 and addr. For variant which have 3 registers's bank, we use LSB,CSB and MSB indexes which contains respectively LSB (or LOW), CSB (or MID) and MSB (or HIGH) register addresses (STMPE1801/STMPE24xx). For variant which have 2 registers's bank, we use LSB and CSB indexes only. In this case the CSB index contains the MSB regs address (STMPE 1601). Signed-off-by: Patrice Chotard Reviewed-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 5806af5..dd0aedf 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -20,6 +20,8 @@ */ enum { REG_RE, REG_FE, REG_IE }; +enum { LSB, CSB, MSB }; + #define CACHE_NR_REGS 3 /* No variant has more than 24 GPIOs */ #define CACHE_NR_BANKS (24 / 8) @@ -39,7 +41,7 @@ static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset) { struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB] - (offset / 8); + u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB + (offset / 8)]; u8 mask = 1 << (offset % 8); int ret; @@ -55,7 +57,7 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB; - u8 reg = stmpe->regs[which] - (offset / 8); + u8 reg = stmpe->regs[which + (offset / 8)]; u8 mask = 1 << (offset % 8); /* @@ -89,7 +91,7 @@ static int stmpe_gpio_direction_output(struct gpio_chip *chip, { struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)]; u8 mask = 1 << (offset % 8); stmpe_gpio_set(chip, offset, val); @@ -102,7 +104,7 @@ static int stmpe_gpio_direction_input(struct gpio_chip *chip, { struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)]; u8 mask = 1 << (offset % 8); return stmpe_set_bits(stmpe, reg, mask, 0); @@ -173,10 +175,16 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); struct stmpe *stmpe = stmpe_gpio->stmpe; int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); - static const u8 regmap[] = { - [REG_RE] = STMPE_IDX_GPRER_LSB, - [REG_FE] = STMPE_IDX_GPFER_LSB, - [REG_IE] = STMPE_IDX_IEGPIOR_LSB, + static const u8 regmap[CACHE_NR_REGS][CACHE_NR_BANKS] = { + [REG_RE][LSB] = STMPE_IDX_GPRER_LSB, + [REG_RE][CSB] = STMPE_IDX_GPRER_CSB, + [REG_RE][MSB] = STMPE_IDX_GPRER_MSB, + [REG_FE][LSB] = STMPE_IDX_GPFER_LSB, + [REG_FE][CSB] = STMPE_IDX_GPFER_CSB, + [REG_FE][MSB] = STMPE_IDX_GPFER_MSB, + [REG_IE][LSB] = STMPE_IDX_IEGPIOR_LSB, + [REG_IE][CSB] = STMPE_IDX_IEGPIOR_CSB, + [REG_IE][MSB] = STMPE_IDX_IEGPIOR_MSB, }; int i, j; @@ -194,7 +202,7 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) continue; stmpe_gpio->oldregs[i][j] = new; - stmpe_reg_write(stmpe, stmpe->regs[regmap[i]] - j, new); + stmpe_reg_write(stmpe, stmpe->regs[regmap[i][j]], new); } } @@ -230,9 +238,9 @@ static void stmpe_dbg_show_one(struct seq_file *s, struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); struct stmpe *stmpe = stmpe_gpio->stmpe; const char *label = gpiochip_is_requested(gc, offset); - int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); bool val = !!stmpe_gpio_get(gc, offset); - u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); + u8 bank = offset / 8; + u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB + bank]; u8 mask = 1 << (offset % 8); int ret; u8 dir; @@ -273,18 +281,16 @@ static void stmpe_dbg_show_one(struct seq_file *s, case STMPE1601: case STMPE2401: case STMPE2403: - edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_MSB] + - num_banks - 1 - (offset / 8); + edge_det_reg = stmpe->regs[STMPE_IDX_GPEDR_LSB + bank]; ret = stmpe_reg_read(stmpe, edge_det_reg); if (ret < 0) return; edge_det = !!(ret & mask); case STMPE1801: - rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB] - - (offset / 8); - fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB] - - (offset / 8); + rise_reg = stmpe->regs[STMPE_IDX_GPRER_LSB + bank]; + fall_reg = stmpe->regs[STMPE_IDX_GPFER_LSB + bank]; + ret = stmpe_reg_read(stmpe, rise_reg); if (ret < 0) return; @@ -295,8 +301,7 @@ static void stmpe_dbg_show_one(struct seq_file *s, fall = !!(ret & mask); case STMPE801: - irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB] - - (offset / 8); + irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB + bank]; break; default: @@ -378,8 +383,9 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) */ if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1801) { stmpe_reg_write(stmpe, statmsbreg + i, status[i]); - stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_MSB] - + i, status[i]); + stmpe_reg_write(stmpe, + stmpe->regs[STMPE_IDX_GPEDR_LSB + i], + status[i]); } } -- cgit v0.10.2 From bd495916f2f0a05193c45b26e21b60c9ffaa8aae Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:13 +0200 Subject: Documentation: dt: Add stmpe1600 compatible string to STMPE MFD This patch adds a new compatible string for stmpe mfd to support stmpe1600 variant. Signed-off-by: Amelie DELAUNAY Acked-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt index 3fb68bf..f9065a5 100644 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ b/Documentation/devicetree/bindings/mfd/stmpe.txt @@ -4,7 +4,7 @@ STMPE is an MFD device which may expose the following inbuilt devices: gpio, keypad, touchscreen, adc, pwm, rotator. Required properties: - - compatible : "st,stmpe[610|801|811|1601|2401|2403]" + - compatible : "st,stmpe[610|801|811|1600|1601|2401|2403]" - reg : I2C/SPI address of the device Optional properties: -- cgit v0.10.2 From 6bb9f0d93399cbde14fc6a1532341a14a85d2df4 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:14 +0200 Subject: mfd: Add STMPE1600 support STMPE1600 is a 16-bit port expander. Datasheet is available here : http://www2.st.com/content/st_com/en/products/interfaces-and-transceivers/ i-o-expanders-and-level-translators/i-o-expanders/stmpe1600.html Signed-off-by: Amelie DELAUNAY Signed-off-by: Patrice Chotard Acked-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index c3f4aab..863c39a 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -57,6 +57,7 @@ static const struct of_device_id stmpe_of_match[] = { { .compatible = "st,stmpe610", .data = (void *)STMPE610, }, { .compatible = "st,stmpe801", .data = (void *)STMPE801, }, { .compatible = "st,stmpe811", .data = (void *)STMPE811, }, + { .compatible = "st,stmpe1600", .data = (void *)STMPE1600, }, { .compatible = "st,stmpe1601", .data = (void *)STMPE1601, }, { .compatible = "st,stmpe1801", .data = (void *)STMPE1801, }, { .compatible = "st,stmpe2401", .data = (void *)STMPE2401, }, @@ -101,6 +102,7 @@ static const struct i2c_device_id stmpe_i2c_id[] = { { "stmpe610", STMPE610 }, { "stmpe801", STMPE801 }, { "stmpe811", STMPE811 }, + { "stmpe1600", STMPE1600 }, { "stmpe1601", STMPE1601 }, { "stmpe1801", STMPE1801 }, { "stmpe2401", STMPE2401 }, diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 1877d1e..cfdae8a 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -553,6 +553,59 @@ static struct stmpe_variant_info stmpe610 = { }; /* + * STMPE1600 + * Compared to all others STMPE variant, LSB and MSB regs are located in this + * order : LSB addr + * MSB addr + 1 + * As there is only 2 * 8bits registers for GPMR/GPSR/IEGPIOPR, CSB index is MSB registers + */ + +static const u8 stmpe1600_regs[] = { + [STMPE_IDX_CHIP_ID] = STMPE1600_REG_CHIP_ID, + [STMPE_IDX_SYS_CTRL] = STMPE1600_REG_SYS_CTRL, + [STMPE_IDX_ICR_LSB] = STMPE1600_REG_SYS_CTRL, + [STMPE_IDX_GPMR_LSB] = STMPE1600_REG_GPMR_LSB, + [STMPE_IDX_GPMR_CSB] = STMPE1600_REG_GPMR_MSB, + [STMPE_IDX_GPSR_LSB] = STMPE1600_REG_GPSR_LSB, + [STMPE_IDX_GPSR_CSB] = STMPE1600_REG_GPSR_MSB, + [STMPE_IDX_GPDR_LSB] = STMPE1600_REG_GPDR_LSB, + [STMPE_IDX_GPDR_CSB] = STMPE1600_REG_GPDR_MSB, + [STMPE_IDX_IEGPIOR_LSB] = STMPE1600_REG_IEGPIOR_LSB, + [STMPE_IDX_IEGPIOR_CSB] = STMPE1600_REG_IEGPIOR_MSB, + [STMPE_IDX_ISGPIOR_LSB] = STMPE1600_REG_ISGPIOR_LSB, +}; + +static struct stmpe_variant_block stmpe1600_blocks[] = { + { + .cell = &stmpe_gpio_cell, + .irq = 0, + .block = STMPE_BLOCK_GPIO, + }, +}; + +static int stmpe1600_enable(struct stmpe *stmpe, unsigned int blocks, + bool enable) +{ + if (blocks & STMPE_BLOCK_GPIO) + return 0; + else + return -EINVAL; +} + +static struct stmpe_variant_info stmpe1600 = { + .name = "stmpe1600", + .id_val = STMPE1600_ID, + .id_mask = 0xffff, + .num_gpios = 16, + .af_bits = 0, + .regs = stmpe1600_regs, + .blocks = stmpe1600_blocks, + .num_blocks = ARRAY_SIZE(stmpe1600_blocks), + .num_irqs = STMPE1600_NR_INTERNAL_IRQS, + .enable = stmpe1600_enable, +}; + +/* * STMPE1601 */ @@ -949,6 +1002,7 @@ static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = { [STMPE610] = &stmpe610, [STMPE801] = &stmpe801, [STMPE811] = &stmpe811, + [STMPE1600] = &stmpe1600, [STMPE1601] = &stmpe1601, [STMPE1801] = &stmpe1801, [STMPE2401] = &stmpe2401, @@ -975,7 +1029,8 @@ static irqreturn_t stmpe_irq(int irq, void *data) int ret; int i; - if (variant->id_val == STMPE801_ID) { + if (variant->id_val == STMPE801_ID || + variant->id_val == STMPE1600_ID) { int base = irq_create_mapping(stmpe->domain, 0); handle_nested_irq(base); @@ -1149,13 +1204,13 @@ static int stmpe_chip_init(struct stmpe *stmpe) return ret; if (stmpe->irq >= 0) { - if (id == STMPE801_ID) + if (id == STMPE801_ID || id == STMPE1600_ID) icr = STMPE_SYS_CTRL_INT_EN; else icr = STMPE_ICR_LSB_GIM; - /* STMPE801 doesn't support Edge interrupts */ - if (id != STMPE801_ID) { + /* STMPE801 and STMPE1600 don't support Edge interrupts */ + if (id != STMPE801_ID && id != STMPE1600_ID) { if (irq_trigger == IRQF_TRIGGER_FALLING || irq_trigger == IRQF_TRIGGER_RISING) icr |= STMPE_ICR_LSB_EDGE; @@ -1163,7 +1218,7 @@ static int stmpe_chip_init(struct stmpe *stmpe) if (irq_trigger == IRQF_TRIGGER_RISING || irq_trigger == IRQF_TRIGGER_HIGH) { - if (id == STMPE801_ID) + if (id == STMPE801_ID || id == STMPE1600_ID) icr |= STMPE_SYS_CTRL_INT_HI; else icr |= STMPE_ICR_LSB_HIGH; diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index f127342..f7efdd8 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -164,6 +164,27 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE811_SYS_CTRL2_TS_OFF (1 << 3) /* + * STMPE1600 + */ +#define STMPE1600_ID 0x0016 +#define STMPE1600_NR_INTERNAL_IRQS 16 + +#define STMPE1600_REG_CHIP_ID 0x00 +#define STMPE1600_REG_SYS_CTRL 0x03 +#define STMPE1600_REG_IEGPIOR_LSB 0x08 +#define STMPE1600_REG_IEGPIOR_MSB 0x09 +#define STMPE1600_REG_ISGPIOR_LSB 0x0A +#define STMPE1600_REG_ISGPIOR_MSB 0x0B +#define STMPE1600_REG_GPMR_LSB 0x10 +#define STMPE1600_REG_GPMR_MSB 0x11 +#define STMPE1600_REG_GPSR_LSB 0x12 +#define STMPE1600_REG_GPSR_MSB 0x13 +#define STMPE1600_REG_GPDR_LSB 0x14 +#define STMPE1600_REG_GPDR_MSB 0x15 +#define STMPE1600_REG_GPPIR_LSB 0x16 +#define STMPE1600_REG_GPPIR_MSB 0x17 + +/* * STMPE1601 */ diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 6b26661..4a827af 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -26,6 +26,7 @@ enum stmpe_partnum { STMPE610, STMPE801, STMPE811, + STMPE1600, STMPE1601, STMPE1801, STMPE2401, -- cgit v0.10.2 From c6a05a0563efd6cddafe4e8c857cf87d1311e83b Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 10 Aug 2016 09:39:15 +0200 Subject: gpio: stmpe: Add STMPE1600 support The particularities of this variant are: - GPIO_XXX_LSB and GPIO_XXX_MSB memory locations are inverted compared to other variants. - There is no Edge detection, Rising Edge and Falling Edge registers. - IRQ flags are cleared when read, no need to write in Status register. Signed-off-by: Amelie DELAUNAY Signed-off-by: Patrice Chotard Reviewed-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dd0aedf..5c0d818 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -144,8 +144,9 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type) if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH) return -EINVAL; - /* STMPE801 doesn't have RE and FE registers */ - if (stmpe_gpio->stmpe->partnum == STMPE801) + /* STMPE801 and STMPE 1600 don't have RE and FE registers */ + if (stmpe_gpio->stmpe->partnum == STMPE801 || + stmpe_gpio->stmpe->partnum == STMPE1600) return 0; if (type & IRQ_TYPE_EDGE_RISING) @@ -189,9 +190,10 @@ static void stmpe_gpio_irq_sync_unlock(struct irq_data *d) int i, j; for (i = 0; i < CACHE_NR_REGS; i++) { - /* STMPE801 doesn't have RE and FE registers */ - if ((stmpe->partnum == STMPE801) && - (i != REG_IE)) + /* STMPE801 and STMPE1600 don't have RE and FE registers */ + if ((stmpe->partnum == STMPE801 || + stmpe->partnum == STMPE1600) && + (i != REG_IE)) continue; for (j = 0; j < num_banks; j++) { @@ -224,11 +226,21 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d) { struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); + struct stmpe *stmpe = stmpe_gpio->stmpe; int offset = d->hwirq; int regoffset = offset / 8; int mask = 1 << (offset % 8); stmpe_gpio->regs[REG_IE][regoffset] |= mask; + + /* + * STMPE1600 workaround: to be able to get IRQ from pins, + * a read must be done on GPMR register, or a write in + * GPSR or GPCR registers + */ + if (stmpe->partnum == STMPE1600) + stmpe_reg_read(stmpe, + stmpe->regs[STMPE_IDX_GPMR_LSB + regoffset]); } static void stmpe_dbg_show_one(struct seq_file *s, @@ -301,6 +313,7 @@ static void stmpe_dbg_show_one(struct seq_file *s, fall = !!(ret & mask); case STMPE801: + case STMPE1600: irqen_reg = stmpe->regs[STMPE_IDX_IEGPIOR_LSB + bank]; break; @@ -347,18 +360,32 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) { struct stmpe_gpio *stmpe_gpio = dev; struct stmpe *stmpe = stmpe_gpio->stmpe; - u8 statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB]; + u8 statmsbreg; int num_banks = DIV_ROUND_UP(stmpe->num_gpios, 8); u8 status[num_banks]; int ret; int i; + /* + * the stmpe_block_read() call below, imposes to set statmsbreg + * with the register located at the lowest address. As STMPE1600 + * variant is the only one which respect registers address's order + * (LSB regs located at lowest address than MSB ones) whereas all + * the others have a registers layout with MSB located before the + * LSB regs. + */ + if (stmpe->partnum == STMPE1600) + statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_LSB]; + else + statmsbreg = stmpe->regs[STMPE_IDX_ISGPIOR_MSB]; + ret = stmpe_block_read(stmpe, statmsbreg, num_banks, status); if (ret < 0) return IRQ_NONE; for (i = 0; i < num_banks; i++) { - int bank = num_banks - i - 1; + int bank = (stmpe_gpio->stmpe->partnum == STMPE1600) ? i : + num_banks - i - 1; unsigned int enabled = stmpe_gpio->regs[REG_IE][bank]; unsigned int stat = status[i]; @@ -378,10 +405,11 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) /* * interrupt status register write has no effect on - * 801 and 1801, bits are cleared when read. - * Edge detect register is not present on 801 and 1801 + * 801/1801/1600, bits are cleared when read. + * Edge detect register is not present on 801/1600/1801 */ - if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1801) { + if (stmpe->partnum != STMPE801 || stmpe->partnum != STMPE1600 || + stmpe->partnum != STMPE1801) { stmpe_reg_write(stmpe, statmsbreg + i, status[i]); stmpe_reg_write(stmpe, stmpe->regs[STMPE_IDX_GPEDR_LSB + i], -- cgit v0.10.2 From e23efa311110648d58268c9b83b21de9d990a78c Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Fri, 29 Jul 2016 11:39:55 +0800 Subject: gpio: pca954x: Add vcc regulator and enable it Some i2c gpio devices are connected to a switchable power supply which needs to be enabled prior to probing the device. This patch allows the drive to enable the devices vcc regulator prior to probing. Signed-off-by: Phil Reid Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 02f2a56..cbe2824 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -21,6 +21,7 @@ #include #include #include +#include #define PCA953X_INPUT 0 #define PCA953X_OUTPUT 1 @@ -113,6 +114,7 @@ struct pca953x_chip { const char *const *names; int chip_type; unsigned long driver_data; + struct regulator *regulator; }; static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val, @@ -746,6 +748,7 @@ static int pca953x_probe(struct i2c_client *client, int irq_base = 0; int ret; u32 invert = 0; + struct regulator *reg; chip = devm_kzalloc(&client->dev, sizeof(struct pca953x_chip), GFP_KERNEL); @@ -765,6 +768,20 @@ static int pca953x_probe(struct i2c_client *client, chip->client = client; + reg = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + if (ret != -EPROBE_DEFER) + dev_err(&client->dev, "reg get err: %d\n", ret); + return ret; + } + ret = regulator_enable(reg); + if (ret) { + dev_err(&client->dev, "reg en err: %d\n", ret); + return ret; + } + chip->regulator = reg; + if (id) { chip->driver_data = id->driver_data; } else { @@ -776,8 +793,10 @@ static int pca953x_probe(struct i2c_client *client, chip->driver_data = (int)(uintptr_t)match->data; } else { id = acpi_match_device(pca953x_acpi_ids, &client->dev); - if (!id) - return -ENODEV; + if (!id) { + ret = -ENODEV; + goto err_exit; + } chip->driver_data = id->driver_data; } @@ -797,15 +816,15 @@ static int pca953x_probe(struct i2c_client *client, else ret = device_pca957x_init(chip, invert); if (ret) - return ret; + goto err_exit; ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip); if (ret) - return ret; + goto err_exit; ret = pca953x_irq_setup(chip, irq_base); if (ret) - return ret; + goto err_exit; if (pdata && pdata->setup) { ret = pdata->setup(client, chip->gpio_chip.base, @@ -816,6 +835,10 @@ static int pca953x_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); return 0; + +err_exit: + regulator_disable(chip->regulator); + return ret; } static int pca953x_remove(struct i2c_client *client) @@ -827,14 +850,14 @@ static int pca953x_remove(struct i2c_client *client) if (pdata && pdata->teardown) { ret = pdata->teardown(client, chip->gpio_chip.base, chip->gpio_chip.ngpio, pdata->context); - if (ret < 0) { + if (ret < 0) dev_err(&client->dev, "%s failed, %d\n", "teardown", ret); - return ret; - } } - return 0; + regulator_disable(chip->regulator); + + return ret; } /* convenience to stop overlong match-table lines */ -- cgit v0.10.2 From f72f4b44dfa88350b7072ca06fee83ce000a4177 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 20 Jul 2016 16:11:36 +0200 Subject: gpio: Add AXP209 GPIO driver The AXP209 PMIC has a bunch of GPIOs accessible, that are usually used to control LEDs or backlight. Add a driver for them Signed-off-by: Maxime Ripard Acked-by: Rob Herring Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio-axp209.txt b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt new file mode 100644 index 0000000..a661130 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-axp209.txt @@ -0,0 +1,30 @@ +AXP209 GPIO controller + +This driver follows the usual GPIO bindings found in +Documentation/devicetree/bindings/gpio/gpio.txt + +Required properties: +- compatible: Should be "x-powers,axp209-gpio" +- #gpio-cells: Should be two. The first cell is the pin number and the + second is the GPIO flags. +- gpio-controller: Marks the device node as a GPIO controller. + +This node must be a subnode of the axp20x PMIC, documented in +Documentation/devicetree/bindings/mfd/axp20x.txt + +Example: + +axp209: pmic@34 { + compatible = "x-powers,axp209"; + reg = <0x34>; + interrupt-parent = <&nmi_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <1>; + + axp_gpio: gpio { + compatible = "x-powers,axp209-gpio"; + gpio-controller; + #gpio-cells = <2>; + }; +}; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6de9062..ed968d1 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -116,6 +116,12 @@ config GPIO_ATH79 Select this option to enable GPIO driver for Atheros AR71XX/AR724X/AR913X SoC devices. +config GPIO_AXP209 + tristate "X-Powers AXP209 PMIC GPIO Support" + depends on MFD_AXP20X + help + Say yes to enable GPIO support for the AXP209 PMIC + config GPIO_BCM_KONA bool "Broadcom Kona GPIO" depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST) diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2a035ed..b7d509e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o +obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o diff --git a/drivers/gpio/gpio-axp209.c b/drivers/gpio/gpio-axp209.c new file mode 100644 index 0000000..3174799 --- /dev/null +++ b/drivers/gpio/gpio-axp209.c @@ -0,0 +1,162 @@ +/* + * AXP20x GPIO driver + * + * Copyright (C) 2016 Maxime Ripard + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXP20X_GPIO_FUNCTIONS 0x7 +#define AXP20X_GPIO_FUNCTION_OUT_LOW 0 +#define AXP20X_GPIO_FUNCTION_OUT_HIGH 1 +#define AXP20X_GPIO_FUNCTION_INPUT 2 + +struct axp20x_gpio { + struct gpio_chip chip; + struct regmap *regmap; +}; + +static int axp20x_gpio_get_reg(unsigned offset) +{ + switch (offset) { + case 0: + return AXP20X_GPIO0_CTRL; + case 1: + return AXP20X_GPIO1_CTRL; + case 2: + return AXP20X_GPIO2_CTRL; + } + + return -EINVAL; +} + +static int axp20x_gpio_input(struct gpio_chip *chip, unsigned offset) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + int reg; + + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return reg; + + return regmap_update_bits(gpio->regmap, reg, + AXP20X_GPIO_FUNCTIONS, + AXP20X_GPIO_FUNCTION_INPUT); +} + +static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + unsigned int val; + int reg, ret; + + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return reg; + + ret = regmap_read(gpio->regmap, reg, &val); + if (ret) + return ret; + + return !!(val & BIT(offset + 4)); +} + +static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + int reg; + + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return reg; + + return regmap_update_bits(gpio->regmap, reg, + AXP20X_GPIO_FUNCTIONS, + value ? AXP20X_GPIO_FUNCTION_OUT_HIGH + : AXP20X_GPIO_FUNCTION_OUT_LOW); +} + +static void axp20x_gpio_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + axp20x_gpio_output(chip, offset, value); +} + +static int axp20x_gpio_probe(struct platform_device *pdev) +{ + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct axp20x_gpio *gpio; + int ret; + + if (!of_device_is_available(pdev->dev.of_node)) + return -ENODEV; + + if (!axp20x) { + dev_err(&pdev->dev, "Parent drvdata not set\n"); + return -EINVAL; + } + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->chip.base = -1; + gpio->chip.can_sleep = true; + gpio->chip.parent = &pdev->dev; + gpio->chip.label = dev_name(&pdev->dev); + gpio->chip.owner = THIS_MODULE; + gpio->chip.get = axp20x_gpio_get; + gpio->chip.set = axp20x_gpio_set; + gpio->chip.direction_input = axp20x_gpio_input; + gpio->chip.direction_output = axp20x_gpio_output; + gpio->chip.ngpio = 3; + + gpio->regmap = axp20x->regmap; + + ret = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (ret) { + dev_err(&pdev->dev, "Failed to register GPIO chip\n"); + return ret; + } + + dev_info(&pdev->dev, "AXP209 GPIO driver loaded\n"); + + return 0; +} + +static const struct of_device_id axp20x_gpio_match[] = { + { .compatible = "x-powers,axp209-gpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, axp20x_gpio_match); + +static struct platform_driver axp20x_gpio_driver = { + .probe = axp20x_gpio_probe, + .driver = { + .name = "axp20x-gpio", + .of_match_table = axp20x_gpio_match, + }, +}; + +module_platform_driver(axp20x_gpio_driver); + +MODULE_AUTHOR("Maxime Ripard "); +MODULE_DESCRIPTION("AXP20x PMIC GPIO driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 0ba19cfc2a667ca986355e11d122c465482f12e2 Mon Sep 17 00:00:00 2001 From: Bin Gao Date: Mon, 25 Jul 2016 14:59:38 -0700 Subject: gpio: Add Intel WhiskeyCove PMIC GPIO driver This patch introduces a separate GPIO driver for Intel WhiskeyCove PMIC. This driver is based on gpio-crystalcove.c. Changes in v7: - Fixed various coding style comments from Andy Shevchenko Changes in v6: - Removed unnecessary wcove_gpio_remove() - Used devm_gpiochip_remove() instead of gpiochip_remove() - Various coding style changes per Mika's comment Changes in v5: - Revisited the interrupt handler code to iterate until all pending interrupts are handled. This change is to avoid missing interrupt when we're inside the interrupt handler. - Used regmap_bulk_read() to read address adjacent registers. Changes in v4: - Converted CTLI_INTCNT_XX macros to less verbose ones INT_DETECT_XX. - Add comments about why there is no .pm for the driver. - Header files re-ordered. - Various coding style change to address Andy's comments. Changes in v3: - Fixed the year in copyright line(2015-->2016). - Removed DRV_NAME macro. - Added kernel-doc for regmap_irq_chip of the wcove_gpio structure. - Line length fix. Changes in v2: - Typo fix (Whsikey --> Whiskey). - Included linux/gpio/driver.h instead of linux/gpio.h - Implemented .set_single_ended(). - Added GPIO register description. - Replaced container_of() with gpiochip_get_data(). - Removed unnecessary "if (gpio > WCOVE_VGPIO_NUM" check. - Removed the device id table and added MODULE_ALIAS(). Signed-off-by: Ajay Thomas Signed-off-by: Bin Gao Reviewed-by: Andy Shevchenko Reviewed-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ed968d1..591f692 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -969,6 +969,19 @@ config GPIO_UCB1400 This enables support for the Philips UCB1400 GPIO pins. The UCB1400 is an AC97 audio codec. +config GPIO_WHISKEY_COVE + tristate "GPIO support for Whiskey Cove PMIC" + depends on INTEL_SOC_PMIC + select GPIOLIB_IRQCHIP + help + Support for GPIO pins on Whiskey Cove PMIC. + + Say Yes if you have a Intel SoC based tablet with Whiskey Cove PMIC + inside. + + This driver can also be built as a module. If so, the module will be + called gpio-wcove. + config GPIO_WM831X tristate "WM831x GPIOs" depends on MFD_WM831X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b7d509e..031195c 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -121,6 +121,7 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c new file mode 100644 index 0000000..f5c88df --- /dev/null +++ b/drivers/gpio/gpio-wcove.c @@ -0,0 +1,455 @@ +/* + * Intel Whiskey Cove PMIC GPIO Driver + * + * This driver is written based on gpio-crystalcove.c + * + * Copyright (C) 2016 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Whiskey Cove PMIC has 13 physical GPIO pins divided into 3 banks: + * Bank 0: Pin 0 - 6 + * Bank 1: Pin 7 - 10 + * Bank 2: Pin 11 -12 + * Each pin has one output control register and one input control register. + */ +#define BANK0_NR_PINS 7 +#define BANK1_NR_PINS 4 +#define BANK2_NR_PINS 2 +#define WCOVE_GPIO_NUM (BANK0_NR_PINS + BANK1_NR_PINS + BANK2_NR_PINS) +#define WCOVE_VGPIO_NUM 94 +/* GPIO output control registers (one per pin): 0x4e44 - 0x4e50 */ +#define GPIO_OUT_CTRL_BASE 0x4e44 +/* GPIO input control registers (one per pin): 0x4e51 - 0x4e5d */ +#define GPIO_IN_CTRL_BASE 0x4e51 + +/* + * GPIO interrupts are organized in two groups: + * Group 0: Bank 0 pins (Pin 0 - 6) + * Group 1: Bank 1 and Bank 2 pins (Pin 7 - 12) + * Each group has two registers (one bit per pin): status and mask. + */ +#define GROUP0_NR_IRQS 7 +#define GROUP1_NR_IRQS 6 +#define IRQ_MASK_BASE 0x4e19 +#define IRQ_STATUS_BASE 0x4e0b +#define UPDATE_IRQ_TYPE BIT(0) +#define UPDATE_IRQ_MASK BIT(1) + +#define CTLI_INTCNT_DIS (0 << 1) +#define CTLI_INTCNT_NE (1 << 1) +#define CTLI_INTCNT_PE (2 << 1) +#define CTLI_INTCNT_BE (3 << 1) + +#define CTLO_DIR_IN (0 << 5) +#define CTLO_DIR_OUT (1 << 5) + +#define CTLO_DRV_MASK (1 << 4) +#define CTLO_DRV_OD (0 << 4) +#define CTLO_DRV_CMOS (1 << 4) + +#define CTLO_DRV_REN (1 << 3) + +#define CTLO_RVAL_2KDOWN (0 << 1) +#define CTLO_RVAL_2KUP (1 << 1) +#define CTLO_RVAL_50KDOWN (2 << 1) +#define CTLO_RVAL_50KUP (3 << 1) + +#define CTLO_INPUT_SET (CTLO_DRV_CMOS | CTLO_DRV_REN | CTLO_RVAL_2KUP) +#define CTLO_OUTPUT_SET (CTLO_DIR_OUT | CTLO_INPUT_SET) + +enum ctrl_register { + CTRL_IN, + CTRL_OUT, +}; + +/* + * struct wcove_gpio - Whiskey Cove GPIO controller + * @buslock: for bus lock/sync and unlock. + * @chip: the abstract gpio_chip structure. + * @dev: the gpio device + * @regmap: the regmap from the parent device. + * @regmap_irq_chip: the regmap of the gpio irq chip. + * @update: pending IRQ setting update, to be written to the chip upon unlock. + * @intcnt: the Interrupt Detect value to be written. + * @set_irq_mask: true if the IRQ mask needs to be set, false to clear. + */ +struct wcove_gpio { + struct mutex buslock; + struct gpio_chip chip; + struct device *dev; + struct regmap *regmap; + struct regmap_irq_chip_data *regmap_irq_chip; + int update; + int intcnt; + bool set_irq_mask; +}; + +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 (reg_type == CTRL_IN) + reg = GPIO_IN_CTRL_BASE + bank; + else + reg = GPIO_OUT_CTRL_BASE + bank; + + return reg; +} + +static void wcove_update_irq_mask(struct wcove_gpio *wg, int gpio) +{ + unsigned int reg, mask; + + if (gpio < GROUP0_NR_IRQS) { + reg = IRQ_MASK_BASE; + mask = BIT(gpio % GROUP0_NR_IRQS); + } else { + reg = IRQ_MASK_BASE + 1; + mask = BIT((gpio - GROUP0_NR_IRQS) % GROUP1_NR_IRQS); + } + + if (wg->set_irq_mask) + regmap_update_bits(wg->regmap, reg, mask, mask); + else + regmap_update_bits(wg->regmap, reg, mask, 0); +} + +static void wcove_update_irq_ctrl(struct wcove_gpio *wg, int gpio) +{ + unsigned int reg = to_reg(gpio, CTRL_IN); + + regmap_update_bits(wg->regmap, reg, CTLI_INTCNT_BE, wg->intcnt); +} + +static int wcove_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio) +{ + struct wcove_gpio *wg = gpiochip_get_data(chip); + + return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), + 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); + + return regmap_write(wg->regmap, to_reg(gpio, CTRL_OUT), + CTLO_OUTPUT_SET | value); +} + +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; + + ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &val); + if (ret) + return ret; + + return val & 0x1; +} + +static void wcove_gpio_set(struct gpio_chip *chip, + unsigned int gpio, int value) +{ + struct wcove_gpio *wg = gpiochip_get_data(chip); + + if (value) + regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 1); + else + regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), 1, 0); +} + +static int wcove_gpio_set_single_ended(struct gpio_chip *chip, + unsigned int gpio, + enum single_ended_mode mode) +{ + struct wcove_gpio *wg = gpiochip_get_data(chip); + + switch (mode) { + case LINE_MODE_OPEN_DRAIN: + return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), + CTLO_DRV_MASK, CTLO_DRV_OD); + case LINE_MODE_PUSH_PULL: + return regmap_update_bits(wg->regmap, to_reg(gpio, CTRL_OUT), + CTLO_DRV_MASK, CTLO_DRV_CMOS); + default: + break; + } + + return -ENOTSUPP; +} + +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); + + switch (type) { + case IRQ_TYPE_NONE: + wg->intcnt = CTLI_INTCNT_DIS; + break; + case IRQ_TYPE_EDGE_BOTH: + wg->intcnt = CTLI_INTCNT_BE; + break; + case IRQ_TYPE_EDGE_RISING: + wg->intcnt = CTLI_INTCNT_PE; + break; + case IRQ_TYPE_EDGE_FALLING: + wg->intcnt = CTLI_INTCNT_NE; + break; + default: + return -EINVAL; + } + + wg->update |= UPDATE_IRQ_TYPE; + + return 0; +} + +static void wcove_bus_lock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct wcove_gpio *wg = gpiochip_get_data(chip); + + mutex_lock(&wg->buslock); +} + +static void wcove_bus_sync_unlock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct wcove_gpio *wg = gpiochip_get_data(chip); + int gpio = data->hwirq; + + if (wg->update & UPDATE_IRQ_TYPE) + wcove_update_irq_ctrl(wg, gpio); + if (wg->update & UPDATE_IRQ_MASK) + wcove_update_irq_mask(wg, gpio); + wg->update = 0; + + mutex_unlock(&wg->buslock); +} + +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); + + wg->set_irq_mask = false; + wg->update |= UPDATE_IRQ_MASK; +} + +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); + + wg->set_irq_mask = true; + wg->update |= UPDATE_IRQ_MASK; +} + +static struct irq_chip wcove_irqchip = { + .name = "Whiskey Cove", + .irq_mask = wcove_irq_mask, + .irq_unmask = wcove_irq_unmask, + .irq_set_type = wcove_irq_type, + .irq_bus_lock = wcove_bus_lock, + .irq_bus_sync_unlock = wcove_bus_sync_unlock, +}; + +static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) +{ + struct wcove_gpio *wg = (struct wcove_gpio *)data; + unsigned int pending, virq, gpio, mask, offset; + u8 p[2]; + + if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) { + dev_err(wg->dev, "Failed to read irq status register\n"); + return IRQ_NONE; + } + + pending = p[0] | (p[1] << 8); + if (!pending) + return IRQ_NONE; + + /* Iterate until no interrupt is pending */ + while (pending) { + /* One iteration is for all pending bits */ + for_each_set_bit(gpio, (const unsigned long *)&pending, + GROUP0_NR_IRQS) { + offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0; + mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) : + BIT(gpio); + virq = irq_find_mapping(wg->chip.irqdomain, gpio); + handle_nested_irq(virq); + regmap_update_bits(wg->regmap, IRQ_STATUS_BASE + offset, + mask, mask); + } + + /* Next iteration */ + if (regmap_bulk_read(wg->regmap, IRQ_STATUS_BASE, p, 2)) { + dev_err(wg->dev, "Failed to read irq status\n"); + break; + } + + pending = p[0] | (p[1] << 8); + } + + return IRQ_HANDLED; +} + +static void wcove_gpio_dbg_show(struct seq_file *s, + struct gpio_chip *chip) +{ + unsigned int ctlo, ctli, irq_mask, irq_status; + struct wcove_gpio *wg = gpiochip_get_data(chip); + int gpio, offset, group, ret = 0; + + for (gpio = 0; gpio < WCOVE_GPIO_NUM; gpio++) { + group = gpio < GROUP0_NR_IRQS ? 0 : 1; + ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &ctlo); + ret += regmap_read(wg->regmap, to_reg(gpio, CTRL_IN), &ctli); + ret += regmap_read(wg->regmap, IRQ_MASK_BASE + group, + &irq_mask); + ret += regmap_read(wg->regmap, IRQ_STATUS_BASE + group, + &irq_status); + if (ret) { + pr_err("Failed to read registers: ctrl out/in or irq status/mask\n"); + break; + } + + offset = gpio % 8; + seq_printf(s, " gpio-%-2d %s %s %s %s ctlo=%2x,%s %s\n", + gpio, ctlo & CTLO_DIR_OUT ? "out" : "in ", + ctli & 0x1 ? "hi" : "lo", + ctli & CTLI_INTCNT_NE ? "fall" : " ", + ctli & CTLI_INTCNT_PE ? "rise" : " ", + ctlo, + irq_mask & BIT(offset) ? "mask " : "unmask", + irq_status & BIT(offset) ? "pending" : " "); + } +} + +static int wcove_gpio_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic; + struct wcove_gpio *wg; + int virq, ret, irq; + struct device *dev; + + /* + * This gpio platform device is created by a mfd device (see + * drivers/mfd/intel_soc_pmic_bxtwc.c for details). Information + * shared by all sub-devices created by the mfd device, the regmap + * pointer for instance, is stored as driver data of the mfd device + * driver. + */ + pmic = dev_get_drvdata(pdev->dev.parent); + if (!pmic) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dev = &pdev->dev; + + wg = devm_kzalloc(dev, sizeof(*wg), GFP_KERNEL); + if (!wg) + return -ENOMEM; + + wg->regmap_irq_chip = pmic->irq_chip_data_level2; + + platform_set_drvdata(pdev, wg); + + mutex_init(&wg->buslock); + wg->chip.label = KBUILD_MODNAME; + wg->chip.direction_input = wcove_gpio_dir_in; + wg->chip.direction_output = wcove_gpio_dir_out; + wg->chip.get = wcove_gpio_get; + wg->chip.set = wcove_gpio_set; + wg->chip.set_single_ended = wcove_gpio_set_single_ended, + wg->chip.base = -1; + wg->chip.ngpio = WCOVE_VGPIO_NUM; + wg->chip.can_sleep = true; + wg->chip.parent = pdev->dev.parent; + wg->chip.dbg_show = wcove_gpio_dbg_show; + wg->dev = dev; + wg->regmap = pmic->regmap; + + ret = devm_gpiochip_add_data(dev, &wg->chip, wg); + if (ret) { + dev_err(dev, "Failed to add gpiochip: %d\n", ret); + return ret; + } + + ret = gpiochip_irqchip_add(&wg->chip, &wcove_irqchip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) { + dev_err(dev, "Failed to add irqchip: %d\n", ret); + return ret; + } + + virq = regmap_irq_get_virq(wg->regmap_irq_chip, irq); + if (virq < 0) { + dev_err(dev, "Failed to get virq by irq %d\n", irq); + return virq; + } + + ret = devm_request_threaded_irq(dev, virq, NULL, + wcove_gpio_irq_handler, IRQF_ONESHOT, pdev->name, wg); + if (ret) { + dev_err(dev, "Failed to request irq %d\n", virq); + return ret; + } + + return 0; +} + +/* + * Whiskey Cove PMIC itself is a analog device(but with digital control + * interface) providing power management support for other devices in + * the accompanied SoC, so we have no .pm for Whiskey Cove GPIO driver. + */ +static struct platform_driver wcove_gpio_driver = { + .driver = { + .name = "bxt_wcove_gpio", + }, + .probe = wcove_gpio_probe, +}; + +module_platform_driver(wcove_gpio_driver); + +MODULE_AUTHOR("Ajay Thomas "); +MODULE_AUTHOR("Bin Gao "); +MODULE_DESCRIPTION("Intel Whiskey Cove GPIO Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:bxt_wcove_gpio"); -- cgit v0.10.2 From 6ea5dcdf7924d967a4d4b3aa7170e37a1be5cf0f Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 2 Aug 2016 09:57:47 -0400 Subject: gpio: Add GPIO support for the Diamond Systems GPIO-MM The Diamond Systems GPIO-MM device features 48 lines of digital I/O via the emulation of dual 82C55A PPI chips. This driver provides GPIO support for these 48 channels of digital I/O. The base port addresses for the devices may be configured via the base array module parameter. Signed-off-by: William Breathitt Gray Signed-off-by: Linus Walleij diff --git a/MAINTAINERS b/MAINTAINERS index 20bb1d0..a9ffa78 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3768,6 +3768,12 @@ F: include/linux/regulator/da9211.h F: include/sound/da[79]*.h F: sound/soc/codecs/da[79]*.[ch] +DIAMOND SYSTEMS GPIO-MM GPIO DRIVER +M: William Breathitt Gray +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-gpio-mm.c + DIGI NEO AND CLASSIC PCI PRODUCTS M: Lidza Louina M: Mark Hounschell diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 591f692..825cf3c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -558,6 +558,19 @@ config GPIO_F7188X To compile this driver as a module, choose M here: the module will be called f7188x-gpio. +config GPIO_GPIO_MM + tristate "Diamond Systems GPIO-MM GPIO support" + depends on ISA_BUS_API + help + Enables GPIO support for the Diamond Systems GPIO-MM and GPIO-MM-12. + + The Diamond Systems GPIO-MM device features 48 lines of digital I/O + via the emulation of dual 82C55A PPI chips. This driver provides GPIO + support for these 48 channels of digital I/O. + + The base port addresses for the devices may be configured via the base + array module parameter. + config GPIO_IT87 tristate "IT87xx GPIO support" help diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 031195c..9665028 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o +obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c new file mode 100644 index 0000000..1e7def9 --- /dev/null +++ b/drivers/gpio/gpio-gpio-mm.c @@ -0,0 +1,267 @@ +/* + * GPIO driver for the Diamond Systems GPIO-MM + * Copyright (C) 2016 William Breathitt Gray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * This driver supports the following Diamond Systems devices: GPIO-MM and + * GPIO-MM-12. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIOMM_EXTENT 8 +#define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT) + +static unsigned int base[MAX_NUM_GPIOMM]; +static unsigned int num_gpiomm; +module_param_array(base, uint, &num_gpiomm, 0); +MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses"); + +/** + * struct gpiomm_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @io_state: bit I/O state (whether bit is set to input or output) + * @out_state: output bits state + * @control: Control registers state + * @lock: synchronization lock to prevent I/O race conditions + * @base: base port address of the GPIO device + */ +struct gpiomm_gpio { + struct gpio_chip chip; + unsigned char io_state[6]; + unsigned char out_state[6]; + unsigned char control[2]; + spinlock_t lock; + unsigned int base; +}; + +static int gpiomm_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); + const unsigned int port = offset / 8; + const unsigned int mask = BIT(offset % 8); + + return !!(gpiommgpio->io_state[port] & mask); +} + +static int gpiomm_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); + const unsigned int io_port = offset / 8; + const unsigned int control_port = io_port / 3; + const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4; + unsigned long flags; + unsigned int control; + + spin_lock_irqsave(&gpiommgpio->lock, flags); + + /* Check if configuring Port C */ + if (io_port == 2 || io_port == 5) { + /* Port C can be configured by nibble */ + if (offset % 8 > 3) { + gpiommgpio->io_state[io_port] |= 0xF0; + gpiommgpio->control[control_port] |= BIT(3); + } else { + gpiommgpio->io_state[io_port] |= 0x0F; + gpiommgpio->control[control_port] |= BIT(0); + } + } else { + gpiommgpio->io_state[io_port] |= 0xFF; + if (io_port == 0 || io_port == 3) + gpiommgpio->control[control_port] |= BIT(4); + else + gpiommgpio->control[control_port] |= BIT(1); + } + + control = BIT(7) | gpiommgpio->control[control_port]; + outb(control, control_addr); + + spin_unlock_irqrestore(&gpiommgpio->lock, flags); + + return 0; +} + +static int gpiomm_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); + const unsigned int io_port = offset / 8; + const unsigned int control_port = io_port / 3; + const unsigned int mask = BIT(offset % 8); + const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4; + const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; + unsigned long flags; + unsigned int control; + + spin_lock_irqsave(&gpiommgpio->lock, flags); + + /* Check if configuring Port C */ + if (io_port == 2 || io_port == 5) { + /* Port C can be configured by nibble */ + if (offset % 8 > 3) { + gpiommgpio->io_state[io_port] &= 0x0F; + gpiommgpio->control[control_port] &= ~BIT(3); + } else { + gpiommgpio->io_state[io_port] &= 0xF0; + gpiommgpio->control[control_port] &= ~BIT(0); + } + } else { + gpiommgpio->io_state[io_port] &= 0x00; + if (io_port == 0 || io_port == 3) + gpiommgpio->control[control_port] &= ~BIT(4); + else + gpiommgpio->control[control_port] &= ~BIT(1); + } + + if (value) + gpiommgpio->out_state[io_port] |= mask; + else + gpiommgpio->out_state[io_port] &= ~mask; + + control = BIT(7) | gpiommgpio->control[control_port]; + outb(control, control_addr); + + outb(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port); + + spin_unlock_irqrestore(&gpiommgpio->lock, flags); + + return 0; +} + +static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); + const unsigned int port = offset / 8; + const unsigned int mask = BIT(offset % 8); + const unsigned int in_port = (port > 2) ? port + 1 : port; + unsigned long flags; + unsigned int port_state; + + spin_lock_irqsave(&gpiommgpio->lock, flags); + + /* ensure that GPIO is set for input */ + if (!(gpiommgpio->io_state[port] & mask)) { + spin_unlock_irqrestore(&gpiommgpio->lock, flags); + return -EINVAL; + } + + port_state = inb(gpiommgpio->base + in_port); + + spin_unlock_irqrestore(&gpiommgpio->lock, flags); + + return !!(port_state & mask); +} + +static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); + const unsigned int port = offset / 8; + const unsigned int mask = BIT(offset % 8); + const unsigned int out_port = (port > 2) ? port + 1 : port; + unsigned long flags; + + spin_lock_irqsave(&gpiommgpio->lock, flags); + + if (value) + gpiommgpio->out_state[port] |= mask; + else + gpiommgpio->out_state[port] &= ~mask; + + outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port); + + spin_unlock_irqrestore(&gpiommgpio->lock, flags); +} + +static int gpiomm_probe(struct device *dev, unsigned int id) +{ + struct gpiomm_gpio *gpiommgpio; + const char *const name = dev_name(dev); + int err; + + gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL); + if (!gpiommgpio) + return -ENOMEM; + + if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base[id], base[id] + GPIOMM_EXTENT); + return -EBUSY; + } + + gpiommgpio->chip.label = name; + gpiommgpio->chip.parent = dev; + gpiommgpio->chip.owner = THIS_MODULE; + gpiommgpio->chip.base = -1; + gpiommgpio->chip.ngpio = 48; + gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction; + gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input; + gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output; + gpiommgpio->chip.get = gpiomm_gpio_get; + gpiommgpio->chip.set = gpiomm_gpio_set; + gpiommgpio->base = base[id]; + + spin_lock_init(&gpiommgpio->lock); + + dev_set_drvdata(dev, gpiommgpio); + + err = gpiochip_add_data(&gpiommgpio->chip, gpiommgpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + /* initialize all GPIO as output */ + outb(0x80, base[id] + 3); + outb(0x00, base[id]); + outb(0x00, base[id] + 1); + outb(0x00, base[id] + 2); + outb(0x80, base[id] + 7); + outb(0x00, base[id] + 4); + outb(0x00, base[id] + 5); + outb(0x00, base[id] + 6); + + return 0; +} + +static int gpiomm_remove(struct device *dev, unsigned int id) +{ + struct gpiomm_gpio *const gpiommgpio = dev_get_drvdata(dev); + + gpiochip_remove(&gpiommgpio->chip); + + return 0; +} + +static struct isa_driver gpiomm_driver = { + .probe = gpiomm_probe, + .driver = { + .name = "gpio-mm" + }, + .remove = gpiomm_remove +}; + +module_isa_driver(gpiomm_driver, num_gpiomm); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From c17c2db99cb3ffc58ae4d184af54f0d172321ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= Date: Wed, 3 Aug 2016 14:05:56 +0200 Subject: gpio: dt-bindings: add brcm,bcm6345-gpio bindings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the device tree bindings for the Broadcom's BCM6345 memory-mapped GPIO controllers. The gpios will be supported by gpio-mmio code of the GPIO generic library. Signed-off-by: Álvaro Fernández Rojas Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.txt b/Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.txt new file mode 100644 index 0000000..e785314 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/brcm,bcm6345-gpio.txt @@ -0,0 +1,46 @@ +Bindings for the Broadcom's brcm,bcm6345-gpio memory-mapped GPIO controllers. + +These bindings can be used on any BCM63xx SoC. However, BCM6338 and BCM6345 +are the only ones which don't need a pinctrl driver. +BCM6338 have 8-bit data and dirout registers, where GPIO state can be read +and/or written, and the direction changed from input to output. +BCM6345 have 16-bit data and dirout registers, where GPIO state can be read +and/or written, and the direction changed from input to output. + +Required properties: + - compatible: should be "brcm,bcm6345-gpio" + - reg-names: must contain + "dat" - data register + "dirout" - direction (output) register + - reg: address + size pairs describing the GPIO register sets; + order must correspond with the order of entries in reg-names + - #gpio-cells: must be set to 2. The first cell is the pin number and + the second cell is used to specify the gpio polarity: + 0 = active high + 1 = active low + - gpio-controller: Marks the device node as a gpio controller. + +Optional properties: + - native-endian: use native endian memory. + +Examples: + - BCM6338: + gpio: gpio-controller@fffe0407 { + compatible = "brcm,bcm6345-gpio"; + reg-names = "dirout", "dat"; + reg = <0xfffe0407 1>, <0xfffe040f 1>; + + #gpio-cells = <2>; + gpio-controller; + }; + + - BCM6345: + gpio: gpio-controller@fffe0406 { + compatible = "brcm,bcm6345-gpio"; + reg-names = "dirout", "dat"; + reg = <0xfffe0406 2>, <0xfffe040a 2>; + native-endian; + + #gpio-cells = <2>; + gpio-controller; + }; -- cgit v0.10.2 From 05cc995f4d44c2b14a1f15f3271cc9715cb81099 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Wed, 3 Aug 2016 14:05:57 +0200 Subject: gpio: mmio: add brcm,bcm6345 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the GPIO found in Broadcom's bcm63xx-gpio chips. This GPIO controller is used in the following Broadcom SoCs: BCM6338, BCM6345. It can be used in newer SoCs, without the capability of pin multiplexing. Signed-off-by: Christian Lamparter Signed-off-by: Álvaro Fernández Rojas Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 6ec144b..d7d03ad 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -573,6 +573,7 @@ static void __iomem *bgpio_map(struct platform_device *pdev, #ifdef CONFIG_OF static const struct of_device_id bgpio_of_match[] = { + { .compatible = "brcm,bcm6345-gpio" }, { .compatible = "wd,mbl-gpio" }, { } }; @@ -593,6 +594,9 @@ static struct bgpio_pdata *bgpio_parse_dt(struct platform_device *pdev, pdata->base = -1; + if (of_device_is_big_endian(pdev->dev.of_node)) + *flags |= BGPIOF_BIG_ENDIAN_BYTE_ORDER; + if (of_property_read_bool(pdev->dev.of_node, "no-output")) *flags |= BGPIOF_NO_OUTPUT; -- cgit v0.10.2 From acf06ff76cb94dd98ba1fa74e63a8ee578673a83 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 12 Aug 2016 01:21:58 +0900 Subject: gpio: refactor gpiochip_find() slightly The if...else... block after the loop can be dropped with a slight refactoring. Signed-off-by: Masahiro Yamada Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 53ff25a..deaf4d4 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1363,19 +1363,15 @@ struct gpio_chip *gpiochip_find(void *data, void *data)) { struct gpio_device *gdev; - struct gpio_chip *chip; + struct gpio_chip *chip = NULL; unsigned long flags; spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(gdev, &gpio_devices, list) - if (gdev->chip && match(gdev->chip, data)) + if (gdev->chip && match(gdev->chip, data)) { + chip = gdev->chip; break; - - /* No match? */ - if (&gdev->list == &gpio_devices) - chip = NULL; - else - chip = gdev->chip; + } spin_unlock_irqrestore(&gpio_lock, flags); -- cgit v0.10.2 From ccb3611f5ac7db5e350a00e80d1b64d87789c984 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Thu, 11 Aug 2016 13:40:13 -0500 Subject: gpio: dt-bindings: remove ti,tps65086-gpio binding This binding is no longer correct, the GPIO information can should be added to the parent node and not into this child node. See Documentation/devicetree/bindings/mfd/tps65086.txt for the correct usage. Signed-off-by: Andrew F. Davis Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio-tps65086.txt b/Documentation/devicetree/bindings/gpio/gpio-tps65086.txt deleted file mode 100644 index ba05107..0000000 --- a/Documentation/devicetree/bindings/gpio/gpio-tps65086.txt +++ /dev/null @@ -1,16 +0,0 @@ -* TPS65086 GPO Controller bindings - -Required properties: - - compatible : Should be "ti,tps65086-gpio". - - gpio-controller : Marks the device node as a GPIO Controller. - - #gpio-cells : Should be two. The first cell is the pin number - and the second cell is used to specify flags. - See ../gpio/gpio.txt for possible values. - -Example: - - gpio4: gpio { - compatible = "ti,tps65086-gpio"; - gpio-controller; - #gpio-cells = <2>; - }; -- cgit v0.10.2 From 4cebaf17ecde6d3f78513932ab239af922750e09 Mon Sep 17 00:00:00 2001 From: Lucile Quirion Date: Fri, 12 Aug 2016 11:16:48 -0400 Subject: gpio: add bindings for Technologic I2C-FPGA gpio controller Device tree binding documentation for Technologic's I2C-FPGA GPIO controller. Signed-off-by: Lucile Quirion [Quirked in a reference to gpio.txt] Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt b/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt new file mode 100644 index 0000000..3f8e71b --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-ts4900.txt @@ -0,0 +1,30 @@ +* Technologic Systems I2C-FPGA's GPIO controller bindings + +This bindings describes the GPIO controller for Technologic's FPGA core. +TS-4900's FPGA encodes the GPIO state on 3 bits, whereas the TS-7970's FPGA +uses 2 bits: it doesn't use a dedicated input bit. + +Required properties: +- compatible: Should be one of the following + "technologic,ts4900-gpio" + "technologic,ts7970-gpio" +- reg: Physical base address of the controller and length + of memory mapped region. +- #gpio-cells: Should be two. The first cell is the pin number. +- gpio-controller: Marks the device node as a gpio controller. + +Optional property: +- ngpios: Number of GPIOs this controller is instantiated with, + the default is 32. See gpio.txt for more details. + +Example: + +&i2c2 { + gpio8: gpio@28 { + compatible = "technologic,ts4900-gpio"; + reg = <0x28>; + #gpio-cells = <2>; + gpio-controller; + ngpios = <32>; + }; +}; -- cgit v0.10.2 From 9c6686322d749814e3e7af492954ca1777444023 Mon Sep 17 00:00:00 2001 From: Lucile Quirion Date: Fri, 12 Aug 2016 11:16:49 -0400 Subject: gpio: add Technologic I2C-FPGA gpio support This driver is generic and aims to support all Technologic Systems's boards embedding FPGA GPIOs with an I2C interface. This driver supports TS-4900, TS-7970, TS-7990 and TS-4100 series. Signed-off-by: Lucile Quirion Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 825cf3c..5eb7c86 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -777,6 +777,13 @@ config GPIO_TPIC2810 To compile this driver as a module, choose M here: the module will be called gpio-tpic2810. +config GPIO_TS4900 + tristate "Technologic Systems FPGA I2C GPIO" + select REGMAP_I2C + help + Say yes here to enabled the GPIO driver for Technologic's FPGA core. + Series supported include TS-4100, TS-4900, TS-7970 and TS-7990. + endmenu menu "MFD GPIO expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9665028..691fb3a 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -112,6 +112,7 @@ obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o obj-$(CONFIG_GPIO_TS4800) += gpio-ts4800.o +obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c new file mode 100644 index 0000000..9dd9aca --- /dev/null +++ b/drivers/gpio/gpio-ts4900.c @@ -0,0 +1,190 @@ +/* + * Digital I/O driver for Technologic Systems I2C FPGA Core + * + * Copyright (C) 2015 Technologic Systems + * Copyright (C) 2016 Savoir-Faire Linux + * + * 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. + */ + +#include +#include +#include +#include +#include + +#define DEFAULT_PIN_NUMBER 32 +/* + * Register bits used by the GPIO device + * Some boards, such as TS-7970 do not have a separate input bit + */ +#define TS4900_GPIO_OE 0x01 +#define TS4900_GPIO_OUT 0x02 +#define TS4900_GPIO_IN 0x04 +#define TS7970_GPIO_IN 0x02 + +struct ts4900_gpio_priv { + struct regmap *regmap; + struct gpio_chip gpio_chip; + unsigned int input_bit; +}; + +static int ts4900_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); + unsigned int reg; + + regmap_read(priv->regmap, offset, ®); + + return !(reg & TS4900_GPIO_OE); +} + +static int ts4900_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); + + /* + * This will clear the output enable bit, the other bits are + * dontcare when this is cleared + */ + return regmap_write(priv->regmap, offset, 0); +} + +static int ts4900_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); + int ret; + + if (value) + ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE | + TS4900_GPIO_OUT); + else + ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE); + + return ret; +} + +static int ts4900_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); + unsigned int reg; + + regmap_read(priv->regmap, offset, ®); + + return !!(reg & priv->input_bit); +} + +static void ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); + + if (value) + regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, + TS4900_GPIO_OUT); + else + regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0); +} + +static const struct regmap_config ts4900_regmap_config = { + .reg_bits = 16, + .val_bits = 8, +}; + +static struct gpio_chip template_chip = { + .label = "ts4900-gpio", + .owner = THIS_MODULE, + .get_direction = ts4900_gpio_get_direction, + .direction_input = ts4900_gpio_direction_input, + .direction_output = ts4900_gpio_direction_output, + .get = ts4900_gpio_get, + .set = ts4900_gpio_set, + .base = -1, + .can_sleep = true, +}; + +static const struct of_device_id ts4900_gpio_of_match_table[] = { + { + .compatible = "technologic,ts4900-gpio", + .data = (void *)TS4900_GPIO_IN, + }, { + .compatible = "technologic,ts7970-gpio", + .data = (void *)TS7970_GPIO_IN, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ts4900_gpio_of_match_table); + +static int ts4900_gpio_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct of_device_id *match; + struct ts4900_gpio_priv *priv; + u32 ngpio; + int ret; + + match = of_match_device(ts4900_gpio_of_match_table, &client->dev); + if (!match) + return -EINVAL; + + if (of_property_read_u32(client->dev.of_node, "ngpios", &ngpio)) + ngpio = DEFAULT_PIN_NUMBER; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->gpio_chip = template_chip; + priv->gpio_chip.label = "ts4900-gpio"; + priv->gpio_chip.ngpio = ngpio; + priv->gpio_chip.parent = &client->dev; + priv->input_bit = (uintptr_t)match->data; + + priv->regmap = devm_regmap_init_i2c(client, &ts4900_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + ret = devm_gpiochip_add_data(&client->dev, &priv->gpio_chip, priv); + if (ret < 0) { + dev_err(&client->dev, "Unable to register gpiochip\n"); + return ret; + } + + i2c_set_clientdata(client, priv); + + return 0; +} + +static const struct i2c_device_id ts4900_gpio_id_table[] = { + { "ts4900-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table); + +static struct i2c_driver ts4900_gpio_driver = { + .driver = { + .name = "ts4900-gpio", + .of_match_table = ts4900_gpio_of_match_table, + }, + .probe = ts4900_gpio_probe, + .id_table = ts4900_gpio_id_table, +}; +module_i2c_driver(ts4900_gpio_driver); + +MODULE_AUTHOR("Technologic Systems"); +MODULE_DESCRIPTION("GPIO interface for Technologic Systems I2C-FPGA core"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 7d9e59ce761f2ebe0bf57e6467cc13d932ee109b Mon Sep 17 00:00:00 2001 From: Bin Gao Date: Mon, 15 Aug 2016 11:03:23 -0700 Subject: gpio: wcove-gpio: add get_direction method This patch adds .get_direction method for the gpio_chip structure of the wcove_gpio driver. Signed-off-by: Bin Gao Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index f5c88df..e11d6a3 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -164,6 +164,19 @@ static int wcove_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio, 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; + + ret = regmap_read(wg->regmap, to_reg(gpio, CTRL_OUT), &val); + if (ret) + return ret; + + return !(val & CTLO_DIR_OUT); +} + static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) { struct wcove_gpio *wg = gpiochip_get_data(chip); @@ -394,6 +407,7 @@ static int wcove_gpio_probe(struct platform_device *pdev) wg->chip.label = KBUILD_MODNAME; wg->chip.direction_input = wcove_gpio_dir_in; wg->chip.direction_output = wcove_gpio_dir_out; + wg->chip.get_direction = wcove_gpio_get_direction; wg->chip.get = wcove_gpio_get; wg->chip.set = wcove_gpio_set; wg->chip.set_single_ended = wcove_gpio_set_single_ended, -- cgit v0.10.2 From 98102880c750d38b3be355fbcbc85a181fa3298a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 22 Aug 2016 12:48:30 -0400 Subject: gpio: msic: drop unused MODULE_ tags from non-modular code The Kconfig currently controlling compilation of this code is: drivers/gpio/Kconfig:config GPIO_MSIC drivers/gpio/Kconfig: bool "Intel MSIC mixed signal gpio support" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is already contained at the top of the file in the comments. We don't replace module.h with init.h since the file already has that. Cc: Alexandre Courbot Cc: Mathias Nyman Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index d756497..1b7ce7f 100644 --- a/drivers/gpio/gpio-msic.c +++ b/drivers/gpio/gpio-msic.c @@ -20,7 +20,6 @@ * */ -#include #include #include #include @@ -328,9 +327,4 @@ static int __init platform_msic_gpio_init(void) { return platform_driver_register(&platform_msic_gpio_driver); } - subsys_initcall(platform_msic_gpio_init); - -MODULE_AUTHOR("Mathias Nyman "); -MODULE_DESCRIPTION("Intel Medfield MSIC GPIO driver"); -MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 2c8d6c869feba71fce7496eda04297d18561d623 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 22 Aug 2016 12:48:31 -0400 Subject: gpio: mxc: drop unused MODULE_ tags from non-modular code The Kconfig currently controlling compilation of this code is: drivers/gpio/Kconfig:config GPIO_MXC drivers/gpio/Kconfig: def_bool y ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information was (or is now) contained at the top of the file in the comments. Note the original e-mail had a missing/typo'd @ symbol anyway. We don't replace module.h with init.h since the file already has that. Cc: Alexandre Courbot Cc: Daniel Mack Cc: Juergen Beisert Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 1b342a3..e35af52 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -2,7 +2,8 @@ * MXC GPIO support. (c) 2008 Daniel Mack * Copyright 2008 Juergen Beisert, kernel@pengutronix.de * - * Based on code from Freescale, + * Based on code from Freescale Semiconductor, + * Authors: Daniel Mack, Juergen Beisert. * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -33,7 +34,6 @@ #include #include #include -#include #include enum mxc_gpio_hwtype { @@ -511,9 +511,3 @@ static int __init gpio_mxc_init(void) return platform_driver_register(&mxc_gpio_driver); } postcore_initcall(gpio_mxc_init); - -MODULE_AUTHOR("Freescale Semiconductor, " - "Daniel Mack , " - "Juergen Beisert "); -MODULE_DESCRIPTION("Freescale MXC GPIO"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From b29c5ddaea75a3d4ed6f50ca25935e552cf89753 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 22 Aug 2016 12:48:32 -0400 Subject: gpio: spear-spics: drop unused MODULE_ tags from non-modular code The Kconfig currently controlling compilation of this code is: drivers/gpio/Kconfig:config GPIO_SPEAR_SPICS drivers/gpio/Kconfig: bool "ST SPEAr13xx SPI Chip Select as GPIO support" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is now contained at the top of the file in the comments. Also note that MODULE_DEVICE_TABLE is a no-op for non-modular code. Cc: Alexandre Courbot Cc: Shiraz Hashim Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c index 7ffd164..2226747 100644 --- a/drivers/gpio/gpio-spear-spics.c +++ b/drivers/gpio/gpio-spear-spics.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -183,7 +183,6 @@ static const struct of_device_id spics_gpio_of_match[] = { { .compatible = "st,spear-spics-gpio" }, {} }; -MODULE_DEVICE_TABLE(of, spics_gpio_of_match); static struct platform_driver spics_gpio_driver = { .probe = spics_gpio_probe, @@ -198,7 +197,3 @@ static int __init spics_gpio_init(void) return platform_driver_register(&spics_gpio_driver); } subsys_initcall(spics_gpio_init); - -MODULE_AUTHOR("Shiraz Hashim "); -MODULE_DESCRIPTION("STMicroelectronics SPEAr SPI Chip Select Abstraction"); -MODULE_LICENSE("GPL"); -- cgit v0.10.2 From adaaf63e24987d197aed8dc11493c4fd9c40f138 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 22 Aug 2016 12:48:33 -0400 Subject: gpio: vf610: drop unused MODULE_ tags from non-modular code The Kconfig currently controlling compilation of this code is: drivers/gpio/Kconfig:config GPIO_VF610 drivers/gpio/Kconfig: def_bool y ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, so that when reading the driver there is no doubt it is builtin-only. We delete the MODULE_LICENSE tag etc. since all that information is now contained at the top of the file in the comments. We don't replace module.h with init.h since the file already has that. Cc: Alexandre Courbot Cc: Stefan Agner Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 6284bdb..3edb09c 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -1,5 +1,5 @@ /* - * vf610 GPIO support through PORT and GPIO module + * Freescale vf610 GPIO support through PORT and GPIO * * Copyright (c) 2014 Toradex AG. * @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -289,7 +288,3 @@ static int __init gpio_vf610_init(void) return platform_driver_register(&vf610_gpio_driver); } device_initcall(gpio_vf610_init); - -MODULE_AUTHOR("Stefan Agner "); -MODULE_DESCRIPTION("Freescale VF610 GPIO"); -MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 54c928d335d6822bfe9a8b67850aaaa74d88a769 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Tue, 23 Aug 2016 14:45:15 +0300 Subject: dt-bindings: GPIO: Add TPIC2810 binding Add binding for TPIC2810 GPO device. Signed-off-by: Andrew F. Davis Signed-off-by: Roger Quadros Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt new file mode 100644 index 0000000..1afc2de --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt @@ -0,0 +1,16 @@ +TPIC2810 GPIO controller bindings + +Required properties: + - compatible : Should be "ti,tpic2810". + - reg : The I2C address of the device + - gpio-controller : Marks the device node as a GPIO controller. + - #gpio-cells : Should be two. For consumer use see gpio.txt. + +Example: + + gpio@60 { + compatible = "ti,tpic2810"; + reg = <0x60>; + gpio-controller; + #gpio-cells = <2>; + }; -- cgit v0.10.2 From dc21c7ad3a8aad79cb14128c321833a47dc921c2 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 31 Aug 2016 14:28:10 +0530 Subject: mfd: lp873x: Add lp873x PMIC support The LP873X chip is a power management IC for Portable Navigation Systems and Tablet Computing devices. It contains the following components: - Regulators. - Configurable General Purpose Output Signals (GPO). PMIC interacts with the main processor through i2c. PMIC has couple of LDOs (Linear Regulators), couple of BUCKs (Step-Down DC-DC Converter Cores) and GPOs (General Purpose Output Signals). Signed-off-by: Keerthy Signed-off-by: Lee Jones diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2d1fb64..69e51d0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1224,6 +1224,20 @@ config MFD_TPS65217 This driver can also be built as a module. If so, the module will be called tps65217. +config MFD_TI_LP873X + tristate "TI LP873X Power Management IC" + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + If you say yes here then you get support for the LP873X series of + Power Management Integrated Circuits (PMIC). + These include voltage regulators, thermal protection, configurable + General Purpose Outputs (GPO) that are used in portable devices. + + This driver can also be built as a module. If so, the module + will be called lp873x. + config MFD_TPS65218 tristate "TI TPS65218 Power Management chips" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2ba3ba3..42acbcd 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -22,6 +22,8 @@ obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o +obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o + obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c new file mode 100644 index 0000000..9af064c --- /dev/null +++ b/drivers/mfd/lp873x.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * + * Author: Keerthy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include + +static const struct regmap_config lp873x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = LP873X_REG_MAX, +}; + +static const struct mfd_cell lp873x_cells[] = { + { .name = "lp873x-regulator", }, + { .name = "lp873x-gpio", }, +}; + +static int lp873x_probe(struct i2c_client *client, + const struct i2c_device_id *ids) +{ + struct lp873x *lp873; + int ret; + unsigned int otpid; + + lp873 = devm_kzalloc(&client->dev, sizeof(*lp873), GFP_KERNEL); + if (!lp873) + return -ENOMEM; + + lp873->dev = &client->dev; + + lp873->regmap = devm_regmap_init_i2c(client, &lp873x_regmap_config); + if (IS_ERR(lp873->regmap)) { + ret = PTR_ERR(lp873->regmap); + dev_err(lp873->dev, + "Failed to initialize register map: %d\n", ret); + return ret; + } + + mutex_init(&lp873->lock); + + ret = regmap_read(lp873->regmap, LP873X_REG_OTP_REV, &otpid); + if (ret) { + dev_err(lp873->dev, "Failed to read OTP ID\n"); + return ret; + } + + lp873->rev = otpid & LP873X_OTP_REV_OTP_ID; + + i2c_set_clientdata(client, lp873); + + ret = mfd_add_devices(lp873->dev, PLATFORM_DEVID_AUTO, lp873x_cells, + ARRAY_SIZE(lp873x_cells), NULL, 0, NULL); + + return ret; +} + +static const struct of_device_id of_lp873x_match_table[] = { + { .compatible = "ti,lp8733", }, + { .compatible = "ti,lp8732", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_lp873x_match_table); + +static const struct i2c_device_id lp873x_id_table[] = { + { "lp873x", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, lp873x_id_table); + +static struct i2c_driver lp873x_driver = { + .driver = { + .name = "lp873x", + .of_match_table = of_lp873x_match_table, + }, + .probe = lp873x_probe, + .id_table = lp873x_id_table, +}; +module_i2c_driver(lp873x_driver); + +MODULE_AUTHOR("J Keerthy "); +MODULE_DESCRIPTION("LP873X chip family Multi-Function Device driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/lp873x.h b/include/linux/mfd/lp873x.h new file mode 100644 index 0000000..83b1bd7 --- /dev/null +++ b/include/linux/mfd/lp873x.h @@ -0,0 +1,269 @@ +/* + * Functions to access LP873X power management chip. + * + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_MFD_LP873X_H +#define __LINUX_MFD_LP873X_H + +#include +#include +#include + +/* LP873x chip id list */ +#define LP873X 0x00 + +/* All register addresses */ +#define LP873X_REG_DEV_REV 0X00 +#define LP873X_REG_OTP_REV 0X01 +#define LP873X_REG_BUCK0_CTRL_1 0X02 +#define LP873X_REG_BUCK0_CTRL_2 0X03 +#define LP873X_REG_BUCK1_CTRL_1 0X04 +#define LP873X_REG_BUCK1_CTRL_2 0X05 +#define LP873X_REG_BUCK0_VOUT 0X06 +#define LP873X_REG_BUCK1_VOUT 0X07 +#define LP873X_REG_LDO0_CTRL 0X08 +#define LP873X_REG_LDO1_CTRL 0X09 +#define LP873X_REG_LDO0_VOUT 0X0A +#define LP873X_REG_LDO1_VOUT 0X0B +#define LP873X_REG_BUCK0_DELAY 0X0C +#define LP873X_REG_BUCK1_DELAY 0X0D +#define LP873X_REG_LDO0_DELAY 0X0E +#define LP873X_REG_LDO1_DELAY 0X0F +#define LP873X_REG_GPO_DELAY 0X10 +#define LP873X_REG_GPO2_DELAY 0X11 +#define LP873X_REG_GPO_CTRL 0X12 +#define LP873X_REG_CONFIG 0X13 +#define LP873X_REG_PLL_CTRL 0X14 +#define LP873X_REG_PGOOD_CTRL1 0X15 +#define LP873X_REG_PGOOD_CTRL2 0X16 +#define LP873X_REG_PG_FAULT 0X17 +#define LP873X_REG_RESET 0X18 +#define LP873X_REG_INT_TOP_1 0X19 +#define LP873X_REG_INT_TOP_2 0X1A +#define LP873X_REG_INT_BUCK 0X1B +#define LP873X_REG_INT_LDO 0X1C +#define LP873X_REG_TOP_STAT 0X1D +#define LP873X_REG_BUCK_STAT 0X1E +#define LP873X_REG_LDO_STAT 0x1F +#define LP873X_REG_TOP_MASK_1 0x20 +#define LP873X_REG_TOP_MASK_2 0x21 +#define LP873X_REG_BUCK_MASK 0x22 +#define LP873X_REG_LDO_MASK 0x23 +#define LP873X_REG_SEL_I_LOAD 0x24 +#define LP873X_REG_I_LOAD_2 0x25 +#define LP873X_REG_I_LOAD_1 0x26 + +#define LP873X_REG_MAX LP873X_REG_I_LOAD_1 + +/* Register field definitions */ +#define LP873X_DEV_REV_DEV_ID 0xC0 +#define LP873X_DEV_REV_ALL_LAYER 0x30 +#define LP873X_DEV_REV_METAL_LAYER 0x0F + +#define LP873X_OTP_REV_OTP_ID 0xFF + +#define LP873X_BUCK0_CTRL_1_BUCK0_FPWM BIT(3) +#define LP873X_BUCK0_CTRL_1_BUCK0_RDIS_EN BIT(2) +#define LP873X_BUCK0_CTRL_1_BUCK0_EN_PIN_CTRL BIT(1) +#define LP873X_BUCK0_CTRL_1_BUCK0_EN BIT(0) + +#define LP873X_BUCK0_CTRL_2_BUCK0_ILIM 0x38 +#define LP873X_BUCK0_CTRL_2_BUCK0_SLEW_RATE 0x07 + +#define LP873X_BUCK1_CTRL_1_BUCK1_FPWM BIT(3) +#define LP873X_BUCK1_CTRL_1_BUCK1_RDIS_EN BIT(2) +#define LP873X_BUCK1_CTRL_1_BUCK1_EN_PIN_CTRL BIT(1) +#define LP873X_BUCK1_CTRL_1_BUCK1_EN BIT(0) + +#define LP873X_BUCK1_CTRL_2_BUCK1_ILIM 0x38 +#define LP873X_BUCK1_CTRL_2_BUCK1_SLEW_RATE 0x07 + +#define LP873X_BUCK0_VOUT_BUCK0_VSET 0xFF + +#define LP873X_BUCK1_VOUT_BUCK1_VSET 0xFF + +#define LP873X_LDO0_CTRL_LDO0_RDIS_EN BIT(2) +#define LP873X_LDO0_CTRL_LDO0_EN_PIN_CTRL BIT(1) +#define LP873X_LDO0_CTRL_LDO0_EN BIT(0) + +#define LP873X_LDO1_CTRL_LDO1_RDIS_EN BIT(2) +#define LP873X_LDO1_CTRL_LDO1_EN_PIN_CTRL BIT(1) +#define LP873X_LDO1_CTRL_LDO1_EN BIT(0) + +#define LP873X_LDO0_VOUT_LDO0_VSET 0x1F + +#define LP873X_LDO1_VOUT_LDO1_VSET 0x1F + +#define LP873X_BUCK0_DELAY_BUCK0_SD_DELAY 0xF0 +#define LP873X_BUCK0_DELAY_BUCK0_SU_DELAY 0x0F + +#define LP873X_BUCK1_DELAY_BUCK1_SD_DELAY 0xF0 +#define LP873X_BUCK1_DELAY_BUCK1_SU_DELAY 0x0F + +#define LP873X_LDO0_DELAY_LDO0_SD_DELAY 0xF0 +#define LP873X_LDO0_DELAY_LDO0_SU_DELAY 0x0F + +#define LP873X_LDO1_DELAY_LDO1_SD_DELAY 0xF0 +#define LP873X_LDO1_DELAY_LDO1_SU_DELAY 0x0F + +#define LP873X_GPO_DELAY_GPO_SD_DELAY 0xF0 +#define LP873X_GPO_DELAY_GPO_SU_DELAY 0x0F + +#define LP873X_GPO2_DELAY_GPO2_SD_DELAY 0xF0 +#define LP873X_GPO2_DELAY_GPO2_SU_DELAY 0x0F + +#define LP873X_GPO_CTRL_GPO2_OD BIT(6) +#define LP873X_GPO_CTRL_GPO2_EN_PIN_CTRL BIT(5) +#define LP873X_GPO_CTRL_GPO2_EN BIT(4) +#define LP873X_GPO_CTRL_GPO_OD BIT(2) +#define LP873X_GPO_CTRL_GPO_EN_PIN_CTRL BIT(1) +#define LP873X_GPO_CTRL_GPO_EN BIT(0) + +#define LP873X_CONFIG_SU_DELAY_SEL BIT(6) +#define LP873X_CONFIG_SD_DELAY_SEL BIT(5) +#define LP873X_CONFIG_CLKIN_PIN_SEL BIT(4) +#define LP873X_CONFIG_CLKIN_PD BIT(3) +#define LP873X_CONFIG_EN_PD BIT(2) +#define LP873X_CONFIG_TDIE_WARN_LEVEL BIT(1) +#define LP873X_EN_SPREAD_SPEC BIT(0) + +#define LP873X_PLL_CTRL_EN_PLL BIT(6) +#define LP873X_EXT_CLK_FREQ 0x1F + +#define LP873X_PGOOD_CTRL1_PGOOD_POL BIT(7) +#define LP873X_PGOOD_CTRL1_PGOOD_OD BIT(6) +#define LP873X_PGOOD_CTRL1_PGOOD_WINDOW_LDO BIT(5) +#define LP873X_PGOOD_CTRL1_PGOOD_WINDOWN_BUCK BIT(4) +#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_LDO1 BIT(3) +#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_LDO0 BIT(2) +#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_BUCK1 BIT(1) +#define LP873X_PGOOD_CTRL1_PGOOD_EN_PGOOD_BUCK0 BIT(0) + +#define LP873X_PGOOD_CTRL2_EN_PGOOD_TWARN BIT(2) +#define LP873X_PGOOD_CTRL2_EN_PG_FAULT_GATE BIT(1) +#define LP873X_PGOOD_CTRL2_PGOOD_MODE BIT(0) + +#define LP873X_PG_FAULT_PG_FAULT_LDO1 BIT(3) +#define LP873X_PG_FAULT_PG_FAULT_LDO0 BIT(2) +#define LP873X_PG_FAULT_PG_FAULT_BUCK1 BIT(1) +#define LP873X_PG_FAULT_PG_FAULT_BUCK0 BIT(0) + +#define LP873X_RESET_SW_RESET BIT(0) + +#define LP873X_INT_TOP_1_PGOOD_INT BIT(7) +#define LP873X_INT_TOP_1_LDO_INT BIT(6) +#define LP873X_INT_TOP_1_BUCK_INT BIT(5) +#define LP873X_INT_TOP_1_SYNC_CLK_INT BIT(4) +#define LP873X_INT_TOP_1_TDIE_SD_INT BIT(3) +#define LP873X_INT_TOP_1_TDIE_WARN_INT BIT(2) +#define LP873X_INT_TOP_1_OVP_INT BIT(1) +#define LP873X_INT_TOP_1_I_MEAS_INT BIT(0) + +#define LP873X_INT_TOP_2_RESET_REG_INT BIT(0) + +#define LP873X_INT_BUCK_BUCK1_PG_INT BIT(6) +#define LP873X_INT_BUCK_BUCK1_SC_INT BIT(5) +#define LP873X_INT_BUCK_BUCK1_ILIM_INT BIT(4) +#define LP873X_INT_BUCK_BUCK0_PG_INT BIT(2) +#define LP873X_INT_BUCK_BUCK0_SC_INT BIT(1) +#define LP873X_INT_BUCK_BUCK0_ILIM_INT BIT(0) + +#define LP873X_INT_LDO_LDO1_PG_INT BIT(6) +#define LP873X_INT_LDO_LDO1_SC_INT BIT(5) +#define LP873X_INT_LDO_LDO1_ILIM_INT BIT(4) +#define LP873X_INT_LDO_LDO0_PG_INT BIT(2) +#define LP873X_INT_LDO_LDO0_SC_INT BIT(1) +#define LP873X_INT_LDO_LDO0_ILIM_INT BIT(0) + +#define LP873X_TOP_STAT_PGOOD_STAT BIT(7) +#define LP873X_TOP_STAT_SYNC_CLK_STAT BIT(4) +#define LP873X_TOP_STAT_TDIE_SD_STAT BIT(3) +#define LP873X_TOP_STAT_TDIE_WARN_STAT BIT(2) +#define LP873X_TOP_STAT_OVP_STAT BIT(1) + +#define LP873X_BUCK_STAT_BUCK1_STAT BIT(7) +#define LP873X_BUCK_STAT_BUCK1_PG_STAT BIT(6) +#define LP873X_BUCK_STAT_BUCK1_ILIM_STAT BIT(4) +#define LP873X_BUCK_STAT_BUCK0_STAT BIT(3) +#define LP873X_BUCK_STAT_BUCK0_PG_STAT BIT(2) +#define LP873X_BUCK_STAT_BUCK0_ILIM_STAT BIT(0) + +#define LP873X_LDO_STAT_LDO1_STAT BIT(7) +#define LP873X_LDO_STAT_LDO1_PG_STAT BIT(6) +#define LP873X_LDO_STAT_LDO1_ILIM_STAT BIT(4) +#define LP873X_LDO_STAT_LDO0_STAT BIT(3) +#define LP873X_LDO_STAT_LDO0_PG_STAT BIT(2) +#define LP873X_LDO_STAT_LDO0_ILIM_STAT BIT(0) + +#define LP873X_TOP_MASK_1_PGOOD_INT_MASK BIT(7) +#define LP873X_TOP_MASK_1_SYNC_CLK_MASK BIT(4) +#define LP873X_TOP_MASK_1_TDIE_WARN_MASK BIT(2) +#define LP873X_TOP_MASK_1_I_MEAS_MASK BIT(0) + +#define LP873X_TOP_MASK_2_RESET_REG_MASK BIT(0) + +#define LP873X_BUCK_MASK_BUCK1_PGF_MASK BIT(7) +#define LP873X_BUCK_MASK_BUCK1_PGR_MASK BIT(6) +#define LP873X_BUCK_MASK_BUCK1_ILIM_MASK BIT(4) +#define LP873X_BUCK_MASK_BUCK0_PGF_MASK BIT(3) +#define LP873X_BUCK_MASK_BUCK0_PGR_MASK BIT(2) +#define LP873X_BUCK_MASK_BUCK0_ILIM_MASK BIT(0) + +#define LP873X_LDO_MASK_LDO1_PGF_MASK BIT(7) +#define LP873X_LDO_MASK_LDO1_PGR_MASK BIT(6) +#define LP873X_LDO_MASK_LDO1_ILIM_MASK BIT(4) +#define LP873X_LDO_MASK_LDO0_PGF_MASK BIT(3) +#define LP873X_LDO_MASK_LDO0_PGR_MASK BIT(2) +#define LP873X_LDO_MASK_LDO0_ILIM_MASK BIT(0) + +#define LP873X_SEL_I_LOAD_CURRENT_BUCK_SELECT BIT(0) + +#define LP873X_I_LOAD_2_BUCK_LOAD_CURRENT BIT(0) + +#define LP873X_I_LOAD_1_BUCK_LOAD_CURRENT 0xFF + +#define LP873X_MAX_REG_ID LP873X_LDO_1 + +/* Number of step-down converters available */ +#define LP873X_NUM_BUCK 2 +/* Number of LDO voltage regulators available */ +#define LP873X_NUM_LDO 2 +/* Number of total regulators available */ +#define LP873X_NUM_REGULATOR (LP873X_NUM_BUCK + LP873X_NUM_LDO) + +enum lp873x_regulator_id { + /* BUCK's */ + LP873X_BUCK_0, + LP873X_BUCK_1, + /* LDOs */ + LP873X_LDO_0, + LP873X_LDO_1, +}; + +/** + * struct lp873x - state holder for the lp873x driver + * @dev: struct device pointer for MFD device + * @rev: revision of the lp873x + * @lock: lock guarding the data structure + * @regmap: register map of the lp873x PMIC + * + * Device data may be used to access the LP873X chip + */ +struct lp873x { + struct device *dev; + u8 rev; + struct mutex lock; /* lock guarding the data structure */ + struct regmap *regmap; +}; +#endif /* __LINUX_MFD_LP873X_H */ -- cgit v0.10.2 From 83f141030cec8861969121582f13ab2caff5c4ba Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 31 Aug 2016 14:28:11 +0530 Subject: gpio: lp873x: Add support for General Purpose Outputs Add driver for lp873x PMIC family GPOs. Two GPOs are supported and can be configured in Open-drain output or Push-pull output. Signed-off-by: Keerthy Acked-by: Linus Walleij Signed-off-by: Lee Jones diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98dd47a..346e9a9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -874,6 +874,16 @@ config GPIO_LP3943 LP3943 can be used as a GPIO expander which provides up to 16 GPIOs. Open drain outputs are required for this usage. +config GPIO_LP873X + tristate "TI LP873X GPO" + depends on MFD_TI_LP873X + help + This driver supports the GPO on TI Lp873x PMICs. 2 GPOs are present + on LP873X PMICs. + + This driver can also be built as a module. If so, the module will be + called gpio-lp873x. + 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 2a035ed..d60432f 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_GPIO_LOONGSON) += gpio-loongson.o 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_LYNXPOINT) += gpio-lynxpoint.o obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX7300) += gpio-max7300.o diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c new file mode 100644 index 0000000..f10d49d --- /dev/null +++ b/drivers/gpio/gpio-lp873x.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2016 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 TPS65218 driver + */ + +#include +#include +#include +#include + +#include + +#define BITS_PER_GPO 0x4 +#define LP873X_GPO_CTRL_OD 0x2 + +struct lp873x_gpio { + struct gpio_chip chip; + struct lp873x *lp873; +}; + +static int lp873x_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + /* This device is output only */ + return 0; +} + +static int lp873x_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + /* This device is output only */ + return -EINVAL; +} + +static int lp873x_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct lp873x_gpio *gpio = gpiochip_get_data(chip); + + /* Set the initial value */ + return regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, + BIT(offset * BITS_PER_GPO), + value ? BIT(offset * BITS_PER_GPO) : 0); +} + +static int lp873x_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct lp873x_gpio *gpio = gpiochip_get_data(chip); + int ret, val; + + ret = regmap_read(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, &val); + if (ret < 0) + return ret; + + return val & BIT(offset * BITS_PER_GPO); +} + +static void lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct lp873x_gpio *gpio = gpiochip_get_data(chip); + + regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, + BIT(offset * BITS_PER_GPO), + value ? BIT(offset * BITS_PER_GPO) : 0); +} + +static int lp873x_gpio_request(struct gpio_chip *gc, unsigned int offset) +{ + struct lp873x_gpio *gpio = gpiochip_get_data(gc); + int ret; + + switch (offset) { + case 0: + /* No MUX Set up Needed for GPO */ + break; + case 1: + /* Setup the CLKIN_PIN_SEL MUX to GPO2 */ + ret = regmap_update_bits(gpio->lp873->regmap, LP873X_REG_CONFIG, + LP873X_CONFIG_CLKIN_PIN_SEL, 0); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int lp873x_gpio_set_single_ended(struct gpio_chip *gc, + unsigned int offset, + enum single_ended_mode mode) +{ + struct lp873x_gpio *gpio = gpiochip_get_data(gc); + + switch (mode) { + case LINE_MODE_OPEN_DRAIN: + return regmap_update_bits(gpio->lp873->regmap, + LP873X_REG_GPO_CTRL, + BIT(offset * BITS_PER_GPO + + LP873X_GPO_CTRL_OD), + BIT(offset * BITS_PER_GPO + + LP873X_GPO_CTRL_OD)); + case LINE_MODE_PUSH_PULL: + return regmap_update_bits(gpio->lp873->regmap, + LP873X_REG_GPO_CTRL, + BIT(offset * BITS_PER_GPO + + LP873X_GPO_CTRL_OD), 0); + default: + return -ENOTSUPP; + } +} + +static struct gpio_chip template_chip = { + .label = "lp873x-gpio", + .owner = THIS_MODULE, + .request = lp873x_gpio_request, + .get_direction = lp873x_gpio_get_direction, + .direction_input = lp873x_gpio_direction_input, + .direction_output = lp873x_gpio_direction_output, + .get = lp873x_gpio_get, + .set = lp873x_gpio_set, + .set_single_ended = lp873x_gpio_set_single_ended, + .base = -1, + .ngpio = 2, + .can_sleep = true, +}; + +static int lp873x_gpio_probe(struct platform_device *pdev) +{ + struct lp873x_gpio *gpio; + int ret; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + platform_set_drvdata(pdev, gpio); + + gpio->lp873 = dev_get_drvdata(pdev->dev.parent); + gpio->chip = template_chip; + gpio->chip.parent = gpio->lp873->dev; + + ret = gpiochip_add_data(&gpio->chip, gpio); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); + return ret; + } + + return 0; +} + +static int lp873x_gpio_remove(struct platform_device *pdev) +{ + struct lp873x_gpio *gpio = platform_get_drvdata(pdev); + + gpiochip_remove(&gpio->chip); + + return 0; +} + +static const struct platform_device_id lp873x_gpio_id_table[] = { + { "lp873x-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, lp873x_gpio_id_table); + +static struct platform_driver lp873x_gpio_driver = { + .driver = { + .name = "lp873x-gpio", + }, + .probe = lp873x_gpio_probe, + .remove = lp873x_gpio_remove, + .id_table = lp873x_gpio_id_table, +}; +module_platform_driver(lp873x_gpio_driver); + +MODULE_AUTHOR("Keerthy "); +MODULE_DESCRIPTION("LP873X GPIO driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From f618ed218dc01207882a8f02b1310c7daaf5156a Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 31 Aug 2016 14:28:12 +0530 Subject: regulator: lp873x: Change the MFD config option as per latest naming Change the MFD config option as per latest naming Signed-off-by: Keerthy Acked-by: Mark Brown Signed-off-by: Lee Jones diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6c88e31..97dc4cc 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -323,7 +323,7 @@ config REGULATOR_LP872X config REGULATOR_LP873X tristate "TI LP873X Power regulators" - depends on MFD_LP873X && OF + depends on MFD_TI_LP873X && OF help This driver supports LP873X voltage regulator chips. LP873X provides two step-down converters and two general-purpose LDO -- cgit v0.10.2 From bf62efeb164343916ebb89dca6dfe5e6b6751700 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 26 Aug 2016 17:25:42 +0200 Subject: gpio: pca954x: fix undefined error code from remove The recent addition of the regulator support has led to the pca953x_remove function returning uninitialized data when no platform data pointer is provided, as gcc warns when using -Wmaybe-uninitialized: drivers/gpio/gpio-pca953x.c: In function 'pca953x_remove': drivers/gpio/gpio-pca953x.c:860:9: error: 'ret' may be used uninitialized in this function [-Werror=maybe-uninitialized] This restores the previous behavior, returning 0 on success. Signed-off-by: Arnd Bergmann Fixes: e23efa311110 ("gpio: pca954x: Add vcc regulator and enable it") Acked-by: Phil Reid Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index cbe2824..b9d31d7 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -853,6 +853,8 @@ static int pca953x_remove(struct i2c_client *client) if (ret < 0) dev_err(&client->dev, "%s failed, %d\n", "teardown", ret); + } else { + ret = 0; } regulator_disable(chip->regulator); -- cgit v0.10.2 From 19de85134ee92d927044cc620961a322bfde9119 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Tue, 30 Aug 2016 17:24:23 +0930 Subject: gpio: dt-bindings: Add documentation for Aspeed GPIO controllers Signed-off-by: Andrew Jeffery Acked-by: Joel Stanley Acked-by: Rob Herring Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt b/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt new file mode 100644 index 0000000..393bb2e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-aspeed.txt @@ -0,0 +1,36 @@ +Aspeed GPIO controller Device Tree Bindings +------------------------------------------- + +Required properties: +- compatible : Either "aspeed,ast2400-gpio" or "aspeed,ast2500-gpio" + +- #gpio-cells : Should be two + - First cell is the GPIO line number + - Second cell is used to specify optional + parameters (unused) + +- reg : Address and length of the register set for the device +- gpio-controller : Marks the device node as a GPIO controller. +- interrupts : Interrupt specifier (see interrupt bindings for + details) +- interrupt-controller : Mark the GPIO controller as an interrupt-controller + +Optional properties: + +- interrupt-parent : The parent interrupt controller, optional if inherited + +The gpio and interrupt properties are further described in their respective +bindings documentation: + +- Documentation/devicetree/bindings/gpio/gpio.txt +- Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + + Example: + gpio@1e780000 { + #gpio-cells = <2>; + compatible = "aspeed,ast2400-gpio"; + gpio-controller; + interrupts = <20>; + reg = <0x1e780000 0x1000>; + interrupt-controller; + }; -- cgit v0.10.2 From 361b79119a4b7f53f728913b5ed2c8d2d10c16f5 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 30 Aug 2016 17:24:27 +0930 Subject: gpio: Add Aspeed driver The Aspeed SoCs contain GPIOs banked by letter, where each bank contains 8 pins. The GPIO banks are then grouped in sets of four in the register layout. The implementation exposes multiple banks through the one driver and requests and releases pins via the pinctrl subsystem. The hardware supports generation of interrupts from all GPIO-capable pins. A number of hardware features are not yet supported: Configuration of interrupt direction (ARM or LPC), debouncing, and WDT reset tolerance for output ports. Signed-off-by: Joel Stanley Signed-off-by: Alistair Popple Signed-off-by: Jeremy Kerr Signed-off-by: Andrew Jeffery Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5eb7c86..17842e6a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -106,6 +106,13 @@ config GPIO_AMDPT driver for GPIO functionality on Promontory IOHub Require ACPI ASL code to enumerate as a platform device. +config GPIO_ASPEED + tristate "Aspeed GPIO support" + depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO + select GPIOLIB_IRQCHIP + help + Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers. + config GPIO_ATH79 tristate "Atheros AR71XX/AR724X/AR913X GPIO support" default y if ATH79 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 691fb3a..75bb229 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o +obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c new file mode 100644 index 0000000..64348a6 --- /dev/null +++ b/drivers/gpio/gpio-aspeed.c @@ -0,0 +1,457 @@ +/* + * Copyright 2015 IBM Corp. + * + * Joel Stanley + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct aspeed_gpio { + struct gpio_chip chip; + spinlock_t lock; + void __iomem *base; + int irq; +}; + +struct aspeed_gpio_bank { + uint16_t val_regs; + uint16_t irq_regs; + const char names[4]; +}; + +static const struct aspeed_gpio_bank aspeed_gpio_banks[] = { + { + .val_regs = 0x0000, + .irq_regs = 0x0008, + .names = { 'A', 'B', 'C', 'D' }, + }, + { + .val_regs = 0x0020, + .irq_regs = 0x0028, + .names = { 'E', 'F', 'G', 'H' }, + }, + { + .val_regs = 0x0070, + .irq_regs = 0x0098, + .names = { 'I', 'J', 'K', 'L' }, + }, + { + .val_regs = 0x0078, + .irq_regs = 0x00e8, + .names = { 'M', 'N', 'O', 'P' }, + }, + { + .val_regs = 0x0080, + .irq_regs = 0x0118, + .names = { 'Q', 'R', 'S', 'T' }, + }, + { + .val_regs = 0x0088, + .irq_regs = 0x0148, + .names = { 'U', 'V', 'W', 'X' }, + }, + /* + * A bank exists for { 'Y', 'Z', "AA", "AB" }, but is not implemented. + * Only half of GPIOs Y support interrupt configuration, and none of Z, + * AA or AB do as they are output only. + */ +}; + +#define GPIO_BANK(x) ((x) >> 5) +#define GPIO_OFFSET(x) ((x) & 0x1f) +#define GPIO_BIT(x) BIT(GPIO_OFFSET(x)) + +#define GPIO_DATA 0x00 +#define GPIO_DIR 0x04 + +#define GPIO_IRQ_ENABLE 0x00 +#define GPIO_IRQ_TYPE0 0x04 +#define GPIO_IRQ_TYPE1 0x08 +#define GPIO_IRQ_TYPE2 0x0c +#define GPIO_IRQ_STATUS 0x10 + +static const struct aspeed_gpio_bank *to_bank(unsigned int offset) +{ + unsigned int bank = GPIO_BANK(offset); + + WARN_ON(bank > ARRAY_SIZE(aspeed_gpio_banks)); + return &aspeed_gpio_banks[bank]; +} + +static void __iomem *bank_val_reg(struct aspeed_gpio *gpio, + const struct aspeed_gpio_bank *bank, + unsigned int reg) +{ + return gpio->base + bank->val_regs + reg; +} + +static void __iomem *bank_irq_reg(struct aspeed_gpio *gpio, + const struct aspeed_gpio_bank *bank, + unsigned int reg) +{ + return gpio->base + bank->irq_regs + reg; +} + +static int aspeed_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(gc); + const struct aspeed_gpio_bank *bank = to_bank(offset); + + return !!(ioread32(bank_val_reg(gpio, bank, GPIO_DATA)) + & GPIO_BIT(offset)); +} + +static void __aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, + int val) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(gc); + const struct aspeed_gpio_bank *bank = to_bank(offset); + void __iomem *addr; + u32 reg; + + addr = bank_val_reg(gpio, bank, GPIO_DATA); + reg = ioread32(addr); + + if (val) + reg |= GPIO_BIT(offset); + else + reg &= ~GPIO_BIT(offset); + + iowrite32(reg, addr); +} + +static void aspeed_gpio_set(struct gpio_chip *gc, unsigned int offset, + int val) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(gc); + unsigned long flags; + + spin_lock_irqsave(&gpio->lock, flags); + + __aspeed_gpio_set(gc, offset, val); + + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static int aspeed_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(gc); + const struct aspeed_gpio_bank *bank = to_bank(offset); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&gpio->lock, flags); + + reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)); + iowrite32(reg & ~GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR)); + + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int aspeed_gpio_dir_out(struct gpio_chip *gc, + unsigned int offset, int val) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(gc); + const struct aspeed_gpio_bank *bank = to_bank(offset); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&gpio->lock, flags); + + reg = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)); + iowrite32(reg | GPIO_BIT(offset), bank_val_reg(gpio, bank, GPIO_DIR)); + + __aspeed_gpio_set(gc, offset, val); + + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct aspeed_gpio *gpio = gpiochip_get_data(gc); + const struct aspeed_gpio_bank *bank = to_bank(offset); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&gpio->lock, flags); + + val = ioread32(bank_val_reg(gpio, bank, GPIO_DIR)) & GPIO_BIT(offset); + + spin_unlock_irqrestore(&gpio->lock, flags); + + return !val; + +} + +static inline int irqd_to_aspeed_gpio_data(struct irq_data *d, + struct aspeed_gpio **gpio, + const struct aspeed_gpio_bank **bank, + u32 *bit) +{ + int offset; + + offset = irqd_to_hwirq(d); + + *gpio = irq_data_get_irq_chip_data(d); + *bank = to_bank(offset); + *bit = GPIO_BIT(offset); + + return 0; +} + +static void aspeed_gpio_irq_ack(struct irq_data *d) +{ + const struct aspeed_gpio_bank *bank; + struct aspeed_gpio *gpio; + unsigned long flags; + void __iomem *status_addr; + u32 bit; + int rc; + + rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit); + if (rc) + return; + + status_addr = bank_irq_reg(gpio, bank, GPIO_IRQ_STATUS); + + spin_lock_irqsave(&gpio->lock, flags); + iowrite32(bit, status_addr); + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static void aspeed_gpio_irq_set_mask(struct irq_data *d, bool set) +{ + const struct aspeed_gpio_bank *bank; + struct aspeed_gpio *gpio; + unsigned long flags; + u32 reg, bit; + void __iomem *addr; + int rc; + + rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit); + if (rc) + return; + + addr = bank_irq_reg(gpio, bank, GPIO_IRQ_ENABLE); + + spin_lock_irqsave(&gpio->lock, flags); + + reg = ioread32(addr); + if (set) + reg |= bit; + else + reg &= bit; + iowrite32(reg, addr); + + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static void aspeed_gpio_irq_mask(struct irq_data *d) +{ + aspeed_gpio_irq_set_mask(d, false); +} + +static void aspeed_gpio_irq_unmask(struct irq_data *d) +{ + aspeed_gpio_irq_set_mask(d, true); +} + +static int aspeed_gpio_set_type(struct irq_data *d, unsigned int type) +{ + u32 type0 = 0; + u32 type1 = 0; + u32 type2 = 0; + u32 bit, reg; + const struct aspeed_gpio_bank *bank; + irq_flow_handler_t handler; + struct aspeed_gpio *gpio; + unsigned long flags; + void __iomem *addr; + int rc; + + rc = irqd_to_aspeed_gpio_data(d, &gpio, &bank, &bit); + if (rc) + return -EINVAL; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_BOTH: + type2 |= bit; + case IRQ_TYPE_EDGE_RISING: + type0 |= bit; + case IRQ_TYPE_EDGE_FALLING: + handler = handle_edge_irq; + break; + case IRQ_TYPE_LEVEL_HIGH: + type0 |= bit; + case IRQ_TYPE_LEVEL_LOW: + type1 |= bit; + handler = handle_level_irq; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&gpio->lock, flags); + + addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE0); + reg = ioread32(addr); + reg = (reg & ~bit) | type0; + iowrite32(reg, addr); + + addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE1); + reg = ioread32(addr); + reg = (reg & ~bit) | type1; + iowrite32(reg, addr); + + addr = bank_irq_reg(gpio, bank, GPIO_IRQ_TYPE2); + reg = ioread32(addr); + reg = (reg & ~bit) | type2; + iowrite32(reg, addr); + + spin_unlock_irqrestore(&gpio->lock, flags); + + irq_set_handler_locked(d, handler); + + return 0; +} + +static void aspeed_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct irq_chip *ic = irq_desc_get_chip(desc); + struct aspeed_gpio *data = gpiochip_get_data(gc); + unsigned int i, p, girq; + unsigned long reg; + + chained_irq_enter(ic, desc); + + for (i = 0; i < ARRAY_SIZE(aspeed_gpio_banks); i++) { + const struct aspeed_gpio_bank *bank = &aspeed_gpio_banks[i]; + + reg = ioread32(bank_irq_reg(data, bank, GPIO_IRQ_STATUS)); + + for_each_set_bit(p, ®, 32) { + girq = irq_find_mapping(gc->irqdomain, i * 32 + p); + generic_handle_irq(girq); + } + + } + + chained_irq_exit(ic, desc); +} + +static struct irq_chip aspeed_gpio_irqchip = { + .name = "aspeed-gpio", + .irq_ack = aspeed_gpio_irq_ack, + .irq_mask = aspeed_gpio_irq_mask, + .irq_unmask = aspeed_gpio_irq_unmask, + .irq_set_type = aspeed_gpio_set_type, +}; + +static int aspeed_gpio_setup_irqs(struct aspeed_gpio *gpio, + struct platform_device *pdev) +{ + int rc; + + rc = platform_get_irq(pdev, 0); + if (rc < 0) + return rc; + + gpio->irq = rc; + + rc = gpiochip_irqchip_add(&gpio->chip, &aspeed_gpio_irqchip, + 0, handle_bad_irq, IRQ_TYPE_NONE); + if (rc) { + dev_info(&pdev->dev, "Could not add irqchip\n"); + return rc; + } + + gpiochip_set_chained_irqchip(&gpio->chip, &aspeed_gpio_irqchip, + gpio->irq, aspeed_gpio_irq_handler); + + return 0; +} + +static int aspeed_gpio_request(struct gpio_chip *chip, unsigned int offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int __init aspeed_gpio_probe(struct platform_device *pdev) +{ + struct aspeed_gpio *gpio; + struct resource *res; + int rc; + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + gpio->base = devm_ioremap_resource(&pdev->dev, res); + if (!gpio->base) + return -ENOMEM; + + spin_lock_init(&gpio->lock); + + gpio->chip.ngpio = ARRAY_SIZE(aspeed_gpio_banks) * 32; + + gpio->chip.parent = &pdev->dev; + gpio->chip.direction_input = aspeed_gpio_dir_in; + gpio->chip.direction_output = aspeed_gpio_dir_out; + gpio->chip.get_direction = aspeed_gpio_get_direction; + gpio->chip.request = aspeed_gpio_request; + gpio->chip.free = aspeed_gpio_free; + gpio->chip.get = aspeed_gpio_get; + gpio->chip.set = aspeed_gpio_set; + gpio->chip.label = dev_name(&pdev->dev); + gpio->chip.base = -1; + + rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); + if (rc < 0) + return rc; + + return aspeed_gpio_setup_irqs(gpio, pdev); +} + +static const struct of_device_id aspeed_gpio_of_table[] = { + { .compatible = "aspeed,ast2400-gpio" }, + { .compatible = "aspeed,ast2500-gpio" }, + {} +}; +MODULE_DEVICE_TABLE(of, aspeed_gpio_of_table); + +static struct platform_driver aspeed_gpio_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = aspeed_gpio_of_table, + }, +}; + +module_platform_driver_probe(aspeed_gpio_driver, aspeed_gpio_probe); + +MODULE_DESCRIPTION("Aspeed GPIO Driver"); -- cgit v0.10.2 From f25fe36d0d0f8c3b951d53e0f22381d850112916 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Mon, 5 Sep 2016 22:00:10 +0300 Subject: gpio: mrvl: dt-binding: remove orion-gpio description The Orion GPIO controller binding description in mrvl-gpio.txt is obsolete, and duplicates the description in gpio-mvebu.txt. Signed-off-by: Baruch Siach Reviewed-by: Andrew Lunn Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt b/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt index 98d1983..c3d0165 100644 --- a/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt +++ b/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt @@ -44,26 +44,3 @@ Example for a PXA3xx platform: interrupt-controller; #interrupt-cells = <0x2>; }; - -* Marvell Orion GPIO Controller - -Required properties: -- compatible : Should be "marvell,orion-gpio" -- reg : Address and length of the register set for controller. -- gpio-controller : So we know this is a gpio controller. -- ngpio : How many gpios this controller has. -- interrupts : Up to 4 Interrupts for the controller. - -Optional properties: -- mask-offset : For SMP Orions, offset for Nth CPU - -Example: - - gpio0: gpio@10100 { - compatible = "marvell,orion-gpio"; - #gpio-cells = <2>; - gpio-controller; - reg = <0x10100 0x40>; - ngpio = <32>; - interrupts = <35>, <36>, <37>, <38>; - }; -- cgit v0.10.2 From 5d2f1d6ef9de5edfea04ffad47703a28bfa21690 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Tue, 6 Sep 2016 12:35:39 +0200 Subject: gpio: rcar: Add r8a7796 (R-Car M3-W) support R-Car Gen3's GPIO blocks are identical to Gen2's in every respect. Based on work for the r8a7795 (R-Car H3) by Ulrich Hecht. Cc: Ulrich Hecht Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Tested-by: Laurent Pinchart Signed-off-by: Linus Walleij diff --git a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt index 8da26b3..7c1ab3b 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt +++ b/Documentation/devicetree/bindings/gpio/renesas,gpio-rcar.txt @@ -11,6 +11,7 @@ Required Properties: - "renesas,gpio-r8a7793": for R8A7793 (R-Car M2-N) compatible GPIO controller. - "renesas,gpio-r8a7794": for R8A7794 (R-Car E2) compatible GPIO controller. - "renesas,gpio-r8a7795": for R8A7795 (R-Car H3) compatible GPIO controller. + - "renesas,gpio-r8a7796": for R8A7796 (R-Car M3-W) compatible GPIO controller. - "renesas,gpio-rcar": for generic R-Car GPIO controller. - reg: Base address and length of each memory resource used by the GPIO diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index b96e0b4..2be48f5 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -348,6 +348,10 @@ static const struct of_device_id gpio_rcar_of_table[] = { /* Gen3 GPIO is identical to Gen2. */ .data = &gpio_rcar_info_gen2, }, { + .compatible = "renesas,gpio-r8a7796", + /* Gen3 GPIO is identical to Gen2. */ + .data = &gpio_rcar_info_gen2, + }, { .compatible = "renesas,gpio-rcar", .data = &gpio_rcar_info_gen1, }, { -- cgit v0.10.2 From 332e99d5ae4056523bcb24ab16cd7a41474c4807 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 7 Sep 2016 09:12:11 +0100 Subject: gpio/gpiolib: Forbid irqchip default trigger if probed over DT Using a default trigger is a bad idea if using DT to configure interrupts, as the device's interrupt specifier will always contain the trigger configuration. Let's warn about that particular situation, and revert to not having a default. Hopefully, the couple of drivers still using this feature will quickly be fixed. Signed-off-by: Marc Zyngier Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index deaf4d4..e183ee2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1613,6 +1613,15 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip, if (gpiochip->of_node) of_node = gpiochip->of_node; #endif + /* + * Specifying a default trigger is a terrible idea if DT is + * used to configure the interrupts, as you may end-up with + * conflicting triggers. Tell the user, and reset to NONE. + */ + if (WARN(of_node && type != IRQ_TYPE_NONE, + "%s: Ignoring %d default trigger\n", of_node->full_name, type)) + type = IRQ_TYPE_NONE; + gpiochip->irqchip = irqchip; gpiochip->irq_handler = handler; gpiochip->irq_default_type = type; -- cgit v0.10.2 From 14bf873e5921fd414cf6f0b31b799eeabd27dd74 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 8 Sep 2016 02:58:32 +0300 Subject: gpio: lpc32xx: remove unused platform data file ARM LPC32xx platform is device-tree only, there is no need to keep a file with GPIO platform data structures, however some of macro definitions should be moved to the driver code, which is the only user of the removed header file. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index fc5f197..92b3ae2 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -68,6 +67,20 @@ #define GPI3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1) #define GPO3_PIN_IN_SEL(x, y) (((x) >> (y)) & 1) +#define LPC32XX_GPIO_P0_MAX 8 +#define LPC32XX_GPIO_P1_MAX 24 +#define LPC32XX_GPIO_P2_MAX 13 +#define LPC32XX_GPIO_P3_MAX 6 +#define LPC32XX_GPI_P3_MAX 29 +#define LPC32XX_GPO_P3_MAX 24 + +#define LPC32XX_GPIO_P0_GRP 0 +#define LPC32XX_GPIO_P1_GRP (LPC32XX_GPIO_P0_GRP + LPC32XX_GPIO_P0_MAX) +#define LPC32XX_GPIO_P2_GRP (LPC32XX_GPIO_P1_GRP + LPC32XX_GPIO_P1_MAX) +#define LPC32XX_GPIO_P3_GRP (LPC32XX_GPIO_P2_GRP + LPC32XX_GPIO_P2_MAX) +#define LPC32XX_GPI_P3_GRP (LPC32XX_GPIO_P3_GRP + LPC32XX_GPIO_P3_MAX) +#define LPC32XX_GPO_P3_GRP (LPC32XX_GPI_P3_GRP + LPC32XX_GPI_P3_MAX) + struct gpio_regs { void __iomem *inp_state; void __iomem *outp_state; diff --git a/include/linux/platform_data/gpio-lpc32xx.h b/include/linux/platform_data/gpio-lpc32xx.h deleted file mode 100644 index a544e96..0000000 --- a/include/linux/platform_data/gpio-lpc32xx.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Author: Kevin Wells - * - * Copyright (C) 2010 NXP Semiconductors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __MACH_GPIO_LPC32XX_H -#define __MACH_GPIO_LPC32XX_H - -/* - * Note! - * Muxed GP pins need to be setup to the GP state in the board level - * code prior to using this driver. - * GPI pins : 28xP3 group - * GPO pins : 24xP3 group - * GPIO pins: 8xP0 group, 24xP1 group, 13xP2 group, 6xP3 group - */ - -#define LPC32XX_GPIO_P0_MAX 8 -#define LPC32XX_GPIO_P1_MAX 24 -#define LPC32XX_GPIO_P2_MAX 13 -#define LPC32XX_GPIO_P3_MAX 6 -#define LPC32XX_GPI_P3_MAX 29 -#define LPC32XX_GPO_P3_MAX 24 - -#define LPC32XX_GPIO_P0_GRP 0 -#define LPC32XX_GPIO_P1_GRP (LPC32XX_GPIO_P0_GRP + LPC32XX_GPIO_P0_MAX) -#define LPC32XX_GPIO_P2_GRP (LPC32XX_GPIO_P1_GRP + LPC32XX_GPIO_P1_MAX) -#define LPC32XX_GPIO_P3_GRP (LPC32XX_GPIO_P2_GRP + LPC32XX_GPIO_P2_MAX) -#define LPC32XX_GPI_P3_GRP (LPC32XX_GPIO_P3_GRP + LPC32XX_GPIO_P3_MAX) -#define LPC32XX_GPO_P3_GRP (LPC32XX_GPI_P3_GRP + LPC32XX_GPI_P3_MAX) - -/* - * A specific GPIO can be selected with this macro - * ie, GPIO_05 can be selected with LPC32XX_GPIO(LPC32XX_GPIO_P3_GRP, 5) - * See the LPC32x0 User's guide for GPIO group numbers - */ -#define LPC32XX_GPIO(x, y) ((x) + (y)) - -#endif /* __MACH_GPIO_LPC32XX_H */ -- cgit v0.10.2 From 53661f3bc64a3203efad5daa6cf4c4e87c53b394 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Sep 2016 11:17:34 +0200 Subject: gpio: pca953x: code shrink There are multiple places in the driver code where a switch (chip->chip_type) is used to determine the proper register offset. Unduplicate the code by adding a simple structure holding the possible offsets that differ between the pca953x and pca957x chip families and use it to avoid the checks. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b9d31d7..5d65c9e 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -95,6 +95,24 @@ MODULE_DEVICE_TABLE(acpi, pca953x_acpi_ids); #define NBANK(chip) DIV_ROUND_UP(chip->gpio_chip.ngpio, BANK_SZ) +struct pca953x_reg_config { + int direction; + int output; + int input; +}; + +static const struct pca953x_reg_config pca953x_regs = { + .direction = PCA953X_DIRECTION, + .output = PCA953X_OUTPUT, + .input = PCA953X_INPUT, +}; + +static const struct pca953x_reg_config pca957x_regs = { + .direction = PCA957X_CFG, + .output = PCA957X_OUT, + .input = PCA957X_IN, +}; + struct pca953x_chip { unsigned gpio_start; u8 reg_output[MAX_BANK]; @@ -115,6 +133,8 @@ struct pca953x_chip { int chip_type; unsigned long driver_data; struct regulator *regulator; + + const struct pca953x_reg_config *regs; }; static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val, @@ -224,20 +244,12 @@ static int pca953x_gpio_direction_input(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 reg_val; - int ret, offset = 0; + int ret; mutex_lock(&chip->i2c_lock); reg_val = chip->reg_direction[off / BANK_SZ] | (1u << (off % BANK_SZ)); - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_DIRECTION; - break; - case PCA957X_TYPE: - offset = PCA957X_CFG; - break; - } - ret = pca953x_write_single(chip, offset, reg_val, off); + ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off); if (ret) goto exit; @@ -252,7 +264,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 reg_val; - int ret, offset = 0; + int ret; mutex_lock(&chip->i2c_lock); /* set output level */ @@ -263,15 +275,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, reg_val = chip->reg_output[off / BANK_SZ] & ~(1u << (off % BANK_SZ)); - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_OUTPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_OUT; - break; - } - ret = pca953x_write_single(chip, offset, reg_val, off); + ret = pca953x_write_single(chip, chip->regs->output, reg_val, off); if (ret) goto exit; @@ -279,15 +283,7 @@ static int pca953x_gpio_direction_output(struct gpio_chip *gc, /* then direction */ reg_val = chip->reg_direction[off / BANK_SZ] & ~(1u << (off % BANK_SZ)); - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_DIRECTION; - break; - case PCA957X_TYPE: - offset = PCA957X_CFG; - break; - } - ret = pca953x_write_single(chip, offset, reg_val, off); + ret = pca953x_write_single(chip, chip->regs->direction, reg_val, off); if (ret) goto exit; @@ -301,18 +297,10 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off) { struct pca953x_chip *chip = gpiochip_get_data(gc); u32 reg_val; - int ret, offset = 0; + int ret; mutex_lock(&chip->i2c_lock); - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_INPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_IN; - break; - } - ret = pca953x_read_single(chip, offset, ®_val, off); + ret = pca953x_read_single(chip, chip->regs->input, ®_val, off); mutex_unlock(&chip->i2c_lock); if (ret < 0) { /* NOTE: diagnostic already emitted; that's all we should @@ -329,7 +317,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 reg_val; - int ret, offset = 0; + int ret; mutex_lock(&chip->i2c_lock); if (val) @@ -339,15 +327,7 @@ static void pca953x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) reg_val = chip->reg_output[off / BANK_SZ] & ~(1u << (off % BANK_SZ)); - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_OUTPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_OUT; - break; - } - ret = pca953x_write_single(chip, offset, reg_val, off); + ret = pca953x_write_single(chip, chip->regs->output, reg_val, off); if (ret) goto exit; @@ -361,19 +341,10 @@ static void pca953x_gpio_set_multiple(struct gpio_chip *gc, { struct pca953x_chip *chip = gpiochip_get_data(gc); u8 reg_val[MAX_BANK]; - int ret, offset = 0; + int ret; int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); int bank; - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_OUTPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_OUT; - break; - } - memcpy(reg_val, chip->reg_output, NBANK(chip)); mutex_lock(&chip->i2c_lock); for(bank=0; bankclient, offset << bank_shift, NBANK(chip), reg_val); + ret = i2c_smbus_write_i2c_block_data(chip->client, + chip->regs->output << bank_shift, + NBANK(chip), reg_val); if (ret) goto exit; @@ -517,7 +490,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) bool pending_seen = false; bool trigger_seen = false; u8 trigger[MAX_BANK]; - int ret, i, offset = 0; + int ret, i; if (chip->driver_data & PCA_PCAL) { /* Read the current interrupt status from the device */ @@ -542,15 +515,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending) return pending_seen; } - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_INPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_IN; - break; - } - ret = pca953x_read_regs(chip, offset, cur_stat); + ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); if (ret) return false; @@ -610,20 +575,13 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base) { struct i2c_client *client = chip->client; - int ret, i, offset = 0; + int ret, i; if (client->irq && irq_base != -1 && (chip->driver_data & PCA_INT)) { - switch (chip->chip_type) { - case PCA953X_TYPE: - offset = PCA953X_INPUT; - break; - case PCA957X_TYPE: - offset = PCA957X_IN; - break; - } - ret = pca953x_read_regs(chip, offset, chip->irq_stat); + ret = pca953x_read_regs(chip, + chip->regs->input, chip->irq_stat); if (ret) return ret; @@ -686,12 +644,14 @@ static int device_pca953x_init(struct pca953x_chip *chip, u32 invert) int ret; u8 val[MAX_BANK]; - ret = pca953x_read_regs(chip, PCA953X_OUTPUT, chip->reg_output); + chip->regs = &pca953x_regs; + + ret = pca953x_read_regs(chip, chip->regs->output, chip->reg_output); if (ret) goto out; - ret = pca953x_read_regs(chip, PCA953X_DIRECTION, - chip->reg_direction); + ret = pca953x_read_regs(chip, chip->regs->direction, + chip->reg_direction); if (ret) goto out; @@ -711,10 +671,13 @@ static int device_pca957x_init(struct pca953x_chip *chip, u32 invert) int ret; u8 val[MAX_BANK]; - ret = pca953x_read_regs(chip, PCA957X_OUT, chip->reg_output); + chip->regs = &pca957x_regs; + + ret = pca953x_read_regs(chip, chip->regs->output, chip->reg_output); if (ret) goto out; - ret = pca953x_read_regs(chip, PCA957X_CFG, chip->reg_direction); + ret = pca953x_read_regs(chip, chip->regs->direction, + chip->reg_direction); if (ret) goto out; -- cgit v0.10.2 From 7acc66e3711b004496c85a64534b2b8aabef8df3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Sep 2016 11:17:35 +0200 Subject: gpio: pca953x: refactor pca953x_write_regs() Avoid the unnecessary if-else in pca953x_write_regs() by splitting the routine into smaller, specialized functions and calling the right one via a function pointer held in struct pca953x_chip. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 5d65c9e..e9defeb 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -135,6 +135,8 @@ struct pca953x_chip { struct regulator *regulator; const struct pca953x_reg_config *regs; + + int (*write_regs)(struct pca953x_chip *, int, u8 *); }; static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val, @@ -174,38 +176,44 @@ static int pca953x_write_single(struct pca953x_chip *chip, int reg, u32 val, return 0; } -static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val) +static int pca953x_write_regs_8(struct pca953x_chip *chip, int reg, u8 *val) { - int ret = 0; + return i2c_smbus_write_byte_data(chip->client, reg, *val); +} - if (chip->gpio_chip.ngpio <= 8) - ret = i2c_smbus_write_byte_data(chip->client, reg, *val); - else if (chip->gpio_chip.ngpio >= 24) { - int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); - ret = i2c_smbus_write_i2c_block_data(chip->client, - (reg << bank_shift) | REG_ADDR_AI, - NBANK(chip), val); - } else { - switch (chip->chip_type) { - case PCA953X_TYPE: { - __le16 word = cpu_to_le16(get_unaligned((u16 *)val)); +static int pca953x_write_regs_16(struct pca953x_chip *chip, int reg, u8 *val) +{ + __le16 word = cpu_to_le16(get_unaligned((u16 *)val)); - ret = i2c_smbus_write_word_data(chip->client, reg << 1, - (__force u16)word); - break; - } - case PCA957X_TYPE: - ret = i2c_smbus_write_byte_data(chip->client, reg << 1, - val[0]); - if (ret < 0) - break; - ret = i2c_smbus_write_byte_data(chip->client, - (reg << 1) + 1, - val[1]); - break; - } - } + return i2c_smbus_write_word_data(chip->client, + reg << 1, (__force u16)word); +} + +static int pca957x_write_regs_16(struct pca953x_chip *chip, int reg, u8 *val) +{ + int ret; + + ret = i2c_smbus_write_byte_data(chip->client, reg << 1, val[0]); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(chip->client, (reg << 1) + 1, val[1]); +} +static int pca953x_write_regs_24(struct pca953x_chip *chip, int reg, u8 *val) +{ + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + + return i2c_smbus_write_i2c_block_data(chip->client, + (reg << bank_shift) | REG_ADDR_AI, + NBANK(chip), val); +} + +static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val) +{ + int ret = 0; + + ret = chip->write_regs(chip, reg, val); if (ret < 0) { dev_err(&chip->client->dev, "failed writing register\n"); return ret; @@ -774,6 +782,17 @@ static int pca953x_probe(struct i2c_client *client, */ pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK); + if (chip->gpio_chip.ngpio <= 8) { + chip->write_regs = pca953x_write_regs_8; + } else if (chip->gpio_chip.ngpio >= 24) { + chip->write_regs = pca953x_write_regs_24; + } else { + if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) + chip->write_regs = pca953x_write_regs_16; + else + chip->write_regs = pca957x_write_regs_16; + } + if (chip->chip_type == PCA953X_TYPE) ret = device_pca953x_init(chip, invert); else -- cgit v0.10.2 From c6e3cf01d31d88d35a8fbf8bf4773b737fa48f21 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Sep 2016 11:17:36 +0200 Subject: gpio: pca953x: refactor pca953x_read_regs() Avoid the unnecessary if-else in pca953x_read_regs() by spltting the routine into smaller, specialized functions and calling the right one via a function pointer held in struct pca953x. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e9defeb..e7bf3b3 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -137,6 +137,7 @@ struct pca953x_chip { const struct pca953x_reg_config *regs; int (*write_regs)(struct pca953x_chip *, int, u8 *); + int (*read_regs)(struct pca953x_chip *, int, u8 *); }; static int pca953x_read_single(struct pca953x_chip *chip, int reg, u32 *val, @@ -222,24 +223,41 @@ static int pca953x_write_regs(struct pca953x_chip *chip, int reg, u8 *val) return 0; } -static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val) +static int pca953x_read_regs_8(struct pca953x_chip *chip, int reg, u8 *val) { int ret; - if (chip->gpio_chip.ngpio <= 8) { - ret = i2c_smbus_read_byte_data(chip->client, reg); - *val = ret; - } else if (chip->gpio_chip.ngpio >= 24) { - int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + ret = i2c_smbus_read_byte_data(chip->client, reg); + *val = ret; - ret = i2c_smbus_read_i2c_block_data(chip->client, - (reg << bank_shift) | REG_ADDR_AI, - NBANK(chip), val); - } else { - ret = i2c_smbus_read_word_data(chip->client, reg << 1); - val[0] = (u16)ret & 0xFF; - val[1] = (u16)ret >> 8; - } + return ret; +} + +static int pca953x_read_regs_16(struct pca953x_chip *chip, int reg, u8 *val) +{ + int ret; + + ret = i2c_smbus_read_word_data(chip->client, reg << 1); + val[0] = (u16)ret & 0xFF; + val[1] = (u16)ret >> 8; + + return ret; +} + +static int pca953x_read_regs_24(struct pca953x_chip *chip, int reg, u8 *val) +{ + int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); + + return i2c_smbus_read_i2c_block_data(chip->client, + (reg << bank_shift) | REG_ADDR_AI, + NBANK(chip), val); +} + +static int pca953x_read_regs(struct pca953x_chip *chip, int reg, u8 *val) +{ + int ret; + + ret = chip->read_regs(chip, reg, val); if (ret < 0) { dev_err(&chip->client->dev, "failed reading register\n"); return ret; @@ -784,13 +802,16 @@ static int pca953x_probe(struct i2c_client *client, if (chip->gpio_chip.ngpio <= 8) { chip->write_regs = pca953x_write_regs_8; + chip->read_regs = pca953x_read_regs_8; } else if (chip->gpio_chip.ngpio >= 24) { chip->write_regs = pca953x_write_regs_24; + chip->read_regs = pca953x_read_regs_24; } else { if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) chip->write_regs = pca953x_write_regs_16; else chip->write_regs = pca957x_write_regs_16; + chip->read_regs = pca953x_read_regs_16; } if (chip->chip_type == PCA953X_TYPE) -- cgit v0.10.2 From 60f547be82acd3ab5369caf981280a2f13b403e9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Sep 2016 11:17:37 +0200 Subject: gpio: pca953x: remove an unused variable The chip_type variable in struct pca953x_chip is no longer required. Remove it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e7bf3b3..e36a9bf 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -130,7 +130,6 @@ struct pca953x_chip { struct i2c_client *client; struct gpio_chip gpio_chip; const char *const *names; - int chip_type; unsigned long driver_data; struct regulator *regulator; @@ -791,8 +790,6 @@ static int pca953x_probe(struct i2c_client *client, } } - chip->chip_type = PCA_CHIP_TYPE(chip->driver_data); - mutex_init(&chip->i2c_lock); /* initialize cached registers from their original values. @@ -814,7 +811,7 @@ static int pca953x_probe(struct i2c_client *client, chip->read_regs = pca953x_read_regs_16; } - if (chip->chip_type == PCA953X_TYPE) + if (PCA_CHIP_TYPE(chip->driver_data) == PCA953X_TYPE) ret = device_pca953x_init(chip, invert); else ret = device_pca957x_init(chip, invert); -- cgit v0.10.2 From ea3d579d8f0cc5f16105c2741e2d409563beb948 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 9 Sep 2016 11:17:38 +0200 Subject: gpio: pca953x: coding style fixes pca953x_gpio_set_multiple() has some coding style issues that make it harder to read. Tweak the code a bit. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e36a9bf..5d05986 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -362,25 +362,28 @@ exit: } static void pca953x_gpio_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) + unsigned long *mask, unsigned long *bits) { struct pca953x_chip *chip = gpiochip_get_data(gc); + unsigned int bank_mask, bank_val; + int bank_shift, bank; u8 reg_val[MAX_BANK]; int ret; - int bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); - int bank; + + bank_shift = fls((chip->gpio_chip.ngpio - 1) / BANK_SZ); memcpy(reg_val, chip->reg_output, NBANK(chip)); mutex_lock(&chip->i2c_lock); - for(bank=0; bank> - ((bank % sizeof(*mask)) * 8); - if(bankmask) { - unsigned bankval = bits[bank / sizeof(*bits)] >> - ((bank % sizeof(*bits)) * 8); - reg_val[bank] = (reg_val[bank] & ~bankmask) | bankval; + for (bank = 0; bank < NBANK(chip); bank++) { + bank_mask = mask[bank / sizeof(*mask)] >> + ((bank % sizeof(*mask)) * 8); + if (bank_mask) { + bank_val = bits[bank / sizeof(*bits)] >> + ((bank % sizeof(*bits)) * 8); + reg_val[bank] = (reg_val[bank] & ~bank_mask) | bank_val; } } + ret = i2c_smbus_write_i2c_block_data(chip->client, chip->regs->output << bank_shift, NBANK(chip), reg_val); -- cgit v0.10.2 From 31963eb039b7d5708cdd3e07d247ac2389eb0c1f Mon Sep 17 00:00:00 2001 From: Amitesh Singh Date: Thu, 8 Sep 2016 17:11:20 +0530 Subject: gpio: fix documentation for gpiod_unexport Both gpio_export and gpio_free APIs are obsolete now. Signed-off-by: Amitesh Singh Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 932e510..4b44dd9 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -670,10 +670,10 @@ int gpiod_export_link(struct device *dev, const char *name, EXPORT_SYMBOL_GPL(gpiod_export_link); /** - * gpiod_unexport - reverse effect of gpio_export() + * gpiod_unexport - reverse effect of gpiod_export() * @gpio: gpio to make unavailable * - * This is implicit on gpio_free(). + * This is implicit on gpiod_free(). */ void gpiod_unexport(struct gpio_desc *desc) { -- cgit v0.10.2 From 6d125412fc16802012a17665638f49b0b0c81f18 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 9 Sep 2016 09:20:03 +0300 Subject: gpio: iop: Use generic GPIO MMIO functions for driver This patch switches the driver to use the generic GPIO MMIO functions that removes a bit of redundant and duplicate code. Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 17842e6a..68c2d64 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -228,7 +228,8 @@ config GPIO_ICH config GPIO_IOP tristate "Intel IOP GPIO" - depends on ARM && (ARCH_IOP32X || ARCH_IOP33X) + depends on ARCH_IOP32X || ARCH_IOP33X || COMPILE_TEST + select GPIO_GENERIC help Say yes here to support the GPIO functionality of a number of Intel IOP32X or IOP33X. diff --git a/drivers/gpio/gpio-iop.c b/drivers/gpio/gpio-iop.c index 860c535..98c7ff2 100644 --- a/drivers/gpio/gpio-iop.c +++ b/drivers/gpio/gpio-iop.c @@ -10,111 +10,40 @@ * your option) any later version. */ -#include -#include -#include -#include -#include -#include +#include +#include +#include #include -#include -#include -#define IOP3XX_N_GPIOS 8 - -#define GPIO_IN 0 -#define GPIO_OUT 1 -#define GPIO_LOW 0 -#define GPIO_HIGH 1 - -/* Memory base offset */ -static void __iomem *base; - -#define IOP3XX_GPIO_REG(reg) (base + (reg)) -#define IOP3XX_GPOE IOP3XX_GPIO_REG(0x0000) -#define IOP3XX_GPID IOP3XX_GPIO_REG(0x0004) -#define IOP3XX_GPOD IOP3XX_GPIO_REG(0x0008) - -static void gpio_line_config(int line, int direction) -{ - unsigned long flags; - u32 val; - - local_irq_save(flags); - val = readl(IOP3XX_GPOE); - if (direction == GPIO_IN) { - val |= BIT(line); - } else if (direction == GPIO_OUT) { - val &= ~BIT(line); - } - writel(val, IOP3XX_GPOE); - local_irq_restore(flags); -} - -static int gpio_line_get(int line) -{ - return !!(readl(IOP3XX_GPID) & BIT(line)); -} - -static void gpio_line_set(int line, int value) -{ - unsigned long flags; - u32 val; - - local_irq_save(flags); - val = readl(IOP3XX_GPOD); - if (value == GPIO_LOW) { - val &= ~BIT(line); - } else if (value == GPIO_HIGH) { - val |= BIT(line); - } - writel(val, IOP3XX_GPOD); - local_irq_restore(flags); -} - -static int iop3xx_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) -{ - gpio_line_config(gpio, GPIO_IN); - return 0; -} - -static int iop3xx_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int level) -{ - gpio_line_set(gpio, level); - gpio_line_config(gpio, GPIO_OUT); - return 0; -} - -static int iop3xx_gpio_get_value(struct gpio_chip *chip, unsigned gpio) -{ - return gpio_line_get(gpio); -} - -static void iop3xx_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) -{ - gpio_line_set(gpio, value); -} - -static struct gpio_chip iop3xx_chip = { - .label = "iop3xx", - .direction_input = iop3xx_gpio_direction_input, - .get = iop3xx_gpio_get_value, - .direction_output = iop3xx_gpio_direction_output, - .set = iop3xx_gpio_set_value, - .base = 0, - .ngpio = IOP3XX_N_GPIOS, -}; +#define IOP3XX_GPOE 0x0000 +#define IOP3XX_GPID 0x0004 +#define IOP3XX_GPOD 0x0008 static int iop3xx_gpio_probe(struct platform_device *pdev) { struct resource *res; + struct gpio_chip *gc; + void __iomem *base; + int err; + + gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); - return devm_gpiochip_add_data(&pdev->dev, &iop3xx_chip, NULL); + err = bgpio_init(gc, &pdev->dev, 1, base + IOP3XX_GPID, + base + IOP3XX_GPOD, NULL, NULL, base + IOP3XX_GPOE, 0); + if (err) + return err; + + gc->base = 0; + gc->owner = THIS_MODULE; + + return devm_gpiochip_add_data(&pdev->dev, gc, NULL); } static struct platform_driver iop3xx_gpio_driver = { -- cgit v0.10.2 From 313b9a9938bf4076425741121d5d766826793e5d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 9 Sep 2016 09:31:54 +0100 Subject: gpio: pca953x: initialize ret to zero to avoid returning garbage ret is not initialized so it contains garbage. Ensure garbage is not returned in the case that pdata && pdata->teardown is false by initializing ret to 0. Signed-off-by: Colin Ian King Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 5d05986..f170c56 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -848,7 +848,7 @@ static int pca953x_remove(struct i2c_client *client) { struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev); struct pca953x_chip *chip = i2c_get_clientdata(client); - int ret; + int ret = 0; if (pdata && pdata->teardown) { ret = pdata->teardown(client, chip->gpio_chip.base, -- cgit v0.10.2 From 9d99c41a126124b35b69ff9737d7ba12df3eaa97 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 10 Sep 2016 12:04:42 +0000 Subject: gpio: lp873x: Use devm_gpiochip_add_data() for gpio registration Use devm_gpiochip_add_data() for GPIO registration and remove the need of driver callback .remove. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index f10d49d..2386b0f 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -154,7 +154,7 @@ static int lp873x_gpio_probe(struct platform_device *pdev) gpio->chip = template_chip; gpio->chip.parent = gpio->lp873->dev; - ret = gpiochip_add_data(&gpio->chip, gpio); + 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; @@ -163,15 +163,6 @@ static int lp873x_gpio_probe(struct platform_device *pdev) return 0; } -static int lp873x_gpio_remove(struct platform_device *pdev) -{ - struct lp873x_gpio *gpio = platform_get_drvdata(pdev); - - gpiochip_remove(&gpio->chip); - - return 0; -} - static const struct platform_device_id lp873x_gpio_id_table[] = { { "lp873x-gpio", }, { /* sentinel */ } @@ -183,7 +174,6 @@ static struct platform_driver lp873x_gpio_driver = { .name = "lp873x-gpio", }, .probe = lp873x_gpio_probe, - .remove = lp873x_gpio_remove, .id_table = lp873x_gpio_id_table, }; module_platform_driver(lp873x_gpio_driver); -- cgit v0.10.2 From e35b5ab0a706f3794d35652b557a5e6a4dde5391 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 11 Sep 2016 14:14:37 +0200 Subject: gpio: constify gpio_chip structures These structures are only used to copy into other structures, so declare them as const. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r disable optional_qualifier@ identifier i; position p; @@ static struct gpio_chip i@p = { ... }; @ok@ identifier r.i; expression e; position p; @@ e = i@p; @bad@ position p != {r.p,ok.p}; identifier r.i; struct gpio_chip e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct gpio_chip i = { ... }; // Signed-off-by: Julia Lawall Acked-by: Joachim Eastwood Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index 9913704..4824628 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -79,7 +79,7 @@ static void arizona_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ARIZONA_GPN_LVL, value); } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "arizona", .owner = THIS_MODULE, .direction_input = arizona_gpio_direction_in, diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 953e4b8..3d1cf01 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -308,7 +308,7 @@ static int bcm_kona_gpio_set_debounce(struct gpio_chip *chip, unsigned gpio, return 0; } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "bcm-kona-gpio", .owner = THIS_MODULE, .request = bcm_kona_gpio_request, diff --git a/drivers/gpio/gpio-da9052.c b/drivers/gpio/gpio-da9052.c index e29553b..dd8977c 100644 --- a/drivers/gpio/gpio-da9052.c +++ b/drivers/gpio/gpio-da9052.c @@ -184,7 +184,7 @@ static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset) return irq; } -static struct gpio_chip reference_gp = { +static const struct gpio_chip reference_gp = { .label = "da9052-gpio", .owner = THIS_MODULE, .get = da9052_gpio_get, diff --git a/drivers/gpio/gpio-da9055.c b/drivers/gpio/gpio-da9055.c index 2c2c18d..82053b5 100644 --- a/drivers/gpio/gpio-da9055.c +++ b/drivers/gpio/gpio-da9055.c @@ -121,7 +121,7 @@ static int da9055_gpio_to_irq(struct gpio_chip *gc, u32 offset) DA9055_IRQ_GPI0 + offset); } -static struct gpio_chip reference_gp = { +static const struct gpio_chip reference_gp = { .label = "da9055-gpio", .owner = THIS_MODULE, .get = da9055_gpio_get, diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index 63a962d..45d29e4 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -273,7 +273,7 @@ exit: return rc; } -static struct gpio_chip it87_template_chip = { +static const struct gpio_chip it87_template_chip = { .label = KBUILD_MODNAME, .owner = THIS_MODULE, .request = it87_gpio_request, diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index 2386b0f..218c706 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -124,7 +124,7 @@ static int lp873x_gpio_set_single_ended(struct gpio_chip *gc, } } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "lp873x-gpio", .owner = THIS_MODULE, .request = lp873x_gpio_request, diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index 98832c9..f12e02e 100644 --- a/drivers/gpio/gpio-lpc18xx.c +++ b/drivers/gpio/gpio-lpc18xx.c @@ -78,7 +78,7 @@ static int lpc18xx_gpio_direction_output(struct gpio_chip *chip, return lpc18xx_gpio_direction(chip, offset, true); } -static struct gpio_chip lpc18xx_chip = { +static const struct gpio_chip lpc18xx_chip = { .label = "lpc18xx/43xx-gpio", .request = gpiochip_generic_request, .free = gpiochip_generic_free, diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index cb14b8d..f5545049 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -90,7 +90,7 @@ static int pisosr_gpio_get(struct gpio_chip *chip, unsigned offset) return (gpio->buffer[offset / 8] >> (offset % 8)) & 0x1; } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "pisosr-gpio", .owner = THIS_MODULE, .get_direction = pisosr_gpio_get_direction, diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index eb43ae4..5450044 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -138,7 +138,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num, return 0; } -static struct gpio_chip sch_gpio_chip = { +static const struct gpio_chip sch_gpio_chip = { .label = "sch_gpio", .owner = THIS_MODULE, .direction_input = sch_gpio_direction_in, diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 5c0d818..b51c5be 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -121,7 +121,7 @@ static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset) return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO); } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "stmpe", .owner = THIS_MODULE, .get_direction = stmpe_gpio_get_direction, diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 8b36593..5baa457 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -124,7 +124,7 @@ static int tc3589x_gpio_single_ended(struct gpio_chip *chip, return -ENOTSUPP; } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "tc3589x", .owner = THIS_MODULE, .direction_input = tc3589x_gpio_direction_input, diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index cace79c..c8b34d7 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -87,7 +87,7 @@ static void tpic2810_set_multiple(struct gpio_chip *chip, unsigned long *mask, tpic2810_set_mask_bits(chip, *mask, *bits); } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "tpic2810", .owner = THIS_MODULE, .get_direction = tpic2810_get_direction, diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 8e25f01..b23c4d2 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -72,7 +72,7 @@ static void tps65086_gpio_set(struct gpio_chip *chip, unsigned offset, BIT(4 + offset), value ? BIT(4 + offset) : 0); } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "tps65086-gpio", .owner = THIS_MODULE, .get_direction = tps65086_gpio_get_direction, diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 1c09a19..03e0dfb 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -172,7 +172,7 @@ static int tps65218_gpio_set_single_ended(struct gpio_chip *gc, return -ENOTSUPP; } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "gpio-tps65218", .owner = THIS_MODULE, .request = tps65218_gpio_request, diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index acfd30a..abc0798 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -90,7 +90,7 @@ static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset, GPIO_SET_MASK, value ? GPIO_SET_MASK : 0); } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "tps65912-gpio", .owner = THIS_MODULE, .get_direction = tps65912_gpio_get_direction, diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index 9dd9aca..5bd2172 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -101,7 +101,7 @@ static const struct regmap_config ts4900_regmap_config = { .val_bits = 8, }; -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "ts4900-gpio", .owner = THIS_MODULE, .get_direction = ts4900_gpio_get_direction, diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index 4b807b0..dfcfbba 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -381,7 +381,7 @@ static int twl_to_irq(struct gpio_chip *chip, unsigned offset) : -EINVAL; } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "twl4030", .owner = THIS_MODULE, .request = twl_request, diff --git a/drivers/gpio/gpio-wm831x.c b/drivers/gpio/gpio-wm831x.c index 21f97bc..533707f 100644 --- a/drivers/gpio/gpio-wm831x.c +++ b/drivers/gpio/gpio-wm831x.c @@ -247,7 +247,7 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) #define wm831x_gpio_dbg_show NULL #endif -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "wm831x", .owner = THIS_MODULE, .direction_input = wm831x_gpio_direction_in, diff --git a/drivers/gpio/gpio-wm8350.c b/drivers/gpio/gpio-wm8350.c index e976570..e46752e 100644 --- a/drivers/gpio/gpio-wm8350.c +++ b/drivers/gpio/gpio-wm8350.c @@ -93,7 +93,7 @@ static int wm8350_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return wm8350->irq_base + WM8350_IRQ_GPIO(offset); } -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "wm8350", .owner = THIS_MODULE, .direction_input = wm8350_gpio_direction_in, diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index 2457aac..68410fd 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -249,7 +249,7 @@ static void wm8994_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) #define wm8994_gpio_dbg_show NULL #endif -static struct gpio_chip template_chip = { +static const struct gpio_chip template_chip = { .label = "wm8994", .owner = THIS_MODULE, .request = wm8994_gpio_request, -- cgit v0.10.2 From 0a1e005374910180c30247386992565b05109eac Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 12 Sep 2016 14:29:51 +0300 Subject: gpiolib: Forbid irqchip default trigger for ACPI enumerated devices Follow DT and forbid default trigger if the GPIO irqchip device is enumerated from ACPI. Triggering for these devices will be configured automatically from ACPI interrupt resources provided by the BIOS. Suggested-by: Linus Walleij Signed-off-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e183ee2..043128d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1614,13 +1614,18 @@ int _gpiochip_irqchip_add(struct gpio_chip *gpiochip, of_node = gpiochip->of_node; #endif /* - * Specifying a default trigger is a terrible idea if DT is + * Specifying a default trigger is a terrible idea if DT or ACPI is * used to configure the interrupts, as you may end-up with * conflicting triggers. Tell the user, and reset to NONE. */ if (WARN(of_node && type != IRQ_TYPE_NONE, "%s: Ignoring %d default trigger\n", of_node->full_name, type)) type = IRQ_TYPE_NONE; + if (has_acpi_companion(gpiochip->parent) && type != IRQ_TYPE_NONE) { + acpi_handle_warn(ACPI_HANDLE(gpiochip->parent), + "Ignoring %d default trigger\n", type); + type = IRQ_TYPE_NONE; + } gpiochip->irqchip = irqchip; gpiochip->irq_handler = handler; -- cgit v0.10.2 From e50237c7c298f7966f8445efc7119f87d3add484 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 13 Sep 2016 13:43:34 +0200 Subject: gpio: aspeed: add MODULE_LICENSE() The build complains about missing MODULE_LICENSE() in the Aspeed GPIO driver. The license is evident from the file header, put in "GPL". Reported-by: Stephen Rothwell Cc: Alistair Popple Cc: Jeremy Kerr Cc: Andrew Jeffery Acked-by: Joel Stanley Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 64348a6..9f7266e 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -455,3 +455,4 @@ static struct platform_driver aspeed_gpio_driver = { module_platform_driver_probe(aspeed_gpio_driver, aspeed_gpio_probe); MODULE_DESCRIPTION("Aspeed GPIO Driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From d147d54899339b321f8319dfb25d05f1eec0e77d Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 13 Sep 2016 14:43:23 +0200 Subject: Revert "gpio: pca953x: initialize ret to zero to avoid returning garbage" This reverts commit 313b9a9938bf4076425741121d5d766826793e5d. This was already fixed by commit bf62efeb164343916ebb89dca6dfe5e6b6751700 "gpio: pca954x: fix undefined error code from remove" The latter is a better fix since it makes it easier to detect erronous code by not assigning a default error code. Reported-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index f170c56..5d05986 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -848,7 +848,7 @@ static int pca953x_remove(struct i2c_client *client) { struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev); struct pca953x_chip *chip = i2c_get_clientdata(client); - int ret = 0; + int ret; if (pdata && pdata->teardown) { ret = pdata->teardown(client, chip->gpio_chip.base, -- cgit v0.10.2 From a86e87e8c7653cbdbb61327ee04072638eecafbf Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:24 -0400 Subject: gpio: palmas: fix implicit assumption module.h is present This file is currently getting module.h from a global gpio header and it will fail to build once we remove module.h from that. However, the driver is controlled with the following Kconfig: drivers/gpio/Kconfig:config GPIO_PALMAS drivers/gpio/Kconfig: bool "TI PALMAS series PMICs GPIO" and hence the line of MODULE_DEVICE_TABLE is a no-op that can simply be deleted. In fact it should have been removed in an earlier commit that did demodularization, however the unseen include prevented my build testing from detecting it. Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index 8394744..3d81819 100644 --- a/drivers/gpio/gpio-palmas.c +++ b/drivers/gpio/gpio-palmas.c @@ -152,7 +152,6 @@ static const struct of_device_id of_palmas_gpio_match[] = { { .compatible = "ti,tps80036-gpio", .data = &tps80036_dev_data,}, { }, }; -MODULE_DEVICE_TABLE(of, of_palmas_gpio_match); static int palmas_gpio_probe(struct platform_device *pdev) { -- cgit v0.10.2 From bb411e771b0e2282e214c83a784ef1c5eaa4afea Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:25 -0400 Subject: gpio: sx150x: fix implicit assumption module.h is present This file is currently getting module.h from a global gpio header and it will faii to build once we remove module.h from that. However, the driver is controlled with the following Kconfig: drivers/gpio/Kconfig:config GPIO_SX150X drivers/gpio/Kconfig: bool "Semtech SX150x I2C GPIO expander" and hence the two lines of MODULE_DEVICE_TABLE are no-ops that can simply be deleted. Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index a177ebd..af95de8 100644 --- a/drivers/gpio/gpio-sx150x.c +++ b/drivers/gpio/gpio-sx150x.c @@ -236,7 +236,6 @@ static const struct i2c_device_id sx150x_id[] = { {"sx1502q", 3}, {} }; -MODULE_DEVICE_TABLE(i2c, sx150x_id); static const struct of_device_id sx150x_of_match[] = { { .compatible = "semtech,sx1508q" }, @@ -245,7 +244,6 @@ static const struct of_device_id sx150x_of_match[] = { { .compatible = "semtech,sx1502q" }, {}, }; -MODULE_DEVICE_TABLE(of, sx150x_of_match); static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val) { -- cgit v0.10.2 From 7de9a6c75be8a529e3531b157913abcc39cb7736 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:26 -0400 Subject: gpio: ts4800: fix implicit assumption module.h is present The Kconfig for this file is: drivers/gpio/Kconfig:config GPIO_TS4800 drivers/gpio/Kconfig: tristate "TS-4800 DIO blocks and compatibles" ...but however it does not include module.h -- it in turn gets it from another header (gpio/driver.h) and we'd like to replace that with a forward delcaration of "struct module;" but if we do, this file will fail to compile. So we fix this first to avoid putting build failures into the bisect commit history. Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c index 0c144a7..9925611 100644 --- a/drivers/gpio/gpio-ts4800.c +++ b/drivers/gpio/gpio-ts4800.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include -- cgit v0.10.2 From 7b5409ee92a461b14969ddd2a2e4583b3474de10 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:27 -0400 Subject: gpio: altera: fix implicit assumption module.h is present The Kconfig for this file is: drivers/gpio/Kconfig:config GPIO_ALTERA drivers/gpio/Kconfig: tristate "Altera GPIO" ...but however it does not include module.h -- it in turn gets it from another header (gpio/driver.h) and we'd like to replace that with a forward delcaration of "struct module;" but if we do, this file will fail to compile. So we fix this first to avoid putting build failures into the bisect commit history. Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 3f87a03..5bddbd5 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -17,6 +17,7 @@ */ #include +#include #include #include -- cgit v0.10.2 From 2034b9dcf56259cb21c93e726266f5c22730d39d Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:28 -0400 Subject: gpio: ath79: fix implicit assumption module.h is present The Kconfig for this file is: drivers/gpio/Kconfig:config GPIO_ATH79 drivers/gpio/Kconfig: tristate "Atheros AR71XX/AR724X/AR913X GPIO support" ...but however it does not include module.h -- it in turn gets it from another header (gpio/driver.h) and we'd like to replace that with a forward delcaration of "struct module;" but if we do, this file will fail to compile. So we fix this first to avoid putting build failures into the bisect commit history. Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index c4f4cdd..9457e20 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #define AR71XX_GPIO_REG_OE 0x00 -- cgit v0.10.2 From 5f604506f1fdaed63aac645d0c2c6104a2ce7ce8 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:29 -0400 Subject: gpio: loongson1: fix implicit assumption module.h is present The Kconfig for this file is: drivers/gpio/Kconfig:config GPIO_LOONGSON1 drivers/gpio/Kconfig: tristate "Loongson1 GPIO support" ...but however it does not include module.h -- it in turn gets it from another header (gpio/driver.h) and we'd like to replace that with a forward delcaration of "struct module;" but if we do, this file will fail to compile. So we fix this first to avoid putting build failures into the bisect commit history. Cc: Keguang Zhang Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-mips@linux-mips.org Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-loongson1.c b/drivers/gpio/gpio-loongson1.c index 10c09bd..ad0a595 100644 --- a/drivers/gpio/gpio-loongson1.c +++ b/drivers/gpio/gpio-loongson1.c @@ -8,6 +8,7 @@ * warranty of any kind, whether express or implied. */ +#include #include #include -- cgit v0.10.2 From 39d80072222428aaf73276c6fd66185e6f622bd9 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:30 -0400 Subject: gpio: wcove: fix implicit assumption module.h is present The Kconfig for this file is: drivers/gpio/Kconfig:config GPIO_WHISKEY_COVE drivers/gpio/Kconfig: tristate "GPIO support for Whiskey Cove PMIC" ...but however it does not include module.h -- it in turn gets it from another header (gpio/driver.h) and we'd like to replace that with a forward delcaration of "struct module;" but if we do, this file will fail to compile. So we fix this first to avoid putting build failures into the bisect commit history. Cc: Ajay Thomas Cc: Bin Gao Cc: Andy Shevchenko Cc: Mika Westerberg Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index e11d6a3..d0ddba7 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include -- cgit v0.10.2 From d47529b2e9fe0ec2eb1f072afad8849f52e385c4 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 12 Sep 2016 18:16:31 -0400 Subject: gpio: don't include module.h in shared driver header Most shared headers in include/linux don't need to know what the internals of a struct module are; all they care about is that it is a struct and hence they may require a pointer to one. The advantage in this is that module.h is including a lot of stuff itself, and an otherwise empty C file that just contains module.h will result in ~750kB from CPP (compared to say 12kB from init.h) So we have approximately 50 instances of "struct module;" in the various include/linux headers already that help us keep module.h out of other headers; here we do the same for gpio. Cc: Linus Walleij Cc: Alexandre Courbot Cc: linux-gpio@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Linus Walleij diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 50882e0..216e6f2 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -16,6 +15,7 @@ struct of_phandle_args; struct device_node; struct seq_file; struct gpio_device; +struct module; #ifdef CONFIG_GPIOLIB -- cgit v0.10.2 From 7f8b96570291def5104cec6f68bae09bd55b691a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 15 Sep 2016 01:30:32 +0000 Subject: gpio: aspeed: fix return value check in aspeed_gpio_probe() In case of error, the function devm_ioremap_resource() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Joel Stanley Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 9f7266e..8aa3406 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -413,8 +413,8 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) return -ENXIO; gpio->base = devm_ioremap_resource(&pdev->dev, res); - if (!gpio->base) - return -ENOMEM; + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); spin_lock_init(&gpio->lock); -- cgit v0.10.2 From 8df759c9e66d91507b653433b14ef2216d19ee29 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 16 Sep 2016 01:51:10 +0000 Subject: gpio: tps65218: use devm_gpiochip_add_data() for gpio registration Use devm_gpiochip_add_data() for GPIO registration and remove the need of driver callback .remove. Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 03e0dfb..d779307 100644 --- a/drivers/gpio/gpio-tps65218.c +++ b/drivers/gpio/gpio-tps65218.c @@ -204,7 +204,8 @@ static int tps65218_gpio_probe(struct platform_device *pdev) tps65218_gpio->gpio_chip.of_node = pdev->dev.of_node; #endif - ret = gpiochip_add_data(&tps65218_gpio->gpio_chip, tps65218_gpio); + ret = devm_gpiochip_add_data(&pdev->dev, &tps65218_gpio->gpio_chip, + tps65218_gpio); if (ret < 0) { dev_err(&pdev->dev, "Failed to register gpiochip, %d\n", ret); return ret; @@ -215,15 +216,6 @@ static int tps65218_gpio_probe(struct platform_device *pdev) return ret; } -static int tps65218_gpio_remove(struct platform_device *pdev) -{ - struct tps65218_gpio *tps65218_gpio = platform_get_drvdata(pdev); - - gpiochip_remove(&tps65218_gpio->gpio_chip); - - return 0; -} - static const struct of_device_id tps65218_dt_match[] = { { .compatible = "ti,tps65218-gpio" }, { } @@ -242,7 +234,6 @@ static struct platform_driver tps65218_gpio_driver = { .of_match_table = of_match_ptr(tps65218_dt_match) }, .probe = tps65218_gpio_probe, - .remove = tps65218_gpio_remove, .id_table = tps65218_gpio_id_table, }; -- cgit v0.10.2 From 35a26144a193536eb8870de99ff4b7093b7879a3 Mon Sep 17 00:00:00 2001 From: Amitesh Singh Date: Fri, 16 Sep 2016 22:28:53 +0530 Subject: gpio: f7188x: use gpiochip_get_data instead of container_of gpiochip_add_data is already used to add data pointer and chip. Lets rely on gpiochip_get_data which is getting used in other gpio_chip functions. Signed-off-by: Amitesh Singh Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 600be84..e8accde 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -214,8 +214,7 @@ static struct f7188x_gpio_bank f81866_gpio_bank[] = { static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { int err; - struct f7188x_gpio_bank *bank = - container_of(chip, struct f7188x_gpio_bank, chip); + struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); struct f7188x_sio *sio = bank->data->sio; u8 dir; -- cgit v0.10.2 From 14063d71e5e6775c676755612fc81deb029685c4 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 19 Sep 2016 10:08:56 +0200 Subject: gpio: tc3589x: add .get_direction() and small cleanup This adds a .get_direction() callback to the TC3589x and renames the function for setting single-ended mode to be more to the point. Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 5baa457..537a433 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -84,9 +84,25 @@ static int tc3589x_gpio_direction_input(struct gpio_chip *chip, return tc3589x_set_bits(tc3589x, reg, BIT(pos), 0); } -static int tc3589x_gpio_single_ended(struct gpio_chip *chip, - unsigned offset, - enum single_ended_mode mode) +static int tc3589x_gpio_get_direction(struct gpio_chip *chip, + unsigned offset) +{ + struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); + struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; + u8 reg = TC3589x_GPIODIR0 + offset / 8; + unsigned pos = offset % 8; + int ret; + + ret = tc3589x_reg_read(tc3589x, reg); + if (ret < 0) + return ret; + + return !!(ret & BIT(pos)); +} + +static int tc3589x_gpio_set_single_ended(struct gpio_chip *chip, + unsigned offset, + enum single_ended_mode mode) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; @@ -127,11 +143,12 @@ static int tc3589x_gpio_single_ended(struct gpio_chip *chip, static const struct gpio_chip template_chip = { .label = "tc3589x", .owner = THIS_MODULE, - .direction_input = tc3589x_gpio_direction_input, .get = tc3589x_gpio_get, - .direction_output = tc3589x_gpio_direction_output, .set = tc3589x_gpio_set, - .set_single_ended = tc3589x_gpio_single_ended, + .direction_output = tc3589x_gpio_direction_output, + .direction_input = tc3589x_gpio_direction_input, + .get_direction = tc3589x_gpio_get_direction, + .set_single_ended = tc3589x_gpio_set_single_ended, .can_sleep = true, }; -- cgit v0.10.2 From 0e4011ebbc1bee52fd65b1cba5da6fddbd588377 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 19 Sep 2016 10:14:29 +0200 Subject: gpio: tc3589x: fix up complaints on unsigned A bunch of variables were just declared "unsigned" and should be "unsigned int". Fix it up for this driver. Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 537a433..5a5a6cb 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -34,7 +34,7 @@ struct tc3589x_gpio { u8 oldregs[CACHE_NR_REGS][CACHE_NR_BANKS]; }; -static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset) +static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; @@ -49,24 +49,24 @@ static int tc3589x_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(ret & mask); } -static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned offset, int val) +static void tc3589x_gpio_set(struct gpio_chip *chip, unsigned int offset, int val) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; u8 reg = TC3589x_GPIODATA0 + (offset / 8) * 2; - unsigned pos = offset % 8; + unsigned int pos = offset % 8; u8 data[] = {val ? BIT(pos) : 0, BIT(pos)}; tc3589x_block_write(tc3589x, reg, ARRAY_SIZE(data), data); } static int tc3589x_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int val) + unsigned int offset, int val) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; u8 reg = TC3589x_GPIODIR0 + offset / 8; - unsigned pos = offset % 8; + unsigned int pos = offset % 8; tc3589x_gpio_set(chip, offset, val); @@ -74,23 +74,23 @@ static int tc3589x_gpio_direction_output(struct gpio_chip *chip, } static int tc3589x_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) + unsigned int offset) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; u8 reg = TC3589x_GPIODIR0 + offset / 8; - unsigned pos = offset % 8; + unsigned int pos = offset % 8; return tc3589x_set_bits(tc3589x, reg, BIT(pos), 0); } static int tc3589x_gpio_get_direction(struct gpio_chip *chip, - unsigned offset) + unsigned int offset) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); struct tc3589x *tc3589x = tc3589x_gpio->tc3589x; u8 reg = TC3589x_GPIODIR0 + offset / 8; - unsigned pos = offset % 8; + unsigned int pos = offset % 8; int ret; ret = tc3589x_reg_read(tc3589x, reg); @@ -101,7 +101,7 @@ static int tc3589x_gpio_get_direction(struct gpio_chip *chip, } static int tc3589x_gpio_set_single_ended(struct gpio_chip *chip, - unsigned offset, + unsigned int offset, enum single_ended_mode mode) { struct tc3589x_gpio *tc3589x_gpio = gpiochip_get_data(chip); @@ -113,7 +113,7 @@ static int tc3589x_gpio_set_single_ended(struct gpio_chip *chip, */ u8 odmreg = TC3589x_GPIOODM0 + (offset / 8) * 2; u8 odereg = TC3589x_GPIOODE0 + (offset / 8) * 2; - unsigned pos = offset % 8; + unsigned int pos = offset % 8; int ret; switch(mode) { -- cgit v0.10.2 From 9132ce450bd141cd8c5776b41e90f146cae4bc3b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 23 Sep 2016 14:34:45 +0200 Subject: ARM: omap2: fix missing include commit d47529b2e9fe "gpio: don't include module.h in shared driver header" removed from the header. It seems arch/arm/mach-omap2/board-rx51-peripherals.c is using __initdata_or_module from through to , so break this dependency so that we get a clean compile. Cc: Paul Gortmaker Cc: Tony Lindgren Fixes: d47529b2e9fe ("gpio: don't include module.h in shared driver header") Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c index a5ab712..6d3af43 100644 --- a/arch/arm/mach-omap2/board-rx51-peripherals.c +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c @@ -29,6 +29,7 @@ #include #include #include +#include #include -- cgit v0.10.2 From 6e643d8ddf0e923ea0601e1dc014ff0d3b7b8260 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 21 Sep 2016 15:07:39 +0000 Subject: gpio: loongson1: remove redundant return value check Remove unneeded error handling on the result of a call to platform_get_resource() when the value is passed to devm_ioremap_resource(). Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-loongson1.c b/drivers/gpio/gpio-loongson1.c index ad0a595..72b6403 100644 --- a/drivers/gpio/gpio-loongson1.c +++ b/drivers/gpio/gpio-loongson1.c @@ -56,11 +56,6 @@ static int ls1x_gpio_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "failed to get I/O memory\n"); - return -EINVAL; - } - gpio_reg_base = devm_ioremap_resource(dev, res); if (IS_ERR(gpio_reg_base)) return PTR_ERR(gpio_reg_base); -- cgit v0.10.2 From 83626bbdf5ca31257066b05db5a38c846ae1e19f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 21 Sep 2016 15:07:56 +0000 Subject: gpio: aspeed: remove redundant return value check Remove unneeded error handling on the result of a call to platform_get_resource() when the value is passed to devm_ioremap_resource(). Signed-off-by: Wei Yongjun Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 8aa3406..03a5925 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -409,9 +409,6 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - gpio->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base); -- cgit v0.10.2 From 81d3753d9ea540c38b906399822a963e3b44a045 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 21 Sep 2016 23:51:22 +0300 Subject: gpio: axp209: Implement get_direction Implement the get_direction callback for the GPIOs found in the AXP209 PMIC. Due to the way they are implemented, in the same register you have the muxing options, GPIO directions and GPIO values. Since you have no control over what value is there at reset, simply use output as the default. Signed-off-by: Maxime Ripard Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-axp209.c b/drivers/gpio/gpio-axp209.c index 3174799..d9c2a51 100644 --- a/drivers/gpio/gpio-axp209.c +++ b/drivers/gpio/gpio-axp209.c @@ -77,6 +77,35 @@ static int axp20x_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(val & BIT(offset + 4)); } +static int axp20x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct axp20x_gpio *gpio = gpiochip_get_data(chip); + unsigned int val; + int reg, ret; + + reg = axp20x_gpio_get_reg(offset); + if (reg < 0) + return reg; + + ret = regmap_read(gpio->regmap, reg, &val); + if (ret) + return ret; + + /* + * This shouldn't really happen if the pin is in use already, + * or if it's not in use yet, it doesn't matter since we're + * going to change the value soon anyway. Default to output. + */ + if ((val & AXP20X_GPIO_FUNCTIONS) > 2) + return 0; + + /* + * The GPIO directions are the three lowest values. + * 2 is input, 0 and 1 are output + */ + return val & 2; +} + static int axp20x_gpio_output(struct gpio_chip *chip, unsigned offset, int value) { @@ -123,6 +152,7 @@ static int axp20x_gpio_probe(struct platform_device *pdev) gpio->chip.label = dev_name(&pdev->dev); gpio->chip.owner = THIS_MODULE; gpio->chip.get = axp20x_gpio_get; + gpio->chip.get_direction = axp20x_gpio_get_direction; gpio->chip.set = axp20x_gpio_set; gpio->chip.direction_input = axp20x_gpio_input; gpio->chip.direction_output = axp20x_gpio_output; -- cgit v0.10.2 From e3296f19c8620a9c47b6734df84c4b3126d2fe46 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Fri, 23 Sep 2016 16:56:58 +0530 Subject: gpio: Added zynq specific check for special pins on bank zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds zynq specific check for bank 0 pins 7 and 8 are special and cannot be used as inputs Signed-off-by: Nava kishore Manne Reported-by: Jonas Karlsson Acked-by: Sören Brinkmann Acked-by: Michal Simek Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index e72794e..6b4d10d 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -96,6 +96,9 @@ /* GPIO upper 16 bit mask */ #define ZYNQ_GPIO_UPPER_MASK 0xFFFF0000 +/* For GPIO quirks */ +#define ZYNQ_GPIO_QUIRK_FOO BIT(0) + /** * struct zynq_gpio - gpio device private data structure * @chip: instance of the gpio_chip @@ -122,6 +125,7 @@ struct zynq_gpio { */ struct zynq_platform_data { const char *label; + u32 quirks; u16 ngpio; int max_bank; int bank_min[ZYNQMP_GPIO_MAX_BANK]; @@ -238,13 +242,19 @@ 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); - /* bank 0 pins 7 and 8 are special and cannot be used as inputs */ - if (bank_num == 0 && (bank_pin_num == 7 || bank_pin_num == 8)) + /* + * On zynq bank 0 pins 7 and 8 are special and cannot be used + * as inputs. + */ + if (is_zynq_gpio && bank_num == 0 && + (bank_pin_num == 7 || bank_pin_num == 8)) return -EINVAL; /* clear the bit in direction mode reg to set the pin as input */ @@ -627,6 +637,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, .ngpio = ZYNQ_GPIO_NR_GPIOS, .max_bank = ZYNQ_GPIO_MAX_BANK, .bank_min[0] = ZYNQ_GPIO_BANK0_PIN_MIN(), -- cgit v0.10.2 From 0f98dd1b27d27412af3aef6a49ea6975988e33e7 Mon Sep 17 00:00:00 2001 From: Bamvor Jian Zhang Date: Wed, 31 Aug 2016 11:45:46 +0200 Subject: gpio/mockup: add virtual gpio device This patch add basic structure of a virtual gpio device(gpio-mockup) for testing gpio subsystem. The tester could manipulate such device through userspace(sysfs or char device) and check the result from debugfs. Currently, it support one or more gpiochip(determined by module parameters with base,ngpio pair). One could test the overlap of different gpiochip and test the direction and/or output values of these chips. Signed-off-by: Kamlakant Patel Signed-off-by: Bamvor Jian Zhang Signed-off-by: Linus Walleij diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 46c030a..61f4dbf 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1364,6 +1364,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Format: such that (rxsize & ~0x1fffc0) == 0. Default: 1024 + gpio-mockup.gpio_mockup_ranges + [HW] Sets the ranges of gpiochip of for this device. + Format: ,,,... + hardlockup_all_cpu_backtrace= [KNL] Should the hard-lockup detector generate backtraces on all cpus. diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0e41c45..79429b9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -279,6 +279,18 @@ config GPIO_MM_LANTIQ (EBU) found on Lantiq SoCs. The gpios are output only as they are created by attaching a 16bit latch to the bus. +config GPIO_MOCKUP + tristate "GPIO Testing Driver" + depends on GPIOLIB + select GPIO_SYSFS + help + This enables GPIO Testing driver, which provides a way to test GPIO + subsystem through sysfs(or char device) and debugfs. GPIO_SYSFS + must be selected for this test. + User could use it through the script in + tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in + it. + config GPIO_MOXART bool "MOXART GPIO support" depends on ARCH_MOXART || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b3e039c..915c7de 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_GPIO_MC9S08DZ60) += gpio-mc9s08dz60.o obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o +obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o obj-$(CONFIG_GPIO_MOXART) += gpio-moxart.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c new file mode 100644 index 0000000..1ef85b0 --- /dev/null +++ b/drivers/gpio/gpio-mockup.c @@ -0,0 +1,214 @@ +/* + * GPIO Testing Device Driver + * + * Copyright (C) 2014 Kamlakant Patel + * Copyright (C) 2015-2016 Bamvor Jian Zhang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include + +#define GPIO_NAME "gpio-mockup" +#define MAX_GC 10 + +enum direction { + OUT, + IN +}; + +/* + * struct gpio_pin_status - structure describing a GPIO status + * @dir: Configures direction of gpio as "in" or "out", 0=in, 1=out + * @value: Configures status of the gpio as 0(low) or 1(high) + */ +struct gpio_pin_status { + enum direction dir; + bool value; +}; + +struct mockup_gpio_controller { + struct gpio_chip gc; + struct gpio_pin_status *stats; +}; + +static int gpio_mockup_ranges[MAX_GC << 1]; +static int gpio_mockup_params_nr; +module_param_array(gpio_mockup_ranges, int, &gpio_mockup_params_nr, 0400); + +const char pins_name_start = 'A'; + +static int mockup_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + + return cntr->stats[offset].value; +} + +static void mockup_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + + cntr->stats[offset].value = !!value; +} + +static int mockup_gpio_dirout(struct gpio_chip *gc, unsigned int offset, + int value) +{ + struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + + mockup_gpio_set(gc, offset, value); + cntr->stats[offset].dir = OUT; + return 0; +} + +static int mockup_gpio_dirin(struct gpio_chip *gc, unsigned int offset) +{ + struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + + cntr->stats[offset].dir = IN; + return 0; +} + +static int mockup_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct mockup_gpio_controller *cntr = gpiochip_get_data(gc); + + return cntr->stats[offset].dir; +} + +static int mockup_gpio_add(struct device *dev, + struct mockup_gpio_controller *cntr, + const char *name, int base, int ngpio) +{ + int ret; + + cntr->gc.base = base; + cntr->gc.ngpio = ngpio; + cntr->gc.label = name; + cntr->gc.owner = THIS_MODULE; + cntr->gc.parent = dev; + cntr->gc.get = mockup_gpio_get; + cntr->gc.set = mockup_gpio_set; + cntr->gc.direction_output = mockup_gpio_dirout; + cntr->gc.direction_input = mockup_gpio_dirin; + cntr->gc.get_direction = mockup_gpio_get_direction; + cntr->stats = devm_kzalloc(dev, sizeof(*cntr->stats) * cntr->gc.ngpio, + GFP_KERNEL); + if (!cntr->stats) { + ret = -ENOMEM; + goto err; + } + ret = devm_gpiochip_add_data(dev, &cntr->gc, cntr); + if (ret) + goto err; + + dev_info(dev, "gpio<%d..%d> add successful!", base, base + ngpio); + return 0; +err: + dev_err(dev, "gpio<%d..%d> add failed!", base, base + ngpio); + return ret; +} + +static int mockup_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mockup_gpio_controller *cntr; + int ret; + int i; + int base; + int ngpio; + char chip_name[sizeof(GPIO_NAME) + 3]; + + if (gpio_mockup_params_nr < 2) + return -EINVAL; + + cntr = devm_kzalloc(dev, sizeof(*cntr) * (gpio_mockup_params_nr >> 1), + GFP_KERNEL); + if (!cntr) + return -ENOMEM; + + platform_set_drvdata(pdev, cntr); + + for (i = 0; i < gpio_mockup_params_nr >> 1; i++) { + base = gpio_mockup_ranges[i * 2]; + if (base == -1) + ngpio = gpio_mockup_ranges[i * 2 + 1]; + else + ngpio = gpio_mockup_ranges[i * 2 + 1] - base; + + if (ngpio >= 0) { + sprintf(chip_name, "%s-%c", GPIO_NAME, + pins_name_start + i); + ret = mockup_gpio_add(dev, &cntr[i], + chip_name, base, ngpio); + } else { + ret = -1; + } + if (ret) { + if (base < 0) + dev_err(dev, "gpio<%d..%d> add failed\n", + base, ngpio); + else + dev_err(dev, "gpio<%d..%d> add failed\n", + base, base + ngpio); + + return ret; + } + } + + return 0; +} + +static struct platform_driver mockup_gpio_driver = { + .driver = { + .name = GPIO_NAME, + }, + .probe = mockup_gpio_probe, +}; + +static struct platform_device *pdev; +static int __init mock_device_init(void) +{ + int err; + + pdev = platform_device_alloc(GPIO_NAME, -1); + if (!pdev) + return -ENOMEM; + + err = platform_device_add(pdev); + if (err) { + platform_device_put(pdev); + return err; + } + + err = platform_driver_register(&mockup_gpio_driver); + if (err) { + platform_device_unregister(pdev); + return err; + } + + return 0; +} + +static void __exit mock_device_exit(void) +{ + platform_driver_unregister(&mockup_gpio_driver); + platform_device_unregister(pdev); +} + +module_init(mock_device_init); +module_exit(mock_device_exit); + +MODULE_AUTHOR("Kamlakant Patel "); +MODULE_AUTHOR("Bamvor Jian Zhang "); +MODULE_DESCRIPTION("GPIO Testing driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From a6a1cf3d963daaf80e467793060be1fa57599a4b Mon Sep 17 00:00:00 2001 From: Bamvor Jian Zhang Date: Wed, 31 Aug 2016 11:45:48 +0200 Subject: gpio: MAINTAINERS: Add an entry for GPIO mockup driver Add an entry for the GPIO mockup driver with myself as maintainer. Signed-off-by: Bamvor Jian Zhang Signed-off-by: Linus Walleij diff --git a/MAINTAINERS b/MAINTAINERS index 5d0f88e..056c1c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5242,6 +5242,13 @@ L: linux-input@vger.kernel.org S: Maintained F: drivers/input/touchscreen/goodix.c +GPIO MOCKUP DRIVER +M: Bamvor Jian Zhang +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-mockup.c +F: tools/testing/selftests/gpio/ + GPIO SUBSYSTEM M: Linus Walleij M: Alexandre Courbot -- cgit v0.10.2 From 3c6e8d05d60d8106b5cdc730cf220b2a4b521b66 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 16 Sep 2016 13:48:12 +0200 Subject: mfd/gpio: Move HTC GPIO driver to GPIO subsystem The HTC GPIO driver is a pure GPIO driver and I just can not see what it is doing inside MFD. Let's just move it to GPIO and take this opportunity to move the platform data to Cc: arm@kernel.org Cc: Russell King Acked-by: Lee Jones Acked-by: Arnd Bergmann Signed-off-by: Linus Walleij diff --git a/arch/arm/mach-pxa/hx4700.c b/arch/arm/mach-pxa/hx4700.c index 4a2f9ab..66184f5 100644 --- a/arch/arm/mach-pxa/hx4700.c +++ b/arch/arm/mach-pxa/hx4700.c @@ -24,10 +24,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c index abc9181..265f48b 100644 --- a/arch/arm/mach-pxa/magician.c +++ b/arch/arm/mach-pxa/magician.c @@ -20,10 +20,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include diff --git a/arch/arm/mach-sa1100/h3xxx.c b/arch/arm/mach-sa1100/h3xxx.c index b1d4faa..b69e766 100644 --- a/arch/arm/mach-sa1100/h3xxx.c +++ b/arch/arm/mach-sa1100/h3xxx.c @@ -14,9 +14,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98dd47a..03be200 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -848,6 +848,14 @@ config GPIO_DLN2 This driver can also be built as a module. If so, the module will be called gpio-dln2. +config HTC_EGPIO + bool "HTC EGPIO support" + depends on GPIOLIB && ARM + help + This driver supports the CPLD egpio chip present on + several HTC phones. It provides basic support for input + pins, output pins, and irqs. + config GPIO_JANZ_TTL tristate "Janz VMOD-TTL Digital IO Module" depends on MFD_JANZ_CMODIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2a035ed..32ebc07 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o +obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o obj-$(CONFIG_GPIO_IT87) += gpio-it87.o diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c new file mode 100644 index 0000000..0b4df60 --- /dev/null +++ b/drivers/gpio/gpio-htc-egpio.c @@ -0,0 +1,440 @@ +/* + * Support for the GPIO/IRQ expander chips present on several HTC phones. + * These are implemented in CPLD chips present on the board. + * + * Copyright (c) 2007 Kevin O'Connor + * Copyright (c) 2007 Philipp Zabel + * + * This file may be distributed under the terms of the GNU GPL license. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct egpio_chip { + int reg_start; + int cached_values; + unsigned long is_out; + struct device *dev; + struct gpio_chip chip; +}; + +struct egpio_info { + spinlock_t lock; + + /* iomem info */ + void __iomem *base_addr; + int bus_shift; /* byte shift */ + int reg_shift; /* bit shift */ + int reg_mask; + + /* irq info */ + int ack_register; + int ack_write; + u16 irqs_enabled; + uint irq_start; + int nirqs; + uint chained_irq; + + /* egpio info */ + struct egpio_chip *chip; + int nchips; +}; + +static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg) +{ + writew(value, ei->base_addr + (reg << ei->bus_shift)); +} + +static inline u16 egpio_readw(struct egpio_info *ei, int reg) +{ + return readw(ei->base_addr + (reg << ei->bus_shift)); +} + +/* + * IRQs + */ + +static inline void ack_irqs(struct egpio_info *ei) +{ + egpio_writew(ei->ack_write, ei, ei->ack_register); + pr_debug("EGPIO ack - write %x to base+%x\n", + ei->ack_write, ei->ack_register << ei->bus_shift); +} + +static void egpio_ack(struct irq_data *data) +{ +} + +/* There does not appear to be a way to proactively mask interrupts + * on the egpio chip itself. So, we simply ignore interrupts that + * aren't desired. */ +static void egpio_mask(struct irq_data *data) +{ + struct egpio_info *ei = irq_data_get_irq_chip_data(data); + ei->irqs_enabled &= ~(1 << (data->irq - ei->irq_start)); + pr_debug("EGPIO mask %d %04x\n", data->irq, ei->irqs_enabled); +} + +static void egpio_unmask(struct irq_data *data) +{ + struct egpio_info *ei = irq_data_get_irq_chip_data(data); + ei->irqs_enabled |= 1 << (data->irq - ei->irq_start); + pr_debug("EGPIO unmask %d %04x\n", data->irq, ei->irqs_enabled); +} + +static struct irq_chip egpio_muxed_chip = { + .name = "htc-egpio", + .irq_ack = egpio_ack, + .irq_mask = egpio_mask, + .irq_unmask = egpio_unmask, +}; + +static void egpio_handler(struct irq_desc *desc) +{ + struct egpio_info *ei = irq_desc_get_handler_data(desc); + int irqpin; + + /* Read current pins. */ + unsigned long readval = egpio_readw(ei, ei->ack_register); + pr_debug("IRQ reg: %x\n", (unsigned int)readval); + /* Ack/unmask interrupts. */ + ack_irqs(ei); + /* Process all set pins. */ + readval &= ei->irqs_enabled; + for_each_set_bit(irqpin, &readval, ei->nirqs) { + /* Run irq handler */ + pr_debug("got IRQ %d\n", irqpin); + generic_handle_irq(ei->irq_start + irqpin); + } +} + +int htc_egpio_get_wakeup_irq(struct device *dev) +{ + struct egpio_info *ei = dev_get_drvdata(dev); + + /* Read current pins. */ + u16 readval = egpio_readw(ei, ei->ack_register); + /* Ack/unmask interrupts. */ + ack_irqs(ei); + /* Return first set pin. */ + readval &= ei->irqs_enabled; + return ei->irq_start + ffs(readval) - 1; +} +EXPORT_SYMBOL(htc_egpio_get_wakeup_irq); + +static inline int egpio_pos(struct egpio_info *ei, int bit) +{ + return bit >> ei->reg_shift; +} + +static inline int egpio_bit(struct egpio_info *ei, int bit) +{ + return 1 << (bit & ((1 << ei->reg_shift)-1)); +} + +/* + * Input pins + */ + +static int egpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct egpio_chip *egpio; + struct egpio_info *ei; + unsigned bit; + int reg; + int value; + + pr_debug("egpio_get_value(%d)\n", chip->base + offset); + + egpio = gpiochip_get_data(chip); + ei = dev_get_drvdata(egpio->dev); + bit = egpio_bit(ei, offset); + reg = egpio->reg_start + egpio_pos(ei, offset); + + value = egpio_readw(ei, reg); + pr_debug("readw(%p + %x) = %x\n", + ei->base_addr, reg << ei->bus_shift, value); + return !!(value & bit); +} + +static int egpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct egpio_chip *egpio; + + egpio = gpiochip_get_data(chip); + return test_bit(offset, &egpio->is_out) ? -EINVAL : 0; +} + + +/* + * Output pins + */ + +static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned long flag; + struct egpio_chip *egpio; + struct egpio_info *ei; + unsigned bit; + int pos; + int reg; + int shift; + + pr_debug("egpio_set(%s, %d(%d), %d)\n", + chip->label, offset, offset+chip->base, value); + + egpio = gpiochip_get_data(chip); + ei = dev_get_drvdata(egpio->dev); + bit = egpio_bit(ei, offset); + pos = egpio_pos(ei, offset); + reg = egpio->reg_start + pos; + shift = pos << ei->reg_shift; + + pr_debug("egpio %s: reg %d = 0x%04x\n", value ? "set" : "clear", + reg, (egpio->cached_values >> shift) & ei->reg_mask); + + spin_lock_irqsave(&ei->lock, flag); + if (value) + egpio->cached_values |= (1 << offset); + else + egpio->cached_values &= ~(1 << offset); + egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg); + spin_unlock_irqrestore(&ei->lock, flag); +} + +static int egpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct egpio_chip *egpio; + + egpio = gpiochip_get_data(chip); + if (test_bit(offset, &egpio->is_out)) { + egpio_set(chip, offset, value); + return 0; + } else { + return -EINVAL; + } +} + +static void egpio_write_cache(struct egpio_info *ei) +{ + int i; + struct egpio_chip *egpio; + int shift; + + for (i = 0; i < ei->nchips; i++) { + egpio = &(ei->chip[i]); + if (!egpio->is_out) + continue; + + for (shift = 0; shift < egpio->chip.ngpio; + shift += (1<reg_shift)) { + + int reg = egpio->reg_start + egpio_pos(ei, shift); + + if (!((egpio->is_out >> shift) & ei->reg_mask)) + continue; + + pr_debug("EGPIO: setting %x to %x, was %x\n", reg, + (egpio->cached_values >> shift) & ei->reg_mask, + egpio_readw(ei, reg)); + + egpio_writew((egpio->cached_values >> shift) + & ei->reg_mask, ei, reg); + } + } +} + + +/* + * Setup + */ + +static int __init egpio_probe(struct platform_device *pdev) +{ + struct htc_egpio_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct resource *res; + struct egpio_info *ei; + struct gpio_chip *chip; + unsigned int irq, irq_end; + int i; + int ret; + + /* Initialize ei data structure. */ + ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL); + if (!ei) + return -ENOMEM; + + spin_lock_init(&ei->lock); + + /* Find chained irq */ + ret = -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) + ei->chained_irq = res->start; + + /* Map egpio chip into virtual address space. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto fail; + ei->base_addr = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!ei->base_addr) + goto fail; + pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr); + + if ((pdata->bus_width != 16) && (pdata->bus_width != 32)) + goto fail; + ei->bus_shift = fls(pdata->bus_width - 1) - 3; + pr_debug("bus_shift = %d\n", ei->bus_shift); + + if ((pdata->reg_width != 8) && (pdata->reg_width != 16)) + goto fail; + ei->reg_shift = fls(pdata->reg_width - 1); + pr_debug("reg_shift = %d\n", ei->reg_shift); + + ei->reg_mask = (1 << pdata->reg_width) - 1; + + platform_set_drvdata(pdev, ei); + + ei->nchips = pdata->num_chips; + ei->chip = devm_kzalloc(&pdev->dev, + sizeof(struct egpio_chip) * ei->nchips, + GFP_KERNEL); + if (!ei->chip) { + ret = -ENOMEM; + goto fail; + } + for (i = 0; i < ei->nchips; i++) { + ei->chip[i].reg_start = pdata->chip[i].reg_start; + ei->chip[i].cached_values = pdata->chip[i].initial_values; + ei->chip[i].is_out = pdata->chip[i].direction; + ei->chip[i].dev = &(pdev->dev); + chip = &(ei->chip[i].chip); + chip->label = "htc-egpio"; + chip->parent = &pdev->dev; + chip->owner = THIS_MODULE; + chip->get = egpio_get; + chip->set = egpio_set; + chip->direction_input = egpio_direction_input; + chip->direction_output = egpio_direction_output; + chip->base = pdata->chip[i].gpio_base; + chip->ngpio = pdata->chip[i].num_gpios; + + gpiochip_add_data(chip, &ei->chip[i]); + } + + /* Set initial pin values */ + egpio_write_cache(ei); + + ei->irq_start = pdata->irq_base; + ei->nirqs = pdata->num_irqs; + ei->ack_register = pdata->ack_register; + + if (ei->chained_irq) { + /* Setup irq handlers */ + ei->ack_write = 0xFFFF; + if (pdata->invert_acks) + ei->ack_write = 0; + irq_end = ei->irq_start + ei->nirqs; + for (irq = ei->irq_start; irq < irq_end; irq++) { + irq_set_chip_and_handler(irq, &egpio_muxed_chip, + handle_simple_irq); + irq_set_chip_data(irq, ei); + irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); + } + irq_set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING); + irq_set_chained_handler_and_data(ei->chained_irq, + egpio_handler, ei); + ack_irqs(ei); + + device_init_wakeup(&pdev->dev, 1); + } + + return 0; + +fail: + printk(KERN_ERR "EGPIO failed to setup\n"); + return ret; +} + +static int __exit egpio_remove(struct platform_device *pdev) +{ + struct egpio_info *ei = platform_get_drvdata(pdev); + unsigned int irq, irq_end; + + if (ei->chained_irq) { + irq_end = ei->irq_start + ei->nirqs; + for (irq = ei->irq_start; irq < irq_end; irq++) { + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); + } + irq_set_chained_handler(ei->chained_irq, NULL); + device_init_wakeup(&pdev->dev, 0); + } + + return 0; +} + +#ifdef CONFIG_PM +static int egpio_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct egpio_info *ei = platform_get_drvdata(pdev); + + if (ei->chained_irq && device_may_wakeup(&pdev->dev)) + enable_irq_wake(ei->chained_irq); + return 0; +} + +static int egpio_resume(struct platform_device *pdev) +{ + struct egpio_info *ei = platform_get_drvdata(pdev); + + if (ei->chained_irq && device_may_wakeup(&pdev->dev)) + disable_irq_wake(ei->chained_irq); + + /* Update registers from the cache, in case + the CPLD was powered off during suspend */ + egpio_write_cache(ei); + return 0; +} +#else +#define egpio_suspend NULL +#define egpio_resume NULL +#endif + + +static struct platform_driver egpio_driver = { + .driver = { + .name = "htc-egpio", + }, + .remove = __exit_p(egpio_remove), + .suspend = egpio_suspend, + .resume = egpio_resume, +}; + +static int __init egpio_init(void) +{ + return platform_driver_probe(&egpio_driver, egpio_probe); +} + +static void __exit egpio_exit(void) +{ + platform_driver_unregister(&egpio_driver); +} + +/* start early for dependencies */ +subsys_initcall(egpio_init); +module_exit(egpio_exit) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kevin O'Connor "); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2d1fb64..8634d60 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -340,14 +340,6 @@ config MFD_HI655X_PMIC help Select this option to enable Hisilicon hi655x series pmic driver. -config HTC_EGPIO - bool "HTC EGPIO support" - depends on GPIOLIB && ARM - help - This driver supports the CPLD egpio chip present on - several HTC phones. It provides basic support for input - pins, output pins, and irqs. - config HTC_PASIC3 tristate "HTC PASIC3 LED/DS1WM chip support" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2ba3ba3..a5f1bf1 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -18,7 +18,6 @@ rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o -obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c deleted file mode 100644 index 513cfc5..0000000 --- a/drivers/mfd/htc-egpio.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Support for the GPIO/IRQ expander chips present on several HTC phones. - * These are implemented in CPLD chips present on the board. - * - * Copyright (c) 2007 Kevin O'Connor - * Copyright (c) 2007 Philipp Zabel - * - * This file may be distributed under the terms of the GNU GPL license. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct egpio_chip { - int reg_start; - int cached_values; - unsigned long is_out; - struct device *dev; - struct gpio_chip chip; -}; - -struct egpio_info { - spinlock_t lock; - - /* iomem info */ - void __iomem *base_addr; - int bus_shift; /* byte shift */ - int reg_shift; /* bit shift */ - int reg_mask; - - /* irq info */ - int ack_register; - int ack_write; - u16 irqs_enabled; - uint irq_start; - int nirqs; - uint chained_irq; - - /* egpio info */ - struct egpio_chip *chip; - int nchips; -}; - -static inline void egpio_writew(u16 value, struct egpio_info *ei, int reg) -{ - writew(value, ei->base_addr + (reg << ei->bus_shift)); -} - -static inline u16 egpio_readw(struct egpio_info *ei, int reg) -{ - return readw(ei->base_addr + (reg << ei->bus_shift)); -} - -/* - * IRQs - */ - -static inline void ack_irqs(struct egpio_info *ei) -{ - egpio_writew(ei->ack_write, ei, ei->ack_register); - pr_debug("EGPIO ack - write %x to base+%x\n", - ei->ack_write, ei->ack_register << ei->bus_shift); -} - -static void egpio_ack(struct irq_data *data) -{ -} - -/* There does not appear to be a way to proactively mask interrupts - * on the egpio chip itself. So, we simply ignore interrupts that - * aren't desired. */ -static void egpio_mask(struct irq_data *data) -{ - struct egpio_info *ei = irq_data_get_irq_chip_data(data); - ei->irqs_enabled &= ~(1 << (data->irq - ei->irq_start)); - pr_debug("EGPIO mask %d %04x\n", data->irq, ei->irqs_enabled); -} - -static void egpio_unmask(struct irq_data *data) -{ - struct egpio_info *ei = irq_data_get_irq_chip_data(data); - ei->irqs_enabled |= 1 << (data->irq - ei->irq_start); - pr_debug("EGPIO unmask %d %04x\n", data->irq, ei->irqs_enabled); -} - -static struct irq_chip egpio_muxed_chip = { - .name = "htc-egpio", - .irq_ack = egpio_ack, - .irq_mask = egpio_mask, - .irq_unmask = egpio_unmask, -}; - -static void egpio_handler(struct irq_desc *desc) -{ - struct egpio_info *ei = irq_desc_get_handler_data(desc); - int irqpin; - - /* Read current pins. */ - unsigned long readval = egpio_readw(ei, ei->ack_register); - pr_debug("IRQ reg: %x\n", (unsigned int)readval); - /* Ack/unmask interrupts. */ - ack_irqs(ei); - /* Process all set pins. */ - readval &= ei->irqs_enabled; - for_each_set_bit(irqpin, &readval, ei->nirqs) { - /* Run irq handler */ - pr_debug("got IRQ %d\n", irqpin); - generic_handle_irq(ei->irq_start + irqpin); - } -} - -int htc_egpio_get_wakeup_irq(struct device *dev) -{ - struct egpio_info *ei = dev_get_drvdata(dev); - - /* Read current pins. */ - u16 readval = egpio_readw(ei, ei->ack_register); - /* Ack/unmask interrupts. */ - ack_irqs(ei); - /* Return first set pin. */ - readval &= ei->irqs_enabled; - return ei->irq_start + ffs(readval) - 1; -} -EXPORT_SYMBOL(htc_egpio_get_wakeup_irq); - -static inline int egpio_pos(struct egpio_info *ei, int bit) -{ - return bit >> ei->reg_shift; -} - -static inline int egpio_bit(struct egpio_info *ei, int bit) -{ - return 1 << (bit & ((1 << ei->reg_shift)-1)); -} - -/* - * Input pins - */ - -static int egpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct egpio_chip *egpio; - struct egpio_info *ei; - unsigned bit; - int reg; - int value; - - pr_debug("egpio_get_value(%d)\n", chip->base + offset); - - egpio = gpiochip_get_data(chip); - ei = dev_get_drvdata(egpio->dev); - bit = egpio_bit(ei, offset); - reg = egpio->reg_start + egpio_pos(ei, offset); - - value = egpio_readw(ei, reg); - pr_debug("readw(%p + %x) = %x\n", - ei->base_addr, reg << ei->bus_shift, value); - return !!(value & bit); -} - -static int egpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - struct egpio_chip *egpio; - - egpio = gpiochip_get_data(chip); - return test_bit(offset, &egpio->is_out) ? -EINVAL : 0; -} - - -/* - * Output pins - */ - -static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - unsigned long flag; - struct egpio_chip *egpio; - struct egpio_info *ei; - unsigned bit; - int pos; - int reg; - int shift; - - pr_debug("egpio_set(%s, %d(%d), %d)\n", - chip->label, offset, offset+chip->base, value); - - egpio = gpiochip_get_data(chip); - ei = dev_get_drvdata(egpio->dev); - bit = egpio_bit(ei, offset); - pos = egpio_pos(ei, offset); - reg = egpio->reg_start + pos; - shift = pos << ei->reg_shift; - - pr_debug("egpio %s: reg %d = 0x%04x\n", value ? "set" : "clear", - reg, (egpio->cached_values >> shift) & ei->reg_mask); - - spin_lock_irqsave(&ei->lock, flag); - if (value) - egpio->cached_values |= (1 << offset); - else - egpio->cached_values &= ~(1 << offset); - egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg); - spin_unlock_irqrestore(&ei->lock, flag); -} - -static int egpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct egpio_chip *egpio; - - egpio = gpiochip_get_data(chip); - if (test_bit(offset, &egpio->is_out)) { - egpio_set(chip, offset, value); - return 0; - } else { - return -EINVAL; - } -} - -static void egpio_write_cache(struct egpio_info *ei) -{ - int i; - struct egpio_chip *egpio; - int shift; - - for (i = 0; i < ei->nchips; i++) { - egpio = &(ei->chip[i]); - if (!egpio->is_out) - continue; - - for (shift = 0; shift < egpio->chip.ngpio; - shift += (1<reg_shift)) { - - int reg = egpio->reg_start + egpio_pos(ei, shift); - - if (!((egpio->is_out >> shift) & ei->reg_mask)) - continue; - - pr_debug("EGPIO: setting %x to %x, was %x\n", reg, - (egpio->cached_values >> shift) & ei->reg_mask, - egpio_readw(ei, reg)); - - egpio_writew((egpio->cached_values >> shift) - & ei->reg_mask, ei, reg); - } - } -} - - -/* - * Setup - */ - -static int __init egpio_probe(struct platform_device *pdev) -{ - struct htc_egpio_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct resource *res; - struct egpio_info *ei; - struct gpio_chip *chip; - unsigned int irq, irq_end; - int i; - int ret; - - /* Initialize ei data structure. */ - ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL); - if (!ei) - return -ENOMEM; - - spin_lock_init(&ei->lock); - - /* Find chained irq */ - ret = -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res) - ei->chained_irq = res->start; - - /* Map egpio chip into virtual address space. */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto fail; - ei->base_addr = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!ei->base_addr) - goto fail; - pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr); - - if ((pdata->bus_width != 16) && (pdata->bus_width != 32)) - goto fail; - ei->bus_shift = fls(pdata->bus_width - 1) - 3; - pr_debug("bus_shift = %d\n", ei->bus_shift); - - if ((pdata->reg_width != 8) && (pdata->reg_width != 16)) - goto fail; - ei->reg_shift = fls(pdata->reg_width - 1); - pr_debug("reg_shift = %d\n", ei->reg_shift); - - ei->reg_mask = (1 << pdata->reg_width) - 1; - - platform_set_drvdata(pdev, ei); - - ei->nchips = pdata->num_chips; - ei->chip = devm_kzalloc(&pdev->dev, - sizeof(struct egpio_chip) * ei->nchips, - GFP_KERNEL); - if (!ei->chip) { - ret = -ENOMEM; - goto fail; - } - for (i = 0; i < ei->nchips; i++) { - ei->chip[i].reg_start = pdata->chip[i].reg_start; - ei->chip[i].cached_values = pdata->chip[i].initial_values; - ei->chip[i].is_out = pdata->chip[i].direction; - ei->chip[i].dev = &(pdev->dev); - chip = &(ei->chip[i].chip); - chip->label = "htc-egpio"; - chip->parent = &pdev->dev; - chip->owner = THIS_MODULE; - chip->get = egpio_get; - chip->set = egpio_set; - chip->direction_input = egpio_direction_input; - chip->direction_output = egpio_direction_output; - chip->base = pdata->chip[i].gpio_base; - chip->ngpio = pdata->chip[i].num_gpios; - - gpiochip_add_data(chip, &ei->chip[i]); - } - - /* Set initial pin values */ - egpio_write_cache(ei); - - ei->irq_start = pdata->irq_base; - ei->nirqs = pdata->num_irqs; - ei->ack_register = pdata->ack_register; - - if (ei->chained_irq) { - /* Setup irq handlers */ - ei->ack_write = 0xFFFF; - if (pdata->invert_acks) - ei->ack_write = 0; - irq_end = ei->irq_start + ei->nirqs; - for (irq = ei->irq_start; irq < irq_end; irq++) { - irq_set_chip_and_handler(irq, &egpio_muxed_chip, - handle_simple_irq); - irq_set_chip_data(irq, ei); - irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - irq_set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING); - irq_set_chained_handler_and_data(ei->chained_irq, - egpio_handler, ei); - ack_irqs(ei); - - device_init_wakeup(&pdev->dev, 1); - } - - return 0; - -fail: - printk(KERN_ERR "EGPIO failed to setup\n"); - return ret; -} - -static int __exit egpio_remove(struct platform_device *pdev) -{ - struct egpio_info *ei = platform_get_drvdata(pdev); - unsigned int irq, irq_end; - - if (ei->chained_irq) { - irq_end = ei->irq_start + ei->nirqs; - for (irq = ei->irq_start; irq < irq_end; irq++) { - irq_set_chip_and_handler(irq, NULL, NULL); - irq_set_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - irq_set_chained_handler(ei->chained_irq, NULL); - device_init_wakeup(&pdev->dev, 0); - } - - return 0; -} - -#ifdef CONFIG_PM -static int egpio_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct egpio_info *ei = platform_get_drvdata(pdev); - - if (ei->chained_irq && device_may_wakeup(&pdev->dev)) - enable_irq_wake(ei->chained_irq); - return 0; -} - -static int egpio_resume(struct platform_device *pdev) -{ - struct egpio_info *ei = platform_get_drvdata(pdev); - - if (ei->chained_irq && device_may_wakeup(&pdev->dev)) - disable_irq_wake(ei->chained_irq); - - /* Update registers from the cache, in case - the CPLD was powered off during suspend */ - egpio_write_cache(ei); - return 0; -} -#else -#define egpio_suspend NULL -#define egpio_resume NULL -#endif - - -static struct platform_driver egpio_driver = { - .driver = { - .name = "htc-egpio", - }, - .remove = __exit_p(egpio_remove), - .suspend = egpio_suspend, - .resume = egpio_resume, -}; - -static int __init egpio_init(void) -{ - return platform_driver_probe(&egpio_driver, egpio_probe); -} - -static void __exit egpio_exit(void) -{ - platform_driver_unregister(&egpio_driver); -} - -/* start early for dependencies */ -subsys_initcall(egpio_init); -module_exit(egpio_exit) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kevin O'Connor "); diff --git a/include/linux/mfd/htc-egpio.h b/include/linux/mfd/htc-egpio.h deleted file mode 100644 index b4201c9..0000000 --- a/include/linux/mfd/htc-egpio.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * HTC simple EGPIO irq and gpio extender - */ - -#ifndef __HTC_EGPIO_H__ -#define __HTC_EGPIO_H__ - -#include - -/* Descriptive values for all-in or all-out htc_egpio_chip descriptors. */ -#define HTC_EGPIO_OUTPUT (~0) -#define HTC_EGPIO_INPUT 0 - -/** - * struct htc_egpio_chip - descriptor to create gpio_chip for register range - * @reg_start: index of first register - * @gpio_base: gpio number of first pin in this register range - * @num_gpios: number of gpios in this register range, max BITS_PER_LONG - * (number of registers = DIV_ROUND_UP(num_gpios, reg_width)) - * @direction: bitfield, '0' = input, '1' = output, - */ -struct htc_egpio_chip { - int reg_start; - int gpio_base; - int num_gpios; - unsigned long direction; - unsigned long initial_values; -}; - -/** - * struct htc_egpio_platform_data - description provided by the arch - * @irq_base: beginning of available IRQs (eg, IRQ_BOARD_START) - * @num_irqs: number of irqs - * @reg_width: number of bits per register, either 8 or 16 bit - * @bus_width: alignment of the registers, either 16 or 32 bit - * @invert_acks: set if chip requires writing '0' to ack an irq, instead of '1' - * @ack_register: location of the irq/ack register - * @chip: pointer to array of htc_egpio_chip descriptors - * @num_chips: number of egpio chip descriptors - */ -struct htc_egpio_platform_data { - int bus_width; - int reg_width; - - int irq_base; - int num_irqs; - int invert_acks; - int ack_register; - - struct htc_egpio_chip *chip; - int num_chips; -}; - -/* Determine the wakeup irq, to be called during early resume */ -extern int htc_egpio_get_wakeup_irq(struct device *dev); - -#endif diff --git a/include/linux/platform_data/gpio-htc-egpio.h b/include/linux/platform_data/gpio-htc-egpio.h new file mode 100644 index 0000000..b4201c9 --- /dev/null +++ b/include/linux/platform_data/gpio-htc-egpio.h @@ -0,0 +1,57 @@ +/* + * HTC simple EGPIO irq and gpio extender + */ + +#ifndef __HTC_EGPIO_H__ +#define __HTC_EGPIO_H__ + +#include + +/* Descriptive values for all-in or all-out htc_egpio_chip descriptors. */ +#define HTC_EGPIO_OUTPUT (~0) +#define HTC_EGPIO_INPUT 0 + +/** + * struct htc_egpio_chip - descriptor to create gpio_chip for register range + * @reg_start: index of first register + * @gpio_base: gpio number of first pin in this register range + * @num_gpios: number of gpios in this register range, max BITS_PER_LONG + * (number of registers = DIV_ROUND_UP(num_gpios, reg_width)) + * @direction: bitfield, '0' = input, '1' = output, + */ +struct htc_egpio_chip { + int reg_start; + int gpio_base; + int num_gpios; + unsigned long direction; + unsigned long initial_values; +}; + +/** + * struct htc_egpio_platform_data - description provided by the arch + * @irq_base: beginning of available IRQs (eg, IRQ_BOARD_START) + * @num_irqs: number of irqs + * @reg_width: number of bits per register, either 8 or 16 bit + * @bus_width: alignment of the registers, either 16 or 32 bit + * @invert_acks: set if chip requires writing '0' to ack an irq, instead of '1' + * @ack_register: location of the irq/ack register + * @chip: pointer to array of htc_egpio_chip descriptors + * @num_chips: number of egpio chip descriptors + */ +struct htc_egpio_platform_data { + int bus_width; + int reg_width; + + int irq_base; + int num_irqs; + int invert_acks; + int ack_register; + + struct htc_egpio_chip *chip; + int num_chips; +}; + +/* Determine the wakeup irq, to be called during early resume */ +extern int htc_egpio_get_wakeup_irq(struct device *dev); + +#endif -- cgit v0.10.2 From 96b2cca64fa3e1a31b573bb308b2944c802aacf3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 27 Sep 2016 15:59:12 -0700 Subject: gpio: stmpe: forbid unused lines to be mapped as IRQs Exploit the new mechanism for masking off disallowed IRQs added by Mika Westerberg to properly manage the STMPE "norequest mask" to disallow also mapping said lines as IRQs. Reviewed-by: Mika Westerberg Reviewed-by: Patrice Chotard Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index b51c5be..432b2ee 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -13,6 +13,7 @@ #include #include #include +#include /* * These registers are modified under the irq bus lock and cached to avoid @@ -449,6 +450,8 @@ static int stmpe_gpio_probe(struct platform_device *pdev) of_property_read_u32(np, "st,norequest-mask", &stmpe_gpio->norequest_mask); + if (stmpe_gpio->norequest_mask) + stmpe_gpio->chip.irq_need_valid_mask = true; if (irq < 0) dev_info(&pdev->dev, @@ -473,6 +476,14 @@ static int stmpe_gpio_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to get irq: %d\n", ret); goto out_disable; } + if (stmpe_gpio->norequest_mask) { + int i; + + /* Forbid unused lines to be mapped as IRQs */ + for (i = 0; i < sizeof(u32); i++) + if (stmpe_gpio->norequest_mask & BIT(i)) + clear_bit(i, stmpe_gpio->chip.irq_valid_mask); + } ret = gpiochip_irqchip_add(&stmpe_gpio->chip, &stmpe_gpio_irq_chip, 0, -- cgit v0.10.2 From 4e2678b540bbcd91b2135e13240baa7712eafa33 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 27 Sep 2016 16:11:10 -0700 Subject: gpio: stmpe: use BIT() macro Avoid custom (1 << bits) shifting by consequently using the BIT() macro from . Reviewed-by: Patrice Chotard Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 432b2ee..e7d422a 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -43,7 +43,7 @@ static int stmpe_gpio_get(struct gpio_chip *chip, unsigned offset) struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; u8 reg = stmpe->regs[STMPE_IDX_GPMR_LSB + (offset / 8)]; - u8 mask = 1 << (offset % 8); + u8 mask = BIT(offset % 8); int ret; ret = stmpe_reg_read(stmpe, reg); @@ -59,7 +59,7 @@ static void stmpe_gpio_set(struct gpio_chip *chip, unsigned offset, int val) struct stmpe *stmpe = stmpe_gpio->stmpe; int which = val ? STMPE_IDX_GPSR_LSB : STMPE_IDX_GPCR_LSB; u8 reg = stmpe->regs[which + (offset / 8)]; - u8 mask = 1 << (offset % 8); + u8 mask = BIT(offset % 8); /* * Some variants have single register for gpio set/clear functionality. @@ -77,7 +77,7 @@ static int stmpe_gpio_get_direction(struct gpio_chip *chip, struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB] - (offset / 8); - u8 mask = 1 << (offset % 8); + u8 mask = BIT(offset % 8); int ret; ret = stmpe_reg_read(stmpe, reg); @@ -93,7 +93,7 @@ static int stmpe_gpio_direction_output(struct gpio_chip *chip, struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)]; - u8 mask = 1 << (offset % 8); + u8 mask = BIT(offset % 8); stmpe_gpio_set(chip, offset, val); @@ -106,7 +106,7 @@ static int stmpe_gpio_direction_input(struct gpio_chip *chip, struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; u8 reg = stmpe->regs[STMPE_IDX_GPDR_LSB + (offset / 8)]; - u8 mask = 1 << (offset % 8); + u8 mask = BIT(offset % 8); return stmpe_set_bits(stmpe, reg, mask, 0); } @@ -116,10 +116,10 @@ static int stmpe_gpio_request(struct gpio_chip *chip, unsigned offset) struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(chip); struct stmpe *stmpe = stmpe_gpio->stmpe; - if (stmpe_gpio->norequest_mask & (1 << offset)) + if (stmpe_gpio->norequest_mask & BIT(offset)) return -EINVAL; - return stmpe_set_altfunc(stmpe, 1 << offset, STMPE_BLOCK_GPIO); + return stmpe_set_altfunc(stmpe, BIT(offset), STMPE_BLOCK_GPIO); } static const struct gpio_chip template_chip = { @@ -140,7 +140,7 @@ static int stmpe_gpio_irq_set_type(struct irq_data *d, unsigned int type) struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); int offset = d->hwirq; int regoffset = offset / 8; - int mask = 1 << (offset % 8); + int mask = BIT(offset % 8); if (type & IRQ_TYPE_LEVEL_LOW || type & IRQ_TYPE_LEVEL_HIGH) return -EINVAL; @@ -218,7 +218,7 @@ static void stmpe_gpio_irq_mask(struct irq_data *d) struct stmpe_gpio *stmpe_gpio = gpiochip_get_data(gc); int offset = d->hwirq; int regoffset = offset / 8; - int mask = 1 << (offset % 8); + int mask = BIT(offset % 8); stmpe_gpio->regs[REG_IE][regoffset] &= ~mask; } @@ -230,7 +230,7 @@ static void stmpe_gpio_irq_unmask(struct irq_data *d) struct stmpe *stmpe = stmpe_gpio->stmpe; int offset = d->hwirq; int regoffset = offset / 8; - int mask = 1 << (offset % 8); + int mask = BIT(offset % 8); stmpe_gpio->regs[REG_IE][regoffset] |= mask; @@ -254,7 +254,7 @@ static void stmpe_dbg_show_one(struct seq_file *s, bool val = !!stmpe_gpio_get(gc, offset); u8 bank = offset / 8; u8 dir_reg = stmpe->regs[STMPE_IDX_GPDR_LSB + bank]; - u8 mask = 1 << (offset % 8); + u8 mask = BIT(offset % 8); int ret; u8 dir; @@ -401,7 +401,7 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) line); handle_nested_irq(child_irq); - stat &= ~(1 << bit); + stat &= ~BIT(bit); } /* -- cgit v0.10.2 From 3085a4a459c91b7e8647d25b38b975c334e78c8c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Oct 2016 09:46:58 +0200 Subject: gpio: make memory-mapped drivers depend on HAS_IOMEM This one is pretty obvious: on UM Linux compilation of things like allmodconfig and allyesconfig will fail due to the absence of IO memory. Simply make these drivers depend on HAS_IOMEM, it has been implicitly assumed all the time, so just make it explicit. The generic MMIO library also assumes that IOMEM is present so make also this depend on HAS_IOMEM. Reported-by: kbuild test robot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 59ba58b..2401443 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -65,6 +65,7 @@ config GPIO_SYSFS exported to userspace; this can be useful when debugging. config GPIO_GENERIC + depends on HAS_IOMEM # Only for IOMEM drivers tristate # put drivers in the right section, in alphabetical order @@ -74,6 +75,7 @@ config GPIO_MAX730X tristate menu "Memory mapped GPIO drivers" + depends on HAS_IOMEM config GPIO_74XX_MMIO tristate "GPIO driver for 74xx-ICs with MMIO access" -- cgit v0.10.2 From ea713bc450054aed3114da74bf76cfda64b698d0 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Oct 2016 10:09:40 +0200 Subject: gpio: OF: separation of concerns The generic GPIO library directly implement code for of_find_gpio() which is only used with CONFIG_OF and causes compilation problems on archs that do not even have stubs for OF functions, especially on UM that does not implement any IO remap functions. Move the function to gpiolib-of.c, implement a static inline stub in gpiolib.h returning PTR_ERR(-ENOENT) if CONFIG_OF_GPIO is not set and be done with it. Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 75e7b39..33b05c8 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -114,6 +114,45 @@ int of_get_named_gpio_flags(struct device_node *np, const char *list_name, } EXPORT_SYMBOL(of_get_named_gpio_flags); +struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, + enum gpio_lookup_flags *flags) +{ + char prop_name[32]; /* 32 is max size of property name */ + enum of_gpio_flags of_flags; + struct gpio_desc *desc; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id) + snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id, + gpio_suffixes[i]); + else + snprintf(prop_name, sizeof(prop_name), "%s", + gpio_suffixes[i]); + + desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, + &of_flags); + if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) + break; + } + + if (IS_ERR(desc)) + return desc; + + if (of_flags & OF_GPIO_ACTIVE_LOW) + *flags |= GPIO_ACTIVE_LOW; + + if (of_flags & OF_GPIO_SINGLE_ENDED) { + if (of_flags & OF_GPIO_ACTIVE_LOW) + *flags |= GPIO_OPEN_DRAIN; + else + *flags |= GPIO_OPEN_SOURCE; + } + + return desc; +} + /** * of_parse_own_gpio() - Get a GPIO hog descriptor, names and flags for GPIO API * @np: device node to get GPIO from diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 19a665f..5404cdc 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2883,45 +2883,6 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) mutex_unlock(&gpio_lookup_lock); } -static struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, - unsigned int idx, - enum gpio_lookup_flags *flags) -{ - char prop_name[32]; /* 32 is max size of property name */ - enum of_gpio_flags of_flags; - struct gpio_desc *desc; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id) - snprintf(prop_name, sizeof(prop_name), "%s-%s", con_id, - gpio_suffixes[i]); - else - snprintf(prop_name, sizeof(prop_name), "%s", - gpio_suffixes[i]); - - desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, - &of_flags); - if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) - break; - } - - if (IS_ERR(desc)) - return desc; - - if (of_flags & OF_GPIO_ACTIVE_LOW) - *flags |= GPIO_ACTIVE_LOW; - - if (of_flags & OF_GPIO_SINGLE_ENDED) { - if (of_flags & OF_GPIO_ACTIVE_LOW) - *flags |= GPIO_OPEN_DRAIN; - else - *flags |= GPIO_OPEN_SOURCE; - } - - return desc; -} - static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id, unsigned int idx, diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 2d9ea5e..9ed2934 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -20,6 +20,7 @@ enum of_gpio_flags; enum gpiod_flags; +enum gpio_lookup_flags; struct acpi_device; /** @@ -86,6 +87,21 @@ struct acpi_gpio_info { /* gpio suffixes used for ACPI and device tree lookup */ static const char * const gpio_suffixes[] = { "gpios", "gpio" }; +#ifdef CONFIG_OF_GPIO +struct gpio_desc *of_find_gpio(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpio_lookup_flags *flags); +#else +static inline struct gpio_desc *of_find_gpio(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpio_lookup_flags *flags) +{ + return ERR_PTR(-ENOENT); +} +#endif /* CONFIG_OF_GPIO */ + #ifdef CONFIG_ACPI void acpi_gpiochip_add(struct gpio_chip *chip); void acpi_gpiochip_remove(struct gpio_chip *chip); -- cgit v0.10.2 From 031ba28a8197a08e67b12d7ec935b24eb3638345 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Oct 2016 10:40:03 +0200 Subject: gpio: acpi: separation of concerns The generic GPIO library directly implement code for acpi_find_gpio() which is only used with CONFIG_ACPI. This was probably done because OF did the same thing, but I removed that so remove this too. Rename the internal acpi_find_gpio() in gpiolib-acpi.c to acpi_populate_gpio_lookup() which seems to be more appropriate anyway so as to avoid a namespace clash with the same function. Make the stub return -ENOENT rather than -ENOSYS (as that is for syscalls!). For some reason the sunxi pin control driver was including the private gpiolib header, it works just fine without it so remove that oneliner. Cc: Rafael J. Wysocki Acked-by: Mika Westerberg Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index af51461..58ece20 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -395,7 +396,7 @@ struct acpi_gpio_lookup { int n; }; -static int acpi_find_gpio(struct acpi_resource *ares, void *data) +static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) { struct acpi_gpio_lookup *lookup = data; @@ -440,7 +441,8 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, INIT_LIST_HEAD(&res_list); - ret = acpi_dev_get_resources(lookup->adev, &res_list, acpi_find_gpio, + ret = acpi_dev_get_resources(lookup->adev, &res_list, + acpi_populate_gpio_lookup, lookup); if (ret < 0) return ret; @@ -513,7 +515,7 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, * Note: if the GPIO resource has multiple entries in the pin list, this * function only returns the first. */ -struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, +static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, int index, struct acpi_gpio_info *info) { @@ -546,6 +548,55 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, return ret ? ERR_PTR(ret) : lookup.desc; } +struct gpio_desc *acpi_find_gpio(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags, + 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 i; + + /* Try first from _DSD */ + for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { + if (con_id && strcmp(con_id, "gpios")) { + snprintf(propname, sizeof(propname), "%s-%s", + con_id, gpio_suffixes[i]); + } else { + snprintf(propname, sizeof(propname), "%s", + gpio_suffixes[i]); + } + + desc = acpi_get_gpiod_by_index(adev, propname, idx, &info); + if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER)) + break; + } + + /* Then from plain _CRS GPIOs */ + if (IS_ERR(desc)) { + if (!acpi_can_fallback_to_crs(adev, con_id)) + return ERR_PTR(-ENOENT); + + 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.polarity == GPIO_ACTIVE_LOW) + *lookupflags |= GPIO_ACTIVE_LOW; + + return desc; +} + /** * acpi_node_get_gpiod() - get a GPIO descriptor from ACPI resources * @fwnode: pointer to an ACPI firmware node to get the GPIO information from diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5404cdc..f0fc3a0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2883,55 +2883,6 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) mutex_unlock(&gpio_lookup_lock); } -static struct gpio_desc *acpi_find_gpio(struct device *dev, - const char *con_id, - unsigned int idx, - enum gpiod_flags flags, - 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 i; - - /* Try first from _DSD */ - for (i = 0; i < ARRAY_SIZE(gpio_suffixes); i++) { - if (con_id && strcmp(con_id, "gpios")) { - snprintf(propname, sizeof(propname), "%s-%s", - con_id, gpio_suffixes[i]); - } else { - snprintf(propname, sizeof(propname), "%s", - gpio_suffixes[i]); - } - - desc = acpi_get_gpiod_by_index(adev, propname, idx, &info); - if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER)) - break; - } - - /* Then from plain _CRS GPIOs */ - if (IS_ERR(desc)) { - if (!acpi_can_fallback_to_crs(adev, con_id)) - return ERR_PTR(-ENOENT); - - 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.polarity == GPIO_ACTIVE_LOW) - *lookupflags |= GPIO_ACTIVE_LOW; - - return desc; -} - static struct gpiod_lookup_table *gpiod_find_lookup_table(struct device *dev) { const char *dev_id = dev ? dev_name(dev) : NULL; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 9ed2934..d4c139c 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -109,9 +109,11 @@ 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); -struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, - const char *propname, int index, - struct acpi_gpio_info *info); +struct gpio_desc *acpi_find_gpio(struct device *dev, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags, + enum gpio_lookup_flags *lookupflags); struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, int index, struct acpi_gpio_info *info); @@ -130,10 +132,11 @@ static inline void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { } static inline struct gpio_desc * -acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, - int index, struct acpi_gpio_info *info) +acpi_find_gpio(struct device *dev, const char *con_id, + unsigned int idx, enum gpiod_flags flags, + enum gpio_lookup_flags *lookupflags) { - return ERR_PTR(-ENOSYS); + return ERR_PTR(-ENOENT); } static inline struct gpio_desc * acpi_node_get_gpiod(struct fwnode_handle *fwnode, const char *propname, diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 54455af..0facbea 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -29,7 +29,6 @@ #include #include "../core.h" -#include "../../gpio/gpiolib.h" #include "pinctrl-sunxi.h" static struct irq_chip sunxi_pinctrl_edge_irq_chip; -- cgit v0.10.2 From f4c1181f0fdeab19fb0b656abfb41bee7ca080b8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Oct 2016 10:59:32 +0200 Subject: gpio: OF: localize some gpiochip init functions of_gpiochip_add() and of_gpiochip_remove() are only used locally in the gpio subsystem so move these functions to the local header. Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d4c139c..82a91aa 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -92,6 +92,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags); +int of_gpiochip_add(struct gpio_chip *gc); +void of_gpiochip_remove(struct gpio_chip *gc); #else static inline struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, @@ -100,6 +102,8 @@ static inline struct gpio_desc *of_find_gpio(struct device *dev, { return ERR_PTR(-ENOENT); } +static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } +static inline void of_gpiochip_remove(struct gpio_chip *gc) { } #endif /* CONFIG_OF_GPIO */ #ifdef CONFIG_ACPI diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 092186c..3f87ea5 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -61,8 +61,6 @@ static inline int of_mm_gpiochip_add(struct device_node *np, } extern void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc); -extern int of_gpiochip_add(struct gpio_chip *gc); -extern void of_gpiochip_remove(struct gpio_chip *gc); extern int of_gpio_simple_xlate(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags); @@ -86,9 +84,6 @@ static inline int of_gpio_simple_xlate(struct gpio_chip *gc, return -ENOSYS; } -static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } -static inline void of_gpiochip_remove(struct gpio_chip *gc) { } - #endif /* CONFIG_OF_GPIO */ /** -- cgit v0.10.2 From e0852940662362a641c059610755169e3852b873 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 3 Oct 2016 11:37:24 +0200 Subject: gpio: add missing static inline of_get_named_gpiod_flags() was missing a static inline version when compiling without OF_GPIO. Add this. Reported-by: kbuild test robot Signed-off-by: Linus Walleij diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 82a91aa..346fbda 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -92,6 +92,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, unsigned int idx, enum gpio_lookup_flags *flags); +struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags); int of_gpiochip_add(struct gpio_chip *gc); void of_gpiochip_remove(struct gpio_chip *gc); #else @@ -102,6 +104,11 @@ static inline struct gpio_desc *of_find_gpio(struct device *dev, { return ERR_PTR(-ENOENT); } +static inline struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags) +{ + return ERR_PTR(-ENOENT); +} static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } static inline void of_gpiochip_remove(struct gpio_chip *gc) { } #endif /* CONFIG_OF_GPIO */ @@ -160,9 +167,6 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev, } #endif -struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np, - const char *list_name, int index, enum of_gpio_flags *flags); - struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); void gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, -- cgit v0.10.2