diff mbox series

[U-Boot,v2,08/14] gpio: add ops for configuration with dir flags

Message ID 20191126084911.19761-9-patrick.delaunay@st.com
State Superseded
Delegated to: Patrick Delaunay
Headers show
Series dm: add support of new binding in gpio and pincontrol | expand

Commit Message

Patrick DELAUNAY Nov. 26, 2019, 8:49 a.m. UTC
This commit manages the dir flags that can be used in gpio specifiers
to indicate if a pull-up resistor or pull-down resistor should be
enabled for output gpio (GPIO_PULL_UP, GPIO_PULL_DOWN) and the
Open Drain/Open Source configuration for input gpio
(GPIO_OPEN_DRAIN, GPIO_OPEN_SOURCE).

These flags are already supported in Linux kernel in gpiolib;
this patch provides the same support in U-Boot.

The dir flags are managed in gpio drivers with two optional ops in gpio
uclass: set_dir_flags and get_dir_flags.

- ops set_dir_flags() set the direction and configuration of each GPIO
  with a only call to dm_gpio_set_dir_flags() / dm_gpio_set_dir()
  and respecting the configuration provided by device tree
  (saved in desc->flags).

- ops get_dir_flags() return dynamically the current gpio configuration,
  it is used by the new API dm_gpio_get_dir_flags().

When these optional ops are absent, the gpio uclass use the mandatory ops
(direction_output, direction_input, get_value) and desc->flags to manage
only the main dir flags:
- GPIOD_IS_IN
- GPIOD_IS_OUT
- GPIOD_IS_OUT_ACTIVE
- GPIOD_ACTIVE_LOW

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
---

Changes in v2:
- change the proposed ops for pin config to set_dir_flags/get_dir_flags
- reused the existing API dm_gpio_set_dir_flags/dm_gpio_set_dir
- add a new API dm_gpio_get_dir_flags

 drivers/gpio/gpio-uclass.c | 157 +++++++++++++++++++++++++++++++------
 include/asm-generic/gpio.h |  65 +++++++++++++--
 2 files changed, 192 insertions(+), 30 deletions(-)

Comments

Simon Glass Dec. 30, 2019, 1:21 a.m. UTC | #1
Hi Patrick,

On Tue, 26 Nov 2019 at 01:49, Patrick Delaunay <patrick.delaunay@st.com> wrote:
>
> This commit manages the dir flags that can be used in gpio specifiers
> to indicate if a pull-up resistor or pull-down resistor should be
> enabled for output gpio (GPIO_PULL_UP, GPIO_PULL_DOWN) and the
> Open Drain/Open Source configuration for input gpio
> (GPIO_OPEN_DRAIN, GPIO_OPEN_SOURCE).
>
> These flags are already supported in Linux kernel in gpiolib;
> this patch provides the same support in U-Boot.
>
> The dir flags are managed in gpio drivers with two optional ops in gpio
> uclass: set_dir_flags and get_dir_flags.
>
> - ops set_dir_flags() set the direction and configuration of each GPIO
>   with a only call to dm_gpio_set_dir_flags() / dm_gpio_set_dir()
>   and respecting the configuration provided by device tree
>   (saved in desc->flags).
>
> - ops get_dir_flags() return dynamically the current gpio configuration,
>   it is used by the new API dm_gpio_get_dir_flags().
>
> When these optional ops are absent, the gpio uclass use the mandatory ops
> (direction_output, direction_input, get_value) and desc->flags to manage
> only the main dir flags:
> - GPIOD_IS_IN
> - GPIOD_IS_OUT
> - GPIOD_IS_OUT_ACTIVE
> - GPIOD_ACTIVE_LOW
>
> Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
> ---
>
> Changes in v2:
> - change the proposed ops for pin config to set_dir_flags/get_dir_flags
> - reused the existing API dm_gpio_set_dir_flags/dm_gpio_set_dir
> - add a new API dm_gpio_get_dir_flags
>
>  drivers/gpio/gpio-uclass.c | 157 +++++++++++++++++++++++++++++++------
>  include/asm-generic/gpio.h |  65 +++++++++++++--
>  2 files changed, 192 insertions(+), 30 deletions(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

>
> diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
> index 0870458e96..241293f4b4 100644
> --- a/drivers/gpio/gpio-uclass.c
> +++ b/drivers/gpio/gpio-uclass.c
> @@ -140,8 +140,27 @@ int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc,
>         if (args->args_count < 2)
>                 return 0;
>
> +       desc->flags = 0;
>         if (args->args[1] & GPIO_ACTIVE_LOW)
> -               desc->flags = GPIOD_ACTIVE_LOW;
> +               desc->flags |= GPIOD_ACTIVE_LOW;
> +
> +       /*
> +        * need to test 2 bits for gpio output binding:
> +        * OPEN_DRAIN (0x6) = SINGLE_ENDED (0x2) | LINE_OPEN_DRAIN (0x4)
> +        * OPEN_SOURCE (0x2) = SINGLE_ENDED (0x2) | LINE_OPEN_SOURCE (0x0)
> +        */
> +       if (args->args[1] & GPIO_SINGLE_ENDED) {
> +               if (args->args[1] & GPIO_LINE_OPEN_DRAIN)
> +                       desc->flags |= GPIOD_OPEN_DRAIN;
> +               else
> +                       desc->flags |= GPIOD_OPEN_SOURCE;
> +       }
> +
> +       if (args->args[1] & GPIO_PULL_UP)
> +               desc->flags |= GPIOD_PULL_UP;
> +
> +       if (args->args[1] & GPIO_PULL_DOWN)
> +               desc->flags |= GPIOD_PULL_DOWN;
>
>         return 0;
>  }
> @@ -476,18 +495,24 @@ int gpio_direction_output(unsigned gpio, int value)
>                                                         desc.offset, value);
>  }
>
> -int dm_gpio_get_value(const struct gpio_desc *desc)
> +static int _gpio_get_value(const struct gpio_desc *desc)
>  {
>         int value;
> +
> +       value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
> +
> +       return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
> +}
> +
> +int dm_gpio_get_value(const struct gpio_desc *desc)
> +{
>         int ret;
>
>         ret = check_reserved(desc, "get_value");
>         if (ret)
>                 return ret;
>
> -       value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
> -
> -       return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
> +       return _gpio_get_value(desc);
>  }
>
>  int dm_gpio_set_value(const struct gpio_desc *desc, int value)
> @@ -504,39 +529,119 @@ int dm_gpio_set_value(const struct gpio_desc *desc, int value)
>         return 0;
>  }
>
> -int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
> +/* check dir flags invalid configuration */
> +static int check_dir_flags(ulong flags)
> +{
> +       if ((flags & GPIOD_IS_OUT) && (flags & GPIOD_IS_IN))
> +               return -EINVAL;
> +
> +       if ((flags & GPIOD_PULL_UP) && (flags & GPIOD_PULL_DOWN))
> +               return -EINVAL;
> +
> +       if ((flags & GPIOD_OPEN_DRAIN) && (flags & GPIOD_OPEN_SOURCE))
> +               return -EINVAL;

Can we use different error numbers here, so that people can figure out
what is going on?

> +
> +       return 0;
> +}
> +
> +static int _dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
>  {
>         struct udevice *dev = desc->dev;
>         struct dm_gpio_ops *ops = gpio_get_ops(dev);
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
>         int ret;
>
> -       ret = check_reserved(desc, "set_dir");
> -       if (ret)
> -               return ret;
> +       ret = check_dir_flags(flags);
> +       if (ret) {
> +               dev_err(dev,
> +                       "%s error: set_dir_flags for gpio %s%d has invalid dir flags 0x%lx\n",
> +                       desc->dev->name,
> +                       uc_priv->bank_name ? uc_priv->bank_name : "",
> +                       desc->offset, flags);

log_debug()
Also should return error

>
> -       if (flags & GPIOD_IS_OUT) {
> -               int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
> +               return ret;
> +       }
>
> -               if (flags & GPIOD_ACTIVE_LOW)
> -                       value = !value;
> -               ret = ops->direction_output(dev, desc->offset, value);
> -       } else  if (flags & GPIOD_IS_IN) {
> -               ret = ops->direction_input(dev, desc->offset);
> +       /* GPIOD_ are directly managed by driver in set_dir_flags*/
> +       if (ops->set_dir_flags) {
> +               ret = ops->set_dir_flags(dev, desc->offset, flags);
> +       } else {
> +               if (flags & GPIOD_IS_OUT) {
> +                       ret = ops->direction_output(dev, desc->offset,
> +                                                   GPIOD_FLAGS_OUTPUT(flags));
> +               } else if (flags & GPIOD_IS_IN) {
> +                       ret = ops->direction_input(dev, desc->offset);
> +               }
>         }
> +
> +       return ret;
> +}
> +
> +int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
> +{
> +       int ret;
> +
> +       ret = check_reserved(desc, "set_dir_flags");
>         if (ret)
>                 return ret;
> -       /*
> -        * Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in
> -        * futures
> -        */
> -       desc->flags = flags;
>
> -       return 0;
> +       /* combine the requested flags (for IN/OUT) and the descriptor flags */
> +       flags |= desc->flags;
> +       ret = _dm_gpio_set_dir_flags(desc, flags);
> +
> +       /* update the descriptor flags */
> +       if (ret)
> +               desc->flags = flags;
> +
> +       return ret;
>  }
>
>  int dm_gpio_set_dir(struct gpio_desc *desc)
>  {
> -       return dm_gpio_set_dir_flags(desc, desc->flags);
> +       int ret;
> +
> +       ret = check_reserved(desc, "set_dir");
> +       if (ret)
> +               return ret;
> +
> +       return _dm_gpio_set_dir_flags(desc, desc->flags);
> +}
> +
> +int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags)
> +{
> +       struct udevice *dev = desc->dev;
> +       struct dm_gpio_ops *ops = gpio_get_ops(dev);
> +       int ret, value;
> +       ulong dir_flags;
> +
> +       ret = check_reserved(desc, "get_dir_flags");
> +       if (ret)
> +               return ret;
> +
> +       /* GPIOD_ are directly provided by driver except GPIOD_ACTIVE_LOW*/

Space before *

> +       if (ops->get_dir_flags) {
> +               ret = ops->get_dir_flags(dev, desc->offset, &dir_flags);
> +               if (ret)
> +                       return ret;
> +
> +               /* GPIOD_ACTIVE_LOW is saved in desc->flags */
> +               value = dir_flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
> +               if (desc->flags & GPIOD_ACTIVE_LOW)
> +                       value = !value;
> +               dir_flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
> +               dir_flags |= (desc->flags & GPIOD_ACTIVE_LOW);
> +               if (value)
> +                       dir_flags |= GPIOD_IS_OUT_ACTIVE;
> +       } else {
> +               dir_flags = desc->flags;
> +               /* only GPIOD_IS_OUT_ACTIVE is provided by uclass */
> +               dir_flags &= ~GPIOD_IS_OUT_ACTIVE;
> +               if ((desc->flags & GPIOD_IS_OUT) && _gpio_get_value(desc))
> +                       dir_flags |= GPIOD_IS_OUT_ACTIVE;
> +       }
> +       *flags = dir_flags;
> +
> +       return 0;
>  }
>
>  /**
> @@ -809,7 +914,7 @@ static int gpio_request_tail(int ret, const char *nodename,
>                 debug("%s: dm_gpio_requestf failed\n", __func__);
>                 goto err;
>         }
> -       ret = dm_gpio_set_dir_flags(desc, flags | desc->flags);
> +       ret = dm_gpio_set_dir_flags(desc, flags);
>         if (ret) {
>                 debug("%s: dm_gpio_set_dir failed\n", __func__);
>                 goto err;
> @@ -1036,6 +1141,10 @@ static int gpio_post_bind(struct udevice *dev)
>                         ops->get_function += gd->reloc_off;
>                 if (ops->xlate)
>                         ops->xlate += gd->reloc_off;
> +               if (ops->set_dir_flags)
> +                       ops->set_dir_flags += gd->reloc_off;
> +               if (ops->get_dir_flags)
> +                       ops->get_dir_flags += gd->reloc_off;
>
>                 reloc_done++;
>         }
> diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
> index 454578c8d2..c6991be1c9 100644
> --- a/include/asm-generic/gpio.h
> +++ b/include/asm-generic/gpio.h
> @@ -117,10 +117,15 @@ struct udevice;
>  struct gpio_desc {
>         struct udevice *dev;    /* Device, NULL for invalid GPIO */
>         unsigned long flags;
> +
>  #define GPIOD_IS_OUT           BIT(1)  /* GPIO is an output */
>  #define GPIOD_IS_IN            BIT(2)  /* GPIO is an input */
>  #define GPIOD_ACTIVE_LOW       BIT(3)  /* value has active low */

s/has/is/ ?

>  #define GPIOD_IS_OUT_ACTIVE    BIT(4)  /* set output active */
> +#define GPIOD_OPEN_DRAIN       BIT(5)  /* GPIO is open drain type */
> +#define GPIOD_OPEN_SOURCE      BIT(6)  /* GPIO is open source type */
> +#define GPIOD_PULL_UP          BIT(7)  /* GPIO has pull up enabled */
> +#define GPIOD_PULL_DOWN                BIT(8)  /* GPIO has pull down enabled */

pull-down or pulldown

>
>         uint offset;            /* GPIO offset within the device */
>         /*
> @@ -129,6 +134,12 @@ struct gpio_desc {
>          */
>  };
>
> +/* helper to compute the value of the gpio output */
> +#define GPIOD_FLAGS_OUTPUT_MASK (GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE)
> +#define GPIOD_FLAGS_OUTPUT(flags) \
> +       (((((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_IS_OUT_ACTIVE) || \
> +         (((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_ACTIVE_LOW)) ? 1 : 0)
> +

You can drop the ? 1 : 0 since boolean expressions do that anyway.

>  /**
>   * dm_gpio_is_valid() - Check if a GPIO is valid
>   *
> @@ -253,6 +264,7 @@ struct dm_gpio_ops {
>                                 int value);
>         int (*get_value)(struct udevice *dev, unsigned offset);
>         int (*set_value)(struct udevice *dev, unsigned offset, int value);
> +
>         /**
>          * get_function() Get the GPIO function
>          *
> @@ -287,6 +299,37 @@ struct dm_gpio_ops {
>          */
>         int (*xlate)(struct udevice *dev, struct gpio_desc *desc,
>                      struct ofnode_phandle_args *args);
> +
> +       /**
> +        * set_dir_flags() - Set GPIO dir flags
> +        *
> +        * This function should set up the GPIO configuration according to the
> +        * information provide by the direction flags bitfield.
> +        *
> +        * This method is optional.
> +        *
> +        * @dev:        GPIO device
> +        * @offset:     GPIO offset within that device
> +        * @flags:      GPIO configuration to use
> +        * @return 0 if OK, -ve on error
> +        */
> +       int (*set_dir_flags)(struct udevice *dev, unsigned int offset,
> +                            ulong flags);
> +
> +       /**
> +        * get_dir_flags() - Get GPIO dir flags
> +        *
> +        * This function return the GPIO direction flags used.
> +        *
> +        * This method is optional.
> +        *
> +        * @dev:        GPIO device
> +        * @offset:     GPIO offset within that device
> +        * @flags:      place to put the used direction flags by GPIO
> +        * @return 0 if OK, -ve on error
> +        */
> +       int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
> +                            ulong *flags);
>  };
>
>  /**
> @@ -586,8 +629,7 @@ int dm_gpio_set_value(const struct gpio_desc *desc, int value);
>  /**
>   * dm_gpio_set_dir() - Set the direction for a GPIO
>   *
> - * This sets up the direction according tot the provided flags. It will do
> - * nothing unless the direction is actually specified.
> + * This sets up the direction according to the GPIO flags: desc->flags.
>   *
>   * @desc:      GPIO description containing device, offset and flags,
>   *             previously returned by gpio_request_by_name()
> @@ -596,11 +638,10 @@ int dm_gpio_set_value(const struct gpio_desc *desc, int value);
>  int dm_gpio_set_dir(struct gpio_desc *desc);
>
>  /**
> - * dm_gpio_set_dir_flags() - Set direction using specific flags
> + * dm_gpio_set_dir_flags() - Set direction using add flags
>   *
> - * This is like dm_gpio_set_dir() except that the flags value is provided
> - * instead of being used from desc->flags. This is needed because in many
> - * cases the GPIO description does not include direction information.
> + * This sets up the direction according tot the provided flags and the GPIO
> + * description (desc->flags) which include direction information.
>   * Note that desc->flags is updated by this function.
>   *
>   * @desc:      GPIO description containing device, offset and flags,
> @@ -610,6 +651,18 @@ int dm_gpio_set_dir(struct gpio_desc *desc);
>   */
>  int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags);
>
> +/**
> + * dm_gpio_get_dir_flags() - Get direction flags
> + *
> + * read the current direction flags
> + *
> + * @desc:      GPIO description containing device, offset and flags,
> + *             previously returned by gpio_request_by_name()
> + * @flags:     place to put the used flags
> + * @return 0 if OK, -ve on error, in which case desc->flags is not updated
> + */
> +int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
> +
>  /**
>   * gpio_get_number() - Get the global GPIO number of a GPIO
>   *
> --
> 2.17.1
>

There's a lot going on in this patch.

Regards,
Simon
Patrick DELAUNAY Jan. 9, 2020, 10:19 a.m. UTC | #2
Hi Simon

> From: Simon Glass <sjg@chromium.org>
> Sent: lundi 30 décembre 2019 02:21
> 
> Hi Patrick,
> 
> On Tue, 26 Nov 2019 at 01:49, Patrick Delaunay <patrick.delaunay@st.com>
> wrote:
> >
> > This commit manages the dir flags that can be used in gpio specifiers
> > to indicate if a pull-up resistor or pull-down resistor should be
> > enabled for output gpio (GPIO_PULL_UP, GPIO_PULL_DOWN) and the Open
> > Drain/Open Source configuration for input gpio (GPIO_OPEN_DRAIN,
> > GPIO_OPEN_SOURCE).
> >
> > These flags are already supported in Linux kernel in gpiolib; this
> > patch provides the same support in U-Boot.
> >
> > The dir flags are managed in gpio drivers with two optional ops in
> > gpio
> > uclass: set_dir_flags and get_dir_flags.
> >
> > - ops set_dir_flags() set the direction and configuration of each GPIO
> >   with a only call to dm_gpio_set_dir_flags() / dm_gpio_set_dir()
> >   and respecting the configuration provided by device tree
> >   (saved in desc->flags).
> >
> > - ops get_dir_flags() return dynamically the current gpio configuration,
> >   it is used by the new API dm_gpio_get_dir_flags().
> >
> > When these optional ops are absent, the gpio uclass use the mandatory
> > ops (direction_output, direction_input, get_value) and desc->flags to
> > manage only the main dir flags:
> > - GPIOD_IS_IN
> > - GPIOD_IS_OUT
> > - GPIOD_IS_OUT_ACTIVE
> > - GPIOD_ACTIVE_LOW
> >
> > Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
> > ---
> >
> > Changes in v2:
> > - change the proposed ops for pin config to
> > set_dir_flags/get_dir_flags
> > - reused the existing API dm_gpio_set_dir_flags/dm_gpio_set_dir
> > - add a new API dm_gpio_get_dir_flags
> >
> >  drivers/gpio/gpio-uclass.c | 157
> > +++++++++++++++++++++++++++++++------
> >  include/asm-generic/gpio.h |  65 +++++++++++++--
> >  2 files changed, 192 insertions(+), 30 deletions(-)
> 
> Reviewed-by: Simon Glass <sjg@chromium.org>
> 
> >
> > diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
> > index 0870458e96..241293f4b4 100644
> > --- a/drivers/gpio/gpio-uclass.c
> > +++ b/drivers/gpio/gpio-uclass.c
> > @@ -140,8 +140,27 @@ int gpio_xlate_offs_flags(struct udevice *dev, struct
> gpio_desc *desc,
> >         if (args->args_count < 2)
> >                 return 0;
> >
> > +       desc->flags = 0;
> >         if (args->args[1] & GPIO_ACTIVE_LOW)
> > -               desc->flags = GPIOD_ACTIVE_LOW;
> > +               desc->flags |= GPIOD_ACTIVE_LOW;
> > +
> > +       /*
> > +        * need to test 2 bits for gpio output binding:
> > +        * OPEN_DRAIN (0x6) = SINGLE_ENDED (0x2) | LINE_OPEN_DRAIN
> (0x4)
> > +        * OPEN_SOURCE (0x2) = SINGLE_ENDED (0x2) |
> LINE_OPEN_SOURCE (0x0)
> > +        */
> > +       if (args->args[1] & GPIO_SINGLE_ENDED) {
> > +               if (args->args[1] & GPIO_LINE_OPEN_DRAIN)
> > +                       desc->flags |= GPIOD_OPEN_DRAIN;
> > +               else
> > +                       desc->flags |= GPIOD_OPEN_SOURCE;
> > +       }
> > +
> > +       if (args->args[1] & GPIO_PULL_UP)
> > +               desc->flags |= GPIOD_PULL_UP;
> > +
> > +       if (args->args[1] & GPIO_PULL_DOWN)
> > +               desc->flags |= GPIOD_PULL_DOWN;
> >
> >         return 0;
> >  }
> > @@ -476,18 +495,24 @@ int gpio_direction_output(unsigned gpio, int value)
> >                                                         desc.offset,
> > value);  }
> >
> > -int dm_gpio_get_value(const struct gpio_desc *desc)
> > +static int _gpio_get_value(const struct gpio_desc *desc)
> >  {
> >         int value;
> > +
> > +       value = gpio_get_ops(desc->dev)->get_value(desc->dev,
> > + desc->offset);
> > +
> > +       return desc->flags & GPIOD_ACTIVE_LOW ? !value : value; }
> > +
> > +int dm_gpio_get_value(const struct gpio_desc *desc) {
> >         int ret;
> >
> >         ret = check_reserved(desc, "get_value");
> >         if (ret)
> >                 return ret;
> >
> > -       value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
> > -
> > -       return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
> > +       return _gpio_get_value(desc);
> >  }
> >
> >  int dm_gpio_set_value(const struct gpio_desc *desc, int value) @@
> > -504,39 +529,119 @@ int dm_gpio_set_value(const struct gpio_desc *desc, int
> value)
> >         return 0;
> >  }
> >
> > -int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
> > +/* check dir flags invalid configuration */ static int
> > +check_dir_flags(ulong flags) {
> > +       if ((flags & GPIOD_IS_OUT) && (flags & GPIOD_IS_IN))
> > +               return -EINVAL;
> > +
> > +       if ((flags & GPIOD_PULL_UP) && (flags & GPIOD_PULL_DOWN))
> > +               return -EINVAL;
> > +
> > +       if ((flags & GPIOD_OPEN_DRAIN) && (flags &
> GPIOD_OPEN_SOURCE))
> > +               return -EINVAL;
> 
> Can we use different error numbers here, so that people can figure out what is
> going on?

It is really a basic error that should be never occur,
I think that EINVAL is the correct return value here (it is difficult to have 3 different errno)

But I will add a debug trace before each return...
 
> > +
> > +       return 0;
> > +}
> > +
> > +static int _dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong
> > +flags)
> >  {
> >         struct udevice *dev = desc->dev;
> >         struct dm_gpio_ops *ops = gpio_get_ops(dev);
> > +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> >         int ret;
> >
> > -       ret = check_reserved(desc, "set_dir");
> > -       if (ret)
> > -               return ret;
> > +       ret = check_dir_flags(flags);
> > +       if (ret) {
> > +               dev_err(dev,
> > +                       "%s error: set_dir_flags for gpio %s%d has invalid dir flags
> 0x%lx\n",
> > +                       desc->dev->name,
> > +                       uc_priv->bank_name ? uc_priv->bank_name : "",
> > +                       desc->offset, flags);
> 
> log_debug()

Ok change to dev_dbg
log_debug is preferered to dev_dbg ?

for me dev_.... functions are preferred when 'dev' is available.

> Also should return error
> 
> >
> > -       if (flags & GPIOD_IS_OUT) {
> > -               int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
> > +               return ret;
> > +       }

In fact return is inserted here (test moved in check_dir_flags)

The final code become :

	ret = check_dir_flags(flags);
	if (ret) {
		dev_dbg(dev,
			"%s error: set_dir_flags for gpio %s%d has invalid dir flags 0x%lx\n",
			desc->dev->name,
			uc_priv->bank_name ? uc_priv->bank_name : "",
			desc->offset, flags);

		return ret;
	}

	/* GPIOD_ are directly managed by driver in set_dir_flags*/

> >
> > -               if (flags & GPIOD_ACTIVE_LOW)
> > -                       value = !value;
> > -               ret = ops->direction_output(dev, desc->offset, value);
> > -       } else  if (flags & GPIOD_IS_IN) {
> > -               ret = ops->direction_input(dev, desc->offset);
> > +       /* GPIOD_ are directly managed by driver in set_dir_flags*/
> > +       if (ops->set_dir_flags) {
> > +               ret = ops->set_dir_flags(dev, desc->offset, flags);
> > +       } else {
> > +               if (flags & GPIOD_IS_OUT) {
> > +                       ret = ops->direction_output(dev, desc->offset,
> > +                                                   GPIOD_FLAGS_OUTPUT(flags));
> > +               } else if (flags & GPIOD_IS_IN) {
> > +                       ret = ops->direction_input(dev, desc->offset);
> > +               }
> >         }
> > +
> > +       return ret;
> > +}
> > +
> > +int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags) {
> > +       int ret;
> > +
> > +       ret = check_reserved(desc, "set_dir_flags");
> >         if (ret)
> >                 return ret;
> > -       /*
> > -        * Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in
> > -        * futures
> > -        */
> > -       desc->flags = flags;
> >
> > -       return 0;
> > +       /* combine the requested flags (for IN/OUT) and the descriptor flags */
> > +       flags |= desc->flags;
> > +       ret = _dm_gpio_set_dir_flags(desc, flags);
> > +
> > +       /* update the descriptor flags */
> > +       if (ret)
> > +               desc->flags = flags;
> > +
> > +       return ret;
> >  }
> >
> >  int dm_gpio_set_dir(struct gpio_desc *desc)  {
> > -       return dm_gpio_set_dir_flags(desc, desc->flags);
> > +       int ret;
> > +
> > +       ret = check_reserved(desc, "set_dir");
> > +       if (ret)
> > +               return ret;
> > +
> > +       return _dm_gpio_set_dir_flags(desc, desc->flags); }
> > +
> > +int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags) {
> > +       struct udevice *dev = desc->dev;
> > +       struct dm_gpio_ops *ops = gpio_get_ops(dev);
> > +       int ret, value;
> > +       ulong dir_flags;
> > +
> > +       ret = check_reserved(desc, "get_dir_flags");
> > +       if (ret)
> > +               return ret;
> > +
> > +       /* GPIOD_ are directly provided by driver except
> > + GPIOD_ACTIVE_LOW*/
> 
> Space before *

ok

> > +       if (ops->get_dir_flags) {
> > +               ret = ops->get_dir_flags(dev, desc->offset, &dir_flags);
> > +               if (ret)
> > +                       return ret;
> > +
> > +               /* GPIOD_ACTIVE_LOW is saved in desc->flags */
> > +               value = dir_flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
> > +               if (desc->flags & GPIOD_ACTIVE_LOW)
> > +                       value = !value;
> > +               dir_flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
> > +               dir_flags |= (desc->flags & GPIOD_ACTIVE_LOW);
> > +               if (value)
> > +                       dir_flags |= GPIOD_IS_OUT_ACTIVE;
> > +       } else {
> > +               dir_flags = desc->flags;
> > +               /* only GPIOD_IS_OUT_ACTIVE is provided by uclass */
> > +               dir_flags &= ~GPIOD_IS_OUT_ACTIVE;
> > +               if ((desc->flags & GPIOD_IS_OUT) && _gpio_get_value(desc))
> > +                       dir_flags |= GPIOD_IS_OUT_ACTIVE;
> > +       }
> > +       *flags = dir_flags;
> > +
> > +       return 0;
> >  }
> >
> >  /**
> > @@ -809,7 +914,7 @@ static int gpio_request_tail(int ret, const char
> *nodename,
> >                 debug("%s: dm_gpio_requestf failed\n", __func__);
> >                 goto err;
> >         }
> > -       ret = dm_gpio_set_dir_flags(desc, flags | desc->flags);
> > +       ret = dm_gpio_set_dir_flags(desc, flags);
> >         if (ret) {
> >                 debug("%s: dm_gpio_set_dir failed\n", __func__);
> >                 goto err;
> > @@ -1036,6 +1141,10 @@ static int gpio_post_bind(struct udevice *dev)
> >                         ops->get_function += gd->reloc_off;
> >                 if (ops->xlate)
> >                         ops->xlate += gd->reloc_off;
> > +               if (ops->set_dir_flags)
> > +                       ops->set_dir_flags += gd->reloc_off;
> > +               if (ops->get_dir_flags)
> > +                       ops->get_dir_flags += gd->reloc_off;
> >
> >                 reloc_done++;
> >         }
> > diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
> > index 454578c8d2..c6991be1c9 100644
> > --- a/include/asm-generic/gpio.h
> > +++ b/include/asm-generic/gpio.h
> > @@ -117,10 +117,15 @@ struct udevice;
> >  struct gpio_desc {
> >         struct udevice *dev;    /* Device, NULL for invalid GPIO */
> >         unsigned long flags;
> > +
> >  #define GPIOD_IS_OUT           BIT(1)  /* GPIO is an output */
> >  #define GPIOD_IS_IN            BIT(2)  /* GPIO is an input */
> >  #define GPIOD_ACTIVE_LOW       BIT(3)  /* value has active low */
> 
> s/has/is/ ?

GPIO is active when value is low

> 
> >  #define GPIOD_IS_OUT_ACTIVE    BIT(4)  /* set output active */
> > +#define GPIOD_OPEN_DRAIN       BIT(5)  /* GPIO is open drain type */
> > +#define GPIOD_OPEN_SOURCE      BIT(6)  /* GPIO is open source type */
> > +#define GPIOD_PULL_UP          BIT(7)  /* GPIO has pull up enabled */
> > +#define GPIOD_PULL_DOWN                BIT(8)  /* GPIO has pull down enabled
> */
> 
> pull-down or pulldown

Ok => pull-down

> >
> >         uint offset;            /* GPIO offset within the device */
> >         /*
> > @@ -129,6 +134,12 @@ struct gpio_desc {
> >          */
> >  };
> >
> > +/* helper to compute the value of the gpio output */ #define
> > +GPIOD_FLAGS_OUTPUT_MASK (GPIOD_ACTIVE_LOW |
> GPIOD_IS_OUT_ACTIVE)
> > +#define GPIOD_FLAGS_OUTPUT(flags) \
> > +       (((((flags) & GPIOD_FLAGS_OUTPUT_MASK) ==
> GPIOD_IS_OUT_ACTIVE) || \
> > +         (((flags) & GPIOD_FLAGS_OUTPUT_MASK) ==
> GPIOD_ACTIVE_LOW)) ?
> > +1 : 0)
> > +
> 
> You can drop the ? 1 : 0 since boolean expressions do that anyway.

Yes I drop it .... 

> >  /**
> >   * dm_gpio_is_valid() - Check if a GPIO is valid
> >   *
> > @@ -253,6 +264,7 @@ struct dm_gpio_ops {
> >                                 int value);
> >         int (*get_value)(struct udevice *dev, unsigned offset);
> >         int (*set_value)(struct udevice *dev, unsigned offset, int
> > value);
> > +
> >         /**
> >          * get_function() Get the GPIO function
> >          *
> > @@ -287,6 +299,37 @@ struct dm_gpio_ops {
> >          */
> >         int (*xlate)(struct udevice *dev, struct gpio_desc *desc,
> >                      struct ofnode_phandle_args *args);
> > +
> > +       /**
> > +        * set_dir_flags() - Set GPIO dir flags
> > +        *
> > +        * This function should set up the GPIO configuration according to the
> > +        * information provide by the direction flags bitfield.
> > +        *
> > +        * This method is optional.
> > +        *
> > +        * @dev:        GPIO device
> > +        * @offset:     GPIO offset within that device
> > +        * @flags:      GPIO configuration to use
> > +        * @return 0 if OK, -ve on error
> > +        */
> > +       int (*set_dir_flags)(struct udevice *dev, unsigned int offset,
> > +                            ulong flags);
> > +
> > +       /**
> > +        * get_dir_flags() - Get GPIO dir flags
> > +        *
> > +        * This function return the GPIO direction flags used.
> > +        *
> > +        * This method is optional.
> > +        *
> > +        * @dev:        GPIO device
> > +        * @offset:     GPIO offset within that device
> > +        * @flags:      place to put the used direction flags by GPIO
> > +        * @return 0 if OK, -ve on error
> > +        */
> > +       int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
> > +                            ulong *flags);
> >  };
> >
> >  /**
> > @@ -586,8 +629,7 @@ int dm_gpio_set_value(const struct gpio_desc
> > *desc, int value);
> >  /**
> >   * dm_gpio_set_dir() - Set the direction for a GPIO
> >   *
> > - * This sets up the direction according tot the provided flags. It
> > will do
> > - * nothing unless the direction is actually specified.
> > + * This sets up the direction according to the GPIO flags: desc->flags.
> >   *
> >   * @desc:      GPIO description containing device, offset and flags,
> >   *             previously returned by gpio_request_by_name()
> > @@ -596,11 +638,10 @@ int dm_gpio_set_value(const struct gpio_desc
> > *desc, int value);  int dm_gpio_set_dir(struct gpio_desc *desc);
> >
> >  /**
> > - * dm_gpio_set_dir_flags() - Set direction using specific flags
> > + * dm_gpio_set_dir_flags() - Set direction using add flags
> >   *
> > - * This is like dm_gpio_set_dir() except that the flags value is
> > provided
> > - * instead of being used from desc->flags. This is needed because in
> > many
> > - * cases the GPIO description does not include direction information.
> > + * This sets up the direction according tot the provided flags and
> > + the GPIO
> > + * description (desc->flags) which include direction information.
> >   * Note that desc->flags is updated by this function.
> >   *
> >   * @desc:      GPIO description containing device, offset and flags,
> > @@ -610,6 +651,18 @@ int dm_gpio_set_dir(struct gpio_desc *desc);
> >   */
> >  int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags);
> >
> > +/**
> > + * dm_gpio_get_dir_flags() - Get direction flags
> > + *
> > + * read the current direction flags
> > + *
> > + * @desc:      GPIO description containing device, offset and flags,
> > + *             previously returned by gpio_request_by_name()
> > + * @flags:     place to put the used flags
> > + * @return 0 if OK, -ve on error, in which case desc->flags is not
> > +updated  */ int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong
> > +*flags);
> > +
> >  /**
> >   * gpio_get_number() - Get the global GPIO number of a GPIO
> >   *
> > --
> > 2.17.1
> >
> 
> There's a lot going on in this patch.

Yes, I can split the work to help the review

1/ prepare
2/ add get_dir_flags
3/ add set_dir_flags

> Regards,
> Simon

Regards
Patrick
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 0870458e96..241293f4b4 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -140,8 +140,27 @@  int gpio_xlate_offs_flags(struct udevice *dev, struct gpio_desc *desc,
 	if (args->args_count < 2)
 		return 0;
 
+	desc->flags = 0;
 	if (args->args[1] & GPIO_ACTIVE_LOW)
-		desc->flags = GPIOD_ACTIVE_LOW;
+		desc->flags |= GPIOD_ACTIVE_LOW;
+
+	/*
+	 * need to test 2 bits for gpio output binding:
+	 * OPEN_DRAIN (0x6) = SINGLE_ENDED (0x2) | LINE_OPEN_DRAIN (0x4)
+	 * OPEN_SOURCE (0x2) = SINGLE_ENDED (0x2) | LINE_OPEN_SOURCE (0x0)
+	 */
+	if (args->args[1] & GPIO_SINGLE_ENDED) {
+		if (args->args[1] & GPIO_LINE_OPEN_DRAIN)
+			desc->flags |= GPIOD_OPEN_DRAIN;
+		else
+			desc->flags |= GPIOD_OPEN_SOURCE;
+	}
+
+	if (args->args[1] & GPIO_PULL_UP)
+		desc->flags |= GPIOD_PULL_UP;
+
+	if (args->args[1] & GPIO_PULL_DOWN)
+		desc->flags |= GPIOD_PULL_DOWN;
 
 	return 0;
 }
@@ -476,18 +495,24 @@  int gpio_direction_output(unsigned gpio, int value)
 							desc.offset, value);
 }
 
-int dm_gpio_get_value(const struct gpio_desc *desc)
+static int _gpio_get_value(const struct gpio_desc *desc)
 {
 	int value;
+
+	value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
+
+	return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
+}
+
+int dm_gpio_get_value(const struct gpio_desc *desc)
+{
 	int ret;
 
 	ret = check_reserved(desc, "get_value");
 	if (ret)
 		return ret;
 
-	value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
-
-	return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
+	return _gpio_get_value(desc);
 }
 
 int dm_gpio_set_value(const struct gpio_desc *desc, int value)
@@ -504,39 +529,119 @@  int dm_gpio_set_value(const struct gpio_desc *desc, int value)
 	return 0;
 }
 
-int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
+/* check dir flags invalid configuration */
+static int check_dir_flags(ulong flags)
+{
+	if ((flags & GPIOD_IS_OUT) && (flags & GPIOD_IS_IN))
+		return -EINVAL;
+
+	if ((flags & GPIOD_PULL_UP) && (flags & GPIOD_PULL_DOWN))
+		return -EINVAL;
+
+	if ((flags & GPIOD_OPEN_DRAIN) && (flags & GPIOD_OPEN_SOURCE))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int _dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
 {
 	struct udevice *dev = desc->dev;
 	struct dm_gpio_ops *ops = gpio_get_ops(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
 	int ret;
 
-	ret = check_reserved(desc, "set_dir");
-	if (ret)
-		return ret;
+	ret = check_dir_flags(flags);
+	if (ret) {
+		dev_err(dev,
+			"%s error: set_dir_flags for gpio %s%d has invalid dir flags 0x%lx\n",
+			desc->dev->name,
+			uc_priv->bank_name ? uc_priv->bank_name : "",
+			desc->offset, flags);
 
-	if (flags & GPIOD_IS_OUT) {
-		int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
+		return ret;
+	}
 
-		if (flags & GPIOD_ACTIVE_LOW)
-			value = !value;
-		ret = ops->direction_output(dev, desc->offset, value);
-	} else  if (flags & GPIOD_IS_IN) {
-		ret = ops->direction_input(dev, desc->offset);
+	/* GPIOD_ are directly managed by driver in set_dir_flags*/
+	if (ops->set_dir_flags) {
+		ret = ops->set_dir_flags(dev, desc->offset, flags);
+	} else {
+		if (flags & GPIOD_IS_OUT) {
+			ret = ops->direction_output(dev, desc->offset,
+						    GPIOD_FLAGS_OUTPUT(flags));
+		} else if (flags & GPIOD_IS_IN) {
+			ret = ops->direction_input(dev, desc->offset);
+		}
 	}
+
+	return ret;
+}
+
+int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
+{
+	int ret;
+
+	ret = check_reserved(desc, "set_dir_flags");
 	if (ret)
 		return ret;
-	/*
-	 * Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in
-	 * futures
-	 */
-	desc->flags = flags;
 
-	return 0;
+	/* combine the requested flags (for IN/OUT) and the descriptor flags */
+	flags |= desc->flags;
+	ret = _dm_gpio_set_dir_flags(desc, flags);
+
+	/* update the descriptor flags */
+	if (ret)
+		desc->flags = flags;
+
+	return ret;
 }
 
 int dm_gpio_set_dir(struct gpio_desc *desc)
 {
-	return dm_gpio_set_dir_flags(desc, desc->flags);
+	int ret;
+
+	ret = check_reserved(desc, "set_dir");
+	if (ret)
+		return ret;
+
+	return _dm_gpio_set_dir_flags(desc, desc->flags);
+}
+
+int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags)
+{
+	struct udevice *dev = desc->dev;
+	struct dm_gpio_ops *ops = gpio_get_ops(dev);
+	int ret, value;
+	ulong dir_flags;
+
+	ret = check_reserved(desc, "get_dir_flags");
+	if (ret)
+		return ret;
+
+	/* GPIOD_ are directly provided by driver except GPIOD_ACTIVE_LOW*/
+	if (ops->get_dir_flags) {
+		ret = ops->get_dir_flags(dev, desc->offset, &dir_flags);
+		if (ret)
+			return ret;
+
+		/* GPIOD_ACTIVE_LOW is saved in desc->flags */
+		value = dir_flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
+		if (desc->flags & GPIOD_ACTIVE_LOW)
+			value = !value;
+		dir_flags &= ~(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE);
+		dir_flags |= (desc->flags & GPIOD_ACTIVE_LOW);
+		if (value)
+			dir_flags |= GPIOD_IS_OUT_ACTIVE;
+	} else {
+		dir_flags = desc->flags;
+		/* only GPIOD_IS_OUT_ACTIVE is provided by uclass */
+		dir_flags &= ~GPIOD_IS_OUT_ACTIVE;
+		if ((desc->flags & GPIOD_IS_OUT) && _gpio_get_value(desc))
+			dir_flags |= GPIOD_IS_OUT_ACTIVE;
+	}
+	*flags = dir_flags;
+
+	return 0;
 }
 
 /**
@@ -809,7 +914,7 @@  static int gpio_request_tail(int ret, const char *nodename,
 		debug("%s: dm_gpio_requestf failed\n", __func__);
 		goto err;
 	}
-	ret = dm_gpio_set_dir_flags(desc, flags | desc->flags);
+	ret = dm_gpio_set_dir_flags(desc, flags);
 	if (ret) {
 		debug("%s: dm_gpio_set_dir failed\n", __func__);
 		goto err;
@@ -1036,6 +1141,10 @@  static int gpio_post_bind(struct udevice *dev)
 			ops->get_function += gd->reloc_off;
 		if (ops->xlate)
 			ops->xlate += gd->reloc_off;
+		if (ops->set_dir_flags)
+			ops->set_dir_flags += gd->reloc_off;
+		if (ops->get_dir_flags)
+			ops->get_dir_flags += gd->reloc_off;
 
 		reloc_done++;
 	}
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 454578c8d2..c6991be1c9 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -117,10 +117,15 @@  struct udevice;
 struct gpio_desc {
 	struct udevice *dev;	/* Device, NULL for invalid GPIO */
 	unsigned long flags;
+
 #define GPIOD_IS_OUT		BIT(1)	/* GPIO is an output */
 #define GPIOD_IS_IN		BIT(2)	/* GPIO is an input */
 #define GPIOD_ACTIVE_LOW	BIT(3)	/* value has active low */
 #define GPIOD_IS_OUT_ACTIVE	BIT(4)	/* set output active */
+#define GPIOD_OPEN_DRAIN	BIT(5)	/* GPIO is open drain type */
+#define GPIOD_OPEN_SOURCE	BIT(6)	/* GPIO is open source type */
+#define GPIOD_PULL_UP		BIT(7)	/* GPIO has pull up enabled */
+#define GPIOD_PULL_DOWN		BIT(8)	/* GPIO has pull down enabled */
 
 	uint offset;		/* GPIO offset within the device */
 	/*
@@ -129,6 +134,12 @@  struct gpio_desc {
 	 */
 };
 
+/* helper to compute the value of the gpio output */
+#define GPIOD_FLAGS_OUTPUT_MASK (GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE)
+#define GPIOD_FLAGS_OUTPUT(flags) \
+	(((((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_IS_OUT_ACTIVE) || \
+	  (((flags) & GPIOD_FLAGS_OUTPUT_MASK) == GPIOD_ACTIVE_LOW)) ? 1 : 0)
+
 /**
  * dm_gpio_is_valid() - Check if a GPIO is valid
  *
@@ -253,6 +264,7 @@  struct dm_gpio_ops {
 				int value);
 	int (*get_value)(struct udevice *dev, unsigned offset);
 	int (*set_value)(struct udevice *dev, unsigned offset, int value);
+
 	/**
 	 * get_function() Get the GPIO function
 	 *
@@ -287,6 +299,37 @@  struct dm_gpio_ops {
 	 */
 	int (*xlate)(struct udevice *dev, struct gpio_desc *desc,
 		     struct ofnode_phandle_args *args);
+
+	/**
+	 * set_dir_flags() - Set GPIO dir flags
+	 *
+	 * This function should set up the GPIO configuration according to the
+	 * information provide by the direction flags bitfield.
+	 *
+	 * This method is optional.
+	 *
+	 * @dev:	GPIO device
+	 * @offset:	GPIO offset within that device
+	 * @flags:	GPIO configuration to use
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*set_dir_flags)(struct udevice *dev, unsigned int offset,
+			     ulong flags);
+
+	/**
+	 * get_dir_flags() - Get GPIO dir flags
+	 *
+	 * This function return the GPIO direction flags used.
+	 *
+	 * This method is optional.
+	 *
+	 * @dev:	GPIO device
+	 * @offset:	GPIO offset within that device
+	 * @flags:	place to put the used direction flags by GPIO
+	 * @return 0 if OK, -ve on error
+	 */
+	int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
+			     ulong *flags);
 };
 
 /**
@@ -586,8 +629,7 @@  int dm_gpio_set_value(const struct gpio_desc *desc, int value);
 /**
  * dm_gpio_set_dir() - Set the direction for a GPIO
  *
- * This sets up the direction according tot the provided flags. It will do
- * nothing unless the direction is actually specified.
+ * This sets up the direction according to the GPIO flags: desc->flags.
  *
  * @desc:	GPIO description containing device, offset and flags,
  *		previously returned by gpio_request_by_name()
@@ -596,11 +638,10 @@  int dm_gpio_set_value(const struct gpio_desc *desc, int value);
 int dm_gpio_set_dir(struct gpio_desc *desc);
 
 /**
- * dm_gpio_set_dir_flags() - Set direction using specific flags
+ * dm_gpio_set_dir_flags() - Set direction using add flags
  *
- * This is like dm_gpio_set_dir() except that the flags value is provided
- * instead of being used from desc->flags. This is needed because in many
- * cases the GPIO description does not include direction information.
+ * This sets up the direction according tot the provided flags and the GPIO
+ * description (desc->flags) which include direction information.
  * Note that desc->flags is updated by this function.
  *
  * @desc:	GPIO description containing device, offset and flags,
@@ -610,6 +651,18 @@  int dm_gpio_set_dir(struct gpio_desc *desc);
  */
 int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags);
 
+/**
+ * dm_gpio_get_dir_flags() - Get direction flags
+ *
+ * read the current direction flags
+ *
+ * @desc:	GPIO description containing device, offset and flags,
+ *		previously returned by gpio_request_by_name()
+ * @flags:	place to put the used flags
+ * @return 0 if OK, -ve on error, in which case desc->flags is not updated
+ */
+int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
+
 /**
  * gpio_get_number() - Get the global GPIO number of a GPIO
  *