diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2019-02-14 08:45:22 +0100 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2019-02-14 08:45:22 +0100 |
commit | 92de6bc3a82df2e745c46155e8334659f871ea6f (patch) | |
tree | 7f447f88a325a52517317124cc0f8d4d391eebb7 /drivers/gpio | |
parent | 5340f23df8fe27a270af3fa1a93cd07293d23dd9 (diff) | |
parent | 15add06841a3b0b4734a72847a73c71fd09ebe52 (diff) |
Merge branch 'ib-pca953x-config' into devel
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/gpio-pca953x.c | 66 | ||||
-rw-r--r-- | drivers/gpio/gpiolib-of.c | 5 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.c | 50 | ||||
-rw-r--r-- | drivers/gpio/gpiolib.h | 2 |
4 files changed, 105 insertions, 18 deletions
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 83d45f00e4d0..1e5579acd275 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -179,6 +179,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) #define PCA957x_BANK_OUTPUT BIT(5) #define PCAL9xxx_BANK_IN_LATCH BIT(8 + 2) +#define PCAL9xxx_BANK_PULL_EN BIT(8 + 3) +#define PCAL9xxx_BANK_PULL_SEL BIT(8 + 4) #define PCAL9xxx_BANK_IRQ_MASK BIT(8 + 5) #define PCAL9xxx_BANK_IRQ_STAT BIT(8 + 6) @@ -200,6 +202,8 @@ static int pca953x_bank_shift(struct pca953x_chip *chip) * - Extended set, above 0x40, often chip specific. * - PCAL6524/PCAL9555A with custom PCAL IRQ handling: * Input latch register 0x40 + 2 * bank_size RW + * Pull-up/pull-down enable reg 0x40 + 3 * bank_size RW + * Pull-up/pull-down select reg 0x40 + 4 * bank_size RW * Interrupt mask register 0x40 + 5 * bank_size RW * Interrupt status register 0x40 + 6 * bank_size R * @@ -248,7 +252,8 @@ static bool pca953x_readable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) { - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK | + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK | PCAL9xxx_BANK_IRQ_STAT; } @@ -269,7 +274,8 @@ static bool pca953x_writeable_register(struct device *dev, unsigned int reg) } if (chip->driver_data & PCA_PCAL) - bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_IRQ_MASK; + bank |= PCAL9xxx_BANK_IN_LATCH | PCAL9xxx_BANK_PULL_EN | + PCAL9xxx_BANK_PULL_SEL | PCAL9xxx_BANK_IRQ_MASK; return pca953x_check_register(chip, reg, bank); } @@ -474,6 +480,61 @@ exit: mutex_unlock(&chip->i2c_lock); } +static int pca953x_gpio_set_pull_up_down(struct pca953x_chip *chip, + unsigned int offset, + unsigned long config) +{ + u8 pull_en_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_EN, offset, + true, false); + u8 pull_sel_reg = pca953x_recalc_addr(chip, PCAL953X_PULL_SEL, offset, + true, false); + u8 bit = BIT(offset % BANK_SZ); + int ret; + + /* + * pull-up/pull-down configuration requires PCAL extended + * registers + */ + if (!(chip->driver_data & PCA_PCAL)) + return -ENOTSUPP; + + mutex_lock(&chip->i2c_lock); + + /* Disable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, 0); + if (ret) + goto exit; + + /* Configure pull-up/pull-down */ + if (config == PIN_CONFIG_BIAS_PULL_UP) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, bit); + else if (config == PIN_CONFIG_BIAS_PULL_DOWN) + ret = regmap_write_bits(chip->regmap, pull_sel_reg, bit, 0); + if (ret) + goto exit; + + /* Enable pull-up/pull-down */ + ret = regmap_write_bits(chip->regmap, pull_en_reg, bit, bit); + +exit: + mutex_unlock(&chip->i2c_lock); + return ret; +} + +static int pca953x_gpio_set_config(struct gpio_chip *gc, unsigned int offset, + unsigned long config) +{ + struct pca953x_chip *chip = gpiochip_get_data(gc); + + switch (config) { + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + return pca953x_gpio_set_pull_up_down(chip, offset, config); + default: + return -ENOTSUPP; + } +} + static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) { struct gpio_chip *gc; @@ -486,6 +547,7 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->set = pca953x_gpio_set_value; gc->get_direction = pca953x_gpio_get_direction; gc->set_multiple = pca953x_gpio_set_multiple; + gc->set_config = pca953x_gpio_set_config; gc->can_sleep = true; gc->base = chip->gpio_start; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index a6e1891217e2..9a8b78477f79 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -345,6 +345,11 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, if (of_flags & OF_GPIO_TRANSITORY) *flags |= GPIO_TRANSITORY; + if (of_flags & OF_GPIO_PULL_UP) + *flags |= GPIO_PULL_UP; + if (of_flags & OF_GPIO_PULL_DOWN) + *flags |= GPIO_PULL_DOWN; + return desc; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 361a09c8138a..9be4c9401172 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2555,6 +2555,14 @@ EXPORT_SYMBOL_GPL(gpiochip_free_own_desc); * rely on gpio_request() having been called beforehand. */ +static int gpio_set_config(struct gpio_chip *gc, unsigned offset, + enum pin_config_param mode) +{ + unsigned long config = { PIN_CONF_PACKED(mode, 0) }; + + return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2602,20 +2610,19 @@ int gpiod_direction_input(struct gpio_desc *desc) if (status == 0) clear_bit(FLAG_IS_OUT, &desc->flags); + if (test_bit(FLAG_PULL_UP, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_UP); + else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + gpio_set_config(chip, gpio_chip_hwgpio(desc), + PIN_CONFIG_BIAS_PULL_DOWN); + trace_gpio_direction(desc_to_gpio(desc), 1, status); return status; } EXPORT_SYMBOL_GPL(gpiod_direction_input); -static int gpio_set_drive_single_ended(struct gpio_chip *gc, unsigned offset, - enum pin_config_param mode) -{ - unsigned long config = { PIN_CONF_PACKED(mode, 0) }; - - return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; -} - static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { struct gpio_chip *gc = desc->gdev->chip; @@ -2712,8 +2719,8 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) gc = desc->gdev->chip; if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { /* First see if we can enable open drain in hardware */ - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_DRAIN); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_DRAIN); if (!ret) goto set_output_value; /* Emulate open drain by not actively driving the line high */ @@ -2721,16 +2728,16 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) return gpiod_direction_input(desc); } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { - ret = gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_OPEN_SOURCE); + ret = gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; /* Emulate open source by not actively driving the line low */ if (!value) return gpiod_direction_input(desc); } else { - gpio_set_drive_single_ended(gc, gpio_chip_hwgpio(desc), - PIN_CONFIG_DRIVE_PUSH_PULL); + gpio_set_config(gc, gpio_chip_hwgpio(desc), + PIN_CONFIG_DRIVE_PUSH_PULL); } set_output_value: @@ -2762,7 +2769,7 @@ int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) } config = pinconf_to_config_packed(PIN_CONFIG_INPUT_DEBOUNCE, debounce); - return chip->set_config(chip, gpio_chip_hwgpio(desc), config); + return gpio_set_config(chip, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_debounce); @@ -2799,7 +2806,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, !transitory); gpio = gpio_chip_hwgpio(desc); - rc = chip->set_config(chip, gpio, packed); + rc = gpio_set_config(chip, gpio, packed); if (rc == -ENOTSUPP) { dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", gpio); @@ -4087,6 +4094,17 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) { + gpiod_err(desc, + "both pull-up and pull-down enabled, invalid configuration\n"); + return -EINVAL; + } + + if (lflags & GPIO_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); + else if (lflags & GPIO_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + status = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); if (status < 0) return status; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index bc57f0dc5953..078ab17b96bf 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -219,6 +219,8 @@ struct gpio_desc { #define FLAG_IRQ_IS_ENABLED 10 /* GPIO is connected to an enabled IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ +#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ +#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ /* Connection label */ const char *label; |