diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c index 9ae4a41a2427..b3b4e77ec147 100644 --- a/drivers/gpio/gpio-regmap.c +++ b/drivers/gpio/gpio-regmap.c @@ -31,6 +31,7 @@ struct gpio_regmap { unsigned int reg_clr_base; unsigned int reg_dir_in_base; unsigned int reg_dir_out_base; + unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output; #ifdef CONFIG_REGMAP_IRQ @@ -138,6 +139,20 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip, return regmap_write(gpio->regmap, reg, mask); } +static bool gpio_regmap_fixed_direction(struct gpio_regmap *gpio, + unsigned int offset) +{ + if (!gpio->fixed_direction_output) + return false; + + /* In this case only some GPIOs are fixed as input/output */ + if (gpio->fixed_direction_mask && + !test_bit(offset, gpio->fixed_direction_mask)) + return false; + + return true; +} + static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -145,7 +160,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip, unsigned int base, val, reg, mask; int invert, ret; - if (gpio->fixed_direction_output) { + if (gpio_regmap_fixed_direction(gpio, offset)) { if (test_bit(offset, gpio->fixed_direction_output)) return GPIO_LINE_DIRECTION_OUT; else @@ -302,12 +317,23 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config goto err_free_gpio; } + if (config->fixed_direction_mask) { + gpio->fixed_direction_mask = bitmap_alloc(chip->ngpio, + GFP_KERNEL); + if (!gpio->fixed_direction_mask) { + ret = -ENOMEM; + goto err_free_gpio; + } + bitmap_copy(gpio->fixed_direction_mask, + config->fixed_direction_mask, chip->ngpio); + } + if (config->fixed_direction_output) { gpio->fixed_direction_output = bitmap_alloc(chip->ngpio, GFP_KERNEL); if (!gpio->fixed_direction_output) { ret = -ENOMEM; - goto err_free_gpio; + goto err_free_bitmap_dirmask; } bitmap_copy(gpio->fixed_direction_output, config->fixed_direction_output, chip->ngpio); @@ -329,7 +355,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config ret = gpiochip_add_data(chip, gpio); if (ret < 0) - goto err_free_bitmap; + goto err_free_bitmap_output; #ifdef CONFIG_REGMAP_IRQ if (config->regmap_irq_chip) { @@ -355,8 +381,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config err_remove_gpiochip: gpiochip_remove(chip); -err_free_bitmap: +err_free_bitmap_output: bitmap_free(gpio->fixed_direction_output); +err_free_bitmap_dirmask: + bitmap_free(gpio->fixed_direction_mask); err_free_gpio: kfree(gpio); return ERR_PTR(ret); @@ -376,6 +404,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio) gpiochip_remove(&gpio->gpio_chip); bitmap_free(gpio->fixed_direction_output); + bitmap_free(gpio->fixed_direction_mask); kfree(gpio); } EXPORT_SYMBOL_GPL(gpio_regmap_unregister); diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h index 12d154732ca9..06255756710d 100644 --- a/include/linux/gpio/regmap.h +++ b/include/linux/gpio/regmap.h @@ -38,6 +38,12 @@ struct regmap; * offset to a register/bitmask pair. If not * given the default gpio_regmap_simple_xlate() * is used. + * @fixed_direction_mask: + * (Optional) Bitmap representing the GPIO lines that + * make use of the @fixed_direction_output list to + * enforce direction of the GPIO. If this is NULL + * and @fixed_direction_output is defined, ALL GPIOs + * are assumed to be fixed direction (out or in). * @fixed_direction_output: * (Optional) Bitmap representing the fixed direction of * the GPIO lines. Useful when there are GPIO lines with a @@ -89,6 +95,7 @@ struct gpio_regmap_config { int reg_stride; int ngpio_per_reg; struct irq_domain *irq_domain; + unsigned long *fixed_direction_mask; unsigned long *fixed_direction_output; #ifdef CONFIG_REGMAP_IRQ