1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2023 PHYTEC Messtechnik GmbH
4 * Author: Teresa Remmet <t.remmet@phytec.de>
5 */
6
7#include <common.h>
8#include <dm/device.h>
9#include <dm/uclass.h>
10#include <i2c.h>
11#include <u-boot/crc.h>
12#include <malloc.h>
13#include <extension_board.h>
14
15#include "phytec_som_detection.h"
16
17struct phytec_eeprom_data eeprom_data;
18
19#if IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION)
20
21int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data,
22				      int bus_num, int addr, int addr_fallback)
23{
24	int ret;
25
26	ret = phytec_eeprom_data_init(data, bus_num, addr);
27	if (ret) {
28		pr_err("%s: init failed. Trying fall back address 0x%x\n",
29		       __func__, addr_fallback);
30		ret = phytec_eeprom_data_init(data, bus_num, addr_fallback);
31	}
32
33	if (ret)
34		pr_err("%s: EEPROM data init failed\n", __func__);
35
36	return ret;
37}
38
39int phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
40			     int bus_num, int addr)
41{
42	int ret;
43
44	ret = phytec_eeprom_data_init(data, bus_num, addr);
45	if (ret)
46		pr_err("%s: EEPROM data init failed\n", __func__);
47
48	return ret;
49}
50
51int phytec_eeprom_data_init(struct phytec_eeprom_data *data,
52			    int bus_num, int addr)
53{
54	int ret, i;
55	unsigned int crc;
56	u8 *ptr;
57	const unsigned int payload_size = sizeof(struct phytec_eeprom_payload);
58
59	if (!data)
60		data = &eeprom_data;
61
62#if CONFIG_IS_ENABLED(DM_I2C)
63	struct udevice *dev;
64
65	ret = i2c_get_chip_for_busnum(bus_num, addr, 2, &dev);
66	if (ret) {
67		pr_err("%s: i2c EEPROM not found: %i.\n", __func__, ret);
68		goto err;
69	}
70
71	ret = dm_i2c_read(dev, 0, (uint8_t *)data, payload_size);
72	if (ret) {
73		pr_err("%s: Unable to read EEPROM data: %i\n", __func__, ret);
74		goto err;
75	}
76#else
77	i2c_set_bus_num(bus_num);
78	ret = i2c_read(addr, 0, 2, (uint8_t *)data,
79		       sizeof(struct phytec_eeprom_data));
80#endif
81
82	if (data->payload.api_rev == 0xff) {
83		pr_err("%s: EEPROM is not flashed. Prototype?\n", __func__);
84		ret = -EINVAL;
85		goto err;
86	}
87
88	ptr = (u8 *)data;
89	for (i = 0; i < payload_size; ++i)
90		if (ptr[i] != 0x0)
91			break;
92
93	if (i == payload_size) {
94		pr_err("%s: EEPROM data is all zero. Erased?\n", __func__);
95		ret = -EINVAL;
96		goto err;
97	}
98
99	/* We are done here for early revisions */
100	if (data->payload.api_rev <= PHYTEC_API_REV1) {
101		data->valid = true;
102		return 0;
103	}
104
105	crc = crc8(0, (const unsigned char *)&data->payload, payload_size);
106	debug("%s: crc: %x\n", __func__, crc);
107
108	if (crc) {
109		pr_err("%s: CRC mismatch. EEPROM data is not usable.\n",
110		       __func__);
111		ret = -EINVAL;
112		goto err;
113	}
114
115	data->valid = true;
116	return 0;
117err:
118	data->valid = false;
119	return ret;
120}
121
122void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data)
123{
124	struct phytec_api2_data *api2;
125	char pcb_sub_rev;
126	unsigned int ksp_no, sub_som_type1, sub_som_type2;
127
128	if (!data)
129		data = &eeprom_data;
130
131	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
132		return;
133
134	api2 = &data->payload.data.data_api2;
135
136	/* Calculate PCB subrevision */
137	pcb_sub_rev = api2->pcb_sub_opt_rev & 0x0f;
138	pcb_sub_rev = pcb_sub_rev ? ((pcb_sub_rev - 1) + 'a') : ' ';
139
140	/* print standard product string */
141	if (api2->som_type <= 1) {
142		printf("SoM: %s-%03u-%s.%s PCB rev: %u%c\n",
143		       phytec_som_type_str[api2->som_type], api2->som_no,
144		       api2->opt, api2->bom_rev, api2->pcb_rev, pcb_sub_rev);
145		return;
146	}
147	/* print KSP/KSM string */
148	if (api2->som_type <= 3) {
149		ksp_no = (api2->ksp_no << 8) | api2->som_no;
150		printf("SoM: %s-%u ",
151		       phytec_som_type_str[api2->som_type], ksp_no);
152	/* print standard product based KSP/KSM strings */
153	} else {
154		switch (api2->som_type) {
155		case 4:
156			sub_som_type1 = 0;
157			sub_som_type2 = 3;
158			break;
159		case 5:
160			sub_som_type1 = 0;
161			sub_som_type2 = 2;
162			break;
163		case 6:
164			sub_som_type1 = 1;
165			sub_som_type2 = 3;
166			break;
167		case 7:
168			sub_som_type1 = 1;
169			sub_som_type2 = 2;
170			break;
171		default:
172			pr_err("%s: Invalid SoM type: %i", __func__, api2->som_type);
173			return;
174		};
175
176		printf("SoM: %s-%03u-%s-%03u ",
177		       phytec_som_type_str[sub_som_type1],
178		       api2->som_no, phytec_som_type_str[sub_som_type2],
179		       api2->ksp_no);
180	}
181
182	printf("Option: %s BOM rev: %s PCB rev: %u%c\n", api2->opt,
183	       api2->bom_rev, api2->pcb_rev, pcb_sub_rev);
184}
185
186char * __maybe_unused phytec_get_opt(struct phytec_eeprom_data *data)
187{
188	char *opt;
189
190	if (!data)
191		data = &eeprom_data;
192
193	if (!data->valid)
194		return NULL;
195
196	if (data->payload.api_rev < PHYTEC_API_REV2)
197		opt = data->payload.data.data_api0.opt;
198	else
199		opt = data->payload.data.data_api2.opt;
200
201	return opt;
202}
203
204u8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data)
205{
206	struct phytec_api2_data *api2;
207
208	if (!data)
209		data = &eeprom_data;
210
211	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
212		return PHYTEC_EEPROM_INVAL;
213
214	api2 = &data->payload.data.data_api2;
215
216	return api2->pcb_rev;
217}
218
219u8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data)
220{
221	if (!data)
222		data = &eeprom_data;
223
224	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
225		return PHYTEC_EEPROM_INVAL;
226
227	return data->payload.data.data_api2.som_type;
228}
229
230#if IS_ENABLED(CONFIG_CMD_EXTENSION)
231struct extension *phytec_add_extension(const char *name, const char *overlay,
232				       const char *other)
233{
234	struct extension *extension;
235
236	if (strlen(overlay) > sizeof(extension->overlay)) {
237		pr_err("Overlay name %s is longer than %lu.\n", overlay,
238		       sizeof(extension->overlay));
239		return NULL;
240	}
241
242	extension = calloc(1, sizeof(struct extension));
243	snprintf(extension->name, sizeof(extension->name), name);
244	snprintf(extension->overlay, sizeof(extension->overlay), overlay);
245	snprintf(extension->other, sizeof(extension->other), other);
246	snprintf(extension->owner, sizeof(extension->owner), "PHYTEC");
247
248	return extension;
249}
250#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
251
252#else
253
254inline int phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
255				    int bus_num, int addr)
256{
257	return PHYTEC_EEPROM_INVAL;
258}
259
260inline int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data,
261					     int bus_num, int addr,
262					     int addr_fallback)
263{
264	return PHYTEC_EEPROM_INVAL;
265}
266
267inline int phytec_eeprom_data_init(struct phytec_eeprom_data *data,
268				   int bus_num, int addr)
269{
270	return PHYTEC_EEPROM_INVAL;
271}
272
273inline void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data)
274{
275}
276
277inline char *__maybe_unused phytec_get_opt(struct phytec_eeprom_data *data)
278{
279	return NULL;
280}
281
282u8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data)
283{
284	return PHYTEC_EEPROM_INVAL;
285}
286
287u8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data)
288{
289	return PHYTEC_EEPROM_INVAL;
290}
291
292#if IS_ENABLED(CONFIG_CMD_EXTENSION)
293inline struct extension *phytec_add_extension(const char *name,
294					      const char *overlay,
295					      const char *other)
296{
297	return NULL;
298}
299#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
300
301#endif /* IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) */
302