gpio: add gpio-hog support
authorHeiko Schocher <hs@denx.de>
Wed, 12 Jun 2019 04:11:46 +0000 (06:11 +0200)
committerTom Rini <trini@konsulko.com>
Sat, 13 Jul 2019 15:11:30 +0000 (11:11 -0400)
add gpio-hog support. GPIO hogging is a mechanism
providing automatic GPIO request and configuration
as part of the gpio-controller's driver probe function.

for more infos see:
doc/device-tree-bindings/gpio/gpio.txt

Signed-off-by: Heiko Schocher <hs@denx.de>
Tested-by: Michal Simek <michal.simek@xilinx.com> (zcu102)
Tested-by: Patrick Delaunay <patrick.delaunay@st.com>
common/board_r.c
doc/device-tree-bindings/gpio/gpio.txt
drivers/gpio/Kconfig
drivers/gpio/gpio-uclass.c
include/asm-generic/gpio.h

index df24021f2ccd89c2d6ef375cde195075a6ff9881..abc31b17b813f33d029dda37e41b713144fd4886 100644 (file)
@@ -49,6 +49,9 @@
 #include <linux/err.h>
 #include <efi_loader.h>
 #include <wdt.h>
+#if defined(CONFIG_DM_GPIO_HOG)
+#include <asm/gpio.h>
+#endif
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -796,6 +799,9 @@ static init_fnc_t init_sequence_r[] = {
 #ifdef CONFIG_CMD_NET
        initr_ethaddr,
 #endif
+#if defined(CONFIG_DM_GPIO_HOG)
+       gpio_hog_probe_all,
+#endif
 #ifdef CONFIG_BOARD_LATE_INIT
        board_late_init,
 #endif
index f7a158d858620f73133aded14765f042742a3fb4..e7744393697217ff2cccaae8faa74fb7ef87da11 100644 (file)
@@ -210,3 +210,58 @@ Example 2:
 Here, three GPIO ranges are defined wrt. two pin controllers. pinctrl1 GPIO
 ranges are defined using pin numbers whereas the GPIO ranges wrt. pinctrl2
 are named "foo" and "bar".
+
+3) GPIO hog definitions
+-----------------------
+
+The GPIO chip may contain GPIO hog definitions. GPIO hogging is a mechanism
+providing automatic GPIO request and configuration as part of the
+gpio-controller's driver probe function.
+
+Each GPIO hog definition is represented as a child node of the GPIO controller.
+Required properties:
+- gpio-hog:   A property specifying that this child node represents a GPIO hog.
+- gpios:      Store the GPIO information (id, flags) for the GPIO to
+             affect.
+
+              ! Not yet support more than one gpio !
+
+Only one of the following properties scanned in the order shown below.
+- input:      A property specifying to set the GPIO direction as input.
+- output-low  A property specifying to set the GPIO direction as output with
+             the value low.
+- output-high A property specifying to set the GPIO direction as output with
+             the value high.
+
+Optional properties:
+- line-name:  The GPIO label name. If not present the node name is used.
+
+Example:
+
+        tca6416@20 {
+                compatible = "ti,tca6416";
+                reg = <0x20>;
+                #gpio-cells = <2>;
+                gpio-controller;
+
+                env_reset {
+                        gpio-hog;
+                        input;
+                        gpios = <6 GPIO_ACTIVE_LOW>;
+                };
+                boot_rescue {
+                        gpio-hog;
+                        input;
+                        gpios = <7 GPIO_ACTIVE_LOW>;
+                };
+        };
+
+For the above Example you can than access the gpio in your boardcode
+with:
+
+        desc = gpio_hog_lookup_name("boot_rescue.gpio-hog");
+        if (desc) {
+                if (dm_gpio_get_value(desc))
+                        printf("\nBooting into Rescue System\n");
+               else
+                       printf("\nBoot normal\n");
index e36a8abc426e8da6a669bf6ed73306123ab3f717..fa1c99700f7383c9e1e4ed84f2e30c39d38bd2a8 100644 (file)
@@ -14,6 +14,16 @@ config DM_GPIO
          particular GPIOs that they provide. The uclass interface
          is defined in include/asm-generic/gpio.h.
 
+config DM_GPIO_HOG
+       bool "Enable GPIO hog support"
+       depends on DM_GPIO
+       default n
+       help
+         Enable gpio hog support
+         The GPIO chip may contain GPIO hog definitions. GPIO hogging
+         is a mechanism providing automatic GPIO request and config-
+         uration as part of the gpio-controller's driver probe function.
+
 config ALTERA_PIO
        bool "Altera PIO driver"
        depends on DM_GPIO
index da5e9ba6e524f584ff472e34167e5a7861f1c8c6..308d0863ada30a1ca1dc83004e0bcd003b6eac75 100644 (file)
@@ -5,6 +5,9 @@
 
 #include <common.h>
 #include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
 #include <dt-bindings/gpio/gpio.h>
 #include <errno.h>
 #include <fdtdec.h>
@@ -141,6 +144,118 @@ static int gpio_find_and_xlate(struct gpio_desc *desc,
                return gpio_xlate_offs_flags(desc->dev, desc, args);
 }
 
+#if defined(CONFIG_DM_GPIO_HOG)
+
+struct gpio_hog_priv {
+       struct gpio_desc gpiod;
+};
+
+struct gpio_hog_data {
+       int gpiod_flags;
+       int value;
+       u32 val[2];
+};
+
+static int gpio_hog_ofdata_to_platdata(struct udevice *dev)
+{
+       struct gpio_hog_data *plat = dev_get_platdata(dev);
+       const char *nodename;
+       int ret;
+
+       plat->value = 0;
+       if (dev_read_bool(dev, "input")) {
+               plat->gpiod_flags = GPIOD_IS_IN;
+       } else if (dev_read_bool(dev, "output-high")) {
+               plat->value = 1;
+               plat->gpiod_flags = GPIOD_IS_OUT;
+       } else if (dev_read_bool(dev, "output-low")) {
+               plat->gpiod_flags = GPIOD_IS_OUT;
+       } else {
+               printf("%s: missing gpio-hog state.\n", __func__);
+               return -EINVAL;
+       }
+       ret = dev_read_u32_array(dev, "gpios", plat->val, 2);
+       if (ret) {
+               printf("%s: wrong gpios property, 2 values needed %d\n",
+                      __func__, ret);
+               return ret;
+       }
+       nodename = dev_read_string(dev, "line-name");
+       if (!nodename)
+               nodename = dev_read_name(dev);
+       device_set_name(dev, nodename);
+
+       return 0;
+}
+
+static int gpio_hog_probe(struct udevice *dev)
+{
+       struct gpio_hog_data *plat = dev_get_platdata(dev);
+       struct gpio_hog_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = gpio_dev_request_index(dev->parent, dev->name, "gpio-hog",
+                                    plat->val[0], plat->gpiod_flags,
+                                    plat->val[1], &priv->gpiod);
+       if (ret < 0) {
+               debug("%s: node %s could not get gpio.\n", __func__,
+                     dev->name);
+               return ret;
+       }
+       dm_gpio_set_dir(&priv->gpiod);
+       if (plat->gpiod_flags == GPIOD_IS_OUT)
+               dm_gpio_set_value(&priv->gpiod, plat->value);
+
+       return 0;
+}
+
+int gpio_hog_probe_all(void)
+{
+       struct udevice *dev;
+       int ret;
+
+       for (uclass_first_device(UCLASS_NOP, &dev);
+            dev;
+            uclass_find_next_device(&dev)) {
+               if (dev->driver == DM_GET_DRIVER(gpio_hog)) {
+                       ret = device_probe(dev);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+struct gpio_desc *gpio_hog_lookup_name(const char *name)
+{
+       struct udevice *dev;
+
+       gpio_hog_probe_all();
+       if (!uclass_get_device_by_name(UCLASS_NOP, name, &dev)) {
+               struct gpio_hog_priv *priv = dev_get_priv(dev);
+
+               return &priv->gpiod;
+       }
+
+       return NULL;
+}
+
+U_BOOT_DRIVER(gpio_hog) = {
+       .name   = "gpio_hog",
+       .id     = UCLASS_NOP,
+       .ofdata_to_platdata = gpio_hog_ofdata_to_platdata,
+       .probe = gpio_hog_probe,
+       .priv_auto_alloc_size = sizeof(struct gpio_hog_priv),
+       .platdata_auto_alloc_size = sizeof(struct gpio_hog_data),
+};
+#else
+struct gpio_desc *gpio_hog_lookup_name(const char *name)
+{
+       return NULL;
+}
+#endif
+
 int dm_gpio_request(struct gpio_desc *desc, const char *label)
 {
        struct udevice *dev = desc->dev;
@@ -640,22 +755,25 @@ int dm_gpio_get_values_as_int(const struct gpio_desc *desc_list, int count)
        return vector;
 }
 
-static int gpio_request_tail(int ret, ofnode node,
+static int gpio_request_tail(int ret, const char *nodename,
                             struct ofnode_phandle_args *args,
                             const char *list_name, int index,
-                            struct gpio_desc *desc, int flags, bool add_index)
+                            struct gpio_desc *desc, int flags,
+                            bool add_index, struct udevice *dev)
 {
-       desc->dev = NULL;
+       desc->dev = dev;
        desc->offset = 0;
        desc->flags = 0;
        if (ret)
                goto err;
 
-       ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
-                                         &desc->dev);
-       if (ret) {
-               debug("%s: uclass_get_device_by_ofnode failed\n", __func__);
-               goto err;
+       if (!desc->dev) {
+               ret = uclass_get_device_by_ofnode(UCLASS_GPIO, args->node,
+                                                 &desc->dev);
+               if (ret) {
+                       debug("%s: uclass_get_device_by_ofnode failed\n", __func__);
+                       goto err;
+               }
        }
        ret = gpio_find_and_xlate(desc, args);
        if (ret) {
@@ -663,8 +781,7 @@ static int gpio_request_tail(int ret, ofnode node,
                goto err;
        }
        ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s",
-                              ofnode_get_name(node),
-                              list_name, index);
+                              nodename, list_name, index);
        if (ret) {
                debug("%s: dm_gpio_requestf failed\n", __func__);
                goto err;
@@ -678,7 +795,7 @@ static int gpio_request_tail(int ret, ofnode node,
        return 0;
 err:
        debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n",
-             __func__, ofnode_get_name(node), list_name, index, ret);
+             __func__, nodename, list_name, index, ret);
        return ret;
 }
 
@@ -692,8 +809,8 @@ static int _gpio_request_by_name_nodev(ofnode node, const char *list_name,
        ret = ofnode_parse_phandle_with_args(node, list_name, "#gpio-cells", 0,
                                             index, &args);
 
-       return gpio_request_tail(ret, node, &args, list_name, index, desc,
-                                flags, add_index);
+       return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
+                                index, desc, flags, add_index, NULL);
 }
 
 int gpio_request_by_name_nodev(ofnode node, const char *list_name, int index,
@@ -707,13 +824,14 @@ int gpio_request_by_name(struct udevice *dev, const char *list_name, int index,
                         struct gpio_desc *desc, int flags)
 {
        struct ofnode_phandle_args args;
+       ofnode node;
        int ret;
 
        ret = dev_read_phandle_with_args(dev, list_name, "#gpio-cells", 0,
                                         index, &args);
-
-       return gpio_request_tail(ret, dev_ofnode(dev), &args, list_name,
-                                index, desc, flags, index > 0);
+       node = dev_ofnode(dev);
+       return gpio_request_tail(ret, ofnode_get_name(node), &args, list_name,
+                                index, desc, flags, index > 0, NULL);
 }
 
 int gpio_request_list_by_name_nodev(ofnode node, const char *list_name,
@@ -854,8 +972,28 @@ static int gpio_pre_remove(struct udevice *dev)
        return gpio_renumber(dev);
 }
 
+int gpio_dev_request_index(struct udevice *dev, const char *nodename,
+                          char *list_name, int index, int flags,
+                          int dtflags, struct gpio_desc *desc)
+{
+       struct ofnode_phandle_args args;
+
+       args.node =  ofnode_null();
+       args.args_count = 2;
+       args.args[0] = index;
+       args.args[1] = dtflags;
+
+       return gpio_request_tail(0, nodename, &args, list_name, index, desc,
+                                flags, 0, dev);
+}
+
 static int gpio_post_bind(struct udevice *dev)
 {
+#if defined(CONFIG_DM_GPIO_HOG)
+       struct udevice *child;
+       ofnode node;
+#endif
+
 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
        struct dm_gpio_ops *ops = (struct dm_gpio_ops *)device_get_ops(dev);
        static int reloc_done;
@@ -885,6 +1023,17 @@ static int gpio_post_bind(struct udevice *dev)
                reloc_done++;
        }
 #endif
+
+#if defined(CONFIG_DM_GPIO_HOG)
+       dev_for_each_subnode(node, dev) {
+               if (ofnode_read_bool(node, "gpio-hog")) {
+                       const char *name = ofnode_get_name(node);
+
+                       device_bind_driver_to_node(dev, "gpio_hog", name,
+                                                  node, &child);
+               }
+       }
+#endif
        return 0;
 }
 
index d03602696f6d9317e2dcaefa5cdf38ee3402d8ee..37f71e5708ed533d8d18aa5e897769ea38b8c17b 100644 (file)
@@ -348,6 +348,22 @@ const char *gpio_get_bank_info(struct udevice *dev, int *offset_count);
  */
 int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc);
 
+/**
+ * gpio_hog_lookup_name() - Look up a named GPIO and return the gpio descr.
+ *
+ * @name:      Name to look up
+ * @return:    Returns gpio_desc for gpio
+ */
+struct gpio_desc *gpio_hog_lookup_name(const char *name);
+
+/**
+ * gpio_hog_probe_all() - probe all gpio devices with
+ * gpio-hog subnodes.
+ *
+ * @return:    Returns return value from device_probe()
+ */
+int gpio_hog_probe_all(void);
+
 /**
  * gpio_lookup_name - Look up a GPIO name and return its details
  *
@@ -503,6 +519,22 @@ int gpio_request_list_by_name_nodev(ofnode node, const char *list_name,
                                    struct gpio_desc *desc_list, int max_count,
                                    int flags);
 
+/**
+ * gpio_dev_request_index() - request single GPIO from gpio device
+ *
+ * @dev:       GPIO device
+ * @nodename:  Name of node
+ * @list_name: Name of GPIO list (e.g. "board-id-gpios")
+ * @index:     Index number of the GPIO in that list use request (0=first)
+ * @flags:     GPIOD_* flags
+ * @dtflags:   GPIO flags read from DT
+ * @desc:      GPIO descriotor filled from this function
+ * @return:    return value from gpio_request_tail()
+ */
+int gpio_dev_request_index(struct udevice *dev, const char *nodename,
+                          char *list_name, int index, int flags,
+                          int dtflags, struct gpio_desc *desc);
+
 /**
  * dm_gpio_free() - Free a single GPIO
  *