blob: f0e35d8d2ecfee9d2592fa7e42c0ff7f713f20f7 [file] [log] [blame]
Teresa Remmeta6f8da52023-08-17 10:57:06 +02001// 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
Teresa Remmeta6f8da52023-08-17 10:57:06 +02007#include <dm/device.h>
8#include <dm/uclass.h>
9#include <i2c.h>
10#include <u-boot/crc.h>
Daniel Schultza440ec22024-04-19 08:55:36 -070011#include <malloc.h>
12#include <extension_board.h>
Teresa Remmeta6f8da52023-08-17 10:57:06 +020013
14#include "phytec_som_detection.h"
15
16struct phytec_eeprom_data eeprom_data;
17
Yannic Moog5dd7f7c2023-12-20 09:45:35 +010018#if IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION)
19
Teresa Remmeta6f8da52023-08-17 10:57:06 +020020int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data,
21 int bus_num, int addr, int addr_fallback)
22{
23 int ret;
24
25 ret = phytec_eeprom_data_init(data, bus_num, addr);
26 if (ret) {
27 pr_err("%s: init failed. Trying fall back address 0x%x\n",
28 __func__, addr_fallback);
29 ret = phytec_eeprom_data_init(data, bus_num, addr_fallback);
30 }
31
32 if (ret)
33 pr_err("%s: EEPROM data init failed\n", __func__);
34
35 return ret;
36}
37
38int phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
39 int bus_num, int addr)
40{
41 int ret;
42
43 ret = phytec_eeprom_data_init(data, bus_num, addr);
44 if (ret)
45 pr_err("%s: EEPROM data init failed\n", __func__);
46
47 return ret;
48}
49
Daniel Schultz0bcb3042024-05-21 23:18:22 -070050int phytec_eeprom_read(u8 *data, int bus_num, int addr, int size, int offset)
Teresa Remmeta6f8da52023-08-17 10:57:06 +020051{
Daniel Schultz0bcb3042024-05-21 23:18:22 -070052 int ret;
Teresa Remmeta6f8da52023-08-17 10:57:06 +020053
54#if CONFIG_IS_ENABLED(DM_I2C)
55 struct udevice *dev;
56
57 ret = i2c_get_chip_for_busnum(bus_num, addr, 2, &dev);
58 if (ret) {
59 pr_err("%s: i2c EEPROM not found: %i.\n", __func__, ret);
Daniel Schultz0bcb3042024-05-21 23:18:22 -070060 return ret;
Teresa Remmeta6f8da52023-08-17 10:57:06 +020061 }
62
Daniel Schultz0bcb3042024-05-21 23:18:22 -070063 ret = dm_i2c_read(dev, offset, (uint8_t *)data, size);
Teresa Remmeta6f8da52023-08-17 10:57:06 +020064 if (ret) {
Yannic Moogdb2dfb02024-04-19 08:55:37 -070065 pr_err("%s: Unable to read EEPROM data: %i\n", __func__, ret);
Daniel Schultz0bcb3042024-05-21 23:18:22 -070066 return ret;
Teresa Remmeta6f8da52023-08-17 10:57:06 +020067 }
68#else
69 i2c_set_bus_num(bus_num);
Daniel Schultz0bcb3042024-05-21 23:18:22 -070070 ret = i2c_read(addr, offset, 2, (uint8_t *)data, size);
Teresa Remmeta6f8da52023-08-17 10:57:06 +020071#endif
Daniel Schultz0bcb3042024-05-21 23:18:22 -070072 return ret;
73}
74
75int phytec_eeprom_data_init(struct phytec_eeprom_data *data,
76 int bus_num, int addr)
77{
78 int ret, i;
79 unsigned int crc;
80 u8 *ptr;
Daniel Schultz0bcb3042024-05-21 23:18:22 -070081
82 if (!data)
83 data = &eeprom_data;
84
85 ret = phytec_eeprom_read((u8 *)data, bus_num, addr,
Daniel Schultz794aec62024-05-21 23:18:23 -070086 PHYTEC_API2_DATA_LEN, 0);
Daniel Schultz0bcb3042024-05-21 23:18:22 -070087 if (ret)
88 goto err;
Teresa Remmeta6f8da52023-08-17 10:57:06 +020089
Yannic Moogdb2dfb02024-04-19 08:55:37 -070090 if (data->payload.api_rev == 0xff) {
Teresa Remmeta6f8da52023-08-17 10:57:06 +020091 pr_err("%s: EEPROM is not flashed. Prototype?\n", __func__);
Yannic Moogdb2dfb02024-04-19 08:55:37 -070092 ret = -EINVAL;
93 goto err;
Teresa Remmeta6f8da52023-08-17 10:57:06 +020094 }
95
Daniel Schultza132b302024-04-19 08:55:39 -070096 ptr = (u8 *)data;
Daniel Schultz794aec62024-05-21 23:18:23 -070097 for (i = 0; i < PHYTEC_API2_DATA_LEN; ++i)
Yannic Moog7bbbe182023-12-20 09:45:34 +010098 if (ptr[i] != 0x0)
Teresa Remmeta6f8da52023-08-17 10:57:06 +020099 break;
100
Daniel Schultz794aec62024-05-21 23:18:23 -0700101 if (i == PHYTEC_API2_DATA_LEN) {
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200102 pr_err("%s: EEPROM data is all zero. Erased?\n", __func__);
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700103 ret = -EINVAL;
104 goto err;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200105 }
106
107 /* We are done here for early revisions */
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700108 if (data->payload.api_rev <= PHYTEC_API_REV1) {
109 data->valid = true;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200110 return 0;
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700111 }
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200112
Daniel Schultz794aec62024-05-21 23:18:23 -0700113 crc = crc8(0, (const unsigned char *)&data->payload, PHYTEC_API2_DATA_LEN);
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200114 debug("%s: crc: %x\n", __func__, crc);
115
116 if (crc) {
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700117 pr_err("%s: CRC mismatch. EEPROM data is not usable.\n",
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200118 __func__);
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700119 ret = -EINVAL;
120 goto err;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200121 }
122
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700123 data->valid = true;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200124 return 0;
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700125err:
126 data->valid = false;
127 return ret;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200128}
129
130void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data)
131{
132 struct phytec_api2_data *api2;
133 char pcb_sub_rev;
134 unsigned int ksp_no, sub_som_type1, sub_som_type2;
135
136 if (!data)
137 data = &eeprom_data;
138
Yannic Moog51b9d532024-04-19 08:55:38 -0700139 if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200140 return;
141
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700142 api2 = &data->payload.data.data_api2;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200143
144 /* Calculate PCB subrevision */
145 pcb_sub_rev = api2->pcb_sub_opt_rev & 0x0f;
146 pcb_sub_rev = pcb_sub_rev ? ((pcb_sub_rev - 1) + 'a') : ' ';
147
148 /* print standard product string */
149 if (api2->som_type <= 1) {
150 printf("SoM: %s-%03u-%s.%s PCB rev: %u%c\n",
151 phytec_som_type_str[api2->som_type], api2->som_no,
152 api2->opt, api2->bom_rev, api2->pcb_rev, pcb_sub_rev);
153 return;
154 }
155 /* print KSP/KSM string */
156 if (api2->som_type <= 3) {
157 ksp_no = (api2->ksp_no << 8) | api2->som_no;
158 printf("SoM: %s-%u ",
159 phytec_som_type_str[api2->som_type], ksp_no);
160 /* print standard product based KSP/KSM strings */
161 } else {
162 switch (api2->som_type) {
163 case 4:
164 sub_som_type1 = 0;
165 sub_som_type2 = 3;
166 break;
167 case 5:
168 sub_som_type1 = 0;
169 sub_som_type2 = 2;
170 break;
171 case 6:
172 sub_som_type1 = 1;
173 sub_som_type2 = 3;
174 break;
175 case 7:
176 sub_som_type1 = 1;
177 sub_som_type2 = 2;
178 break;
179 default:
Yannic Moog46c507e2023-12-20 09:45:36 +0100180 pr_err("%s: Invalid SoM type: %i", __func__, api2->som_type);
181 return;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200182 };
183
184 printf("SoM: %s-%03u-%s-%03u ",
185 phytec_som_type_str[sub_som_type1],
186 api2->som_no, phytec_som_type_str[sub_som_type2],
187 api2->ksp_no);
188 }
189
190 printf("Option: %s BOM rev: %s PCB rev: %u%c\n", api2->opt,
191 api2->bom_rev, api2->pcb_rev, pcb_sub_rev);
192}
193
194char * __maybe_unused phytec_get_opt(struct phytec_eeprom_data *data)
195{
196 char *opt;
197
198 if (!data)
199 data = &eeprom_data;
200
Yannic Moog51b9d532024-04-19 08:55:38 -0700201 if (!data->valid)
202 return NULL;
203
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700204 if (data->payload.api_rev < PHYTEC_API_REV2)
205 opt = data->payload.data.data_api0.opt;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200206 else
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700207 opt = data->payload.data.data_api2.opt;
Teresa Remmeta6f8da52023-08-17 10:57:06 +0200208
209 return opt;
210}
Teresa Remmete3d3ac42023-08-17 10:57:10 +0200211
212u8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data)
213{
214 struct phytec_api2_data *api2;
215
216 if (!data)
217 data = &eeprom_data;
218
Yannic Moog51b9d532024-04-19 08:55:38 -0700219 if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
Teresa Remmete3d3ac42023-08-17 10:57:10 +0200220 return PHYTEC_EEPROM_INVAL;
221
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700222 api2 = &data->payload.data.data_api2;
Teresa Remmete3d3ac42023-08-17 10:57:10 +0200223
224 return api2->pcb_rev;
225}
Yannic Moog5dd7f7c2023-12-20 09:45:35 +0100226
Benjamin Hahn455dcf72024-03-06 17:18:31 +0100227u8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data)
228{
229 if (!data)
230 data = &eeprom_data;
Yannic Moog51b9d532024-04-19 08:55:38 -0700231
232 if (!data->valid || data->payload.api_rev < PHYTEC_API_REV2)
Benjamin Hahn455dcf72024-03-06 17:18:31 +0100233 return PHYTEC_EEPROM_INVAL;
234
Yannic Moogdb2dfb02024-04-19 08:55:37 -0700235 return data->payload.data.data_api2.som_type;
Benjamin Hahn455dcf72024-03-06 17:18:31 +0100236}
237
Daniel Schultza440ec22024-04-19 08:55:36 -0700238#if IS_ENABLED(CONFIG_CMD_EXTENSION)
239struct extension *phytec_add_extension(const char *name, const char *overlay,
240 const char *other)
241{
242 struct extension *extension;
243
244 if (strlen(overlay) > sizeof(extension->overlay)) {
245 pr_err("Overlay name %s is longer than %lu.\n", overlay,
246 sizeof(extension->overlay));
247 return NULL;
248 }
249
250 extension = calloc(1, sizeof(struct extension));
251 snprintf(extension->name, sizeof(extension->name), name);
252 snprintf(extension->overlay, sizeof(extension->overlay), overlay);
253 snprintf(extension->other, sizeof(extension->other), other);
254 snprintf(extension->owner, sizeof(extension->owner), "PHYTEC");
255
256 return extension;
257}
258#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
259
Yannic Moog5dd7f7c2023-12-20 09:45:35 +0100260#else
261
262inline int phytec_eeprom_data_setup(struct phytec_eeprom_data *data,
263 int bus_num, int addr)
264{
265 return PHYTEC_EEPROM_INVAL;
266}
267
268inline int phytec_eeprom_data_setup_fallback(struct phytec_eeprom_data *data,
269 int bus_num, int addr,
270 int addr_fallback)
271{
272 return PHYTEC_EEPROM_INVAL;
273}
274
275inline int phytec_eeprom_data_init(struct phytec_eeprom_data *data,
276 int bus_num, int addr)
277{
278 return PHYTEC_EEPROM_INVAL;
279}
280
281inline void __maybe_unused phytec_print_som_info(struct phytec_eeprom_data *data)
282{
283}
284
285inline char *__maybe_unused phytec_get_opt(struct phytec_eeprom_data *data)
286{
287 return NULL;
288}
289
290u8 __maybe_unused phytec_get_rev(struct phytec_eeprom_data *data)
291{
292 return PHYTEC_EEPROM_INVAL;
293}
294
Benjamin Hahn69ea0e32024-03-12 10:39:11 +0100295u8 __maybe_unused phytec_get_som_type(struct phytec_eeprom_data *data)
296{
297 return PHYTEC_EEPROM_INVAL;
298}
Daniel Schultza440ec22024-04-19 08:55:36 -0700299
300#if IS_ENABLED(CONFIG_CMD_EXTENSION)
301inline struct extension *phytec_add_extension(const char *name,
302 const char *overlay,
303 const char *other)
304{
305 return NULL;
306}
307#endif /* IS_ENABLED(CONFIG_CMD_EXTENSION) */
Benjamin Hahn69ea0e32024-03-12 10:39:11 +0100308
Yannic Moog5dd7f7c2023-12-20 09:45:35 +0100309#endif /* IS_ENABLED(CONFIG_PHYTEC_SOM_DETECTION) */