summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorKent Gibson <warthog618@gmail.com>2019-11-05 10:04:29 +0800
committerBartosz Golaszewski <bgolaszewski@baylibre.com>2019-11-12 16:30:31 +0100
commite588bb1eae31be73fbec2b731be986a7c09635a4 (patch)
tree6dad3bb5d9f7e13813b63bd8459eb38df01219b1 /drivers/gpio
parentb043ed7ef0b358d7f32dc57c218f925fd2802aba (diff)
gpio: add new SET_CONFIG ioctl() to gpio chardev
Add the GPIOHANDLE_SET_CONFIG_IOCTL to the gpio chardev. The ioctl allows some of the configuration of a requested handle to be changed without having to release the line. The primary use case is the changing of direction for bi-directional lines. Based on initial work by Bartosz Golaszewski <bgolaszewski@baylibre.com> Signed-off-by: Kent Gibson <warthog618@gmail.com> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpiolib.c69
1 files changed, 69 insertions, 0 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index a9826627f347..dba5f08f308c 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -476,6 +476,73 @@ static int linehandle_validate_flags(u32 flags)
return 0;
}
+static void linehandle_configure_flag(unsigned long *flagsp,
+ u32 bit, bool active)
+{
+ if (active)
+ set_bit(bit, flagsp);
+ else
+ clear_bit(bit, flagsp);
+}
+
+static long linehandle_set_config(struct linehandle_state *lh,
+ void __user *ip)
+{
+ struct gpiohandle_config gcnf;
+ struct gpio_desc *desc;
+ int i, ret;
+ u32 lflags;
+ unsigned long *flagsp;
+
+ if (copy_from_user(&gcnf, ip, sizeof(gcnf)))
+ return -EFAULT;
+
+ lflags = gcnf.flags;
+ ret = linehandle_validate_flags(lflags);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < lh->numdescs; i++) {
+ desc = lh->descs[i];
+ flagsp = &desc->flags;
+
+ linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW,
+ lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW);
+
+ linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN,
+ lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN);
+
+ linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE,
+ lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE);
+
+ linehandle_configure_flag(flagsp, FLAG_PULL_UP,
+ lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP);
+
+ linehandle_configure_flag(flagsp, FLAG_PULL_DOWN,
+ lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN);
+
+ linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE,
+ lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE);
+
+ /*
+ * Lines have to be requested explicitly for input
+ * or output, else the line will be treated "as is".
+ */
+ if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
+ int val = !!gcnf.default_values[i];
+
+ ret = gpiod_direction_output(desc, val);
+ if (ret)
+ return ret;
+ } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
+ ret = gpiod_direction_input(desc);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
static long linehandle_ioctl(struct file *filep, unsigned int cmd,
unsigned long arg)
{
@@ -526,6 +593,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
lh->descs,
NULL,
vals);
+ } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) {
+ return linehandle_set_config(lh, ip);
}
return -EINVAL;
}