diff options
-rw-r--r-- | drivers/gpio/gpiolib.c | 44 | ||||
-rw-r--r-- | include/linux/gpio.h | 3 |
2 files changed, 45 insertions, 2 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e633a2afbe90..5d25a33d5359 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -58,6 +58,7 @@ struct gpio_desc { #define FLAG_TRIG_FALL 5 /* trigger on falling edge */ #define FLAG_TRIG_RISE 6 /* trigger on rising edge */ #define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */ +#define FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */ #define ID_SHIFT 16 /* add new flags before this one */ @@ -1264,6 +1265,7 @@ void gpio_free(unsigned gpio) module_put(desc->chip->owner); clear_bit(FLAG_ACTIVE_LOW, &desc->flags); clear_bit(FLAG_REQUESTED, &desc->flags); + clear_bit(FLAG_OPEN_DRAIN, &desc->flags); } else WARN_ON(extra_checks); @@ -1285,6 +1287,9 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) if (err) return err; + if (flags & GPIOF_OPEN_DRAIN) + set_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags); + if (flags & GPIOF_DIR_IN) err = gpio_direction_input(gpio); else @@ -1434,6 +1439,10 @@ int gpio_direction_output(unsigned gpio, int value) struct gpio_desc *desc = &gpio_desc[gpio]; int status = -EINVAL; + /* Open drain pin should not be driven to 1 */ + if (value && test_bit(FLAG_OPEN_DRAIN, &desc->flags)) + return gpio_direction_input(gpio); + spin_lock_irqsave(&gpio_lock, flags); if (!gpio_is_valid(gpio)) @@ -1570,6 +1579,31 @@ int __gpio_get_value(unsigned gpio) } EXPORT_SYMBOL_GPL(__gpio_get_value); +/* + * _gpio_set_open_drain_value() - Set the open drain gpio's value. + * @gpio: Gpio whose state need to be set. + * @chip: Gpio chip. + * @value: Non-zero for setting it HIGH otherise it will set to LOW. + */ +static void _gpio_set_open_drain_value(unsigned gpio, + struct gpio_chip *chip, int value) +{ + int err = 0; + if (value) { + err = chip->direction_input(chip, gpio - chip->base); + if (!err) + clear_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + } else { + err = chip->direction_output(chip, gpio - chip->base, 0); + if (!err) + set_bit(FLAG_IS_OUT, &gpio_desc[gpio].flags); + } + trace_gpio_direction(gpio, value, err); + if (err < 0) + pr_err("%s: Error in set_value for open drain gpio%d err %d\n", + __func__, gpio, err); +} + /** * __gpio_set_value() - assign a gpio's value * @gpio: gpio whose value will be assigned @@ -1586,7 +1620,10 @@ void __gpio_set_value(unsigned gpio, int value) chip = gpio_to_chip(gpio); WARN_ON(chip->can_sleep); trace_gpio_value(gpio, 0, value); - chip->set(chip, gpio - chip->base, value); + if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags)) + _gpio_set_open_drain_value(gpio, chip, value); + else + chip->set(chip, gpio - chip->base, value); } EXPORT_SYMBOL_GPL(__gpio_set_value); @@ -1653,7 +1690,10 @@ void gpio_set_value_cansleep(unsigned gpio, int value) might_sleep_if(extra_checks); chip = gpio_to_chip(gpio); trace_gpio_value(gpio, 0, value); - chip->set(chip, gpio - chip->base, value); + if (test_bit(FLAG_OPEN_DRAIN, &gpio_desc[gpio].flags)) + _gpio_set_open_drain_value(gpio, chip, value); + else + chip->set(chip, gpio - chip->base, value); } EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 38ac48b7d3a8..66a5b2e9d35a 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -14,6 +14,9 @@ #define GPIOF_OUT_INIT_LOW (GPIOF_DIR_OUT | GPIOF_INIT_LOW) #define GPIOF_OUT_INIT_HIGH (GPIOF_DIR_OUT | GPIOF_INIT_HIGH) +/* Gpio pin is open drain */ +#define GPIOF_OPEN_DRAIN (1 << 2) + /** * struct gpio - a structure describing a GPIO with configuration * @gpio: the GPIO number |