diff options
author | Heiko Schocher <hs@denx.de> | 2019-06-12 06:11:46 +0200 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2019-07-13 11:11:30 -0400 |
commit | 5fc7cf8c8e268590f3b0037eecea7f6798209f78 (patch) | |
tree | 73ec4655db11a32f1fe7a8495b9c42d81093de76 /drivers/gpio/gpio-uclass.c | |
parent | 42f15397275c6341abbdbb7a2d1188992274c5bb (diff) |
gpio: add gpio-hog support
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>
Diffstat (limited to 'drivers/gpio/gpio-uclass.c')
-rw-r--r-- | drivers/gpio/gpio-uclass.c | 181 |
1 files changed, 165 insertions, 16 deletions
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c index da5e9ba6e5..308d0863ad 100644 --- a/drivers/gpio/gpio-uclass.c +++ b/drivers/gpio/gpio-uclass.c @@ -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; } |