]>
Commit | Line | Data |
---|---|---|
4f745bf4 HB |
1 | /* |
2 | * (C) Copyright 2011 | |
3 | * Holger Brunck, Keymile GmbH Hannover, [email protected] | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
4f745bf4 HB |
6 | */ |
7 | ||
8 | #include <common.h> | |
eca86fad | 9 | #include <cli_hush.h> |
4f745bf4 HB |
10 | #include <i2c.h> |
11 | #include "common.h" | |
12 | ||
16ac90c7 VL |
13 | #define MAC_STR_SZ 20 |
14 | ||
283857da | 15 | static int ivm_calc_crc(unsigned char *buf, int len) |
4f745bf4 HB |
16 | { |
17 | const unsigned short crc_tab[16] = { | |
18 | 0x0000, 0xCC01, 0xD801, 0x1400, | |
19 | 0xF001, 0x3C00, 0x2800, 0xE401, | |
20 | 0xA001, 0x6C00, 0x7800, 0xB401, | |
21 | 0x5000, 0x9C01, 0x8801, 0x4400}; | |
22 | ||
23 | unsigned short crc = 0; /* final result */ | |
24 | unsigned short r1 = 0; /* temp */ | |
25 | unsigned char byte = 0; /* input buffer */ | |
26 | int i; | |
27 | ||
28 | /* calculate CRC from array data */ | |
29 | for (i = 0; i < len; i++) { | |
30 | byte = buf[i]; | |
31 | ||
32 | /* lower 4 bits */ | |
33 | r1 = crc_tab[crc & 0xF]; | |
34 | crc = ((crc) >> 4) & 0x0FFF; | |
35 | crc = crc ^ r1 ^ crc_tab[byte & 0xF]; | |
36 | ||
37 | /* upper 4 bits */ | |
38 | r1 = crc_tab[crc & 0xF]; | |
39 | crc = (crc >> 4) & 0x0FFF; | |
40 | crc = crc ^ r1 ^ crc_tab[(byte >> 4) & 0xF]; | |
41 | } | |
42 | return crc; | |
43 | } | |
44 | ||
45 | static int ivm_set_value(char *name, char *value) | |
46 | { | |
47 | char tempbuf[256]; | |
48 | ||
49 | if (value != NULL) { | |
50 | sprintf(tempbuf, "%s=%s", name, value); | |
51 | return set_local_var(tempbuf, 0); | |
52 | } else { | |
53 | unset_local_var(name); | |
54 | } | |
55 | return 0; | |
56 | } | |
57 | ||
58 | static int ivm_get_value(unsigned char *buf, int len, char *name, int off, | |
59 | int check) | |
60 | { | |
61 | unsigned short val; | |
62 | unsigned char valbuf[30]; | |
63 | ||
64 | if ((buf[off + 0] != buf[off + 2]) && | |
65 | (buf[off + 2] != buf[off + 4])) { | |
66 | printf("%s Error corrupted %s\n", __func__, name); | |
67 | val = -1; | |
68 | } else { | |
69 | val = buf[off + 0] + (buf[off + 1] << 8); | |
70 | if ((val == 0) && (check == 1)) | |
71 | val = -1; | |
72 | } | |
73 | sprintf((char *)valbuf, "%x", val); | |
74 | ivm_set_value(name, (char *)valbuf); | |
75 | return val; | |
76 | } | |
77 | ||
78 | #define INV_BLOCKSIZE 0x100 | |
79 | #define INV_DATAADDRESS 0x21 | |
80 | #define INVENTORYDATASIZE (INV_BLOCKSIZE - INV_DATAADDRESS - 3) | |
81 | ||
82 | #define IVM_POS_SHORT_TEXT 0 | |
83 | #define IVM_POS_MANU_ID 1 | |
84 | #define IVM_POS_MANU_SERIAL 2 | |
85 | #define IVM_POS_PART_NUMBER 3 | |
86 | #define IVM_POS_BUILD_STATE 4 | |
87 | #define IVM_POS_SUPPLIER_PART_NUMBER 5 | |
88 | #define IVM_POS_DELIVERY_DATE 6 | |
89 | #define IVM_POS_SUPPLIER_BUILD_STATE 7 | |
90 | #define IVM_POS_CUSTOMER_ID 8 | |
91 | #define IVM_POS_CUSTOMER_PROD_ID 9 | |
92 | #define IVM_POS_HISTORY 10 | |
93 | #define IVM_POS_SYMBOL_ONLY 11 | |
94 | ||
95 | static char convert_char(char c) | |
96 | { | |
97 | return (c < ' ' || c > '~') ? '.' : c; | |
98 | } | |
99 | ||
100 | static int ivm_findinventorystring(int type, | |
101 | unsigned char *const string, | |
102 | unsigned long maxlen, | |
103 | unsigned char *buf) | |
104 | { | |
105 | int xcode = 0; | |
106 | unsigned long cr = 0; | |
107 | unsigned long addr = INV_DATAADDRESS; | |
108 | unsigned long size = 0; | |
109 | unsigned long nr = type; | |
110 | int stop = 0; /* stop on semicolon */ | |
111 | ||
112 | memset(string, '\0', maxlen); | |
113 | switch (type) { | |
114 | case IVM_POS_SYMBOL_ONLY: | |
115 | nr = 0; | |
116 | stop = 1; | |
117 | break; | |
118 | default: | |
119 | nr = type; | |
120 | stop = 0; | |
121 | } | |
122 | ||
123 | /* Look for the requested number of CR. */ | |
124 | while ((cr != nr) && (addr < INVENTORYDATASIZE)) { | |
2716077c | 125 | if (buf[addr] == '\r') |
4f745bf4 HB |
126 | cr++; |
127 | addr++; | |
128 | } | |
129 | ||
130 | /* | |
131 | * the expected number of CR was found until the end of the IVM | |
132 | * content --> fill string | |
133 | */ | |
134 | if (addr < INVENTORYDATASIZE) { | |
135 | /* Copy the IVM string in the corresponding string */ | |
136 | for (; (buf[addr] != '\r') && | |
137 | ((buf[addr] != ';') || (!stop)) && | |
138 | (size < (maxlen - 1) && | |
139 | (addr < INVENTORYDATASIZE)); addr++) { | |
140 | size += sprintf((char *)string + size, "%c", | |
141 | convert_char (buf[addr])); | |
142 | } | |
143 | ||
144 | /* | |
145 | * copy phase is done: check if everything is ok. If not, | |
146 | * the inventory data is most probably corrupted: tell | |
147 | * the world there is a problem! | |
148 | */ | |
149 | if (addr == INVENTORYDATASIZE) { | |
150 | xcode = -1; | |
151 | printf("Error end of string not found\n"); | |
62c9b960 | 152 | } else if ((size > (maxlen - 1)) && |
4f745bf4 HB |
153 | (buf[addr] != '\r')) { |
154 | xcode = -1; | |
155 | printf("string too long till next CR\n"); | |
156 | } | |
157 | } else { | |
158 | /* | |
159 | * some CR are missing... | |
160 | * the inventory data is most probably corrupted | |
161 | */ | |
162 | xcode = -1; | |
163 | printf("not enough cr found\n"); | |
164 | } | |
165 | return xcode; | |
166 | } | |
167 | ||
168 | #define GET_STRING(name, which, len) \ | |
169 | if (ivm_findinventorystring(which, valbuf, len, buf) == 0) { \ | |
170 | ivm_set_value(name, (char *)valbuf); \ | |
171 | } | |
172 | ||
173 | static int ivm_check_crc(unsigned char *buf, int block) | |
174 | { | |
175 | unsigned long crc; | |
176 | unsigned long crceeprom; | |
177 | ||
178 | crc = ivm_calc_crc(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 2); | |
179 | crceeprom = (buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 1] + \ | |
180 | buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 2] * 256); | |
181 | if (crc != crceeprom) { | |
182 | if (block == 0) | |
183 | printf("Error CRC Block: %d EEprom: calculated: \ | |
184 | %lx EEprom: %lx\n", block, crc, crceeprom); | |
185 | return -1; | |
186 | } | |
187 | return 0; | |
188 | } | |
189 | ||
16ac90c7 VL |
190 | /* take care of the possible MAC address offset and the IVM content offset */ |
191 | static int process_mac(unsigned char *valbuf, unsigned char *buf, | |
b2f2c7be | 192 | int offset, bool unique) |
322bb205 | 193 | { |
16ac90c7 | 194 | unsigned char mac[6]; |
322bb205 HB |
195 | unsigned long val = (buf[4] << 16) + (buf[5] << 8) + buf[6]; |
196 | ||
16ac90c7 VL |
197 | /* use an intermediate buffer, to not change IVM content |
198 | * MAC address is at offset 1 | |
199 | */ | |
200 | memcpy(mac, buf+1, 6); | |
322bb205 | 201 | |
b2f2c7be HB |
202 | /* MAC adress can be set to locally administred, this is only allowed |
203 | * for interfaces which have now connection to the outside. For these | |
204 | * addresses we need to set the second bit in the first byte. | |
205 | */ | |
206 | if (!unique) | |
207 | mac[0] |= 0x2; | |
208 | ||
16ac90c7 VL |
209 | if (offset) { |
210 | val += offset; | |
211 | mac[3] = (val >> 16) & 0xff; | |
212 | mac[4] = (val >> 8) & 0xff; | |
213 | mac[5] = val & 0xff; | |
214 | } | |
215 | ||
216 | sprintf((char *)valbuf, "%pM", mac); | |
322bb205 HB |
217 | return 0; |
218 | } | |
219 | ||
4f745bf4 HB |
220 | static int ivm_analyze_block2(unsigned char *buf, int len) |
221 | { | |
16ac90c7 | 222 | unsigned char valbuf[MAC_STR_SZ]; |
4f745bf4 HB |
223 | unsigned long count; |
224 | ||
6478021f HB |
225 | /* IVM_MAC Adress begins at offset 1 */ |
226 | sprintf((char *)valbuf, "%pM", buf + 1); | |
4f745bf4 | 227 | ivm_set_value("IVM_MacAddress", (char *)valbuf); |
4f745bf4 HB |
228 | /* IVM_MacCount */ |
229 | count = (buf[10] << 24) + | |
230 | (buf[11] << 16) + | |
231 | (buf[12] << 8) + | |
232 | buf[13]; | |
233 | if (count == 0xffffffff) | |
234 | count = 1; | |
235 | sprintf((char *)valbuf, "%lx", count); | |
236 | ivm_set_value("IVM_MacCount", (char *)valbuf); | |
237 | return 0; | |
238 | } | |
239 | ||
16ac90c7 | 240 | int ivm_analyze_eeprom(unsigned char *buf, int len) |
4f745bf4 HB |
241 | { |
242 | unsigned short val; | |
243 | unsigned char valbuf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN]; | |
244 | unsigned char *tmp; | |
245 | ||
246 | if (ivm_check_crc(buf, 0) != 0) | |
247 | return -1; | |
248 | ||
249 | ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN, | |
250 | "IVM_BoardId", 0, 1); | |
251 | val = ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN, | |
252 | "IVM_HWKey", 6, 1); | |
253 | if (val != 0xffff) { | |
254 | sprintf((char *)valbuf, "%x", ((val / 100) % 10)); | |
255 | ivm_set_value("IVM_HWVariant", (char *)valbuf); | |
256 | sprintf((char *)valbuf, "%x", (val % 100)); | |
257 | ivm_set_value("IVM_HWVersion", (char *)valbuf); | |
258 | } | |
259 | ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN, | |
260 | "IVM_Functions", 12, 0); | |
261 | ||
262 | GET_STRING("IVM_Symbol", IVM_POS_SYMBOL_ONLY, 8) | |
263 | GET_STRING("IVM_DeviceName", IVM_POS_SHORT_TEXT, 64) | |
00caae6d | 264 | tmp = (unsigned char *)env_get("IVM_DeviceName"); |
4f745bf4 HB |
265 | if (tmp) { |
266 | int len = strlen((char *)tmp); | |
267 | int i = 0; | |
268 | ||
269 | while (i < len) { | |
270 | if (tmp[i] == ';') { | |
271 | ivm_set_value("IVM_ShortText", | |
272 | (char *)&tmp[i + 1]); | |
273 | break; | |
274 | } | |
275 | i++; | |
276 | } | |
277 | if (i >= len) | |
278 | ivm_set_value("IVM_ShortText", NULL); | |
279 | } else { | |
280 | ivm_set_value("IVM_ShortText", NULL); | |
281 | } | |
282 | GET_STRING("IVM_ManufacturerID", IVM_POS_MANU_ID, 32) | |
283 | GET_STRING("IVM_ManufacturerSerialNumber", IVM_POS_MANU_SERIAL, 20) | |
284 | GET_STRING("IVM_ManufacturerPartNumber", IVM_POS_PART_NUMBER, 32) | |
285 | GET_STRING("IVM_ManufacturerBuildState", IVM_POS_BUILD_STATE, 32) | |
286 | GET_STRING("IVM_SupplierPartNumber", IVM_POS_SUPPLIER_PART_NUMBER, 32) | |
287 | GET_STRING("IVM_DelieveryDate", IVM_POS_DELIVERY_DATE, 32) | |
288 | GET_STRING("IVM_SupplierBuildState", IVM_POS_SUPPLIER_BUILD_STATE, 32) | |
289 | GET_STRING("IVM_CustomerID", IVM_POS_CUSTOMER_ID, 32) | |
290 | GET_STRING("IVM_CustomerProductID", IVM_POS_CUSTOMER_PROD_ID, 32) | |
291 | ||
292 | if (ivm_check_crc(&buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN * 2], 2) != 0) | |
293 | return 0; | |
294 | ivm_analyze_block2(&buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN * 2], | |
295 | CONFIG_SYS_IVM_EEPROM_PAGE_LEN); | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
16ac90c7 VL |
300 | static int ivm_populate_env(unsigned char *buf, int len) |
301 | { | |
302 | unsigned char *page2; | |
303 | unsigned char valbuf[MAC_STR_SZ]; | |
304 | ||
305 | /* do we have the page 2 filled ? if not return */ | |
306 | if (ivm_check_crc(buf, 2)) | |
307 | return 0; | |
308 | page2 = &buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN*2]; | |
309 | ||
b2f2c7be | 310 | #ifndef CONFIG_KMTEGR1 |
16ac90c7 | 311 | /* if an offset is defined, add it */ |
b2f2c7be | 312 | process_mac(valbuf, page2, CONFIG_PIGGY_MAC_ADRESS_OFFSET, true); |
382bee57 | 313 | env_set((char *)"ethaddr", (char *)valbuf); |
16ac90c7 VL |
314 | #ifdef CONFIG_KMVECT1 |
315 | /* KMVECT1 has two ethernet interfaces */ | |
b2f2c7be | 316 | process_mac(valbuf, page2, 1, true); |
382bee57 | 317 | env_set((char *)"eth1addr", (char *)valbuf); |
b2f2c7be HB |
318 | #endif |
319 | #else | |
320 | /* KMTEGR1 has a special setup. eth0 has no connection to the outside and | |
321 | * gets an locally administred MAC address, eth1 is the debug interface and | |
322 | * gets the official MAC address from the IVM | |
323 | */ | |
324 | process_mac(valbuf, page2, CONFIG_PIGGY_MAC_ADRESS_OFFSET, false); | |
382bee57 | 325 | env_set((char *)"ethaddr", (char *)valbuf); |
b2f2c7be | 326 | process_mac(valbuf, page2, CONFIG_PIGGY_MAC_ADRESS_OFFSET, true); |
382bee57 | 327 | env_set((char *)"eth1addr", (char *)valbuf); |
16ac90c7 VL |
328 | #endif |
329 | ||
330 | return 0; | |
331 | } | |
332 | ||
60c4ae00 | 333 | int ivm_read_eeprom(unsigned char *buf, int len) |
16ac90c7 VL |
334 | { |
335 | int ret; | |
336 | ||
337 | i2c_set_bus_num(CONFIG_KM_IVM_BUS); | |
338 | /* add deblocking here */ | |
339 | i2c_make_abort(); | |
340 | ||
341 | ret = i2c_read(CONFIG_SYS_IVM_EEPROM_ADR, 0, 1, buf, len); | |
342 | if (ret != 0) { | |
343 | printf("Error reading EEprom\n"); | |
344 | return -2; | |
345 | } | |
346 | ||
347 | return ivm_populate_env(buf, len); | |
348 | } |