diff mbox series

[4/5] sysinfo: Add gpio-sysinfo driver

Message ID 20210301204603.2730666-5-sean.anderson@seco.com
State Superseded
Delegated to: Tom Rini
Headers show
Series sysinfo: Add gpio sysinfo driver | expand

Commit Message

Sean Anderson March 1, 2021, 8:46 p.m. UTC
This uses the newly-added dm_gpio_get_values_as_int_base3 function to
implement a sysinfo device. The revision map is stored in the device tree.

Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---

 .../sysinfo/gpio-sysinfo.txt                  |  37 +++++
 drivers/sysinfo/Kconfig                       |   8 +
 drivers/sysinfo/Makefile                      |   1 +
 drivers/sysinfo/gpio.c                        | 138 ++++++++++++++++++
 4 files changed, 184 insertions(+)
 create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
 create mode 100644 drivers/sysinfo/gpio.c

Comments

Sean Anderson March 1, 2021, 9:07 p.m. UTC | #1
On 3/1/21 3:46 PM, Sean Anderson wrote:
> This uses the newly-added dm_gpio_get_values_as_int_base3 function to
> implement a sysinfo device. The revision map is stored in the device tree.
> 
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> 
>   .../sysinfo/gpio-sysinfo.txt                  |  37 +++++
>   drivers/sysinfo/Kconfig                       |   8 +
>   drivers/sysinfo/Makefile                      |   1 +
>   drivers/sysinfo/gpio.c                        | 138 ++++++++++++++++++
>   4 files changed, 184 insertions(+)
>   create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
>   create mode 100644 drivers/sysinfo/gpio.c
> 
> diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> new file mode 100644
> index 0000000000..b5739d94e9
> --- /dev/null
> +++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> @@ -0,0 +1,37 @@
> +GPIO-based Sysinfo device
> +
> +This binding describes several GPIOs which specify a board revision. Each GPIO
> +forms a digit in a ternary revision number. This revision is then mapped to a
> +name using the revisions and names properties.
> +
> +Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1,
> +and 0, respectively. The first GPIO forms the least-significant digit of the
> +revision. For example, consider the property
> +
> +	gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>;
> +
> +If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the
> +revision would be
> +
> +	0t201 = 2*9 + 0*3 + 1*3 = 19
> +
> +If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down,
> +then the revision would be
> +
> +	0t012 = 0*9 + 1*3 + 2*1 = 5
> +
> +Required properties:
> +- compatible: should be "gpio-sysinfo".
> +- gpios: should be a list of gpios forming the revision number,
> +  least-significant-digit first
> +- revisions: a list of known revisions; any revisions not present will have the
> +  name "unknown"
> +- names: the name of each revision in revisions
> +
> +Example:
> +sysinfo {
> +	compatible = "gpio-sysinfo";
> +	gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>;
> +	revisions = <19>, <5>;
> +	names = "rev_a", "foo";
> +};
> diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
> index 85c1e81e41..381dcd8844 100644
> --- a/drivers/sysinfo/Kconfig
> +++ b/drivers/sysinfo/Kconfig
> @@ -30,4 +30,12 @@ config SYSINFO_SMBIOS
>   	  one which provides a way to specify this SMBIOS information in the
>   	  devicetree, without needing any board-specific functionality.
>   
> +config SYSINFO_GPIO
> +	bool "Enable gpio sysinfo driver"
> +	help
> +	  Support querying gpios to determine board revision. This uses gpios to
> +	  form a ternary number (when they are pulled-up, -down, or floating).
> +	  This ternary number is then mapped to a board revision name using
> +	  device tree properties.
> +
>   endif
> diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
> index 6d04fcba1d..d9f708b7ea 100644
> --- a/drivers/sysinfo/Makefile
> +++ b/drivers/sysinfo/Makefile
> @@ -4,5 +4,6 @@
>   # Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
>   obj-y += sysinfo-uclass.o
>   obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
> +obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
>   obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
>   obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
> diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c
> new file mode 100644
> index 0000000000..6a0eff3ec9
> --- /dev/null
> +++ b/drivers/sysinfo/gpio.c
> @@ -0,0 +1,138 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <dm/device_compat.h>
> +#include <log.h>
> +#include <sysinfo.h>
> +#include <asm/gpio.h>
> +
> +struct sysinfo_gpio_priv {
> +	struct gpio_desc *gpios;
> +	int gpio_num, revision;
> +};
> +
> +static int sysinfo_gpio_detect(struct udevice *dev)
> +{
> +	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> +
> +	priv->revision = dm_gpio_get_values_as_int_base3(priv->gpios,
> +							 priv->gpio_num);
> +	return priv->revision < 0 ? priv->revision : 0;
> +}
> +
> +static int sysinfo_gpio_get_int(struct udevice *dev, int id, int *val)
> +{
> +	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> +
> +	switch (id) {
> +	case SYSINFO_ID_REVISION:
> +		if (priv->revision < 0) {

Looks like I forgot to remove this brace. Will be fixed in v2.

--Sean

> +			return priv->revision;
> +		*val = priv->revision;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	};
> +}
> +
> +static int sysinfo_gpio_get_str(struct udevice *dev, int id, size_t size, char *val)
> +{
> +	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> +
> +	switch (id) {
> +	case SYSINFO_ID_REVISION: {
> +		const char *name = NULL;
> +		int i, ret;
> +		u32 revision;
> +
> +		if (priv->revision < 0)
> +			return priv->revision;
> +
> +		for (i = 0; i < priv->gpio_num; i++) {
> +			ret = dev_read_u32_index(dev, "revisions", i,
> +						 &revision);
> +			if (ret) {
> +				if (ret != -EOVERFLOW)
> +					return ret;
> +				break;
> +			}
> +
> +			if (revision == priv->revision) {
> +				ret = dev_read_string_index(dev, "names", i,
> +							    &name);
> +				if (ret < 0)
> +					return ret;
> +				break;
> +			}
> +		}
> +		if (!name)
> +			name = "unknown";
> +
> +		strncpy(val, name, size);
> +		val[size - 1] = '\0';
> +		return 0;
> +	} default:
> +		return -EINVAL;
> +	};
> +}
> +
> +static const struct sysinfo_ops sysinfo_gpio_ops = {
> +	.detect = sysinfo_gpio_detect,
> +	.get_int = sysinfo_gpio_get_int,
> +	.get_str = sysinfo_gpio_get_str,
> +};
> +
> +static int sysinfo_gpio_probe(struct udevice *dev)
> +{
> +	int ret;
> +	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> +
> +	priv->gpio_num = gpio_get_list_count(dev, "gpios");
> +	if (priv->gpio_num < 0) {
> +		dev_err(dev, "could not get gpios length (err = %d)\n",
> +			priv->gpio_num);
> +		return priv->gpio_num;
> +	}
> +
> +	priv->gpios = calloc(priv->gpio_num, sizeof(*priv->gpios));
> +	if (!priv->gpios) {
> +		dev_err(dev, "could not allocate memory for %d gpios\n",
> +			priv->gpio_num);
> +		return -ENOMEM;
> +	}
> +
> +	ret = gpio_request_list_by_name(dev, "gpios", priv->gpios,
> +					priv->gpio_num, GPIOD_IS_IN);
> +	if (ret != priv->gpio_num) {
> +		dev_err(dev, "could not get gpios (err = %d)\n",
> +			priv->gpio_num);
> +		return ret;
> +	}
> +
> +	if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) {
> +		dev_err(dev, "revisions or names properties missing\n");
> +		return -ENOENT;
> +	}
> +
> +	priv->revision = -ENOENT;
> +
> +	return 0;
> +}
> +
> +static const struct udevice_id sysinfo_gpio_ids[] = {
> +	{ .compatible = "gpio-sysinfo" },
> +	{ /* sentinel */ }
> +};
> +
> +U_BOOT_DRIVER(sysinfo_gpio) = {
> +	.name           = "sysinfo_gpio",
> +	.id             = UCLASS_SYSINFO,
> +	.of_match       = sysinfo_gpio_ids,
> +	.ops		= &sysinfo_gpio_ops,
> +	.priv_auto	= sizeof(struct sysinfo_gpio_priv),
> +	.probe          = sysinfo_gpio_probe,
> +};
>
Simon Glass March 5, 2021, 4:08 a.m. UTC | #2
Hi Sean,

On Mon, 1 Mar 2021 at 16:08, Sean Anderson <sean.anderson@seco.com> wrote:
>
>
>
> On 3/1/21 3:46 PM, Sean Anderson wrote:
> > This uses the newly-added dm_gpio_get_values_as_int_base3 function to
> > implement a sysinfo device. The revision map is stored in the device tree.
> >
> > Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> > ---
> >
> >   .../sysinfo/gpio-sysinfo.txt                  |  37 +++++
> >   drivers/sysinfo/Kconfig                       |   8 +
> >   drivers/sysinfo/Makefile                      |   1 +
> >   drivers/sysinfo/gpio.c                        | 138 ++++++++++++++++++
> >   4 files changed, 184 insertions(+)
> >   create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> >   create mode 100644 drivers/sysinfo/gpio.c

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

Please see below

> >
> > diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> > new file mode 100644
> > index 0000000000..b5739d94e9
> > --- /dev/null
> > +++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> > @@ -0,0 +1,37 @@
> > +GPIO-based Sysinfo device
> > +
> > +This binding describes several GPIOs which specify a board revision. Each GPIO
> > +forms a digit in a ternary revision number. This revision is then mapped to a
> > +name using the revisions and names properties.
> > +
> > +Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1,
> > +and 0, respectively. The first GPIO forms the least-significant digit of the
> > +revision. For example, consider the property
> > +
> > +     gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>;
> > +
> > +If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the
> > +revision would be
> > +
> > +     0t201 = 2*9 + 0*3 + 1*3 = 19
> > +
> > +If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down,
> > +then the revision would be
> > +
> > +     0t012 = 0*9 + 1*3 + 2*1 = 5
> > +
> > +Required properties:
> > +- compatible: should be "gpio-sysinfo".
> > +- gpios: should be a list of gpios forming the revision number,
> > +  least-significant-digit first
> > +- revisions: a list of known revisions; any revisions not present will have the
> > +  name "unknown"
> > +- names: the name of each revision in revisions
> > +
> > +Example:
> > +sysinfo {
> > +     compatible = "gpio-sysinfo";
> > +     gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>;
> > +     revisions = <19>, <5>;
> > +     names = "rev_a", "foo";
> > +};
> > diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
> > index 85c1e81e41..381dcd8844 100644
> > --- a/drivers/sysinfo/Kconfig
> > +++ b/drivers/sysinfo/Kconfig
> > @@ -30,4 +30,12 @@ config SYSINFO_SMBIOS
> >         one which provides a way to specify this SMBIOS information in the
> >         devicetree, without needing any board-specific functionality.
> >
> > +config SYSINFO_GPIO
> > +     bool "Enable gpio sysinfo driver"

depends on DM_GPIO ?

> > +     help
> > +       Support querying gpios to determine board revision. This uses gpios to
> > +       form a ternary number (when they are pulled-up, -down, or floating).
> > +       This ternary number is then mapped to a board revision name using
> > +       device tree properties.
> > +
> >   endif
> > diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
> > index 6d04fcba1d..d9f708b7ea 100644
> > --- a/drivers/sysinfo/Makefile
> > +++ b/drivers/sysinfo/Makefile
> > @@ -4,5 +4,6 @@
> >   # Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
> >   obj-y += sysinfo-uclass.o
> >   obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
> > +obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
> >   obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
> >   obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
> > diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c
> > new file mode 100644
> > index 0000000000..6a0eff3ec9
> > --- /dev/null
> > +++ b/drivers/sysinfo/gpio.c
> > @@ -0,0 +1,138 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
> > + */
> > +
> > +#include <common.h>
> > +#include <dm.h>
> > +#include <dm/device_compat.h>

should be at end

> > +#include <log.h>
> > +#include <sysinfo.h>
> > +#include <asm/gpio.h>
> > +
> > +struct sysinfo_gpio_priv {

needs comment

> > +     struct gpio_desc *gpios;
> > +     int gpio_num, revision;
> > +};
> > +
> > +static int sysinfo_gpio_detect(struct udevice *dev)
> > +{
> > +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> > +
> > +     priv->revision = dm_gpio_get_values_as_int_base3(priv->gpios,
> > +                                                      priv->gpio_num);
> > +     return priv->revision < 0 ? priv->revision : 0;
> > +}
> > +
> > +static int sysinfo_gpio_get_int(struct udevice *dev, int id, int *val)
> > +{
> > +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> > +
> > +     switch (id) {
> > +     case SYSINFO_ID_REVISION:
> > +             if (priv->revision < 0) {
>
> Looks like I forgot to remove this brace. Will be fixed in v2.
>
> --Sean
>
> > +                     return priv->revision;
> > +             *val = priv->revision;
> > +             return 0;
> > +     default:
> > +             return -EINVAL;
> > +     };
> > +}
> > +
> > +static int sysinfo_gpio_get_str(struct udevice *dev, int id, size_t size, char *val)
> > +{
> > +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> > +
> > +     switch (id) {
> > +     case SYSINFO_ID_REVISION: {
> > +             const char *name = NULL;
> > +             int i, ret;
> > +             u32 revision;
> > +
> > +             if (priv->revision < 0)
> > +                     return priv->revision;
> > +
> > +             for (i = 0; i < priv->gpio_num; i++) {
> > +                     ret = dev_read_u32_index(dev, "revisions", i,
> > +                                              &revision);
> > +                     if (ret) {
> > +                             if (ret != -EOVERFLOW)
> > +                                     return ret;
> > +                             break;
> > +                     }
> > +
> > +                     if (revision == priv->revision) {
> > +                             ret = dev_read_string_index(dev, "names", i,
> > +                                                         &name);
> > +                             if (ret < 0)
> > +                                     return ret;
> > +                             break;
> > +                     }
> > +             }
> > +             if (!name)
> > +                     name = "unknown";
> > +
> > +             strncpy(val, name, size);
> > +             val[size - 1] = '\0';
> > +             return 0;
> > +     } default:
> > +             return -EINVAL;
> > +     };
> > +}
> > +
> > +static const struct sysinfo_ops sysinfo_gpio_ops = {
> > +     .detect = sysinfo_gpio_detect,
> > +     .get_int = sysinfo_gpio_get_int,
> > +     .get_str = sysinfo_gpio_get_str,
> > +};
> > +
> > +static int sysinfo_gpio_probe(struct udevice *dev)
> > +{
> > +     int ret;
> > +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
> > +
> > +     priv->gpio_num = gpio_get_list_count(dev, "gpios");
> > +     if (priv->gpio_num < 0) {
> > +             dev_err(dev, "could not get gpios length (err = %d)\n",
> > +                     priv->gpio_num);
> > +             return priv->gpio_num;
> > +     }
> > +
> > +     priv->gpios = calloc(priv->gpio_num, sizeof(*priv->gpios));
> > +     if (!priv->gpios) {
> > +             dev_err(dev, "could not allocate memory for %d gpios\n",
> > +                     priv->gpio_num);
> > +             return -ENOMEM;
> > +     }
> > +
> > +     ret = gpio_request_list_by_name(dev, "gpios", priv->gpios,
> > +                                     priv->gpio_num, GPIOD_IS_IN);
> > +     if (ret != priv->gpio_num) {
> > +             dev_err(dev, "could not get gpios (err = %d)\n",
> > +                     priv->gpio_num);
> > +             return ret;
> > +     }
> > +
> > +     if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) {

I think this is a bit grubby since they are not bool properties...can
you use dev_read_prop() instead?

> > +             dev_err(dev, "revisions or names properties missing\n");
> > +             return -ENOENT;
> > +     }
> > +
> > +     priv->revision = -ENOENT;
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct udevice_id sysinfo_gpio_ids[] = {
> > +     { .compatible = "gpio-sysinfo" },
> > +     { /* sentinel */ }
> > +};
> > +
> > +U_BOOT_DRIVER(sysinfo_gpio) = {
> > +     .name           = "sysinfo_gpio",
> > +     .id             = UCLASS_SYSINFO,
> > +     .of_match       = sysinfo_gpio_ids,
> > +     .ops            = &sysinfo_gpio_ops,
> > +     .priv_auto      = sizeof(struct sysinfo_gpio_priv),
> > +     .probe          = sysinfo_gpio_probe,
> > +};
> >

Regards,
Simon
Sean Anderson March 5, 2021, 3:19 p.m. UTC | #3
On 3/4/21 11:08 PM, Simon Glass wrote:
 > Hi Sean,
 >
 > On Mon, 1 Mar 2021 at 16:08, Sean Anderson <sean.anderson@seco.com> wrote:
 >>
 >>
 >>
 >> On 3/1/21 3:46 PM, Sean Anderson wrote:
 >>> This uses the newly-added dm_gpio_get_values_as_int_base3 function to
 >>> implement a sysinfo device. The revision map is stored in the device tree.
 >>>
 >>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
 >>> ---
 >>>
 >>>    .../sysinfo/gpio-sysinfo.txt                  |  37 +++++
 >>>    drivers/sysinfo/Kconfig                       |   8 +
 >>>    drivers/sysinfo/Makefile                      |   1 +
 >>>    drivers/sysinfo/gpio.c                        | 138 ++++++++++++++++++
 >>>    4 files changed, 184 insertions(+)
 >>>    create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
 >>>    create mode 100644 drivers/sysinfo/gpio.c
 >
 > Reviewed-by: Simon Glass <sjg@chromium.org>
 >
 > Please see below
 >
 >>>
 >>> diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
 >>> new file mode 100644
 >>> index 0000000000..b5739d94e9
 >>> --- /dev/null
 >>> +++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
 >>> @@ -0,0 +1,37 @@
 >>> +GPIO-based Sysinfo device
 >>> +
 >>> +This binding describes several GPIOs which specify a board revision. Each GPIO
 >>> +forms a digit in a ternary revision number. This revision is then mapped to a
 >>> +name using the revisions and names properties.
 >>> +
 >>> +Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1,
 >>> +and 0, respectively. The first GPIO forms the least-significant digit of the
 >>> +revision. For example, consider the property
 >>> +
 >>> +     gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>;
 >>> +
 >>> +If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the
 >>> +revision would be
 >>> +
 >>> +     0t201 = 2*9 + 0*3 + 1*3 = 19
 >>> +
 >>> +If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down,
 >>> +then the revision would be
 >>> +
 >>> +     0t012 = 0*9 + 1*3 + 2*1 = 5
 >>> +
 >>> +Required properties:
 >>> +- compatible: should be "gpio-sysinfo".
 >>> +- gpios: should be a list of gpios forming the revision number,
 >>> +  least-significant-digit first
 >>> +- revisions: a list of known revisions; any revisions not present will have the
 >>> +  name "unknown"
 >>> +- names: the name of each revision in revisions
 >>> +
 >>> +Example:
 >>> +sysinfo {
 >>> +     compatible = "gpio-sysinfo";
 >>> +     gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>;
 >>> +     revisions = <19>, <5>;
 >>> +     names = "rev_a", "foo";
 >>> +};
 >>> diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
 >>> index 85c1e81e41..381dcd8844 100644
 >>> --- a/drivers/sysinfo/Kconfig
 >>> +++ b/drivers/sysinfo/Kconfig
 >>> @@ -30,4 +30,12 @@ config SYSINFO_SMBIOS
 >>>          one which provides a way to specify this SMBIOS information in the
 >>>          devicetree, without needing any board-specific functionality.
 >>>
 >>> +config SYSINFO_GPIO
 >>> +     bool "Enable gpio sysinfo driver"
 >
 > depends on DM_GPIO ?
 >
 >>> +     help
 >>> +       Support querying gpios to determine board revision. This uses gpios to
 >>> +       form a ternary number (when they are pulled-up, -down, or floating).
 >>> +       This ternary number is then mapped to a board revision name using
 >>> +       device tree properties.
 >>> +
 >>>    endif
 >>> diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
 >>> index 6d04fcba1d..d9f708b7ea 100644
 >>> --- a/drivers/sysinfo/Makefile
 >>> +++ b/drivers/sysinfo/Makefile
 >>> @@ -4,5 +4,6 @@
 >>>    # Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
 >>>    obj-y += sysinfo-uclass.o
 >>>    obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
 >>> +obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
 >>>    obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
 >>>    obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
 >>> diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c
 >>> new file mode 100644
 >>> index 0000000000..6a0eff3ec9
 >>> --- /dev/null
 >>> +++ b/drivers/sysinfo/gpio.c
 >>> @@ -0,0 +1,138 @@
 >>> +// SPDX-License-Identifier: GPL-2.0+
 >>> +/*
 >>> + * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
 >>> + */
 >>> +
 >>> +#include <common.h>
 >>> +#include <dm.h>
 >>> +#include <dm/device_compat.h>
 >
 > should be at end

Can you clarify the ordering rules? I was following [1] which perscribes

	<common.h>
	<others.h>
	<asm/...>
	<arch/arm/...>
	<linux/...>
	"local.h"

[1] https://www.denx.de/wiki/U-Boot/CodingStyle#Include_files

 >
 >>> +#include <log.h>
 >>> +#include <sysinfo.h>
 >>> +#include <asm/gpio.h>
 >>> +
 >>> +struct sysinfo_gpio_priv {
 >
 > needs comment
 >
 >>> +     struct gpio_desc *gpios;
 >>> +     int gpio_num, revision;
 >>> +};
 >>> +
 >>> +static int sysinfo_gpio_detect(struct udevice *dev)
 >>> +{
 >>> +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
 >>> +
 >>> +     priv->revision = dm_gpio_get_values_as_int_base3(priv->gpios,
 >>> +                                                      priv->gpio_num);
 >>> +     return priv->revision < 0 ? priv->revision : 0;
 >>> +}
 >>> +
 >>> +static int sysinfo_gpio_get_int(struct udevice *dev, int id, int *val)
 >>> +{
 >>> +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
 >>> +
 >>> +     switch (id) {
 >>> +     case SYSINFO_ID_REVISION:
 >>> +             if (priv->revision < 0) {
 >>
 >> Looks like I forgot to remove this brace. Will be fixed in v2.
 >>
 >> --Sean
 >>
 >>> +                     return priv->revision;
 >>> +             *val = priv->revision;
 >>> +             return 0;
 >>> +     default:
 >>> +             return -EINVAL;
 >>> +     };
 >>> +}
 >>> +
 >>> +static int sysinfo_gpio_get_str(struct udevice *dev, int id, size_t size, char *val)
 >>> +{
 >>> +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
 >>> +
 >>> +     switch (id) {
 >>> +     case SYSINFO_ID_REVISION: {
 >>> +             const char *name = NULL;
 >>> +             int i, ret;
 >>> +             u32 revision;
 >>> +
 >>> +             if (priv->revision < 0)
 >>> +                     return priv->revision;
 >>> +
 >>> +             for (i = 0; i < priv->gpio_num; i++) {
 >>> +                     ret = dev_read_u32_index(dev, "revisions", i,
 >>> +                                              &revision);
 >>> +                     if (ret) {
 >>> +                             if (ret != -EOVERFLOW)
 >>> +                                     return ret;
 >>> +                             break;
 >>> +                     }
 >>> +
 >>> +                     if (revision == priv->revision) {
 >>> +                             ret = dev_read_string_index(dev, "names", i,
 >>> +                                                         &name);
 >>> +                             if (ret < 0)
 >>> +                                     return ret;
 >>> +                             break;
 >>> +                     }
 >>> +             }
 >>> +             if (!name)
 >>> +                     name = "unknown";
 >>> +
 >>> +             strncpy(val, name, size);
 >>> +             val[size - 1] = '\0';
 >>> +             return 0;
 >>> +     } default:
 >>> +             return -EINVAL;
 >>> +     };
 >>> +}
 >>> +
 >>> +static const struct sysinfo_ops sysinfo_gpio_ops = {
 >>> +     .detect = sysinfo_gpio_detect,
 >>> +     .get_int = sysinfo_gpio_get_int,
 >>> +     .get_str = sysinfo_gpio_get_str,
 >>> +};
 >>> +
 >>> +static int sysinfo_gpio_probe(struct udevice *dev)
 >>> +{
 >>> +     int ret;
 >>> +     struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
 >>> +
 >>> +     priv->gpio_num = gpio_get_list_count(dev, "gpios");
 >>> +     if (priv->gpio_num < 0) {
 >>> +             dev_err(dev, "could not get gpios length (err = %d)\n",
 >>> +                     priv->gpio_num);
 >>> +             return priv->gpio_num;
 >>> +     }
 >>> +
 >>> +     priv->gpios = calloc(priv->gpio_num, sizeof(*priv->gpios));
 >>> +     if (!priv->gpios) {
 >>> +             dev_err(dev, "could not allocate memory for %d gpios\n",
 >>> +                     prhttps://www.denx.de/wiki/U-Boot/CodingStyleiv->gpio_num);
 >>> +             return -ENOMEM;
 >>> +     }
 >>> +
 >>> +     ret = gpio_request_list_by_name(dev, "gpios", priv->gpios,
 >>> +                                     priv->gpio_num, GPIOD_IS_IN);
 >>> +     if (ret != priv->gpio_num) {
 >>> +             dev_err(dev, "could not get gpios (err = %d)\n",
 >>> +                     priv->gpio_num);
 >>> +             return ret;
 >>> +     }
 >>> +
 >>> +     if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) {
 >
 > I think this is a bit grubby since they are not bool properties...can
 > you use dev_read_prop() instead?

I suppose. I think this is cleaner, since dev_read_bool is really an
alternatively-named dev_prop_exists.

--Sean

 >>> +             dev_err(dev, "revisions or names properties missing\n");
 >>> +             return -ENOENT;
 >>> +     }
 >>> +
 >>> +     priv->revision = -ENOENT;
 >>> +
 >>> +     return 0;
 >>> +}
 >>> +
 >>> +static const struct udevice_id sysinfo_gpio_ids[] = {
 >>> +     { .compatible = "gpio-sysinfo" },
 >>> +     { /* sentinel */ }
 >>> +};
 >>> +
 >>> +U_BOOT_DRIVER(sysinfo_gpio) = {
 >>> +     .name           = "sysinfo_gpio",
 >>> +     .id             = UCLASS_SYSINFO,
 >>> +     .of_match       = sysinfo_gpio_ids,
 >>> +     .ops            = &sysinfo_gpio_ops,
 >>> +     .priv_auto      = sizeof(struct sysinfo_gpio_priv),
 >>> +     .probe          = sysinfo_gpio_probe,
 >>> +};
 >>>
 >
 > Regards,
 > Simon
 >
Simon Glass March 5, 2021, 4:39 p.m. UTC | #4
Hi Sean,

On Fri, 5 Mar 2021 at 08:19, Sean Anderson <sean.anderson@seco.com> wrote:
>
>
>
> On 3/4/21 11:08 PM, Simon Glass wrote:
>  > Hi Sean,
>  >
>  > On Mon, 1 Mar 2021 at 16:08, Sean Anderson <sean.anderson@seco.com> wrote:
>  >>
>  >>
>  >>
>  >> On 3/1/21 3:46 PM, Sean Anderson wrote:
>  >>> This uses the newly-added dm_gpio_get_values_as_int_base3 function to
>  >>> implement a sysinfo device. The revision map is stored in the device tree.
>  >>>
>  >>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>  >>> ---
>  >>>
>  >>>    .../sysinfo/gpio-sysinfo.txt                  |  37 +++++
>  >>>    drivers/sysinfo/Kconfig                       |   8 +
>  >>>    drivers/sysinfo/Makefile                      |   1 +
>  >>>    drivers/sysinfo/gpio.c                        | 138 ++++++++++++++++++
>  >>>    4 files changed, 184 insertions(+)
>  >>>    create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
>  >>>    create mode 100644 drivers/sysinfo/gpio.c
>  >
>  > Reviewed-by: Simon Glass <sjg@chromium.org>
>  >
>  > Please see below
>  >
>  >>>
>  >>> diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
>  >>> new file mode 100644
>  >>> index 0000000000..b5739d94e9
>  >>> --- /dev/null
>  >>> +++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
>  >>> @@ -0,0 +1,37 @@
>  >>> +GPIO-based Sysinfo device
>  >>> +
>  >>> +This binding describes several GPIOs which specify a board revision. Each GPIO
>  >>> +forms a digit in a ternary revision number. This revision is then mapped to a
>  >>> +name using the revisions and names properties.
>  >>> +
>  >>> +Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1,
>  >>> +and 0, respectively. The first GPIO forms the least-significant digit of the
>  >>> +revision. For example, consider the property
>  >>> +
>  >>> +     gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>;
>  >>> +
>  >>> +If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the
>  >>> +revision would be
>  >>> +
>  >>> +     0t201 = 2*9 + 0*3 + 1*3 = 19
>  >>> +
>  >>> +If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down,
>  >>> +then the revision would be
>  >>> +
>  >>> +     0t012 = 0*9 + 1*3 + 2*1 = 5
>  >>> +
>  >>> +Required properties:
>  >>> +- compatible: should be "gpio-sysinfo".
>  >>> +- gpios: should be a list of gpios forming the revision number,
>  >>> +  least-significant-digit first
>  >>> +- revisions: a list of known revisions; any revisions not present will have the
>  >>> +  name "unknown"
>  >>> +- names: the name of each revision in revisions
>  >>> +
>  >>> +Example:
>  >>> +sysinfo {
>  >>> +     compatible = "gpio-sysinfo";
>  >>> +     gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>;
>  >>> +     revisions = <19>, <5>;
>  >>> +     names = "rev_a", "foo";
>  >>> +};
>  >>> diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
>  >>> index 85c1e81e41..381dcd8844 100644
>  >>> --- a/drivers/sysinfo/Kconfig
>  >>> +++ b/drivers/sysinfo/Kconfig
>  >>> @@ -30,4 +30,12 @@ config SYSINFO_SMBIOS
>  >>>          one which provides a way to specify this SMBIOS information in the
>  >>>          devicetree, without needing any board-specific functionality.
>  >>>
>  >>> +config SYSINFO_GPIO
>  >>> +     bool "Enable gpio sysinfo driver"
>  >
>  > depends on DM_GPIO ?
>  >
>  >>> +     help
>  >>> +       Support querying gpios to determine board revision. This uses gpios to
>  >>> +       form a ternary number (when they are pulled-up, -down, or floating).
>  >>> +       This ternary number is then mapped to a board revision name using
>  >>> +       device tree properties.
>  >>> +
>  >>>    endif
>  >>> diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
>  >>> index 6d04fcba1d..d9f708b7ea 100644
>  >>> --- a/drivers/sysinfo/Makefile
>  >>> +++ b/drivers/sysinfo/Makefile
>  >>> @@ -4,5 +4,6 @@
>  >>>    # Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
>  >>>    obj-y += sysinfo-uclass.o
>  >>>    obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
>  >>> +obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
>  >>>    obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
>  >>>    obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
>  >>> diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c
>  >>> new file mode 100644
>  >>> index 0000000000..6a0eff3ec9
>  >>> --- /dev/null
>  >>> +++ b/drivers/sysinfo/gpio.c
>  >>> @@ -0,0 +1,138 @@
>  >>> +// SPDX-License-Identifier: GPL-2.0+
>  >>> +/*
>  >>> + * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
>  >>> + */
>  >>> +
>  >>> +#include <common.h>
>  >>> +#include <dm.h>
>  >>> +#include <dm/device_compat.h>
>  >
>  > should be at end
>
> Can you clarify the ordering rules? I was following [1] which perscribes
>
>         <common.h>
>         <others.h>
>         <asm/...>
>         <arch/arm/...>
>         <linux/...>
>         "local.h"
>
> [1] https://www.denx.de/wiki/U-Boot/CodingStyle#Include_files

Yes that's right, but dm/ is a directory so it goes with the other
dirs at the end. I'll update the page to include dm/
[..]

>  >>> +
>  >>> +     if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) {
>  >
>  > I think this is a bit grubby since they are not bool properties...can
>  > you use dev_read_prop() instead?
>
> I suppose. I think this is cleaner, since dev_read_bool is really an
> alternatively-named dev_prop_exists.

Well, adding that would be a nice step and I agree it would be even better!

Regards,
Simon
Sean Anderson March 5, 2021, 5:24 p.m. UTC | #5
On 3/5/21 11:39 AM, Simon Glass wrote:
> Hi Sean,
> 
> On Fri, 5 Mar 2021 at 08:19, Sean Anderson <sean.anderson@seco.com> wrote:
>>
>>
>>
>> On 3/4/21 11:08 PM, Simon Glass wrote:
>>   > Hi Sean,
>>   >
>>   > On Mon, 1 Mar 2021 at 16:08, Sean Anderson <sean.anderson@seco.com> wrote:
>>   >>
>>   >>
>>   >>
>>   >> On 3/1/21 3:46 PM, Sean Anderson wrote:
>>   >>> This uses the newly-added dm_gpio_get_values_as_int_base3 function to
>>   >>> implement a sysinfo device. The revision map is stored in the device tree.
>>   >>>
>>   >>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
>>   >>> ---
>>   >>>
>>   >>>    .../sysinfo/gpio-sysinfo.txt                  |  37 +++++
>>   >>>    drivers/sysinfo/Kconfig                       |   8 +
>>   >>>    drivers/sysinfo/Makefile                      |   1 +
>>   >>>    drivers/sysinfo/gpio.c                        | 138 ++++++++++++++++++
>>   >>>    4 files changed, 184 insertions(+)
>>   >>>    create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
>>   >>>    create mode 100644 drivers/sysinfo/gpio.c
>>   >
>>   > Reviewed-by: Simon Glass <sjg@chromium.org>
>>   >
>>   > Please see below
>>   >
>>   >>>
>>   >>> diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
>>   >>> new file mode 100644
>>   >>> index 0000000000..b5739d94e9
>>   >>> --- /dev/null
>>   >>> +++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
>>   >>> @@ -0,0 +1,37 @@
>>   >>> +GPIO-based Sysinfo device
>>   >>> +
>>   >>> +This binding describes several GPIOs which specify a board revision. Each GPIO
>>   >>> +forms a digit in a ternary revision number. This revision is then mapped to a
>>   >>> +name using the revisions and names properties.
>>   >>> +
>>   >>> +Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1,
>>   >>> +and 0, respectively. The first GPIO forms the least-significant digit of the
>>   >>> +revision. For example, consider the property
>>   >>> +
>>   >>> +     gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>;
>>   >>> +
>>   >>> +If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the
>>   >>> +revision would be
>>   >>> +
>>   >>> +     0t201 = 2*9 + 0*3 + 1*3 = 19
>>   >>> +
>>   >>> +If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down,
>>   >>> +then the revision would be
>>   >>> +
>>   >>> +     0t012 = 0*9 + 1*3 + 2*1 = 5
>>   >>> +
>>   >>> +Required properties:
>>   >>> +- compatible: should be "gpio-sysinfo".
>>   >>> +- gpios: should be a list of gpios forming the revision number,
>>   >>> +  least-significant-digit first
>>   >>> +- revisions: a list of known revisions; any revisions not present will have the
>>   >>> +  name "unknown"
>>   >>> +- names: the name of each revision in revisions
>>   >>> +
>>   >>> +Example:
>>   >>> +sysinfo {
>>   >>> +     compatible = "gpio-sysinfo";
>>   >>> +     gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>;
>>   >>> +     revisions = <19>, <5>;
>>   >>> +     names = "rev_a", "foo";
>>   >>> +};
>>   >>> diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
>>   >>> index 85c1e81e41..381dcd8844 100644
>>   >>> --- a/drivers/sysinfo/Kconfig
>>   >>> +++ b/drivers/sysinfo/Kconfig
>>   >>> @@ -30,4 +30,12 @@ config SYSINFO_SMBIOS
>>   >>>          one which provides a way to specify this SMBIOS information in the
>>   >>>          devicetree, without needing any board-specific functionality.
>>   >>>
>>   >>> +config SYSINFO_GPIO
>>   >>> +     bool "Enable gpio sysinfo driver"
>>   >
>>   > depends on DM_GPIO ?
>>   >
>>   >>> +     help
>>   >>> +       Support querying gpios to determine board revision. This uses gpios to
>>   >>> +       form a ternary number (when they are pulled-up, -down, or floating).
>>   >>> +       This ternary number is then mapped to a board revision name using
>>   >>> +       device tree properties.
>>   >>> +
>>   >>>    endif
>>   >>> diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
>>   >>> index 6d04fcba1d..d9f708b7ea 100644
>>   >>> --- a/drivers/sysinfo/Makefile
>>   >>> +++ b/drivers/sysinfo/Makefile
>>   >>> @@ -4,5 +4,6 @@
>>   >>>    # Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
>>   >>>    obj-y += sysinfo-uclass.o
>>   >>>    obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
>>   >>> +obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
>>   >>>    obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
>>   >>>    obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
>>   >>> diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c
>>   >>> new file mode 100644
>>   >>> index 0000000000..6a0eff3ec9
>>   >>> --- /dev/null
>>   >>> +++ b/drivers/sysinfo/gpio.c
>>   >>> @@ -0,0 +1,138 @@
>>   >>> +// SPDX-License-Identifier: GPL-2.0+
>>   >>> +/*
>>   >>> + * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
>>   >>> + */
>>   >>> +
>>   >>> +#include <common.h>
>>   >>> +#include <dm.h>
>>   >>> +#include <dm/device_compat.h>
>>   >
>>   > should be at end
>>
>> Can you clarify the ordering rules? I was following [1] which perscribes
>>
>>          <common.h>
>>          <others.h>
>>          <asm/...>
>>          <arch/arm/...>
>>          <linux/...>
>>          "local.h"
>>
>> [1] https://www.denx.de/wiki/U-Boot/CodingStyle#Include_files
> 
> Yes that's right, but dm/ is a directory so it goes with the other
> dirs at the end. I'll update the page to include dm/

Ok, so the general rule should be that directories go last?

--Sean

> [..]
> 
>>   >>> +
>>   >>> +     if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) {
>>   >
>>   > I think this is a bit grubby since they are not bool properties...can
>>   > you use dev_read_prop() instead?
>>
>> I suppose. I think this is cleaner, since dev_read_bool is really an
>> alternatively-named dev_prop_exists.
> 
> Well, adding that would be a nice step and I agree it would be even better!
> 
> Regards,
> Simon
>
Simon Glass March 5, 2021, 7:43 p.m. UTC | #6
Hi Sean,

On Fri, 5 Mar 2021 at 10:24, Sean Anderson <sean.anderson@seco.com> wrote:
>
>
>
> On 3/5/21 11:39 AM, Simon Glass wrote:
> > Hi Sean,
> >
> > On Fri, 5 Mar 2021 at 08:19, Sean Anderson <sean.anderson@seco.com> wrote:
> >>
> >>
> >>
> >> On 3/4/21 11:08 PM, Simon Glass wrote:
> >>   > Hi Sean,
> >>   >
> >>   > On Mon, 1 Mar 2021 at 16:08, Sean Anderson <sean.anderson@seco.com> wrote:
> >>   >>
> >>   >>
> >>   >>
> >>   >> On 3/1/21 3:46 PM, Sean Anderson wrote:
> >>   >>> This uses the newly-added dm_gpio_get_values_as_int_base3 function to
> >>   >>> implement a sysinfo device. The revision map is stored in the device tree.
> >>   >>>
> >>   >>> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> >>   >>> ---
> >>   >>>
> >>   >>>    .../sysinfo/gpio-sysinfo.txt                  |  37 +++++
> >>   >>>    drivers/sysinfo/Kconfig                       |   8 +
> >>   >>>    drivers/sysinfo/Makefile                      |   1 +
> >>   >>>    drivers/sysinfo/gpio.c                        | 138 ++++++++++++++++++
> >>   >>>    4 files changed, 184 insertions(+)
> >>   >>>    create mode 100644 doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> >>   >>>    create mode 100644 drivers/sysinfo/gpio.c
> >>   >
> >>   > Reviewed-by: Simon Glass <sjg@chromium.org>
> >>   >
> >>   > Please see below
> >>   >
> >>   >>>
> >>   >>> diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> >>   >>> new file mode 100644
> >>   >>> index 0000000000..b5739d94e9
> >>   >>> --- /dev/null
> >>   >>> +++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
> >>   >>> @@ -0,0 +1,37 @@
> >>   >>> +GPIO-based Sysinfo device
> >>   >>> +
> >>   >>> +This binding describes several GPIOs which specify a board revision. Each GPIO
> >>   >>> +forms a digit in a ternary revision number. This revision is then mapped to a
> >>   >>> +name using the revisions and names properties.
> >>   >>> +
> >>   >>> +Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1,
> >>   >>> +and 0, respectively. The first GPIO forms the least-significant digit of the
> >>   >>> +revision. For example, consider the property
> >>   >>> +
> >>   >>> +     gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>;
> >>   >>> +
> >>   >>> +If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the
> >>   >>> +revision would be
> >>   >>> +
> >>   >>> +     0t201 = 2*9 + 0*3 + 1*3 = 19
> >>   >>> +
> >>   >>> +If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down,
> >>   >>> +then the revision would be
> >>   >>> +
> >>   >>> +     0t012 = 0*9 + 1*3 + 2*1 = 5
> >>   >>> +
> >>   >>> +Required properties:
> >>   >>> +- compatible: should be "gpio-sysinfo".
> >>   >>> +- gpios: should be a list of gpios forming the revision number,
> >>   >>> +  least-significant-digit first
> >>   >>> +- revisions: a list of known revisions; any revisions not present will have the
> >>   >>> +  name "unknown"
> >>   >>> +- names: the name of each revision in revisions
> >>   >>> +
> >>   >>> +Example:
> >>   >>> +sysinfo {
> >>   >>> +     compatible = "gpio-sysinfo";
> >>   >>> +     gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>;
> >>   >>> +     revisions = <19>, <5>;
> >>   >>> +     names = "rev_a", "foo";
> >>   >>> +};
> >>   >>> diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
> >>   >>> index 85c1e81e41..381dcd8844 100644
> >>   >>> --- a/drivers/sysinfo/Kconfig
> >>   >>> +++ b/drivers/sysinfo/Kconfig
> >>   >>> @@ -30,4 +30,12 @@ config SYSINFO_SMBIOS
> >>   >>>          one which provides a way to specify this SMBIOS information in the
> >>   >>>          devicetree, without needing any board-specific functionality.
> >>   >>>
> >>   >>> +config SYSINFO_GPIO
> >>   >>> +     bool "Enable gpio sysinfo driver"
> >>   >
> >>   > depends on DM_GPIO ?
> >>   >
> >>   >>> +     help
> >>   >>> +       Support querying gpios to determine board revision. This uses gpios to
> >>   >>> +       form a ternary number (when they are pulled-up, -down, or floating).
> >>   >>> +       This ternary number is then mapped to a board revision name using
> >>   >>> +       device tree properties.
> >>   >>> +
> >>   >>>    endif
> >>   >>> diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
> >>   >>> index 6d04fcba1d..d9f708b7ea 100644
> >>   >>> --- a/drivers/sysinfo/Makefile
> >>   >>> +++ b/drivers/sysinfo/Makefile
> >>   >>> @@ -4,5 +4,6 @@
> >>   >>>    # Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
> >>   >>>    obj-y += sysinfo-uclass.o
> >>   >>>    obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
> >>   >>> +obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
> >>   >>>    obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
> >>   >>>    obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
> >>   >>> diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c
> >>   >>> new file mode 100644
> >>   >>> index 0000000000..6a0eff3ec9
> >>   >>> --- /dev/null
> >>   >>> +++ b/drivers/sysinfo/gpio.c
> >>   >>> @@ -0,0 +1,138 @@
> >>   >>> +// SPDX-License-Identifier: GPL-2.0+
> >>   >>> +/*
> >>   >>> + * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
> >>   >>> + */
> >>   >>> +
> >>   >>> +#include <common.h>
> >>   >>> +#include <dm.h>
> >>   >>> +#include <dm/device_compat.h>
> >>   >
> >>   > should be at end
> >>
> >> Can you clarify the ordering rules? I was following [1] which perscribes
> >>
> >>          <common.h>
> >>          <others.h>
> >>          <asm/...>
> >>          <arch/arm/...>
> >>          <linux/...>
> >>          "local.h"
> >>
> >> [1] https://www.denx.de/wiki/U-Boot/CodingStyle#Include_files
> >
> > Yes that's right, but dm/ is a directory so it goes with the other
> > dirs at the end. I'll update the page to include dm/
>
> Ok, so the general rule should be that directories go last?

Yes that's right.

Regards,
SImon
diff mbox series

Patch

diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
new file mode 100644
index 0000000000..b5739d94e9
--- /dev/null
+++ b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
@@ -0,0 +1,37 @@ 
+GPIO-based Sysinfo device
+
+This binding describes several GPIOs which specify a board revision. Each GPIO
+forms a digit in a ternary revision number. This revision is then mapped to a
+name using the revisions and names properties.
+
+Each GPIO may be floating, pulled-up, or pulled-down, mapping to digits 2, 1,
+and 0, respectively. The first GPIO forms the least-significant digit of the
+revision. For example, consider the property
+
+	gpios = <&gpio 0>, <&gpio 1>, <&gpio 2>;
+
+If GPIO 0 is pulled-up, GPIO 1 is pulled-down, and GPIO 2 is floating, then the
+revision would be
+
+	0t201 = 2*9 + 0*3 + 1*3 = 19
+
+If instead GPIO 0 is floating, GPIO 1 is pulled-up, and GPIO 2 is pulled-down,
+then the revision would be
+
+	0t012 = 0*9 + 1*3 + 2*1 = 5
+
+Required properties:
+- compatible: should be "gpio-sysinfo".
+- gpios: should be a list of gpios forming the revision number,
+  least-significant-digit first
+- revisions: a list of known revisions; any revisions not present will have the
+  name "unknown"
+- names: the name of each revision in revisions
+
+Example:
+sysinfo {
+	compatible = "gpio-sysinfo";
+	gpios = <&gpio_a 15>, <&gpio_a 16>, <&gpio_a 17>;
+	revisions = <19>, <5>;
+	names = "rev_a", "foo";
+};
diff --git a/drivers/sysinfo/Kconfig b/drivers/sysinfo/Kconfig
index 85c1e81e41..381dcd8844 100644
--- a/drivers/sysinfo/Kconfig
+++ b/drivers/sysinfo/Kconfig
@@ -30,4 +30,12 @@  config SYSINFO_SMBIOS
 	  one which provides a way to specify this SMBIOS information in the
 	  devicetree, without needing any board-specific functionality.
 
+config SYSINFO_GPIO
+	bool "Enable gpio sysinfo driver"
+	help
+	  Support querying gpios to determine board revision. This uses gpios to
+	  form a ternary number (when they are pulled-up, -down, or floating).
+	  This ternary number is then mapped to a board revision name using
+	  device tree properties.
+
 endif
diff --git a/drivers/sysinfo/Makefile b/drivers/sysinfo/Makefile
index 6d04fcba1d..d9f708b7ea 100644
--- a/drivers/sysinfo/Makefile
+++ b/drivers/sysinfo/Makefile
@@ -4,5 +4,6 @@ 
 # Mario Six,  Guntermann & Drunck GmbH, mario.six@gdsys.cc
 obj-y += sysinfo-uclass.o
 obj-$(CONFIG_SYSINFO_GAZERBEAM) += gazerbeam.o
+obj-$(CONFIG_SYSINFO_GPIO) += gpio.o
 obj-$(CONFIG_SYSINFO_SANDBOX) += sandbox.o
 obj-$(CONFIG_SYSINFO_SMBIOS) += smbios.o
diff --git a/drivers/sysinfo/gpio.c b/drivers/sysinfo/gpio.c
new file mode 100644
index 0000000000..6a0eff3ec9
--- /dev/null
+++ b/drivers/sysinfo/gpio.c
@@ -0,0 +1,138 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <log.h>
+#include <sysinfo.h>
+#include <asm/gpio.h>
+
+struct sysinfo_gpio_priv {
+	struct gpio_desc *gpios;
+	int gpio_num, revision;
+};
+
+static int sysinfo_gpio_detect(struct udevice *dev)
+{
+	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
+
+	priv->revision = dm_gpio_get_values_as_int_base3(priv->gpios,
+							 priv->gpio_num);
+	return priv->revision < 0 ? priv->revision : 0;
+}
+
+static int sysinfo_gpio_get_int(struct udevice *dev, int id, int *val)
+{
+	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
+
+	switch (id) {
+	case SYSINFO_ID_REVISION:
+		if (priv->revision < 0) {
+			return priv->revision;
+		*val = priv->revision;
+		return 0;
+	default:
+		return -EINVAL;
+	};
+}
+
+static int sysinfo_gpio_get_str(struct udevice *dev, int id, size_t size, char *val)
+{
+	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
+
+	switch (id) {
+	case SYSINFO_ID_REVISION: {
+		const char *name = NULL;
+		int i, ret;
+		u32 revision;
+
+		if (priv->revision < 0)
+			return priv->revision;
+
+		for (i = 0; i < priv->gpio_num; i++) {
+			ret = dev_read_u32_index(dev, "revisions", i,
+						 &revision);
+			if (ret) {
+				if (ret != -EOVERFLOW)
+					return ret;
+				break;
+			}
+
+			if (revision == priv->revision) {
+				ret = dev_read_string_index(dev, "names", i,
+							    &name);
+				if (ret < 0)
+					return ret;
+				break;
+			}
+		}
+		if (!name)
+			name = "unknown";
+
+		strncpy(val, name, size);
+		val[size - 1] = '\0';
+		return 0;
+	} default:
+		return -EINVAL;
+	};
+}
+
+static const struct sysinfo_ops sysinfo_gpio_ops = {
+	.detect = sysinfo_gpio_detect,
+	.get_int = sysinfo_gpio_get_int,
+	.get_str = sysinfo_gpio_get_str,
+};
+
+static int sysinfo_gpio_probe(struct udevice *dev)
+{
+	int ret;
+	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
+
+	priv->gpio_num = gpio_get_list_count(dev, "gpios");
+	if (priv->gpio_num < 0) {
+		dev_err(dev, "could not get gpios length (err = %d)\n",
+			priv->gpio_num);
+		return priv->gpio_num;
+	}
+
+	priv->gpios = calloc(priv->gpio_num, sizeof(*priv->gpios));
+	if (!priv->gpios) {
+		dev_err(dev, "could not allocate memory for %d gpios\n",
+			priv->gpio_num);
+		return -ENOMEM;
+	}
+
+	ret = gpio_request_list_by_name(dev, "gpios", priv->gpios,
+					priv->gpio_num, GPIOD_IS_IN);
+	if (ret != priv->gpio_num) {
+		dev_err(dev, "could not get gpios (err = %d)\n",
+			priv->gpio_num);
+		return ret;
+	}
+
+	if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) {
+		dev_err(dev, "revisions or names properties missing\n");
+		return -ENOENT;
+	}
+
+	priv->revision = -ENOENT;
+
+	return 0;
+}
+
+static const struct udevice_id sysinfo_gpio_ids[] = {
+	{ .compatible = "gpio-sysinfo" },
+	{ /* sentinel */ }
+};
+
+U_BOOT_DRIVER(sysinfo_gpio) = {
+	.name           = "sysinfo_gpio",
+	.id             = UCLASS_SYSINFO,
+	.of_match       = sysinfo_gpio_ids,
+	.ops		= &sysinfo_gpio_ops,
+	.priv_auto	= sizeof(struct sysinfo_gpio_priv),
+	.probe          = sysinfo_gpio_probe,
+};