aboutsummaryrefslogtreecommitdiff
path: root/drivers/sysinfo/gpio.c
blob: 1d7f050998afa589f0db4f855202a16311927f63 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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,
};