1 /* 2 * (C) Copyright 2011 CompuLab, Ltd. <www.compulab.co.il> 3 * 4 * Authors: Nikita Kiryanov <nikita@compulab.co.il> 5 * Igor Grinberg <grinberg@compulab.co.il> 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <i2c.h> 12 #include <eeprom_layout.h> 13 #include <eeprom_field.h> 14 #include <asm/setup.h> 15 #include <linux/kernel.h> 16 #include "eeprom.h" 17 18 #ifndef CONFIG_SYS_I2C_EEPROM_ADDR 19 # define CONFIG_SYS_I2C_EEPROM_ADDR 0x50 20 # define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1 21 #endif 22 23 #ifndef CONFIG_SYS_I2C_EEPROM_BUS 24 #define CONFIG_SYS_I2C_EEPROM_BUS 0 25 #endif 26 27 #define EEPROM_LAYOUT_VER_OFFSET 44 28 #define BOARD_SERIAL_OFFSET 20 29 #define BOARD_SERIAL_OFFSET_LEGACY 8 30 #define BOARD_REV_OFFSET 0 31 #define BOARD_REV_OFFSET_LEGACY 6 32 #define BOARD_REV_SIZE 2 33 #define PRODUCT_NAME_OFFSET 128 34 #define PRODUCT_NAME_SIZE 16 35 #define MAC_ADDR_OFFSET 4 36 #define MAC_ADDR_OFFSET_LEGACY 0 37 38 #define LAYOUT_INVALID 0 39 #define LAYOUT_LEGACY 0xff 40 41 static int cl_eeprom_bus; 42 static int cl_eeprom_layout; /* Implicitly LAYOUT_INVALID */ 43 44 static int cl_eeprom_read(uint offset, uchar *buf, int len) 45 { 46 int res; 47 unsigned int current_i2c_bus = i2c_get_bus_num(); 48 49 res = i2c_set_bus_num(cl_eeprom_bus); 50 if (res < 0) 51 return res; 52 53 res = i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, offset, 54 CONFIG_SYS_I2C_EEPROM_ADDR_LEN, buf, len); 55 56 i2c_set_bus_num(current_i2c_bus); 57 58 return res; 59 } 60 61 static int cl_eeprom_setup(uint eeprom_bus) 62 { 63 int res; 64 65 /* 66 * We know the setup was already done when the layout is set to a valid 67 * value and we're using the same bus as before. 68 */ 69 if (cl_eeprom_layout != LAYOUT_INVALID && eeprom_bus == cl_eeprom_bus) 70 return 0; 71 72 cl_eeprom_bus = eeprom_bus; 73 res = cl_eeprom_read(EEPROM_LAYOUT_VER_OFFSET, 74 (uchar *)&cl_eeprom_layout, 1); 75 if (res) { 76 cl_eeprom_layout = LAYOUT_INVALID; 77 return res; 78 } 79 80 if (cl_eeprom_layout == 0 || cl_eeprom_layout >= 0x20) 81 cl_eeprom_layout = LAYOUT_LEGACY; 82 83 return 0; 84 } 85 86 void get_board_serial(struct tag_serialnr *serialnr) 87 { 88 u32 serial[2]; 89 uint offset; 90 91 memset(serialnr, 0, sizeof(*serialnr)); 92 93 if (cl_eeprom_setup(CONFIG_SYS_I2C_EEPROM_BUS)) 94 return; 95 96 offset = (cl_eeprom_layout != LAYOUT_LEGACY) ? 97 BOARD_SERIAL_OFFSET : BOARD_SERIAL_OFFSET_LEGACY; 98 99 if (cl_eeprom_read(offset, (uchar *)serial, 8)) 100 return; 101 102 if (serial[0] != 0xffffffff && serial[1] != 0xffffffff) { 103 serialnr->low = serial[0]; 104 serialnr->high = serial[1]; 105 } 106 } 107 108 /* 109 * Routine: cl_eeprom_read_mac_addr 110 * Description: read mac address and store it in buf. 111 */ 112 int cl_eeprom_read_mac_addr(uchar *buf, uint eeprom_bus) 113 { 114 uint offset; 115 int err; 116 117 err = cl_eeprom_setup(eeprom_bus); 118 if (err) 119 return err; 120 121 offset = (cl_eeprom_layout != LAYOUT_LEGACY) ? 122 MAC_ADDR_OFFSET : MAC_ADDR_OFFSET_LEGACY; 123 124 return cl_eeprom_read(offset, buf, 6); 125 } 126 127 static u32 board_rev; 128 129 /* 130 * Routine: cl_eeprom_get_board_rev 131 * Description: read system revision from eeprom 132 */ 133 u32 cl_eeprom_get_board_rev(uint eeprom_bus) 134 { 135 char str[5]; /* Legacy representation can contain at most 4 digits */ 136 uint offset = BOARD_REV_OFFSET_LEGACY; 137 138 if (board_rev) 139 return board_rev; 140 141 if (cl_eeprom_setup(eeprom_bus)) 142 return 0; 143 144 if (cl_eeprom_layout != LAYOUT_LEGACY) 145 offset = BOARD_REV_OFFSET; 146 147 if (cl_eeprom_read(offset, (uchar *)&board_rev, BOARD_REV_SIZE)) 148 return 0; 149 150 /* 151 * Convert legacy syntactic representation to semantic 152 * representation. i.e. for rev 1.00: 0x100 --> 0x64 153 */ 154 if (cl_eeprom_layout == LAYOUT_LEGACY) { 155 sprintf(str, "%x", board_rev); 156 board_rev = simple_strtoul(str, NULL, 10); 157 } 158 159 return board_rev; 160 }; 161 162 /* 163 * Routine: cl_eeprom_get_board_rev 164 * Description: read system revision from eeprom 165 * 166 * @buf: buffer to store the product name 167 * @eeprom_bus: i2c bus num of the eeprom 168 * 169 * @return: 0 on success, < 0 on failure 170 */ 171 int cl_eeprom_get_product_name(uchar *buf, uint eeprom_bus) 172 { 173 int err; 174 175 if (buf == NULL) 176 return -EINVAL; 177 178 err = cl_eeprom_setup(eeprom_bus); 179 if (err) 180 return err; 181 182 err = cl_eeprom_read(PRODUCT_NAME_OFFSET, buf, PRODUCT_NAME_SIZE); 183 if (!err) /* Protect ourselves from invalid data (unterminated str) */ 184 buf[PRODUCT_NAME_SIZE - 1] = '\0'; 185 186 return err; 187 } 188 189 #ifdef CONFIG_CMD_EEPROM_LAYOUT 190 /** 191 * eeprom_field_print_bin_ver() - print a "version field" which contains binary 192 * data 193 * 194 * Treat the field data as simple binary data, and print it formatted as a 195 * version number (2 digits after decimal point). 196 * The field size must be exactly 2 bytes. 197 * 198 * Sample output: 199 * Field Name 123.45 200 * 201 * @field: an initialized field to print 202 */ 203 void eeprom_field_print_bin_ver(const struct eeprom_field *field) 204 { 205 if ((field->buf[0] == 0xff) && (field->buf[1] == 0xff)) { 206 field->buf[0] = 0; 207 field->buf[1] = 0; 208 } 209 210 printf(PRINT_FIELD_SEGMENT, field->name); 211 int major = (field->buf[1] << 8 | field->buf[0]) / 100; 212 int minor = (field->buf[1] << 8 | field->buf[0]) - major * 100; 213 printf("%d.%02d\n", major, minor); 214 } 215 216 /** 217 * eeprom_field_update_bin_ver() - update a "version field" which contains 218 * binary data 219 * 220 * This function takes a version string in the form of x.y (x and y are both 221 * decimal values, y is limited to two digits), translates it to the binary 222 * form, then writes it to the field. The field size must be exactly 2 bytes. 223 * 224 * This function strictly enforces the data syntax, and will not update the 225 * field if there's any deviation from it. It also protects from overflow. 226 * 227 * @field: an initialized field 228 * @value: a version string 229 * 230 * Returns 0 on success, -1 on failure. 231 */ 232 int eeprom_field_update_bin_ver(struct eeprom_field *field, char *value) 233 { 234 char *endptr; 235 char *tok = strtok(value, "."); 236 if (tok == NULL) 237 return -1; 238 239 int num = simple_strtol(tok, &endptr, 0); 240 if (*endptr != '\0') 241 return -1; 242 243 tok = strtok(NULL, ""); 244 if (tok == NULL) 245 return -1; 246 247 int remainder = simple_strtol(tok, &endptr, 0); 248 if (*endptr != '\0') 249 return -1; 250 251 num = num * 100 + remainder; 252 if (num >> 16) 253 return -1; 254 255 field->buf[0] = (unsigned char)num; 256 field->buf[1] = num >> 8; 257 258 return 0; 259 } 260 261 char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 262 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; 263 264 /** 265 * eeprom_field_print_date() - print a field which contains date data 266 * 267 * Treat the field data as simple binary data, and print it formatted as a date. 268 * Sample output: 269 * Field Name 07/Feb/2014 270 * Field Name 56/BAD/9999 271 * 272 * @field: an initialized field to print 273 */ 274 void eeprom_field_print_date(const struct eeprom_field *field) 275 { 276 printf(PRINT_FIELD_SEGMENT, field->name); 277 printf("%02d/", field->buf[0]); 278 if (field->buf[1] >= 1 && field->buf[1] <= 12) 279 printf("%s", months[field->buf[1] - 1]); 280 else 281 printf("BAD"); 282 283 printf("/%d\n", field->buf[3] << 8 | field->buf[2]); 284 } 285 286 static int validate_date(unsigned char day, unsigned char month, 287 unsigned int year) 288 { 289 int days_in_february; 290 291 switch (month) { 292 case 0: 293 case 2: 294 case 4: 295 case 6: 296 case 7: 297 case 9: 298 case 11: 299 if (day > 31) 300 return -1; 301 break; 302 case 3: 303 case 5: 304 case 8: 305 case 10: 306 if (day > 30) 307 return -1; 308 break; 309 case 1: 310 days_in_february = 28; 311 if (year % 4 == 0) { 312 if (year % 100 != 0) 313 days_in_february = 29; 314 else if (year % 400 == 0) 315 days_in_february = 29; 316 } 317 318 if (day > days_in_february) 319 return -1; 320 321 break; 322 default: 323 return -1; 324 } 325 326 return 0; 327 } 328 329 /** 330 * eeprom_field_update_date() - update a date field which contains binary data 331 * 332 * This function takes a date string in the form of x/Mon/y (x and y are both 333 * decimal values), translates it to the binary representation, then writes it 334 * to the field. 335 * 336 * This function strictly enforces the data syntax, and will not update the 337 * field if there's any deviation from it. It also protects from overflow in the 338 * year value, and checks the validity of the date. 339 * 340 * @field: an initialized field 341 * @value: a date string 342 * 343 * Returns 0 on success, -1 on failure. 344 */ 345 int eeprom_field_update_date(struct eeprom_field *field, char *value) 346 { 347 char *endptr; 348 char *tok1 = strtok(value, "/"); 349 char *tok2 = strtok(NULL, "/"); 350 char *tok3 = strtok(NULL, "/"); 351 352 if (tok1 == NULL || tok2 == NULL || tok3 == NULL) { 353 printf("%s: syntax error\n", field->name); 354 return -1; 355 } 356 357 unsigned char day = (unsigned char)simple_strtol(tok1, &endptr, 0); 358 if (*endptr != '\0' || day == 0) { 359 printf("%s: invalid day\n", field->name); 360 return -1; 361 } 362 363 unsigned char month; 364 for (month = 1; month <= 12; month++) 365 if (!strcmp(tok2, months[month - 1])) 366 break; 367 368 unsigned int year = simple_strtol(tok3, &endptr, 0); 369 if (*endptr != '\0') { 370 printf("%s: invalid year\n", field->name); 371 return -1; 372 } 373 374 if (validate_date(day, month - 1, year)) { 375 printf("%s: invalid date\n", field->name); 376 return -1; 377 } 378 379 if (year >> 16) { 380 printf("%s: year overflow\n", field->name); 381 return -1; 382 } 383 384 field->buf[0] = day; 385 field->buf[1] = month; 386 field->buf[2] = (unsigned char)year; 387 field->buf[3] = (unsigned char)(year >> 8); 388 389 return 0; 390 } 391 392 #define LAYOUT_VERSION_LEGACY 1 393 #define LAYOUT_VERSION_VER1 2 394 #define LAYOUT_VERSION_VER2 3 395 #define LAYOUT_VERSION_VER3 4 396 397 extern struct eeprom_field layout_unknown[1]; 398 399 #define DEFINE_PRINT_UPDATE(x) eeprom_field_print_##x, eeprom_field_update_##x 400 401 #ifdef CONFIG_CM_T3X 402 struct eeprom_field layout_legacy[5] = { 403 { "MAC address", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 404 { "Board Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin) }, 405 { "Serial Number", 8, NULL, DEFINE_PRINT_UPDATE(bin) }, 406 { "Board Configuration", 64, NULL, DEFINE_PRINT_UPDATE(ascii) }, 407 { RESERVED_FIELDS, 176, NULL, eeprom_field_print_reserved, 408 eeprom_field_update_ascii }, 409 }; 410 #else 411 #define layout_legacy layout_unknown 412 #endif 413 414 #if defined(CONFIG_CM_T3X) || defined(CONFIG_CM_T3517) 415 struct eeprom_field layout_v1[12] = { 416 { "Major Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) }, 417 { "Minor Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) }, 418 { "1st MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 419 { "2nd MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 420 { "Production Date", 4, NULL, DEFINE_PRINT_UPDATE(date) }, 421 { "Serial Number", 12, NULL, DEFINE_PRINT_UPDATE(bin_rev) }, 422 { RESERVED_FIELDS, 96, NULL, DEFINE_PRINT_UPDATE(reserved) }, 423 { "Product Name", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 424 { "Product Options #1", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 425 { "Product Options #2", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 426 { "Product Options #3", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 427 { RESERVED_FIELDS, 64, NULL, eeprom_field_print_reserved, 428 eeprom_field_update_ascii }, 429 }; 430 #else 431 #define layout_v1 layout_unknown 432 #endif 433 434 struct eeprom_field layout_v2[15] = { 435 { "Major Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) }, 436 { "Minor Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) }, 437 { "1st MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 438 { "2nd MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 439 { "Production Date", 4, NULL, DEFINE_PRINT_UPDATE(date) }, 440 { "Serial Number", 12, NULL, DEFINE_PRINT_UPDATE(bin_rev) }, 441 { "3rd MAC Address (WIFI)", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 442 { "4th MAC Address (Bluetooth)", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 443 { "Layout Version", 1, NULL, DEFINE_PRINT_UPDATE(bin) }, 444 { RESERVED_FIELDS, 83, NULL, DEFINE_PRINT_UPDATE(reserved) }, 445 { "Product Name", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 446 { "Product Options #1", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 447 { "Product Options #2", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 448 { "Product Options #3", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 449 { RESERVED_FIELDS, 64, NULL, eeprom_field_print_reserved, 450 eeprom_field_update_ascii }, 451 }; 452 453 struct eeprom_field layout_v3[16] = { 454 { "Major Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) }, 455 { "Minor Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) }, 456 { "1st MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 457 { "2nd MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 458 { "Production Date", 4, NULL, DEFINE_PRINT_UPDATE(date) }, 459 { "Serial Number", 12, NULL, DEFINE_PRINT_UPDATE(bin_rev) }, 460 { "3rd MAC Address (WIFI)", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 461 { "4th MAC Address (Bluetooth)", 6, NULL, DEFINE_PRINT_UPDATE(mac) }, 462 { "Layout Version", 1, NULL, DEFINE_PRINT_UPDATE(bin) }, 463 { "CompuLab EEPROM ID", 3, NULL, DEFINE_PRINT_UPDATE(bin) }, 464 { RESERVED_FIELDS, 80, NULL, DEFINE_PRINT_UPDATE(reserved) }, 465 { "Product Name", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 466 { "Product Options #1", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 467 { "Product Options #2", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 468 { "Product Options #3", 16, NULL, DEFINE_PRINT_UPDATE(ascii) }, 469 { RESERVED_FIELDS, 64, NULL, eeprom_field_print_reserved, 470 eeprom_field_update_ascii }, 471 }; 472 473 void eeprom_layout_assign(struct eeprom_layout *layout, int layout_version) 474 { 475 switch (layout->layout_version) { 476 case LAYOUT_VERSION_LEGACY: 477 layout->fields = layout_legacy; 478 layout->num_of_fields = ARRAY_SIZE(layout_legacy); 479 break; 480 case LAYOUT_VERSION_VER1: 481 layout->fields = layout_v1; 482 layout->num_of_fields = ARRAY_SIZE(layout_v1); 483 break; 484 case LAYOUT_VERSION_VER2: 485 layout->fields = layout_v2; 486 layout->num_of_fields = ARRAY_SIZE(layout_v2); 487 break; 488 case LAYOUT_VERSION_VER3: 489 layout->fields = layout_v3; 490 layout->num_of_fields = ARRAY_SIZE(layout_v3); 491 break; 492 default: 493 __eeprom_layout_assign(layout, layout_version); 494 } 495 } 496 497 int eeprom_parse_layout_version(char *str) 498 { 499 if (!strcmp(str, "legacy")) 500 return LAYOUT_VERSION_LEGACY; 501 else if (!strcmp(str, "v1")) 502 return LAYOUT_VERSION_VER1; 503 else if (!strcmp(str, "v2")) 504 return LAYOUT_VERSION_VER2; 505 else if (!strcmp(str, "v3")) 506 return LAYOUT_VERSION_VER3; 507 else 508 return LAYOUT_VERSION_UNRECOGNIZED; 509 } 510 511 int eeprom_layout_detect(unsigned char *data) 512 { 513 switch (data[EEPROM_LAYOUT_VER_OFFSET]) { 514 case 0xff: 515 case 0: 516 return LAYOUT_VERSION_VER1; 517 case 2: 518 return LAYOUT_VERSION_VER2; 519 case 3: 520 return LAYOUT_VERSION_VER3; 521 } 522 523 if (data[EEPROM_LAYOUT_VER_OFFSET] >= 0x20) 524 return LAYOUT_VERSION_LEGACY; 525 526 return LAYOUT_VERSION_UNRECOGNIZED; 527 } 528 #endif 529