Skip to content

Commit

Permalink
gpio: gpiolib: Support for open drain/collector gpios
Browse files Browse the repository at this point in the history
Adding support for the open drain gpio on which client
can specify the open drain property through GPIO flag
GPIOF_OPEN_DRAIN at the time of gpio request.
The open drain pins are normally pulled high and it
cannot be driven to output with value of 1 and so
when client request for setting the pin to HIGH, the
gpio will be set to input direction to make pin in tristate
and hence PULL-UP on pins will make the state to HIGH.
The open drain pin can be driven to LOW by setting output
with value of 0.

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviwed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
  • Loading branch information
Laxman Dewangan authored and Grant Likely committed Mar 5, 2012
1 parent 3d2ddfd commit aca5ce1
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
44 changes: 42 additions & 2 deletions drivers/gpio/gpiolib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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);

Expand All @@ -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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand All @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions include/linux/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit aca5ce1

Please sign in to comment.