diff options
author | Andrey Smirnov <andrew.smirnov@gmail.com> | 2017-06-03 20:11:53 -0700 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2017-06-06 08:27:16 +0200 |
commit | 37e6bee7e55ca1fbea5aefa0e64184fa10936276 (patch) | |
tree | 16fe4a827a3703e805318d21643a9f199d75b5a1 /drivers | |
parent | bbc499914739cd51f51cd23ade0a2dca19359804 (diff) | |
download | barebox-37e6bee7e55ca1fbea5aefa0e64184fa10936276.tar.gz barebox-37e6bee7e55ca1fbea5aefa0e64184fa10936276.tar.xz |
gpiolib: Add support for GPIO "hog" nodes
Add code to support 'gpio-hog' nodes used in some .dts files in Linux
kernel.
Cc: cphealy@gmail.com
Cc: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/gpiolib.c | 96 |
1 files changed, 95 insertions, 1 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 28f6a5ebfd..a3e17ada0d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -5,6 +5,7 @@ #include <command.h> #include <complete.h> #include <gpio.h> +#include <of_gpio.h> #include <errno.h> #include <malloc.h> @@ -304,6 +305,99 @@ static int gpiochip_find_base(int start, int ngpio) return base; } +static int of_hog_gpio(struct device_node *np, struct gpio_chip *chip, + unsigned int idx) +{ + struct device_node *chip_np = chip->dev->device_node; + unsigned long flags = 0; + u32 gpio_cells, gpio_num, gpio_flags; + int ret, gpio; + const char *name = NULL; + + ret = of_property_read_u32(chip_np, "#gpio-cells", &gpio_cells); + if (ret) + return ret; + + /* + * Support for GPIOs that don't have #gpio-cells set to 2 is + * not implemented + */ + if (WARN_ON(gpio_cells != 2)) + return -ENOTSUPP; + + ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells, + &gpio_num); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, "gpios", idx * gpio_cells + 1, + &gpio_flags); + if (ret) + return ret; + + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + gpio = gpio_get_num(chip->dev, gpio_num); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret < 0) { + dev_err(chip->dev, "unable to get gpio %u\n", gpio_num); + return ret; + } + + + /* + * Note that, in order to be compatible with Linux, the code + * below interprets 'output-high' as to mean 'output-active'. + * That is, when processed for active-low GPIO, it will result + * in output being asserted logically 'active', but physically + * 'low'. + * + * Conversely it means that specifying 'output-low' for + * 'active-low' GPIO would result in 'high' level observed on + * the corresponding pin + * + */ + if (of_property_read_bool(np, "input")) + flags |= GPIOF_DIR_IN; + else if (of_property_read_bool(np, "output-low")) + flags |= GPIOF_OUT_INIT_INACTIVE; + else if (of_property_read_bool(np, "output-high")) + flags |= GPIOF_OUT_INIT_ACTIVE; + else + return -EINVAL; + + of_property_read_string(np, "line-name", &name); + + return gpio_request_one(gpio, flags, name); +} + +static int of_gpiochip_scan_hogs(struct gpio_chip *chip) +{ + struct device_node *np; + int ret, i; + + for_each_available_child_of_node(chip->dev->device_node, np) { + if (!of_property_read_bool(np, "gpio-hog")) + continue; + + for (ret = 0, i = 0; + !ret; + ret = of_hog_gpio(np, chip, i), i++) + ; + /* + * We ignore -EOVERFLOW because it serves us as an + * indicator that there's no more GPIOs to handle. + */ + if (ret < 0 && ret != -EOVERFLOW) + return ret; + } + + return 0; +} + int gpiochip_add(struct gpio_chip *chip) { int base, i; @@ -322,7 +416,7 @@ int gpiochip_add(struct gpio_chip *chip) for (i = chip->base; i < chip->base + chip->ngpio; i++) gpio_desc[i].chip = chip; - return 0; + return of_gpiochip_scan_hogs(chip); } void gpiochip_remove(struct gpio_chip *chip) |