Commit 2148ad77 authored by Kent Gibson's avatar Kent Gibson Committed by Bartosz Golaszewski

gpiolib: add support for disabling line bias

Allow pull up/down bias to be disabled, allowing the line to float
or to be biased only by external circuitry.
Use case is for where the bias has been applied previously, either
by default or by the user, but that setting may conflict with the
current use of the line.
Signed-off-by: default avatarKent Gibson <warthog618@gmail.com>
Signed-off-by: default avatarBartosz Golaszewski <bgolaszewski@baylibre.com>
parent 7b479a84
...@@ -424,6 +424,7 @@ struct linehandle_state { ...@@ -424,6 +424,7 @@ struct linehandle_state {
GPIOHANDLE_REQUEST_ACTIVE_LOW | \ GPIOHANDLE_REQUEST_ACTIVE_LOW | \
GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ GPIOHANDLE_REQUEST_BIAS_PULL_UP | \
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \
GPIOHANDLE_REQUEST_BIAS_DISABLE | \
GPIOHANDLE_REQUEST_OPEN_DRAIN | \ GPIOHANDLE_REQUEST_OPEN_DRAIN | \
GPIOHANDLE_REQUEST_OPEN_SOURCE) GPIOHANDLE_REQUEST_OPEN_SOURCE)
...@@ -555,12 +556,21 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ...@@ -555,12 +556,21 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
(lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)))
return -EINVAL; return -EINVAL;
/* PULL_UP and PULL_DOWN flags only make sense for input mode. */ /* Bias flags only allowed for input mode. */
if (!(lflags & GPIOHANDLE_REQUEST_INPUT) && if (!(lflags & GPIOHANDLE_REQUEST_INPUT) &&
((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || ((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) ||
(lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ||
(lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)))
return -EINVAL; return -EINVAL;
/* Only one bias flag can be set. */
if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
(lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
(lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)))
return -EINVAL;
lh = kzalloc(sizeof(*lh), GFP_KERNEL); lh = kzalloc(sizeof(*lh), GFP_KERNEL);
if (!lh) if (!lh)
return -ENOMEM; return -ENOMEM;
...@@ -601,6 +611,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ...@@ -601,6 +611,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
set_bit(FLAG_OPEN_DRAIN, &desc->flags); set_bit(FLAG_OPEN_DRAIN, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
set_bit(FLAG_OPEN_SOURCE, &desc->flags); set_bit(FLAG_OPEN_SOURCE, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE)
set_bit(FLAG_BIAS_DISABLE, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)
set_bit(FLAG_PULL_DOWN, &desc->flags); set_bit(FLAG_PULL_DOWN, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)
...@@ -925,6 +937,14 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ...@@ -925,6 +937,14 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
(lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
return -EINVAL; return -EINVAL;
/* Only one bias flag can be set. */
if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) &&
(lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN |
GPIOHANDLE_REQUEST_BIAS_PULL_UP))) ||
((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) &&
(lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)))
return -EINVAL;
le = kzalloc(sizeof(*le), GFP_KERNEL); le = kzalloc(sizeof(*le), GFP_KERNEL);
if (!le) if (!le)
return -ENOMEM; return -ENOMEM;
...@@ -951,6 +971,8 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ...@@ -951,6 +971,8 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
set_bit(FLAG_ACTIVE_LOW, &desc->flags); set_bit(FLAG_ACTIVE_LOW, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE)
set_bit(FLAG_BIAS_DISABLE, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)
set_bit(FLAG_PULL_DOWN, &desc->flags); set_bit(FLAG_PULL_DOWN, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)
...@@ -1108,6 +1130,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -1108,6 +1130,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE | lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE |
GPIOLINE_FLAG_IS_OUT); GPIOLINE_FLAG_IS_OUT);
if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
lineinfo.flags |= GPIOLINE_FLAG_BIAS_DISABLE;
if (test_bit(FLAG_PULL_DOWN, &desc->flags)) if (test_bit(FLAG_PULL_DOWN, &desc->flags))
lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
if (test_bit(FLAG_PULL_UP, &desc->flags)) if (test_bit(FLAG_PULL_UP, &desc->flags))
...@@ -2807,6 +2831,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) ...@@ -2807,6 +2831,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
clear_bit(FLAG_OPEN_SOURCE, &desc->flags); clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
clear_bit(FLAG_PULL_UP, &desc->flags); clear_bit(FLAG_PULL_UP, &desc->flags);
clear_bit(FLAG_PULL_DOWN, &desc->flags); clear_bit(FLAG_PULL_DOWN, &desc->flags);
clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
clear_bit(FLAG_IS_HOGGED, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags);
ret = true; ret = true;
} }
...@@ -2933,6 +2958,7 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset, ...@@ -2933,6 +2958,7 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
unsigned arg; unsigned arg;
switch (mode) { switch (mode) {
case PIN_CONFIG_BIAS_DISABLE:
case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_DOWN:
case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_UP:
arg = 1; arg = 1;
...@@ -2946,6 +2972,26 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset, ...@@ -2946,6 +2972,26 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
} }
static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc)
{
int bias = 0;
int ret = 0;
if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
bias = PIN_CONFIG_BIAS_DISABLE;
else if (test_bit(FLAG_PULL_UP, &desc->flags))
bias = PIN_CONFIG_BIAS_PULL_UP;
else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
bias = PIN_CONFIG_BIAS_PULL_DOWN;
if (bias) {
ret = gpio_set_config(chip, gpio_chip_hwgpio(desc), bias);
if (ret != -ENOTSUPP)
return ret;
}
return 0;
}
/** /**
* gpiod_direction_input - set the GPIO direction to input * gpiod_direction_input - set the GPIO direction to input
* @desc: GPIO to set to input * @desc: GPIO to set to input
...@@ -2990,15 +3036,10 @@ int gpiod_direction_input(struct gpio_desc *desc) ...@@ -2990,15 +3036,10 @@ int gpiod_direction_input(struct gpio_desc *desc)
__func__); __func__);
return -EIO; return -EIO;
} }
if (ret == 0) if (ret == 0) {
clear_bit(FLAG_IS_OUT, &desc->flags); clear_bit(FLAG_IS_OUT, &desc->flags);
ret = gpio_set_bias(chip, desc);
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, ret); trace_gpio_direction(desc_to_gpio(desc), 1, ret);
......
...@@ -110,6 +110,7 @@ struct gpio_desc { ...@@ -110,6 +110,7 @@ struct gpio_desc {
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ #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_UP 13 /* GPIO has pull up enabled */
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ #define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
/* Connection label */ /* Connection label */
const char *label; const char *label;
......
...@@ -35,6 +35,7 @@ struct gpiochip_info { ...@@ -35,6 +35,7 @@ struct gpiochip_info {
#define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4) #define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4)
#define GPIOLINE_FLAG_BIAS_PULL_UP (1UL << 5) #define GPIOLINE_FLAG_BIAS_PULL_UP (1UL << 5)
#define GPIOLINE_FLAG_BIAS_PULL_DOWN (1UL << 6) #define GPIOLINE_FLAG_BIAS_PULL_DOWN (1UL << 6)
#define GPIOLINE_FLAG_BIAS_DISABLE (1UL << 7)
/** /**
* struct gpioline_info - Information about a certain GPIO line * struct gpioline_info - Information about a certain GPIO line
...@@ -66,6 +67,7 @@ struct gpioline_info { ...@@ -66,6 +67,7 @@ struct gpioline_info {
#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
#define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5) #define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5)
#define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6) #define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6)
#define GPIOHANDLE_REQUEST_BIAS_DISABLE (1UL << 7)
/** /**
* struct gpiohandle_request - Information about a GPIO handle request * struct gpiohandle_request - Information about a GPIO handle request
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment