[U-Boot] [PATCH v3] gpio: add gpio-hog support
Heiko Schocher
hs at denx.de
Thu May 30 03:28:54 UTC 2019
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 at denx.de>
---
clean travis build, see result:
https://travis-ci.org/hsdenx/u-boot-test/builds/538734780
Changes in v3:
- probe now all gpio devices with gpio-hogs
from board_r before board_late_init() call
or if someone wants to access gpio-hog
based gpios with gpio_hog_lookup_name()
This is needed, because we cannot be sure,
if a gpio device which contains gpio-hogs
is probed.
- add line-name property as Michal recommended
- renamed gpio_priv_one into gpio_priv_one_hog
and protected through CONFIG_DM_GPIO_HOG
Changes in v2:
- move gpio_hog() call from post_probe() to post_bind()
call. Check if current gpio device has gpio-hog
definitions, if so, probe it.
common/board_r.c | 6 +
doc/device-tree-bindings/gpio/gpio.txt | 55 +++++++
drivers/gpio/Kconfig | 10 ++
drivers/gpio/gpio-uclass.c | 217 ++++++++++++++++++++++---
include/asm-generic/gpio.h | 32 ++++
5 files changed, 301 insertions(+), 19 deletions(-)
diff --git a/common/board_r.c b/common/board_r.c
index 150e8cd424..507da4c74a 100644
--- a/common/board_r.c
+++ b/common/board_r.c
@@ -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
diff --git a/doc/device-tree-bindings/gpio/gpio.txt b/doc/device-tree-bindings/gpio/gpio.txt
index f7a158d858..e774439369 100644
--- a/doc/device-tree-bindings/gpio/gpio.txt
+++ b/doc/device-tree-bindings/gpio/gpio.txt
@@ -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 at 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");
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e36a8abc42..fa1c99700f 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -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
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index da5e9ba6e5..4ecff115f1 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -5,6 +5,7 @@
#include <common.h>
#include <dm.h>
+#include <dm/device-internal.h>
#include <dt-bindings/gpio/gpio.h>
#include <errno.h>
#include <fdtdec.h>
@@ -15,6 +16,18 @@
DECLARE_GLOBAL_DATA_PTR;
+#if defined(CONFIG_DM_GPIO_HOG)
+struct gpio_priv_one_hog {
+ struct list_head list;
+ char *name;
+ struct gpio_desc gpiod;
+};
+
+static struct list_head hogs;
+static int list_init;
+static int gpio_hogs_probed;
+#endif
+
/**
* gpio_to_device() - Convert global GPIO number to device, number
*
@@ -141,6 +154,147 @@ 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)
+int gpio_hog_probe_all(void)
+{
+ struct udevice *dev;
+ ofnode node;
+ int ret;
+
+ /*
+ * We need to probe all gpio-hog devices
+ * at some point, as we cannot be sure,
+ * that all gpio devices, which contain a
+ * gpio-hog definition are probed.
+ *
+ */
+ if (gpio_hogs_probed)
+ return 0;
+
+ for (uclass_first_device(UCLASS_GPIO, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ dev_for_each_subnode(node, dev) {
+ if (ofnode_read_bool(node, "gpio-hog")) {
+ ret = device_probe(dev);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ }
+ gpio_hogs_probed = 1;
+
+ return 0;
+}
+
+struct gpio_desc *gpio_hog_lookup_name(const char *name)
+{
+ struct list_head *entry;
+ struct gpio_priv_one_hog *cur;
+
+ /* be sure, all gpio devices with gpio-hogs are probed */
+ gpio_hog_probe_all();
+ list_for_each(entry, &hogs) {
+ cur = list_entry(entry, struct gpio_priv_one_hog, list);
+ if (strcmp(cur->name, name) == 0)
+ return &cur->gpiod;
+ }
+
+ return NULL;
+}
+
+static int gpio_hog(struct udevice *dev)
+{
+ ofnode node;
+ int found = 0;
+ int ret;
+ struct gpio_dev_priv *uc_priv = NULL;
+
+ if (!list_init) {
+ INIT_LIST_HEAD(&hogs);
+ list_init = 1;
+ }
+ dev_for_each_subnode(node, dev) {
+ if (ofnode_read_bool(node, "gpio-hog")) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return 0;
+
+ uc_priv = dev_get_uclass_priv(dev);
+ if (!uc_priv) {
+ printf("%s: missing private data.\n", __func__);
+ return 0;
+ }
+
+ /* scan for gpio-hog subnodes */
+ dev_for_each_subnode(node, dev) {
+ u32 val[2];
+ int value = 0;
+ int gpiod_flags;
+ struct gpio_priv_one_hog *new;
+ const char *nodename;
+
+ if (!ofnode_read_bool(node, "gpio-hog"))
+ continue;
+
+ if (ofnode_read_bool(node, "input")) {
+ gpiod_flags = GPIOD_IS_IN;
+ } else if (ofnode_read_bool(node, "output-high")) {
+ value = 1;
+ gpiod_flags = GPIOD_IS_OUT;
+ } else if (ofnode_read_bool(node, "output-low")) {
+ gpiod_flags = GPIOD_IS_OUT;
+ } else {
+ printf("%s: missing gpio-hog state.\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = ofnode_read_u32_array(node, "gpios", val, 2);
+ if (ret) {
+ printf("%s: wrong gpios property, 2 values needed, ret: %d\n", __func__, ret);
+ return ret;
+ }
+
+ new = calloc(1, sizeof(struct gpio_priv_one_hog));
+ nodename = ofnode_read_string(node, "line-name");
+ if (!nodename)
+ nodename = ofnode_get_name(node);
+ ret = gpio_dev_request_index(dev, nodename, "gpio-hog",
+ val[0], gpiod_flags, val[1],
+ &new->gpiod);
+ if (ret < 0) {
+ debug("%s: node %s could not get gpio.\n", __func__,
+ ofnode_get_name(node));
+ free(new);
+ return ret;
+ }
+
+ new->name = uc_priv->name[new->gpiod.offset];
+ list_add_tail(&new->list, &hogs);
+ dm_gpio_set_dir(&new->gpiod);
+ if (gpiod_flags == GPIOD_IS_OUT)
+ dm_gpio_set_value(&new->gpiod, value);
+ }
+
+ return 0;
+}
+#else
+static int gpio_hog(struct udevice *dev)
+{
+ return 0;
+}
+
+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;
@@ -149,8 +303,9 @@ int dm_gpio_request(struct gpio_desc *desc, const char *label)
int ret;
uc_priv = dev_get_uclass_priv(dev);
- if (uc_priv->name[desc->offset])
- return -EBUSY;
+ if (uc_priv)
+ if (uc_priv->name[desc->offset])
+ return -EBUSY;
str = strdup(label);
if (!str)
return -ENOMEM;
@@ -640,22 +795,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 +821,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 +835,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 +849,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 +864,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,
@@ -832,12 +990,17 @@ int gpio_get_number(const struct gpio_desc *desc)
static int gpio_post_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ int ret;
uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
if (!uc_priv->name)
return -ENOMEM;
- return gpio_renumber(NULL);
+ ret = gpio_renumber(NULL);
+ if (ret)
+ return ret;
+
+ return gpio_hog(dev);
}
static int gpio_pre_remove(struct udevice *dev)
@@ -854,6 +1017,21 @@ 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_NEEDS_MANUAL_RELOC)
@@ -885,6 +1063,7 @@ static int gpio_post_bind(struct udevice *dev)
reloc_done++;
}
#endif
+
return 0;
}
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index d03602696f..37f71e5708 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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
*
--
2.21.0
More information about the U-Boot
mailing list