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