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 --- drivers/gpio/Kconfig | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98dd47a30fc7..6de9062119df 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 v1.2.3 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 --- drivers/mfd/stmpe.c | 21 ++++++++++++++------- drivers/mfd/stmpe.h | 2 ++ 2 files changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 94c7cc02fdab..c053b2b67bad 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 84adb46b3e2f..406f9f2d8935 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 -- cgit v1.2.3 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 --- drivers/mfd/stmpe.c | 23 +++++++++++++++-------- drivers/mfd/stmpe.h | 7 +++++-- 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index c053b2b67bad..85005253663d 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 406f9f2d8935..4ae343de71eb 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 v1.2.3 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 --- drivers/gpio/gpio-stmpe.c | 85 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index f675132de10e..fdac5d9e5b8d 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 v1.2.3 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 --- drivers/gpio/gpio-stmpe.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index fdac5d9e5b8d..5806af549ddf 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 v1.2.3 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 --- drivers/mfd/stmpe.c | 4 ++-- drivers/mfd/stmpe.h | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 85005253663d..a0704767a050 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 4ae343de71eb..4ba112362167 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 v1.2.3 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 --- drivers/mfd/stmpe.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- drivers/mfd/stmpe.h | 49 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 87 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index a0704767a050..1877d1ea2e7c 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 4ba112362167..f1273420c8e8 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) -- cgit v1.2.3 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 --- drivers/gpio/gpio-stmpe.c | 48 ++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 5806af549ddf..dd0aedf298fb 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 v1.2.3 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 --- drivers/mfd/stmpe-i2c.c | 2 ++ drivers/mfd/stmpe.c | 65 +++++++++++++++++++++++++++++++++++++++++++++---- drivers/mfd/stmpe.h | 21 ++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index c3f4aab53b07..863c39a3353c 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 1877d1ea2e7c..cfdae8a3d779 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -552,6 +552,59 @@ static struct stmpe_variant_info stmpe610 = { .get_altfunc = stmpe811_get_altfunc, }; +/* + * 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 f1273420c8e8..f7efdd8a5fc6 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -163,6 +163,27 @@ int stmpe_remove(struct stmpe *stmpe); #define STMPE811_SYS_CTRL2_GPIO_OFF (1 << 2) #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 */ -- cgit v1.2.3 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 --- drivers/gpio/gpio-stmpe.c | 48 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index dd0aedf298fb..5c0d8189a304 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 02f2a5621bb0..cbe2824461eb 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 v1.2.3 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 --- drivers/gpio/Kconfig | 6 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-axp209.c | 162 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 drivers/gpio/gpio-axp209.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6de9062119df..ed968d112399 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 2a035ed8f168..b7d509e27ed1 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 000000000000..3174799c27c6 --- /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 v1.2.3 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 --- drivers/gpio/Kconfig | 13 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-wcove.c | 455 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 469 insertions(+) create mode 100644 drivers/gpio/gpio-wcove.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index ed968d112399..591f692dae63 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 b7d509e27ed1..031195cdde7f 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 000000000000..f5c88df96eaa --- /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 v1.2.3 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 --- drivers/gpio/Kconfig | 13 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-gpio-mm.c | 267 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 drivers/gpio/gpio-gpio-mm.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 591f692dae63..825cf3c3d579 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 031195cdde7f..96650289888f 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 000000000000..1e7def9449ce --- /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 v1.2.3 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 --- drivers/gpio/gpio-mmio.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 6ec144baeb11..d7d03ad052d0 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 v1.2.3 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 --- drivers/gpio/gpiolib.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 53ff25ac66d8..deaf4d4dc68e 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 v1.2.3 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 --- drivers/gpio/Kconfig | 7 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-ts4900.c | 190 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 drivers/gpio/gpio-ts4900.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 825cf3c3d579..5eb7c864d4a5 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 96650289888f..691fb3acd9a1 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 000000000000..9dd9aca820e7 --- /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 v1.2.3 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 --- drivers/gpio/gpio-wcove.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index f5c88df96eaa..e11d6a3fb641 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 v1.2.3 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 --- drivers/gpio/gpio-msic.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c index d75649787e6c..1b7ce7f85886 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 v1.2.3 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 --- drivers/gpio/gpio-mxc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 1b342a3842c8..e35af5249478 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 v1.2.3 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 --- drivers/gpio/gpio-spear-spics.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c index 7ffd16495286..22267479ba68 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 v1.2.3 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 --- drivers/gpio/gpio-vf610.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 6284bdbe1e0c..3edb09cb9ee0 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 v1.2.3 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 --- drivers/mfd/Kconfig | 14 ++++++++ drivers/mfd/Makefile | 2 ++ drivers/mfd/lp873x.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 drivers/mfd/lp873x.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2d1fb6420592..69e51d0250bf 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 2ba3ba35f745..42acbcdeb4f6 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 000000000000..9af064c940ee --- /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"); -- cgit v1.2.3 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 --- drivers/gpio/Kconfig | 10 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-lp873x.c | 193 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 drivers/gpio/gpio-lp873x.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98dd47a30fc7..346e9a9b4397 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 2a035ed8f168..d60432f2a126 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 000000000000..f10d49da1554 --- /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 v1.2.3 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 --- drivers/regulator/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6c88e31c01f7..97dc4cc3ef43 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index cbe2824461eb..b9d31d737dbf 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 v1.2.3 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 --- drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-aspeed.c | 457 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 465 insertions(+) create mode 100644 drivers/gpio/gpio-aspeed.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5eb7c864d4a5..17842e6a56dc 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 691fb3acd9a1..75bb229213c1 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 000000000000..64348a6fb60f --- /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 v1.2.3 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 --- drivers/gpio/gpio-rcar.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index b96e0b466f74..2be48f5eba36 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -347,6 +347,10 @@ static const struct of_device_id gpio_rcar_of_table[] = { .compatible = "renesas,gpio-r8a7795", /* 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 v1.2.3 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 --- drivers/gpio/gpiolib.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index deaf4d4dc68e..e183ee2f5f65 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 v1.2.3 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 --- drivers/gpio/gpio-lpc32xx.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index fc5f197906ac..92b3ae2a6735 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; -- cgit v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 133 ++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index b9d31d737dbf..5d65c9e8af25 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 75 ++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 5d65c9e8af25..e9defeb9c212 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 49 ++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e9defeb9c212..e7bf3b3cebcd 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e7bf3b3cebcd..e36a9bf08fb1 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index e36a9bf08fb1..5d059866d17a 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 v1.2.3 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 --- drivers/gpio/gpiolib-sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 932e510aec50..4b44dd97c07f 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 v1.2.3 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 --- drivers/gpio/Kconfig | 3 +- drivers/gpio/gpio-iop.c | 115 +++++++++--------------------------------------- 2 files changed, 24 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 17842e6a56dc..68c2d647be9a 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 860c535922fd..98c7ff2a76e7 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 5d059866d17a..f170c5678289 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 v1.2.3 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 --- drivers/gpio/gpio-lp873x.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index f10d49da1554..2386b0f410fa 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 v1.2.3 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 --- drivers/gpio/gpio-arizona.c | 2 +- drivers/gpio/gpio-bcm-kona.c | 2 +- drivers/gpio/gpio-da9052.c | 2 +- drivers/gpio/gpio-da9055.c | 2 +- drivers/gpio/gpio-it87.c | 2 +- drivers/gpio/gpio-lp873x.c | 2 +- drivers/gpio/gpio-lpc18xx.c | 2 +- drivers/gpio/gpio-pisosr.c | 2 +- drivers/gpio/gpio-sch.c | 2 +- drivers/gpio/gpio-stmpe.c | 2 +- drivers/gpio/gpio-tc3589x.c | 2 +- drivers/gpio/gpio-tpic2810.c | 2 +- drivers/gpio/gpio-tps65086.c | 2 +- drivers/gpio/gpio-tps65218.c | 2 +- drivers/gpio/gpio-tps65912.c | 2 +- drivers/gpio/gpio-ts4900.c | 2 +- drivers/gpio/gpio-twl4030.c | 2 +- drivers/gpio/gpio-wm831x.c | 2 +- drivers/gpio/gpio-wm8350.c | 2 +- drivers/gpio/gpio-wm8994.c | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index 991370494922..482462889c8f 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 953e4b829e32..3d1cf018e8e7 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 e29553b7ccdb..dd8977cf3e85 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 2c2c18dc6c4f..82053b52cba0 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 63a962d18cd6..45d29e488dbb 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 2386b0f410fa..218c706359aa 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 98832c9f614a..f12e02e1016d 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 cb14b8d1d512..f5545049c187 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 eb43ae4835c1..545004445846 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 5c0d8189a304..b51c5be55c3a 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 8b3659352e49..5baa45768ec5 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 cace79c1b70a..c8b34d787eed 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 8e25f01ac314..b23c4d2429be 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 1c09a19ae10c..03e0dfbe9720 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 acfd30a13a56..abc0798ef843 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 9dd9aca820e7..5bd21725e604 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 4b807b0e0c8e..dfcfbba74416 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 21f97bcd0062..533707f943f4 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 e9765707d5c1..e46752e73dd9 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 2457aac8592e..68410fda6138 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 v1.2.3 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 --- drivers/gpio/gpiolib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e183ee2f5f65..043128d3e831 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 v1.2.3 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 --- drivers/gpio/gpio-aspeed.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 64348a6fb60f..9f7266e05f0a 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 v1.2.3 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 --- drivers/gpio/gpio-pca953x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index f170c5678289..5d059866d17a 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 v1.2.3 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 --- drivers/gpio/gpio-palmas.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-palmas.c b/drivers/gpio/gpio-palmas.c index 839474430229..3d818195e351 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 v1.2.3 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 --- drivers/gpio/gpio-sx150x.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c index a177ebd921d5..af95de89db01 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 v1.2.3 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 --- drivers/gpio/gpio-ts4800.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c index 0c144a72f9af..99256115bea5 100644 --- a/drivers/gpio/gpio-ts4800.c +++ b/drivers/gpio/gpio-ts4800.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include -- cgit v1.2.3 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 --- drivers/gpio/gpio-altera.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index 3f87a03abc22..5bddbd507ca9 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -17,6 +17,7 @@ */ #include +#include #include #include -- cgit v1.2.3 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 --- drivers/gpio/gpio-ath79.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index c4f4cddc7c1a..9457e2022bf6 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 v1.2.3 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 --- drivers/gpio/gpio-loongson1.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-loongson1.c b/drivers/gpio/gpio-loongson1.c index 10c09bdd8514..ad0a5958fcd0 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 v1.2.3 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 --- drivers/gpio/gpio-wcove.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index e11d6a3fb641..d0ddba7a9d08 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include -- cgit v1.2.3 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 --- drivers/gpio/gpio-aspeed.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 9f7266e05f0a..8aa340677f65 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 v1.2.3 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 --- drivers/gpio/gpio-tps65218.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-tps65218.c b/drivers/gpio/gpio-tps65218.c index 03e0dfbe9720..d779307a9685 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 v1.2.3 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 --- drivers/gpio/gpio-f7188x.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 600be8418707..e8accde62aa7 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 v1.2.3 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 --- drivers/gpio/gpio-tc3589x.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 5baa45768ec5..537a4335ddec 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 v1.2.3 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 --- drivers/gpio/gpio-tc3589x.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 537a4335ddec..5a5a6cb00eea 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 v1.2.3 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 --- drivers/gpio/gpio-loongson1.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-loongson1.c b/drivers/gpio/gpio-loongson1.c index ad0a5958fcd0..72b64039241a 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 v1.2.3 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 --- drivers/gpio/gpio-aspeed.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 8aa340677f65..03a5925a423c 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 v1.2.3 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 --- drivers/gpio/gpio-axp209.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-axp209.c b/drivers/gpio/gpio-axp209.c index 3174799c27c6..d9c2a517c6df 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 v1.2.3 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 --- drivers/gpio/gpio-zynq.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index e72794e463aa..6b4d10d6e10f 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 v1.2.3 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 --- drivers/gpio/Kconfig | 12 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-mockup.c | 214 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 drivers/gpio/gpio-mockup.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0e41c45a2936..79429b9b4e20 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 b3e039c3ae8d..915c7de1dd1b 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 000000000000..1ef85b0c2b1f --- /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 v1.2.3 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 --- drivers/gpio/Kconfig | 8 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-htc-egpio.c | 440 ++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/Kconfig | 8 - drivers/mfd/Makefile | 1 - drivers/mfd/htc-egpio.c | 440 ------------------------------------------ 6 files changed, 449 insertions(+), 449 deletions(-) create mode 100644 drivers/gpio/gpio-htc-egpio.c delete mode 100644 drivers/mfd/htc-egpio.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98dd47a30fc7..03be200619a4 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 2a035ed8f168..32ebc07074ed 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 000000000000..0b4df6051097 --- /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 2d1fb6420592..8634d60837fc 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 2ba3ba35f745..a5f1bf153b64 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 513cfc5c8fb6..000000000000 --- 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 "); -- cgit v1.2.3 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 --- drivers/gpio/gpio-stmpe.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index b51c5be55c3a..432b2ee173c7 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 v1.2.3 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 --- drivers/gpio/gpio-stmpe.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 432b2ee173c7..e7d422a6b90b 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 v1.2.3 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 --- drivers/gpio/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 59ba58b8ac13..2401443cbf28 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 v1.2.3 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 --- drivers/gpio/gpiolib-of.c | 39 +++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.c | 39 --------------------------------------- drivers/gpio/gpiolib.h | 16 ++++++++++++++++ 3 files changed, 55 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 75e7b3919ea7..33b05c8de42f 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 19a665f8d455..5404cdcfed19 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 2d9ea5e0cab3..9ed29342f41e 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 v1.2.3 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 --- drivers/gpio/gpiolib-acpi.c | 57 +++++++++++++++++++++++++++++++++-- drivers/gpio/gpiolib.c | 49 ------------------------------ drivers/gpio/gpiolib.h | 15 +++++---- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 1 - 4 files changed, 63 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index af514618d7fb..58ece201b8e6 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 5404cdcfed19..f0fc3a0d37c8 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 9ed29342f41e..d4c139ceb0a3 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 54455af566ec..0facbea5f465 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 v1.2.3 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 --- drivers/gpio/gpiolib.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d4c139ceb0a3..82a91aa66db4 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 -- cgit v1.2.3 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 --- drivers/gpio/gpiolib.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 82a91aa66db4..346fbda39220 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 v1.2.3