sysinfo: Add gpio-sysinfo driver
authorSean Anderson <sean.anderson@seco.com>
Tue, 20 Apr 2021 14:50:57 +0000 (10:50 -0400)
committerTom Rini <trini@konsulko.com>
Tue, 4 May 2021 11:57:18 +0000 (07:57 -0400)
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>
Reviewed-by: Simon Glass <sjg@chromium.org>
doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt [new file with mode: 0644]
drivers/sysinfo/Kconfig
drivers/sysinfo/Makefile
drivers/sysinfo/gpio.c [new file with mode: 0644]

diff --git a/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt b/doc/device-tree-bindings/sysinfo/gpio-sysinfo.txt
new file mode 100644 (file)
index 0000000..b5739d9
--- /dev/null
@@ -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";
+};
index 85c1e81..381dcd8 100644 (file)
@@ -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
index 6d04fcb..d9f708b 100644 (file)
@@ -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 (file)
index 0000000..1d7f050
--- /dev/null
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <sysinfo.h>
+#include <asm/gpio.h>
+#include <dm/device_compat.h>
+
+/**
+ * struct sysinfo_gpio_priv - GPIO sysinfo private data
+ * @gpios: List of GPIOs used to detect the revision
+ * @gpio_num: The number of GPIOs in @gpios
+ * @revision: The revision as detected from the GPIOs.
+ */
+struct sysinfo_gpio_priv {
+       struct gpio_desc *gpios;
+       int gpio_num, revision;
+};
+
+static int sysinfo_gpio_detect(struct udevice *dev)
+{
+       int ret;
+       struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
+
+       ret = dm_gpio_get_values_as_int_base3(priv->gpios, priv->gpio_num);
+       if (ret < 0)
+               return ret;
+
+       priv->revision = ret;
+       return 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_BOARD_MODEL:
+               *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_BOARD_MODEL: {
+               const char *name = NULL;
+               int i, ret;
+               u32 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;
+       }
+
+       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,
+};