1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2021 Marek Vasut <marek.vasut+renesas@gmail.com>
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <i2c_eeprom.h>
9 #include <log.h>
10 #include <sysinfo.h>
11 
12 #define BOARD_CODE_MASK		0xF8
13 #define BOARD_REV_MASK		0x07
14 #define BOARD_CODE_SHIFT	0x03
15 
16 #define BOARD_SALVATOR_X	0x0
17 #define BOARD_KRIEK		0x1
18 #define BOARD_STARTER_KIT	0x2
19 #define BOARD_EAGLE		0x3
20 #define BOARD_SALVATOR_XS	0x4
21 #define BOARD_CONDOR		0x6
22 #define BOARD_DRAAK		0x7
23 #define BOARD_EBISU		0x8
24 #define BOARD_STARTER_KIT_PRE	0xB
25 #define BOARD_EBISU_4D		0xD
26 #define BOARD_CONDOR_I		0x10
27 
28 /**
29  * struct sysinfo_rcar_priv - sysinfo private data
30  * @boardname: board model and revision
31  * @val: board ID value from eeprom
32  */
33 struct sysinfo_rcar_priv {
34 	char	boardmodel[64];
35 	u8	val;
36 };
37 
sysinfo_rcar_detect(struct udevice * dev)38 static int sysinfo_rcar_detect(struct udevice *dev)
39 {
40 	struct sysinfo_rcar_priv *priv = dev_get_priv(dev);
41 
42 	return priv->val == 0xff;
43 }
44 
sysinfo_rcar_get_str(struct udevice * dev,int id,size_t size,char * val)45 static int sysinfo_rcar_get_str(struct udevice *dev, int id, size_t size, char *val)
46 {
47 	struct sysinfo_rcar_priv *priv = dev_get_priv(dev);
48 
49 	switch (id) {
50 	case SYSINFO_ID_BOARD_MODEL:
51 		strncpy(val, priv->boardmodel, size);
52 		val[size - 1] = '\0';
53 		return 0;
54 	default:
55 		return -EINVAL;
56 	};
57 }
58 
59 static const struct sysinfo_ops sysinfo_rcar_ops = {
60 	.detect = sysinfo_rcar_detect,
61 	.get_str = sysinfo_rcar_get_str,
62 };
63 
sysinfo_rcar_parse(struct sysinfo_rcar_priv * priv)64 static void sysinfo_rcar_parse(struct sysinfo_rcar_priv *priv)
65 {
66 	const u8 board_id = (priv->val & BOARD_CODE_MASK) >> BOARD_CODE_SHIFT;
67 	const u8 board_rev = priv->val & BOARD_REV_MASK;
68 	bool salvator_xs = false;
69 	bool ebisu_4d = false;
70 	bool condor_i = false;
71 	char rev_major = '?';
72 	char rev_minor = '?';
73 
74 	switch (board_id) {
75 	case BOARD_SALVATOR_XS:
76 		salvator_xs = true;
77 		fallthrough;
78 	case BOARD_SALVATOR_X:
79 		if (!(board_rev & ~1)) { /* Only rev 0 and 1 is valid */
80 			rev_major = '1';
81 			rev_minor = '0' + (board_rev & BIT(0));
82 		}
83 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
84 			 "Renesas Salvator-X%s board rev %c.%c",
85 			 salvator_xs ? "S" : "", rev_major, rev_minor);
86 		return;
87 	case BOARD_STARTER_KIT:
88 		if (!(board_rev & ~1)) { /* Only rev 0 and 1 is valid */
89 			rev_major = (board_rev & BIT(0)) ? '3' : '1';
90 			rev_minor = '0';
91 		}
92 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
93 			 "Renesas Starter Kit board rev %c.%c",
94 			 rev_major, rev_minor);
95 		return;
96 	case BOARD_STARTER_KIT_PRE:
97 		if (!(board_rev & ~3)) { /* Only rev 0..3 is valid */
98 			rev_major = (board_rev & BIT(1)) ? '2' : '1';
99 			rev_minor = (board_rev == 3) ? '1' : '0';
100 		}
101 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
102 			 "Renesas Starter Kit Premier board rev %c.%c",
103 			 rev_major, rev_minor);
104 		return;
105 	case BOARD_EAGLE:
106 		if (!board_rev) { /* Only rev 0 is valid */
107 			rev_major = '1';
108 			rev_minor = '0';
109 		}
110 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
111 			 "Renesas Eagle board rev %c.%c",
112 			 rev_major, rev_minor);
113 		return;
114 	case BOARD_EBISU_4D:
115 		ebisu_4d = true;
116 		fallthrough;
117 	case BOARD_EBISU:
118 		if (!board_rev) { /* Only rev 0 is valid */
119 			rev_major = '1';
120 			rev_minor = '0';
121 		}
122 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
123 			 "Renesas Ebisu%s board rev %c.%c",
124 			 ebisu_4d ? "-4D" : "", rev_major, rev_minor);
125 		return;
126 	case BOARD_DRAAK:
127 		if (!board_rev) { /* Only rev 0 is valid */
128 			rev_major = '1';
129 			rev_minor = '0';
130 		}
131 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
132 			 "Renesas Draak board rev %c.%c",
133 			 rev_major, rev_minor);
134 		return;
135 	case BOARD_KRIEK:
136 		if (!board_rev) { /* Only rev 0 is valid */
137 			rev_major = '1';
138 			rev_minor = '0';
139 		}
140 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
141 			 "Renesas Kriek board rev %c.%c",
142 			 rev_major, rev_minor);
143 		return;
144 	case BOARD_CONDOR_I:
145 		condor_i = true;
146 		fallthrough;
147 	case BOARD_CONDOR:
148 		if (!board_rev) { /* Only rev 0 is valid */
149 			rev_major = '1';
150 			rev_minor = '0';
151 		}
152 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
153 			"Renesas Condor%s board rev %c.%c",
154 			condor_i ? "-I" : "", rev_major, rev_minor);
155 		return;
156 	default:
157 		snprintf(priv->boardmodel, sizeof(priv->boardmodel),
158 			 "Renesas -Unknown- board rev ?.?");
159 		priv->val = 0xff;
160 		return;
161 	}
162 }
163 
sysinfo_rcar_probe(struct udevice * dev)164 static int sysinfo_rcar_probe(struct udevice *dev)
165 {
166 	struct sysinfo_rcar_priv *priv = dev_get_priv(dev);
167 	struct ofnode_phandle_args phandle_args;
168 	struct udevice *i2c_eeprom;
169 	u32 offset;
170 	int ret;
171 
172 	offset = dev_read_u32_default(dev, "offset", 0x70);
173 
174 	ret = dev_read_phandle_with_args(dev, "i2c-eeprom", NULL,
175 					 0, 0, &phandle_args);
176 	if (ret) {
177 		debug("%s: i2c-eeprom backing device not specified\n",
178 		      dev->name);
179 		return ret;
180 	}
181 
182 	ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, phandle_args.node,
183 					  &i2c_eeprom);
184 	if (ret) {
185 		debug("%s: could not get backing device\n", dev->name);
186 		return ret;
187 	}
188 
189 	ret = i2c_eeprom_read(i2c_eeprom, offset, &priv->val, 1);
190 	if (ret < 0) {
191 		debug("%s: read failed\n", __func__);
192 		return -EIO;
193 	}
194 
195 	sysinfo_rcar_parse(priv);
196 
197 	return 0;
198 }
199 
200 static const struct udevice_id sysinfo_rcar_ids[] = {
201 	{ .compatible = "renesas,rcar-sysinfo" },
202 	{ /* sentinel */ }
203 };
204 
205 U_BOOT_DRIVER(sysinfo_rcar) = {
206 	.name           = "sysinfo_rcar",
207 	.id             = UCLASS_SYSINFO,
208 	.of_match       = sysinfo_rcar_ids,
209 	.ops		= &sysinfo_rcar_ops,
210 	.priv_auto	= sizeof(struct sysinfo_rcar_priv),
211 	.probe          = sysinfo_rcar_probe,
212 };
213