diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b7d7bb8bd20dd5f8b44d00f96ededb07aa54d9b0..9969116603066669fe3ea4daf9bf800dfb2629bb 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); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b8b10a409c7b9c6584e6e176d9246aec5c394258..ca9bc1e4803c2979c3ea3d68b1ce893b2d154ce5 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -110,6 +110,7 @@ struct gpio_desc { #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_DOWN 14 /* GPIO has pull down enabled */ +#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ /* Connection label */ const char *label; diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 39e6c7854d63d7df2a56299a6ae103cd7ad0e05b..7cc21c3b083977c8ed53b33c509c6ae2c6248368 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -35,6 +35,7 @@ struct gpiochip_info { #define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4) #define GPIOLINE_FLAG_BIAS_PULL_UP (1UL << 5) #define GPIOLINE_FLAG_BIAS_PULL_DOWN (1UL << 6) +#define GPIOLINE_FLAG_BIAS_DISABLE (1UL << 7) /** * struct gpioline_info - Information about a certain GPIO line @@ -66,6 +67,7 @@ struct gpioline_info { #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) #define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5) #define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6) +#define GPIOHANDLE_REQUEST_BIAS_DISABLE (1UL << 7) /** * struct gpiohandle_request - Information about a GPIO handle request