1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
4  */
5 
6 #include <dm.h>
7 #include <log.h>
8 #include <sysinfo.h>
9 #include <asm/gpio.h>
10 #include <dm/device_compat.h>
11 
12 /**
13  * struct sysinfo_gpio_priv - GPIO sysinfo private data
14  * @gpios: List of GPIOs used to detect the revision
15  * @gpio_num: The number of GPIOs in @gpios
16  * @revision: The revision as detected from the GPIOs.
17  */
18 struct sysinfo_gpio_priv {
19 	struct gpio_desc *gpios;
20 	int gpio_num, revision;
21 };
22 
sysinfo_gpio_detect(struct udevice * dev)23 static int sysinfo_gpio_detect(struct udevice *dev)
24 {
25 	int ret;
26 	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
27 
28 	ret = dm_gpio_get_values_as_int_base3(priv->gpios, priv->gpio_num);
29 	if (ret < 0)
30 		return ret;
31 
32 	priv->revision = ret;
33 	return 0;
34 }
35 
sysinfo_gpio_get_int(struct udevice * dev,int id,int * val)36 static int sysinfo_gpio_get_int(struct udevice *dev, int id, int *val)
37 {
38 	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
39 
40 	switch (id) {
41 	case SYSID_BOARD_MODEL:
42 		*val = priv->revision;
43 		return 0;
44 	default:
45 		return -EINVAL;
46 	};
47 }
48 
sysinfo_gpio_get_str(struct udevice * dev,int id,size_t size,char * val)49 static int sysinfo_gpio_get_str(struct udevice *dev, int id, size_t size, char *val)
50 {
51 	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
52 
53 	switch (id) {
54 	case SYSID_BOARD_MODEL: {
55 		const char *name = NULL;
56 		int i, ret;
57 		u32 revision;
58 
59 		for (i = 0; ; i++) {
60 			ret = dev_read_u32_index(dev, "revisions", i,
61 						 &revision);
62 			if (ret) {
63 				if (ret != -EOVERFLOW)
64 					return ret;
65 				break;
66 			}
67 
68 			if (revision == priv->revision) {
69 				ret = dev_read_string_index(dev, "names", i,
70 							    &name);
71 				if (ret < 0)
72 					return ret;
73 				break;
74 			}
75 		}
76 		if (!name)
77 			name = "unknown";
78 
79 		strncpy(val, name, size);
80 		val[size - 1] = '\0';
81 		return 0;
82 	}
83 	default:
84 		return -EINVAL;
85 	};
86 }
87 
88 static const struct sysinfo_ops sysinfo_gpio_ops = {
89 	.detect = sysinfo_gpio_detect,
90 	.get_int = sysinfo_gpio_get_int,
91 	.get_str = sysinfo_gpio_get_str,
92 };
93 
sysinfo_gpio_probe(struct udevice * dev)94 static int sysinfo_gpio_probe(struct udevice *dev)
95 {
96 	int ret;
97 	struct sysinfo_gpio_priv *priv = dev_get_priv(dev);
98 
99 	priv->gpio_num = gpio_get_list_count(dev, "gpios");
100 	if (priv->gpio_num < 0) {
101 		dev_err(dev, "could not get gpios length (err = %d)\n",
102 			priv->gpio_num);
103 		return priv->gpio_num;
104 	}
105 
106 	priv->gpios = calloc(priv->gpio_num, sizeof(*priv->gpios));
107 	if (!priv->gpios) {
108 		dev_err(dev, "could not allocate memory for %d gpios\n",
109 			priv->gpio_num);
110 		return -ENOMEM;
111 	}
112 
113 	ret = gpio_request_list_by_name(dev, "gpios", priv->gpios,
114 					priv->gpio_num, GPIOD_IS_IN);
115 	if (ret != priv->gpio_num) {
116 		dev_err(dev, "could not get gpios (err = %d)\n",
117 			priv->gpio_num);
118 		return ret;
119 	}
120 
121 	if (!dev_read_bool(dev, "revisions") || !dev_read_bool(dev, "names")) {
122 		dev_err(dev, "revisions or names properties missing\n");
123 		return -ENOENT;
124 	}
125 
126 	return 0;
127 }
128 
129 static const struct udevice_id sysinfo_gpio_ids[] = {
130 	{ .compatible = "gpio-sysinfo" },
131 	{ /* sentinel */ }
132 };
133 
134 U_BOOT_DRIVER(sysinfo_gpio) = {
135 	.name           = "sysinfo_gpio",
136 	.id             = UCLASS_SYSINFO,
137 	.of_match       = sysinfo_gpio_ids,
138 	.ops		= &sysinfo_gpio_ops,
139 	.priv_auto	= sizeof(struct sysinfo_gpio_priv),
140 	.probe          = sysinfo_gpio_probe,
141 };
142