Prepare v2023.10
[platform/kernel/u-boot.git] / board / xilinx / common / fru_ops.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) Copyright 2019 - 2022, Xilinx, Inc.
4  * (C) Copyright 2022 - 2023, Advanced Micro Devices, Inc.
5  */
6
7 #include <common.h>
8 #include <cpu_func.h>
9 #include <env.h>
10 #include <fdtdec.h>
11 #include <log.h>
12 #include <malloc.h>
13 #include <net.h>
14 #include <asm/io.h>
15 #include <asm/arch/hardware.h>
16
17 #include "fru.h"
18
19 struct fru_table fru_data __section(".data");
20
21 static u16 fru_cal_area_len(u8 len)
22 {
23         return len * FRU_COMMON_HDR_LEN_MULTIPLIER;
24 }
25
26 static u8 fru_version(u8 ver)
27 {
28         return ver & FRU_COMMON_HDR_VER_MASK;
29 }
30
31 static int fru_check_language(u8 code)
32 {
33         if (code != FRU_LANG_CODE_ENGLISH && code != FRU_LANG_CODE_ENGLISH_1) {
34                 printf("FRU_ERROR: Only English Language is supported\n");
35                 return -EINVAL;
36         }
37
38         return 0;
39 }
40
41 u8 fru_checksum(u8 *addr, u8 len)
42 {
43         u8 checksum = 0;
44         u8 cnt = len;
45
46         while (len--) {
47                 if (*addr == 0)
48                         cnt--;
49
50                 checksum += *addr;
51                 addr++;
52         }
53
54         /* If all data bytes are 0's return error */
55         if (!cnt)
56                 return EINVAL;
57
58         return checksum;
59 }
60
61 static int fru_check_type_len(u8 type_len, u8 language, u8 *type)
62 {
63         int len;
64
65         *type = (type_len & FRU_TYPELEN_CODE_MASK) >> FRU_TYPELEN_TYPE_SHIFT;
66
67         len = type_len & FRU_TYPELEN_LEN_MASK;
68
69         return len;
70 }
71
72 /* Return len */
73 static u8 fru_gen_type_len(u8 *addr, char *name)
74 {
75         int len = strlen(name);
76         struct fru_board_info_member *member;
77
78         member = (struct fru_board_info_member *)addr;
79         member->type_len = FRU_TYPELEN_TYPE_ASCII8 << FRU_TYPELEN_TYPE_SHIFT;
80         member->type_len |= len;
81
82         debug("%lx/%lx: Add %s to 0x%lx (len 0x%x)\n", (ulong)addr,
83               (ulong)&member->type_len,  name, (ulong)&member->name, len);
84         memcpy(&member->name, name, len);
85
86         /* Add +1 for type_len parameter */
87         return 1 + len;
88 }
89
90 int fru_generate(unsigned long addr, char *manufacturer, char *board_name,
91                  char *serial_no, char *part_no, char *revision)
92 {
93         struct fru_common_hdr *header = (struct fru_common_hdr *)addr;
94         struct fru_board_info_header *board_info;
95         u8 *member;
96         u8 len, pad, modulo;
97
98         header->version = 1; /* Only version 1.0 is supported now */
99         header->off_internal = 0; /* not present */
100         header->off_chassis = 0; /* not present */
101         header->off_board = (sizeof(*header)) / 8; /* Starting offset 8 */
102         header->off_product = 0; /* not present */
103         header->off_multirec = 0; /* not present */
104         header->pad = 0;
105         /*
106          * This unsigned byte can be used to calculate a zero checksum
107          * for the data area following the header. I.e. the modulo 256 sum of
108          * the record data bytes plus the checksum byte equals zero.
109          */
110         header->crc = 0; /* Clear before calculation */
111         header->crc = 0 - fru_checksum((u8 *)header, sizeof(*header));
112
113         /* board info is just right after header */
114         board_info = (void *)((u8 *)header + sizeof(*header));
115
116         debug("header %lx, board_info %lx\n", (ulong)header, (ulong)board_info);
117
118         board_info->ver = 1; /* 1.0 spec */
119         board_info->lang_code = 0; /* English */
120         board_info->time[0] = 0; /* unspecified */
121         board_info->time[1] = 0; /* unspecified */
122         board_info->time[2] = 0; /* unspecified */
123
124         /* Member fields are just after board_info header */
125         member = (u8 *)board_info + sizeof(*board_info);
126
127         len = fru_gen_type_len(member, manufacturer); /* Board Manufacturer */
128         member += len;
129         len = fru_gen_type_len(member, board_name); /* Board Product name */
130         member += len;
131         len = fru_gen_type_len(member, serial_no); /* Board Serial number */
132         member += len;
133         len = fru_gen_type_len(member, part_no); /* Board part number */
134         member += len;
135         len = fru_gen_type_len(member, "U-Boot generator"); /* File ID */
136         member += len;
137         len = fru_gen_type_len(member, revision); /* Revision */
138         member += len;
139
140         *member++ = 0xc1; /* Indication of no more fields */
141
142         len = member - (u8 *)board_info; /* Find current length */
143         len += 1; /* Add checksum there too for calculation */
144
145         modulo = len % 8;
146
147         if (modulo) {
148                 /* Do not fill last item which is checksum */
149                 for (pad = 0; pad < 8 - modulo; pad++)
150                         *member++ = 0;
151
152                 /* Increase structure size */
153                 len += 8 - modulo;
154         }
155
156         board_info->len = len / 8; /* Size in multiples of 8 bytes */
157
158         *member = 0; /* Clear before calculation */
159         *member = 0 - fru_checksum((u8 *)board_info, len);
160
161         debug("checksum %x(addr %x)\n", *member, len);
162
163         env_set_hex("fru_addr", addr);
164         env_set_hex("filesize", (unsigned long)member - addr + 1);
165
166         return 0;
167 }
168
169 static int fru_parse_board(unsigned long addr)
170 {
171         u8 i, type;
172         int len;
173         u8 *data, *term, *limit, *next_addr, *eof;
174
175         memcpy(&fru_data.brd.ver, (void *)addr, 6);
176
177         /*
178          * eof marks the last data byte (without checksum). That's why checksum
179          * is address length - 1 and last data byte is length - 2.
180          */
181         eof = (u8 *)(fru_data.brd.len * 8 + addr - 2);
182
183         addr += 6;
184         data = (u8 *)&fru_data.brd.manufacturer_type_len;
185
186         /* Record max structure limit not to write data over allocated space */
187         limit = (u8 *)&fru_data.brd + sizeof(struct fru_board_data);
188
189         for (i = 0; ; i++, data += FRU_BOARD_MAX_LEN) {
190                 len = fru_check_type_len(*(u8 *)addr, fru_data.brd.lang_code,
191                                          &type);
192                 next_addr = (u8 *)addr + 1;
193
194                 if ((u8 *)addr >= eof) {
195                         debug("Reach EOF record: addr %lx, eof %lx\n", addr,
196                               (unsigned long)eof);
197                         break;
198                 }
199
200                 /*
201                  * Stop capture if the type is ASCII and valid field length
202                  * is 1 (0xc1) and next FRU data is less than 0x20 (space " ")
203                  * or it is 0x7f (delete 'DEL').
204                  */
205                 if (type == FRU_TYPELEN_TYPE_ASCII8 && len == 1 &&
206                     (*next_addr < 0x20 || *next_addr == 0x7F))
207                         break;
208
209                 /* Stop when amount of chars is more then fields to record */
210                 if (data + len > limit)
211                         break;
212                 /* This record type/len field */
213                 *data++ = *(u8 *)addr;
214
215                 /* Add offset to match data */
216                 addr += 1;
217
218                 /* If len is 0 it means empty field that's why skip writing */
219                 if (!len)
220                         continue;
221
222                 /* Record data field */
223                 memcpy(data, (u8 *)addr, len);
224                 term = data + (u8)len;
225                 *term = 0;
226                 addr += len;
227         }
228
229         if (i < FRU_BOARD_AREA_TOTAL_FIELDS) {
230                 printf("Board area require minimum %d fields\n",
231                        FRU_BOARD_AREA_TOTAL_FIELDS);
232                 return -EINVAL;
233         }
234
235         return 0;
236 }
237
238 static int fru_parse_multirec(unsigned long addr)
239 {
240         struct fru_multirec_hdr mrc;
241         u8 checksum = 0;
242         u8 hdr_len = sizeof(struct fru_multirec_hdr);
243         int mac_len = 0;
244
245         debug("%s: multirec addr %lx\n", __func__, addr);
246
247         do {
248                 memcpy(&mrc.rec_type, (void *)addr, hdr_len);
249
250                 checksum = fru_checksum((u8 *)addr, hdr_len);
251                 if (checksum) {
252                         debug("%s header CRC error\n", __func__);
253                         return -EINVAL;
254                 }
255
256                 if (mrc.rec_type == FRU_MULTIREC_TYPE_OEM) {
257                         struct fru_multirec_mac *mac = (void *)addr + hdr_len;
258                         u32 type = FRU_DUT_MACID;
259
260                         if (CONFIG_IS_ENABLED(FRU_SC))
261                                 type = FRU_SC_MACID;
262
263                         if (mac->ver == type) {
264                                 mac_len = mrc.len - FRU_MULTIREC_MAC_OFFSET;
265                                 memcpy(&fru_data.mac.macid, mac->macid, mac_len);
266                         }
267                 }
268                 addr += mrc.len + hdr_len;
269         } while (!(mrc.type & FRU_LAST_REC));
270
271         return 0;
272 }
273
274 int fru_capture(unsigned long addr)
275 {
276         struct fru_common_hdr *hdr;
277         u8 checksum = 0;
278         unsigned long multirec_addr = addr;
279
280         checksum = fru_checksum((u8 *)addr, sizeof(struct fru_common_hdr));
281         if (checksum) {
282                 printf("%s Common header CRC error\n", __func__);
283                 return -EINVAL;
284         }
285
286         hdr = (struct fru_common_hdr *)addr;
287         memset((void *)&fru_data, 0, sizeof(fru_data));
288         memcpy((void *)&fru_data, (void *)hdr,
289                sizeof(struct fru_common_hdr));
290
291         fru_data.captured = true;
292
293         if (hdr->off_board) {
294                 addr += fru_cal_area_len(hdr->off_board);
295                 fru_parse_board(addr);
296         }
297
298         env_set_hex("fru_addr", addr);
299
300         if (hdr->off_multirec) {
301                 multirec_addr += fru_cal_area_len(hdr->off_multirec);
302                 fru_parse_multirec(multirec_addr);
303         }
304
305         return 0;
306 }
307
308 static int fru_display_board(struct fru_board_data *brd, int verbose)
309 {
310         u32 time = 0;
311         u8 type;
312         int len;
313         u8 *data;
314         static const char * const typecode[] = {
315                 "Binary/Unspecified",
316                 "BCD plus",
317                 "6-bit ASCII",
318                 "8-bit ASCII",
319                 "2-byte UNICODE"
320         };
321         static const char * const boardinfo[] = {
322                 "Manufacturer Name",
323                 "Product Name",
324                 "Serial No",
325                 "Part Number",
326                 "File ID",
327                 /* Xilinx spec */
328                 "Revision Number",
329         };
330
331         if (verbose) {
332                 printf("*****BOARD INFO*****\n");
333                 printf("Version:%d\n", fru_version(brd->ver));
334                 printf("Board Area Length:%d\n", fru_cal_area_len(brd->len));
335         }
336
337         if (fru_check_language(brd->lang_code))
338                 return -EINVAL;
339
340         time = brd->time[2] << 16 | brd->time[1] << 8 |
341                brd->time[0];
342
343         if (verbose)
344                 printf("Time in Minutes from 0:00hrs 1/1/96: %d\n", time);
345
346         data = (u8 *)&brd->manufacturer_type_len;
347
348         for (u8 i = 0; i < (sizeof(boardinfo) / sizeof(*boardinfo)); i++) {
349                 len = fru_check_type_len(*data++, brd->lang_code,
350                                          &type);
351
352                 /* Empty record has no len/type filled */
353                 if (!len) {
354                         debug("%s not found\n", boardinfo[i]);
355                         continue;
356                 }
357
358                 if (type <= FRU_TYPELEN_TYPE_ASCII8 &&
359                     (brd->lang_code == FRU_LANG_CODE_ENGLISH ||
360                      brd->lang_code == FRU_LANG_CODE_ENGLISH_1))
361                         debug("Type code: %s\n", typecode[type]);
362                 else
363                         debug("Type code: %s\n", typecode[type + 1]);
364
365                 switch (type) {
366                 case FRU_TYPELEN_TYPE_BINARY:
367                         debug("Length: %d\n", len);
368                         printf(" %s: 0x%x\n", boardinfo[i], *data);
369                         break;
370                 case FRU_TYPELEN_TYPE_ASCII8:
371                         debug("Length: %d\n", len);
372                         printf(" %s: %s\n", boardinfo[i], data);
373                         break;
374                 default:
375                         debug("Unsupported type %x\n", type);
376                 }
377
378                 data += FRU_BOARD_MAX_LEN;
379         }
380
381         return 0;
382 }
383
384 static void fru_display_common_hdr(struct fru_common_hdr *hdr, int verbose)
385 {
386         if (!verbose)
387                 return;
388
389         printf("*****COMMON HEADER*****\n");
390         printf("Version:%d\n", fru_version(hdr->version));
391         if (hdr->off_internal)
392                 printf("Internal Use Area Offset:%d\n",
393                        fru_cal_area_len(hdr->off_internal));
394         else
395                 printf("*** No Internal Area ***\n");
396
397         if (hdr->off_chassis)
398                 printf("Chassis Info Area Offset:%d\n",
399                        fru_cal_area_len(hdr->off_chassis));
400         else
401                 printf("*** No Chassis Info Area ***\n");
402
403         if (hdr->off_board)
404                 printf("Board Area Offset:%d\n",
405                        fru_cal_area_len(hdr->off_board));
406         else
407                 printf("*** No Board Area ***\n");
408
409         if (hdr->off_product)
410                 printf("Product Info Area Offset:%d\n",
411                        fru_cal_area_len(hdr->off_product));
412         else
413                 printf("*** No Product Info Area ***\n");
414
415         if (hdr->off_multirec)
416                 printf("MultiRecord Area Offset:%d\n",
417                        fru_cal_area_len(hdr->off_multirec));
418         else
419                 printf("*** No MultiRecord Area ***\n");
420 }
421
422 int fru_display(int verbose)
423 {
424         if (!fru_data.captured) {
425                 printf("FRU data not available please run fru parse\n");
426                 return -EINVAL;
427         }
428
429         fru_display_common_hdr(&fru_data.hdr, verbose);
430
431         return fru_display_board(&fru_data.brd, verbose);
432 }