1193326Sed// SPDX-License-Identifier: GPL-2.0-or-later
2193326Sed/*
3193326Sed * Copyright (C) 2023 PHYTEC Messtechnik GmbH
4193326Sed * Author: Teresa Remmet <t.remmet@phytec.de>
5193326Sed */
6193326Sed
7193326Sed#include <common.h>
8193326Sed#include <dm/device.h>
9193326Sed#include <dm/uclass.h>
10193326Sed#include <i2c.h>
11193326Sed#include <u-boot/crc.h>
12193326Sed#include <malloc.h>
13193326Sed#include <extension_board.h>
14193326Sed
15249423Sdim#include "phytec_som_detection.h"
16193326Sed
17193326Sedstruct phytec_eeprom_data eeprom_data;
18193326Sed
19234353Sdim#if IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION)
20193326Sed
21249423Sdimint phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data,
22249423Sdim				      int bus_num, int addr, int addr_fallback)
23249423Sdim{
24249423Sdim	int ret;
25193326Sed
26193326Sed	ret = phytec_eeprom_data_init(data, bus_num, addr);
27193326Sed	if (ret) {
28193326Sed		pr_err("%s: init failed. Trying fall back address 0x%x\n",
29193326Sed		       __func__, addr_fallback);
30193326Sed		ret = phytec_eeprom_data_init(data, bus_num, addr_fallback);
31193326Sed	}
32193326Sed
33199990Srdivacky	if (ret)
34193326Sed		pr_err("%s: EEPROM data init failed\n", __func__);
35193326Sed
36218893Sdim	return ret;
37288943Sdim}
38208600Srdivacky
39226633Sdimint phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
40226633Sdim			     int bus_num, int addr)
41226633Sdim{
42226633Sdim	int ret;
43226633Sdim
44226633Sdim	ret = phytec_eeprom_data_init(data, bus_num, addr);
45226633Sdim	if (ret)
46226633Sdim		pr_err("%s: EEPROM data init failed\n", __func__);
47226633Sdim
48208600Srdivacky	return ret;
49226633Sdim}
50226633Sdim
51208600Srdivackyint phytec_eeprom_data_init(struct phytec_eeprom_data *data,
52296417Sdim			    int bus_num, int addr)
53296417Sdim{
54208600Srdivacky	int ret, i;
55208600Srdivacky	unsigned int crc;
56218893Sdim	u8 *ptr;
57218893Sdim	const unsigned int payload_size = sizeof(struct phytec_eeprom_payload);
58218893Sdim
59218893Sdim	if (!data)
60239462Sdim		data = &eeprom_data;
61239462Sdim
62239462Sdim#if CONFIG_IS_ENABLED(DM_I2C)
63239462Sdim	struct udevice *dev;
64218893Sdim
65193326Sed	ret = i2c_get_chip_for_busnum(bus_num, addr, 2, &dev);
66288943Sdim	if (ret) {
67288943Sdim		pr_err("%s: i2c EEPROM not found: %i.\n", __func__, ret);
68288943Sdim		goto err;
69193326Sed	}
70193326Sed
71193326Sed	ret = dm_i2c_read(dev, 0, (uint8_t *)data, payload_size);
72193326Sed	if (ret) {
73193326Sed		pr_err("%s: Unable to read EEPROM data: %i\n", __func__, ret);
74193326Sed		goto err;
75193326Sed	}
76193326Sed#else
77193326Sed	i2c_set_bus_num(bus_num);
78193326Sed	ret = i2c_read(addr, 0, 2, (uint8_t *)data,
79193326Sed		       sizeof(struct phytec_eeprom_data));
80239462Sdim#endif
81296417Sdim
82239462Sdim	if (data->payload.api_rev == 0xff) {
83239462Sdim		pr_err("%s: EEPROM is not flashed. Prototype?\n", __func__);
84193326Sed		ret = -EINVAL;
85226633Sdim		goto err;
86208600Srdivacky	}
87296417Sdim
88234353Sdim	ptr = (u8 *)data;
89234353Sdim	for (i = 0; i < payload_size; ++i)
90226633Sdim		if (ptr[i] != 0x0)
91234353Sdim			break;
92226633Sdim
93226633Sdim	if (i == payload_size) {
94226633Sdim		pr_err("%s: EEPROM data is all zero. Erased?\n", __func__);
95226633Sdim		ret = -EINVAL;
96208600Srdivacky		goto err;
97208600Srdivacky	}
98193326Sed
99193326Sed	/* We are done here for early revisions */
100193326Sed	if (data->payload.api_rev <= PHYTEC_API_REV1) {
101198092Srdivacky		data->valid = true;
102288943Sdim		return 0;
103288943Sdim	}
104288943Sdim
105288943Sdim	crc = crc8(0, (const unsigned char *)&data->payload, payload_size);
106288943Sdim	debug("%s: crc: %x\n", __func__, crc);
107193326Sed
108193326Sed	if (crc) {
109193326Sed		pr_err("%s: CRC mismatch. EEPROM data is not usable.\n",
110193326Sed		       __func__);
111221345Sdim		ret = -EINVAL;
112221345Sdim		goto err;
113221345Sdim	}
114193326Sed
115224145Sdim	data->valid = true;
116224145Sdim	return 0;
117224145Sdimerr:
118193326Sed	data->valid = false;
119193326Sed	return ret;
120234353Sdim}
121234353Sdim
122234353Sdimvoid __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data)
123234353Sdim{
124234353Sdim	struct phytec_api2_data *api2;
125234353Sdim	char pcb_sub_rev;
126234353Sdim	unsigned int ksp_no, sub_som_type1, sub_som_type2;
127234353Sdim
128234353Sdim	if (!data)
129234353Sdim		data = &eeprom_data;
130239462Sdim
131234353Sdim	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
132234353Sdim		return;
133234353Sdim
134234353Sdim	api2 = &data->payload.data.data_api2;
135234353Sdim
136234353Sdim	/* Calculate PCB subrevision */
137234353Sdim	pcb_sub_rev = api2->pcb_sub_opt_rev & 0x0f;
138193326Sed	pcb_sub_rev = pcb_sub_rev ? ((pcb_sub_rev - 1) + 'a') : ' ';
139193326Sed
140193326Sed	/* print standard product string */
141224145Sdim	if (api2->som_type <= 1) {
142193326Sed		printf("SoM: %s-%03u-%s.%s PCB rev: %u%c\n",
143193326Sed		       phytec_som_type_str[api2->som_type], api2->som_no,
144193326Sed		       api2->opt, api2->bom_rev, api2->pcb_rev, pcb_sub_rev);
145193326Sed		return;
146198092Srdivacky	}
147193326Sed	/* print KSP/KSM string */
148198092Srdivacky	if (api2->som_type <= 3) {
149193326Sed		ksp_no = (api2->ksp_no << 8) | api2->som_no;
150198092Srdivacky		printf("SoM: %s-%u ",
151193326Sed		       phytec_som_type_str[api2->som_type], ksp_no);
152193326Sed	/* print standard product based KSP/KSM strings */
153193326Sed	} else {
154198398Srdivacky		switch (api2->som_type) {
155193326Sed		case 4:
156193326Sed			sub_som_type1 = 0;
157193326Sed			sub_som_type2 = 3;
158193326Sed			break;
159193326Sed		case 5:
160193326Sed			sub_som_type1 = 0;
161193326Sed			sub_som_type2 = 2;
162198092Srdivacky			break;
163288943Sdim		case 6:
164218893Sdim			sub_som_type1 = 1;
165198092Srdivacky			sub_som_type2 = 3;
166193326Sed			break;
167201361Srdivacky		case 7:
168288943Sdim			sub_som_type1 = 1;
169193326Sed			sub_som_type2 = 2;
170193326Sed			break;
171193326Sed		default:
172251662Sdim			pr_err("%s: Invalid SoM type: %i", __func__, api2->som_type);
173251662Sdim			return;
174251662Sdim		};
175251662Sdim
176193326Sed		printf("SoM: %s-%03u-%s-%03u ",
177193326Sed		       phytec_som_type_str[sub_som_type1],
178234353Sdim		       api2->som_no, phytec_som_type_str[sub_som_type2],
179261991Sdim		       api2->ksp_no);
180218893Sdim	}
181210299Sed
182199482Srdivacky	printf("Option: %s BOM rev: %s PCB rev: %u%c\n", api2->opt,
183224145Sdim	       api2->bom_rev, api2->pcb_rev, pcb_sub_rev);
184218893Sdim}
185218893Sdim
186234353Sdimchar * __maybe_unused phytec_get_opt(struct phytec_eeprom_data *data)
187234353Sdim{
188234353Sdim	char *opt;
189239462Sdim
190234353Sdim	if (!data)
191234353Sdim		data = &eeprom_data;
192234353Sdim
193234353Sdim	if (!data->valid)
194234353Sdim		return NULL;
195193326Sed
196193326Sed	if (data->payload.api_rev < PHYTEC_API_REV2)
197224145Sdim		opt = data->payload.data.data_api0.opt;
198224145Sdim	else
199193326Sed		opt = data->payload.data.data_api2.opt;
200200583Srdivacky
201226633Sdim	return opt;
202296417Sdim}
203296417Sdim
204226633Sdimu8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data)
205193326Sed{
206193326Sed	struct phytec_api2_data *api2;
207193326Sed
208193326Sed	if (!data)
209193326Sed		data = &eeprom_data;
210193326Sed
211193326Sed	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
212193326Sed		return PHYTEC_EEPROM_INVAL;
213193326Sed
214193326Sed	api2 = &data->payload.data.data_api2;
215193326Sed
216193326Sed	return api2->pcb_rev;
217249423Sdim}
218249423Sdim
219288943Sdimu8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data)
220261991Sdim{
221249423Sdim	if (!data)
222249423Sdim		data = &eeprom_data;
223249423Sdim
224239462Sdim	if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
225193326Sed		return PHYTEC_EEPROM_INVAL;
226193326Sed
227208600Srdivacky	return data->payload.data.data_api2.som_type;
228208600Srdivacky}
229208600Srdivacky
230208600Srdivacky#if IS_ENABLED(CONFIG_CMD_EXTENSION)
231208600Srdivackystruct extension *phytec_add_extension(const char *name, const char *overlay,
232208600Srdivacky				       const char *other)
233208600Srdivacky{
234208600Srdivacky	struct extension *extension;
235208600Srdivacky
236249423Sdim	if (strlen(overlay) > sizeof(extension->overlay)) {
237208600Srdivacky		pr_err("Overlay name %s is longer than %lu.\n", overlay,
238208600Srdivacky		       sizeof(extension->overlay));
239208600Srdivacky		return NULL;
240208600Srdivacky	}
241208600Srdivacky
242208600Srdivacky	extension = calloc(1, sizeof(struct extension));
243208600Srdivacky	snprintf(extension->name, sizeof(extension->name), name);
244226633Sdim	snprintf(extension->overlay, sizeof(extension->overlay), overlay);
245226633Sdim	snprintf(extension->other, sizeof(extension->other), other);
246208600Srdivacky	snprintf(extension->owner, sizeof(extension->owner), "PHYTEC");
247208600Srdivacky
248208600Srdivacky	return extension;
249226633Sdim}
250226633Sdim#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
251226633Sdim
252226633Sdim#else
253226633Sdim
254239462Sdiminline int phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
255226633Sdim				    int bus_num, int addr)
256226633Sdim{
257226633Sdim	return PHYTEC_EEPROM_INVAL;
258226633Sdim}
259226633Sdim
260210299Sedinline int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data,
261226633Sdim					     int bus_num, int addr,
262239462Sdim					     int addr_fallback)
263296417Sdim{
264296417Sdim	return PHYTEC_EEPROM_INVAL;
265208600Srdivacky}
266208600Srdivacky
267193326Sedinline int phytec_eeprom_data_init(struct phytec_eeprom_data *data,
268296417Sdim				   int bus_num, int addr)
269239462Sdim{
270296417Sdim	return PHYTEC_EEPROM_INVAL;
271239462Sdim}
272239462Sdim
273193326Sedinline void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data)
274239462Sdim{
275239462Sdim}
276218893Sdim
277239462Sdiminline char *__maybe_unused phytec_get_opt(struct phytec_eeprom_data *data)
278239462Sdim{
279239462Sdim	return NULL;
280239462Sdim}
281239462Sdim
282212904Sdimu8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data)
283239462Sdim{
284239462Sdim	return PHYTEC_EEPROM_INVAL;
285239462Sdim}
286239462Sdim
287239462Sdimu8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data)
288193326Sed{
289239462Sdim	return PHYTEC_EEPROM_INVAL;
290239462Sdim}
291239462Sdim
292239462Sdim#if IS_ENABLED(CONFIG_CMD_EXTENSION)
293239462Sdiminline struct extension *phytec_add_extension(const char *name,
294239462Sdim					      const char *overlay,
295239462Sdim					      const char *other)
296239462Sdim{
297239462Sdim	return NULL;
298198092Srdivacky}
299296417Sdim#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
300296417Sdim
301239462Sdim#endif /* IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) */
302198092Srdivacky