sysinfo: Add gpio-sysinfo driver

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>
This commit is contained in:
Sean Anderson 2021-04-20 10:50:57 -04:00 committed by Tom Rini
parent 4d65c6bcd7
commit 54aa07fdfc
4 changed files with 187 additions and 0 deletions

View File

@ -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";
};

View 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

View 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

141
drivers/sysinfo/gpio.c Normal file
View File

@ -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,
};