xilinx: cmd: Add support for FRU commands
[platform/kernel/u-boot.git] / board / xilinx / common / fru_ops.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * (C) Copyright 2019 - 2020 Xilinx, Inc.
4  */
5
6 #include <common.h>
7 #include <cpu_func.h>
8 #include <env.h>
9 #include <fdtdec.h>
10 #include <log.h>
11 #include <malloc.h>
12 #include <asm/io.h>
13 #include <asm/arch/hardware.h>
14
15 #include "fru.h"
16
17 struct fru_table fru_data __section(.data);
18
19 static u16 fru_cal_area_len(u8 len)
20 {
21         return len * FRU_COMMON_HDR_LEN_MULTIPLIER;
22 }
23
24 static u8 fru_version(u8 ver)
25 {
26         return ver & FRU_COMMON_HDR_VER_MASK;
27 }
28
29 static int fru_check_language(u8 code)
30 {
31         if (code != FRU_LANG_CODE_ENGLISH && code != FRU_LANG_CODE_ENGLISH_1) {
32                 printf("FRU_ERROR: Only English Language is supported\n");
33                 return -EINVAL;
34         }
35
36         return 0;
37 }
38
39 u8 fru_checksum(u8 *addr, u8 len)
40 {
41         u8 checksum = 0;
42
43         while (len--) {
44                 checksum += *addr;
45                 addr++;
46         }
47
48         return checksum;
49 }
50
51 static int fru_check_type_len(u8 type_len, u8 language, u8 *type)
52 {
53         int len;
54
55         if (type_len == FRU_TYPELEN_EOF)
56                 return -EINVAL;
57
58         *type = (type_len & FRU_TYPELEN_CODE_MASK) >> FRU_TYPELEN_TYPE_SHIFT;
59
60         len = type_len & FRU_TYPELEN_LEN_MASK;
61
62         return len;
63 }
64
65 static int fru_parse_board(unsigned long addr)
66 {
67         u8 i, type;
68         int len;
69         u8 *data, *term;
70
71         memcpy(&fru_data.brd.ver, (void *)addr, 6);
72         addr += 6;
73         data = (u8 *)&fru_data.brd.manufacturer_type_len;
74
75         for (i = 0; ; i++, data += FRU_BOARD_MAX_LEN) {
76                 len = fru_check_type_len(*(u8 *)addr, fru_data.brd.lang_code,
77                                          &type);
78                 /*
79                  * Stop cature if it end of fields
80                  */
81                 if (len == -EINVAL)
82                         break;
83
84                 /* This record type/len field */
85                 *data++ = *(u8 *)addr;
86
87                 /* Add offset to match data */
88                 addr += 1;
89
90                 /* If len is 0 it means empty field that's why skip writing */
91                 if (!len)
92                         continue;
93
94                 /* Record data field */
95                 memcpy(data, (u8 *)addr, len);
96                 term = data + (u8)len;
97                 *term = 0;
98                 addr += len;
99         }
100
101         if (i < FRU_BOARD_AREA_TOTAL_FIELDS) {
102                 printf("Board area require minimum %d fields\n",
103                        FRU_BOARD_AREA_TOTAL_FIELDS);
104                 return -EINVAL;
105         }
106
107         return 0;
108 }
109
110 int fru_capture(unsigned long addr)
111 {
112         struct fru_common_hdr *hdr;
113         u8 checksum = 0;
114
115         checksum = fru_checksum((u8 *)addr, sizeof(struct fru_common_hdr));
116         if (checksum) {
117                 printf("%s Common header CRC error\n", __func__);
118                 return -EINVAL;
119         }
120
121         hdr = (struct fru_common_hdr *)addr;
122
123         memcpy((void *)&fru_data.hdr, (void *)hdr,
124                sizeof(struct fru_common_hdr));
125
126         fru_data.captured = true;
127
128         if (hdr->off_board) {
129                 addr += fru_cal_area_len(hdr->off_board);
130                 fru_parse_board(addr);
131         }
132
133         env_set_hex("fru_addr", addr);
134
135         return 0;
136 }
137
138 static int fru_display_board(struct fru_board_data *brd, int verbose)
139 {
140         u32 time = 0;
141         u8 type;
142         int len;
143         u8 *data;
144         static const char * const typecode[] = {
145                 "Binary/Unspecified",
146                 "BCD plus",
147                 "6-bit ASCII",
148                 "8-bit ASCII",
149                 "2-byte UNICODE"
150         };
151         static const char * const boardinfo[] = {
152                 "Manufacturer Name",
153                 "Product Name",
154                 "Serial No",
155                 "Part Number",
156                 "File ID"
157         };
158
159         if (verbose) {
160                 printf("*****BOARD INFO*****\n");
161                 printf("Version:%d\n", fru_version(brd->ver));
162                 printf("Board Area Length:%d\n", fru_cal_area_len(brd->len));
163         }
164
165         if (fru_check_language(brd->lang_code))
166                 return -EINVAL;
167
168         time = brd->time[2] << 16 | brd->time[1] << 8 |
169                brd->time[0];
170
171         if (verbose)
172                 printf("Time in Minutes from 0:00hrs 1/1/96: %d\n", time);
173
174         data = (u8 *)&brd->manufacturer_type_len;
175
176         for (u8 i = 0; i < (sizeof(boardinfo) / sizeof(*boardinfo)); i++) {
177                 len = fru_check_type_len(*data++, brd->lang_code,
178                                          &type);
179                 if (len == -EINVAL) {
180                         printf("**** EOF for Board Area ****\n");
181                         break;
182                 }
183
184                 if (type <= FRU_TYPELEN_TYPE_ASCII8 &&
185                     (brd->lang_code == FRU_LANG_CODE_ENGLISH ||
186                      brd->lang_code == FRU_LANG_CODE_ENGLISH_1))
187                         debug("Type code: %s\n", typecode[type]);
188                 else
189                         debug("Type code: %s\n", typecode[type + 1]);
190
191                 if (!len) {
192                         debug("%s not found\n", boardinfo[i]);
193                         continue;
194                 }
195
196                 switch (type) {
197                 case FRU_TYPELEN_TYPE_BINARY:
198                         debug("Length: %d\n", len);
199                         printf(" %s: 0x%x\n", boardinfo[i], *data);
200                         break;
201                 case FRU_TYPELEN_TYPE_ASCII8:
202                         debug("Length: %d\n", len);
203                         printf(" %s: %s\n", boardinfo[i], data);
204                         break;
205                 default:
206                         debug("Unsupported type %x\n", type);
207                 }
208
209                 data += FRU_BOARD_MAX_LEN;
210         }
211
212         return 0;
213 }
214
215 static void fru_display_common_hdr(struct fru_common_hdr *hdr, int verbose)
216 {
217         if (!verbose)
218                 return;
219
220         printf("*****COMMON HEADER*****\n");
221         printf("Version:%d\n", fru_version(hdr->version));
222         if (hdr->off_internal)
223                 printf("Internal Use Area Offset:%d\n",
224                        fru_cal_area_len(hdr->off_internal));
225         else
226                 printf("*** No Internal Area ***\n");
227
228         if (hdr->off_chassis)
229                 printf("Chassis Info Area Offset:%d\n",
230                        fru_cal_area_len(hdr->off_chassis));
231         else
232                 printf("*** No Chassis Info Area ***\n");
233
234         if (hdr->off_board)
235                 printf("Board Area Offset:%d\n",
236                        fru_cal_area_len(hdr->off_board));
237         else
238                 printf("*** No Board Area ***\n");
239
240         if (hdr->off_product)
241                 printf("Product Info Area Offset:%d\n",
242                        fru_cal_area_len(hdr->off_product));
243         else
244                 printf("*** No Product Info Area ***\n");
245
246         if (hdr->off_multirec)
247                 printf("MultiRecord Area Offset:%d\n",
248                        fru_cal_area_len(hdr->off_multirec));
249         else
250                 printf("*** No MultiRecord Area ***\n");
251 }
252
253 int fru_display(int verbose)
254 {
255         if (!fru_data.captured) {
256                 printf("FRU data not available please run fru parse\n");
257                 return -EINVAL;
258         }
259
260         fru_display_common_hdr(&fru_data.hdr, verbose);
261
262         return fru_display_board(&fru_data.brd, verbose);
263 }