summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpiolib.c
diff options
context:
space:
mode:
authorKent Gibson <warthog618@gmail.com>2019-11-05 03:04:25 +0100
committerBartosz Golaszewski <bgolaszewski@baylibre.com>2019-11-12 16:30:30 +0100
commit2148ad7790ea4f1f0081e6404fbb776bdbc793bb (patch)
tree43deadf60dfe7760cbb917f5460d416cd3c43ed5 /drivers/gpio/gpiolib.c
parentgpiolib: add support for pull up/down to lineevent_create (diff)
downloadlinux-2148ad7790ea4f1f0081e6404fbb776bdbc793bb.tar.xz
linux-2148ad7790ea4f1f0081e6404fbb776bdbc793bb.zip
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: Kent Gibson <warthog618@gmail.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Diffstat (limited to 'drivers/gpio/gpiolib.c')
-rw-r--r--drivers/gpio/gpiolib.c61
1 files changed, 51 insertions, 10 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index b7d7bb8bd20d..996911660306 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -424,6 +424,7 @@ struct linehandle_state {
GPIOHANDLE_REQUEST_ACTIVE_LOW | \
GPIOHANDLE_REQUEST_BIAS_PULL_UP | \
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \
+ GPIOHANDLE_REQUEST_BIAS_DISABLE | \
GPIOHANDLE_REQUEST_OPEN_DRAIN | \
GPIOHANDLE_REQUEST_OPEN_SOURCE)
@@ -555,12 +556,21 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
(lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)))
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) &&
- ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ||
+ ((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) ||
+ (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ||
(lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)))
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);
if (!lh)
return -ENOMEM;
@@ -601,6 +611,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
set_bit(FLAG_OPEN_DRAIN, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)
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)
set_bit(FLAG_PULL_DOWN, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)
@@ -925,6 +937,14 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
(lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
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);
if (!le)
return -ENOMEM;
@@ -951,6 +971,8 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW)
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)
set_bit(FLAG_PULL_DOWN, &desc->flags);
if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP)
@@ -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))
lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE |
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))
lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
if (test_bit(FLAG_PULL_UP, &desc->flags))
@@ -2807,6 +2831,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
clear_bit(FLAG_PULL_UP, &desc->flags);
clear_bit(FLAG_PULL_DOWN, &desc->flags);
+ clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
clear_bit(FLAG_IS_HOGGED, &desc->flags);
ret = true;
}
@@ -2933,6 +2958,7 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
unsigned arg;
switch (mode) {
+ case PIN_CONFIG_BIAS_DISABLE:
case PIN_CONFIG_BIAS_PULL_DOWN:
case PIN_CONFIG_BIAS_PULL_UP:
arg = 1;
@@ -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;
}
+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
* @desc: GPIO to set to input
@@ -2990,15 +3036,10 @@ int gpiod_direction_input(struct gpio_desc *desc)
__func__);
return -EIO;
}
- if (ret == 0)
+ if (ret == 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);
+ ret = gpio_set_bias(chip, desc);
+ }
trace_gpio_direction(desc_to_gpio(desc), 1, ret);