183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2689be5f8SIgor Grinberg /*
3689be5f8SIgor Grinberg * (C) Copyright 2011 CompuLab, Ltd. <www.compulab.co.il>
4689be5f8SIgor Grinberg *
5689be5f8SIgor Grinberg * Authors: Nikita Kiryanov <nikita@compulab.co.il>
6689be5f8SIgor Grinberg * Igor Grinberg <grinberg@compulab.co.il>
7689be5f8SIgor Grinberg */
8689be5f8SIgor Grinberg
9689be5f8SIgor Grinberg #include <common.h>
10*62465631SSimon Glass #include <eeprom.h>
11689be5f8SIgor Grinberg #include <i2c.h>
128af5734bSNikita Kiryanov #include <eeprom_layout.h>
138af5734bSNikita Kiryanov #include <eeprom_field.h>
145d982856SSimon Glass #include <asm/setup.h>
158af5734bSNikita Kiryanov #include <linux/kernel.h>
1653af877fSNikita Kiryanov #include "eeprom.h"
17689be5f8SIgor Grinberg
18d3f041c0SIgor Grinberg #ifndef CONFIG_SYS_I2C_EEPROM_ADDR
19d3f041c0SIgor Grinberg # define CONFIG_SYS_I2C_EEPROM_ADDR 0x50
20d3f041c0SIgor Grinberg # define CONFIG_SYS_I2C_EEPROM_ADDR_LEN 1
21d3f041c0SIgor Grinberg #endif
22d3f041c0SIgor Grinberg
237d2f669bSNikita Kiryanov #ifndef CONFIG_SYS_I2C_EEPROM_BUS
247d2f669bSNikita Kiryanov #define CONFIG_SYS_I2C_EEPROM_BUS 0
257d2f669bSNikita Kiryanov #endif
267d2f669bSNikita Kiryanov
27689be5f8SIgor Grinberg #define EEPROM_LAYOUT_VER_OFFSET 44
28689be5f8SIgor Grinberg #define BOARD_SERIAL_OFFSET 20
29689be5f8SIgor Grinberg #define BOARD_SERIAL_OFFSET_LEGACY 8
30689be5f8SIgor Grinberg #define BOARD_REV_OFFSET 0
31689be5f8SIgor Grinberg #define BOARD_REV_OFFSET_LEGACY 6
32689be5f8SIgor Grinberg #define BOARD_REV_SIZE 2
3353af877fSNikita Kiryanov #define PRODUCT_NAME_OFFSET 128
3453af877fSNikita Kiryanov #define PRODUCT_NAME_SIZE 16
35689be5f8SIgor Grinberg #define MAC_ADDR_OFFSET 4
36689be5f8SIgor Grinberg #define MAC_ADDR_OFFSET_LEGACY 0
37689be5f8SIgor Grinberg
38689be5f8SIgor Grinberg #define LAYOUT_INVALID 0
39689be5f8SIgor Grinberg #define LAYOUT_LEGACY 0xff
40689be5f8SIgor Grinberg
41e7a2447bSNikita Kiryanov static int cl_eeprom_bus;
42689be5f8SIgor Grinberg static int cl_eeprom_layout; /* Implicitly LAYOUT_INVALID */
43689be5f8SIgor Grinberg
cl_eeprom_read(uint offset,uchar * buf,int len)44689be5f8SIgor Grinberg static int cl_eeprom_read(uint offset, uchar *buf, int len)
45689be5f8SIgor Grinberg {
4652658fdaSNikita Kiryanov int res;
4752658fdaSNikita Kiryanov unsigned int current_i2c_bus = i2c_get_bus_num();
4852658fdaSNikita Kiryanov
49e7a2447bSNikita Kiryanov res = i2c_set_bus_num(cl_eeprom_bus);
5052658fdaSNikita Kiryanov if (res < 0)
5152658fdaSNikita Kiryanov return res;
5252658fdaSNikita Kiryanov
5352658fdaSNikita Kiryanov res = i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, offset,
54689be5f8SIgor Grinberg CONFIG_SYS_I2C_EEPROM_ADDR_LEN, buf, len);
5552658fdaSNikita Kiryanov
5652658fdaSNikita Kiryanov i2c_set_bus_num(current_i2c_bus);
5752658fdaSNikita Kiryanov
5852658fdaSNikita Kiryanov return res;
59689be5f8SIgor Grinberg }
60689be5f8SIgor Grinberg
cl_eeprom_setup(uint eeprom_bus)61e7a2447bSNikita Kiryanov static int cl_eeprom_setup(uint eeprom_bus)
62689be5f8SIgor Grinberg {
63689be5f8SIgor Grinberg int res;
64689be5f8SIgor Grinberg
65e7a2447bSNikita Kiryanov /*
66e7a2447bSNikita Kiryanov * We know the setup was already done when the layout is set to a valid
67e7a2447bSNikita Kiryanov * value and we're using the same bus as before.
68e7a2447bSNikita Kiryanov */
69e7a2447bSNikita Kiryanov if (cl_eeprom_layout != LAYOUT_INVALID && eeprom_bus == cl_eeprom_bus)
70689be5f8SIgor Grinberg return 0;
71689be5f8SIgor Grinberg
72e7a2447bSNikita Kiryanov cl_eeprom_bus = eeprom_bus;
73689be5f8SIgor Grinberg res = cl_eeprom_read(EEPROM_LAYOUT_VER_OFFSET,
74689be5f8SIgor Grinberg (uchar *)&cl_eeprom_layout, 1);
75689be5f8SIgor Grinberg if (res) {
76689be5f8SIgor Grinberg cl_eeprom_layout = LAYOUT_INVALID;
77689be5f8SIgor Grinberg return res;
78689be5f8SIgor Grinberg }
79689be5f8SIgor Grinberg
80689be5f8SIgor Grinberg if (cl_eeprom_layout == 0 || cl_eeprom_layout >= 0x20)
81689be5f8SIgor Grinberg cl_eeprom_layout = LAYOUT_LEGACY;
82689be5f8SIgor Grinberg
83689be5f8SIgor Grinberg return 0;
84689be5f8SIgor Grinberg }
85689be5f8SIgor Grinberg
get_board_serial(struct tag_serialnr * serialnr)86689be5f8SIgor Grinberg void get_board_serial(struct tag_serialnr *serialnr)
87689be5f8SIgor Grinberg {
88689be5f8SIgor Grinberg u32 serial[2];
89689be5f8SIgor Grinberg uint offset;
90689be5f8SIgor Grinberg
91689be5f8SIgor Grinberg memset(serialnr, 0, sizeof(*serialnr));
92689be5f8SIgor Grinberg
93e7a2447bSNikita Kiryanov if (cl_eeprom_setup(CONFIG_SYS_I2C_EEPROM_BUS))
94689be5f8SIgor Grinberg return;
95689be5f8SIgor Grinberg
96689be5f8SIgor Grinberg offset = (cl_eeprom_layout != LAYOUT_LEGACY) ?
97689be5f8SIgor Grinberg BOARD_SERIAL_OFFSET : BOARD_SERIAL_OFFSET_LEGACY;
98689be5f8SIgor Grinberg
99689be5f8SIgor Grinberg if (cl_eeprom_read(offset, (uchar *)serial, 8))
100689be5f8SIgor Grinberg return;
101689be5f8SIgor Grinberg
102689be5f8SIgor Grinberg if (serial[0] != 0xffffffff && serial[1] != 0xffffffff) {
103689be5f8SIgor Grinberg serialnr->low = serial[0];
104689be5f8SIgor Grinberg serialnr->high = serial[1];
105689be5f8SIgor Grinberg }
106689be5f8SIgor Grinberg }
107689be5f8SIgor Grinberg
108689be5f8SIgor Grinberg /*
109689be5f8SIgor Grinberg * Routine: cl_eeprom_read_mac_addr
110689be5f8SIgor Grinberg * Description: read mac address and store it in buf.
111689be5f8SIgor Grinberg */
cl_eeprom_read_mac_addr(uchar * buf,uint eeprom_bus)112e7a2447bSNikita Kiryanov int cl_eeprom_read_mac_addr(uchar *buf, uint eeprom_bus)
113689be5f8SIgor Grinberg {
114689be5f8SIgor Grinberg uint offset;
115e93e809fSNikita Kiryanov int err;
116689be5f8SIgor Grinberg
117e93e809fSNikita Kiryanov err = cl_eeprom_setup(eeprom_bus);
118e93e809fSNikita Kiryanov if (err)
119e93e809fSNikita Kiryanov return err;
120689be5f8SIgor Grinberg
121689be5f8SIgor Grinberg offset = (cl_eeprom_layout != LAYOUT_LEGACY) ?
122689be5f8SIgor Grinberg MAC_ADDR_OFFSET : MAC_ADDR_OFFSET_LEGACY;
123689be5f8SIgor Grinberg
124689be5f8SIgor Grinberg return cl_eeprom_read(offset, buf, 6);
125689be5f8SIgor Grinberg }
126689be5f8SIgor Grinberg
127a937fd16SIgor Grinberg static u32 board_rev;
128a937fd16SIgor Grinberg
129689be5f8SIgor Grinberg /*
130689be5f8SIgor Grinberg * Routine: cl_eeprom_get_board_rev
131689be5f8SIgor Grinberg * Description: read system revision from eeprom
132689be5f8SIgor Grinberg */
cl_eeprom_get_board_rev(uint eeprom_bus)13372898ac7SNikita Kiryanov u32 cl_eeprom_get_board_rev(uint eeprom_bus)
134689be5f8SIgor Grinberg {
135689be5f8SIgor Grinberg char str[5]; /* Legacy representation can contain at most 4 digits */
136689be5f8SIgor Grinberg uint offset = BOARD_REV_OFFSET_LEGACY;
137689be5f8SIgor Grinberg
138a937fd16SIgor Grinberg if (board_rev)
139a937fd16SIgor Grinberg return board_rev;
140a937fd16SIgor Grinberg
14172898ac7SNikita Kiryanov if (cl_eeprom_setup(eeprom_bus))
142689be5f8SIgor Grinberg return 0;
143689be5f8SIgor Grinberg
144689be5f8SIgor Grinberg if (cl_eeprom_layout != LAYOUT_LEGACY)
145689be5f8SIgor Grinberg offset = BOARD_REV_OFFSET;
146689be5f8SIgor Grinberg
147a937fd16SIgor Grinberg if (cl_eeprom_read(offset, (uchar *)&board_rev, BOARD_REV_SIZE))
148689be5f8SIgor Grinberg return 0;
149689be5f8SIgor Grinberg
150689be5f8SIgor Grinberg /*
151689be5f8SIgor Grinberg * Convert legacy syntactic representation to semantic
152689be5f8SIgor Grinberg * representation. i.e. for rev 1.00: 0x100 --> 0x64
153689be5f8SIgor Grinberg */
154689be5f8SIgor Grinberg if (cl_eeprom_layout == LAYOUT_LEGACY) {
155a937fd16SIgor Grinberg sprintf(str, "%x", board_rev);
156a937fd16SIgor Grinberg board_rev = simple_strtoul(str, NULL, 10);
157689be5f8SIgor Grinberg }
158689be5f8SIgor Grinberg
159a937fd16SIgor Grinberg return board_rev;
160689be5f8SIgor Grinberg };
16153af877fSNikita Kiryanov
16253af877fSNikita Kiryanov /*
16353af877fSNikita Kiryanov * Routine: cl_eeprom_get_board_rev
16453af877fSNikita Kiryanov * Description: read system revision from eeprom
16553af877fSNikita Kiryanov *
16653af877fSNikita Kiryanov * @buf: buffer to store the product name
16753af877fSNikita Kiryanov * @eeprom_bus: i2c bus num of the eeprom
16853af877fSNikita Kiryanov *
16953af877fSNikita Kiryanov * @return: 0 on success, < 0 on failure
17053af877fSNikita Kiryanov */
cl_eeprom_get_product_name(uchar * buf,uint eeprom_bus)17153af877fSNikita Kiryanov int cl_eeprom_get_product_name(uchar *buf, uint eeprom_bus)
17253af877fSNikita Kiryanov {
17353af877fSNikita Kiryanov int err;
17453af877fSNikita Kiryanov
17553af877fSNikita Kiryanov if (buf == NULL)
17653af877fSNikita Kiryanov return -EINVAL;
17753af877fSNikita Kiryanov
17853af877fSNikita Kiryanov err = cl_eeprom_setup(eeprom_bus);
17953af877fSNikita Kiryanov if (err)
18053af877fSNikita Kiryanov return err;
18153af877fSNikita Kiryanov
18253af877fSNikita Kiryanov err = cl_eeprom_read(PRODUCT_NAME_OFFSET, buf, PRODUCT_NAME_SIZE);
18353af877fSNikita Kiryanov if (!err) /* Protect ourselves from invalid data (unterminated str) */
18453af877fSNikita Kiryanov buf[PRODUCT_NAME_SIZE - 1] = '\0';
18553af877fSNikita Kiryanov
18653af877fSNikita Kiryanov return err;
18753af877fSNikita Kiryanov }
1888af5734bSNikita Kiryanov
1898af5734bSNikita Kiryanov #ifdef CONFIG_CMD_EEPROM_LAYOUT
1908af5734bSNikita Kiryanov /**
1918af5734bSNikita Kiryanov * eeprom_field_print_bin_ver() - print a "version field" which contains binary
1928af5734bSNikita Kiryanov * data
1938af5734bSNikita Kiryanov *
1948af5734bSNikita Kiryanov * Treat the field data as simple binary data, and print it formatted as a
1958af5734bSNikita Kiryanov * version number (2 digits after decimal point).
1968af5734bSNikita Kiryanov * The field size must be exactly 2 bytes.
1978af5734bSNikita Kiryanov *
1988af5734bSNikita Kiryanov * Sample output:
1998af5734bSNikita Kiryanov * Field Name 123.45
2008af5734bSNikita Kiryanov *
2018af5734bSNikita Kiryanov * @field: an initialized field to print
2028af5734bSNikita Kiryanov */
eeprom_field_print_bin_ver(const struct eeprom_field * field)2038af5734bSNikita Kiryanov void eeprom_field_print_bin_ver(const struct eeprom_field *field)
2048af5734bSNikita Kiryanov {
2058af5734bSNikita Kiryanov if ((field->buf[0] == 0xff) && (field->buf[1] == 0xff)) {
2068af5734bSNikita Kiryanov field->buf[0] = 0;
2078af5734bSNikita Kiryanov field->buf[1] = 0;
2088af5734bSNikita Kiryanov }
2098af5734bSNikita Kiryanov
2108af5734bSNikita Kiryanov printf(PRINT_FIELD_SEGMENT, field->name);
2118af5734bSNikita Kiryanov int major = (field->buf[1] << 8 | field->buf[0]) / 100;
2128af5734bSNikita Kiryanov int minor = (field->buf[1] << 8 | field->buf[0]) - major * 100;
2138af5734bSNikita Kiryanov printf("%d.%02d\n", major, minor);
2148af5734bSNikita Kiryanov }
2158af5734bSNikita Kiryanov
2168af5734bSNikita Kiryanov /**
2178af5734bSNikita Kiryanov * eeprom_field_update_bin_ver() - update a "version field" which contains
2188af5734bSNikita Kiryanov * binary data
2198af5734bSNikita Kiryanov *
2208af5734bSNikita Kiryanov * This function takes a version string in the form of x.y (x and y are both
2218af5734bSNikita Kiryanov * decimal values, y is limited to two digits), translates it to the binary
2228af5734bSNikita Kiryanov * form, then writes it to the field. The field size must be exactly 2 bytes.
2238af5734bSNikita Kiryanov *
2248af5734bSNikita Kiryanov * This function strictly enforces the data syntax, and will not update the
2258af5734bSNikita Kiryanov * field if there's any deviation from it. It also protects from overflow.
2268af5734bSNikita Kiryanov *
2278af5734bSNikita Kiryanov * @field: an initialized field
2288af5734bSNikita Kiryanov * @value: a version string
2298af5734bSNikita Kiryanov *
2308af5734bSNikita Kiryanov * Returns 0 on success, -1 on failure.
2318af5734bSNikita Kiryanov */
eeprom_field_update_bin_ver(struct eeprom_field * field,char * value)2328af5734bSNikita Kiryanov int eeprom_field_update_bin_ver(struct eeprom_field *field, char *value)
2338af5734bSNikita Kiryanov {
2348af5734bSNikita Kiryanov char *endptr;
2358af5734bSNikita Kiryanov char *tok = strtok(value, ".");
2368af5734bSNikita Kiryanov if (tok == NULL)
2378af5734bSNikita Kiryanov return -1;
2388af5734bSNikita Kiryanov
2398af5734bSNikita Kiryanov int num = simple_strtol(tok, &endptr, 0);
2408af5734bSNikita Kiryanov if (*endptr != '\0')
2418af5734bSNikita Kiryanov return -1;
2428af5734bSNikita Kiryanov
2438af5734bSNikita Kiryanov tok = strtok(NULL, "");
2448af5734bSNikita Kiryanov if (tok == NULL)
2458af5734bSNikita Kiryanov return -1;
2468af5734bSNikita Kiryanov
2478af5734bSNikita Kiryanov int remainder = simple_strtol(tok, &endptr, 0);
2488af5734bSNikita Kiryanov if (*endptr != '\0')
2498af5734bSNikita Kiryanov return -1;
2508af5734bSNikita Kiryanov
2518af5734bSNikita Kiryanov num = num * 100 + remainder;
2528af5734bSNikita Kiryanov if (num >> 16)
2538af5734bSNikita Kiryanov return -1;
2548af5734bSNikita Kiryanov
2558af5734bSNikita Kiryanov field->buf[0] = (unsigned char)num;
2568af5734bSNikita Kiryanov field->buf[1] = num >> 8;
2578af5734bSNikita Kiryanov
2588af5734bSNikita Kiryanov return 0;
2598af5734bSNikita Kiryanov }
2608af5734bSNikita Kiryanov
2618af5734bSNikita Kiryanov char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
2628af5734bSNikita Kiryanov "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
2638af5734bSNikita Kiryanov
2648af5734bSNikita Kiryanov /**
2658af5734bSNikita Kiryanov * eeprom_field_print_date() - print a field which contains date data
2668af5734bSNikita Kiryanov *
2678af5734bSNikita Kiryanov * Treat the field data as simple binary data, and print it formatted as a date.
2688af5734bSNikita Kiryanov * Sample output:
2698af5734bSNikita Kiryanov * Field Name 07/Feb/2014
2708af5734bSNikita Kiryanov * Field Name 56/BAD/9999
2718af5734bSNikita Kiryanov *
2728af5734bSNikita Kiryanov * @field: an initialized field to print
2738af5734bSNikita Kiryanov */
eeprom_field_print_date(const struct eeprom_field * field)2748af5734bSNikita Kiryanov void eeprom_field_print_date(const struct eeprom_field *field)
2758af5734bSNikita Kiryanov {
2768af5734bSNikita Kiryanov printf(PRINT_FIELD_SEGMENT, field->name);
2778af5734bSNikita Kiryanov printf("%02d/", field->buf[0]);
2788af5734bSNikita Kiryanov if (field->buf[1] >= 1 && field->buf[1] <= 12)
2798af5734bSNikita Kiryanov printf("%s", months[field->buf[1] - 1]);
2808af5734bSNikita Kiryanov else
2818af5734bSNikita Kiryanov printf("BAD");
2828af5734bSNikita Kiryanov
2838af5734bSNikita Kiryanov printf("/%d\n", field->buf[3] << 8 | field->buf[2]);
2848af5734bSNikita Kiryanov }
2858af5734bSNikita Kiryanov
validate_date(unsigned char day,unsigned char month,unsigned int year)2868af5734bSNikita Kiryanov static int validate_date(unsigned char day, unsigned char month,
2878af5734bSNikita Kiryanov unsigned int year)
2888af5734bSNikita Kiryanov {
2898af5734bSNikita Kiryanov int days_in_february;
2908af5734bSNikita Kiryanov
2918af5734bSNikita Kiryanov switch (month) {
2928af5734bSNikita Kiryanov case 0:
2938af5734bSNikita Kiryanov case 2:
2948af5734bSNikita Kiryanov case 4:
2958af5734bSNikita Kiryanov case 6:
2968af5734bSNikita Kiryanov case 7:
2978af5734bSNikita Kiryanov case 9:
2988af5734bSNikita Kiryanov case 11:
2998af5734bSNikita Kiryanov if (day > 31)
3008af5734bSNikita Kiryanov return -1;
3018af5734bSNikita Kiryanov break;
3028af5734bSNikita Kiryanov case 3:
3038af5734bSNikita Kiryanov case 5:
3048af5734bSNikita Kiryanov case 8:
3058af5734bSNikita Kiryanov case 10:
3068af5734bSNikita Kiryanov if (day > 30)
3078af5734bSNikita Kiryanov return -1;
3088af5734bSNikita Kiryanov break;
3098af5734bSNikita Kiryanov case 1:
3108af5734bSNikita Kiryanov days_in_february = 28;
3118af5734bSNikita Kiryanov if (year % 4 == 0) {
3128af5734bSNikita Kiryanov if (year % 100 != 0)
3138af5734bSNikita Kiryanov days_in_february = 29;
3148af5734bSNikita Kiryanov else if (year % 400 == 0)
3158af5734bSNikita Kiryanov days_in_february = 29;
3168af5734bSNikita Kiryanov }
3178af5734bSNikita Kiryanov
3188af5734bSNikita Kiryanov if (day > days_in_february)
3198af5734bSNikita Kiryanov return -1;
3208af5734bSNikita Kiryanov
3218af5734bSNikita Kiryanov break;
3228af5734bSNikita Kiryanov default:
3238af5734bSNikita Kiryanov return -1;
3248af5734bSNikita Kiryanov }
3258af5734bSNikita Kiryanov
3268af5734bSNikita Kiryanov return 0;
3278af5734bSNikita Kiryanov }
3288af5734bSNikita Kiryanov
3298af5734bSNikita Kiryanov /**
3308af5734bSNikita Kiryanov * eeprom_field_update_date() - update a date field which contains binary data
3318af5734bSNikita Kiryanov *
3328af5734bSNikita Kiryanov * This function takes a date string in the form of x/Mon/y (x and y are both
3338af5734bSNikita Kiryanov * decimal values), translates it to the binary representation, then writes it
3348af5734bSNikita Kiryanov * to the field.
3358af5734bSNikita Kiryanov *
3368af5734bSNikita Kiryanov * This function strictly enforces the data syntax, and will not update the
3378af5734bSNikita Kiryanov * field if there's any deviation from it. It also protects from overflow in the
3388af5734bSNikita Kiryanov * year value, and checks the validity of the date.
3398af5734bSNikita Kiryanov *
3408af5734bSNikita Kiryanov * @field: an initialized field
3418af5734bSNikita Kiryanov * @value: a date string
3428af5734bSNikita Kiryanov *
3438af5734bSNikita Kiryanov * Returns 0 on success, -1 on failure.
3448af5734bSNikita Kiryanov */
eeprom_field_update_date(struct eeprom_field * field,char * value)3458af5734bSNikita Kiryanov int eeprom_field_update_date(struct eeprom_field *field, char *value)
3468af5734bSNikita Kiryanov {
3478af5734bSNikita Kiryanov char *endptr;
3488af5734bSNikita Kiryanov char *tok1 = strtok(value, "/");
3498af5734bSNikita Kiryanov char *tok2 = strtok(NULL, "/");
3508af5734bSNikita Kiryanov char *tok3 = strtok(NULL, "/");
3518af5734bSNikita Kiryanov
3528af5734bSNikita Kiryanov if (tok1 == NULL || tok2 == NULL || tok3 == NULL) {
3538af5734bSNikita Kiryanov printf("%s: syntax error\n", field->name);
3548af5734bSNikita Kiryanov return -1;
3558af5734bSNikita Kiryanov }
3568af5734bSNikita Kiryanov
3578af5734bSNikita Kiryanov unsigned char day = (unsigned char)simple_strtol(tok1, &endptr, 0);
3588af5734bSNikita Kiryanov if (*endptr != '\0' || day == 0) {
3598af5734bSNikita Kiryanov printf("%s: invalid day\n", field->name);
3608af5734bSNikita Kiryanov return -1;
3618af5734bSNikita Kiryanov }
3628af5734bSNikita Kiryanov
3638af5734bSNikita Kiryanov unsigned char month;
3648af5734bSNikita Kiryanov for (month = 1; month <= 12; month++)
3658af5734bSNikita Kiryanov if (!strcmp(tok2, months[month - 1]))
3668af5734bSNikita Kiryanov break;
3678af5734bSNikita Kiryanov
3688af5734bSNikita Kiryanov unsigned int year = simple_strtol(tok3, &endptr, 0);
3698af5734bSNikita Kiryanov if (*endptr != '\0') {
3708af5734bSNikita Kiryanov printf("%s: invalid year\n", field->name);
3718af5734bSNikita Kiryanov return -1;
3728af5734bSNikita Kiryanov }
3738af5734bSNikita Kiryanov
3748af5734bSNikita Kiryanov if (validate_date(day, month - 1, year)) {
3758af5734bSNikita Kiryanov printf("%s: invalid date\n", field->name);
3768af5734bSNikita Kiryanov return -1;
3778af5734bSNikita Kiryanov }
3788af5734bSNikita Kiryanov
3798af5734bSNikita Kiryanov if (year >> 16) {
3808af5734bSNikita Kiryanov printf("%s: year overflow\n", field->name);
3818af5734bSNikita Kiryanov return -1;
3828af5734bSNikita Kiryanov }
3838af5734bSNikita Kiryanov
3848af5734bSNikita Kiryanov field->buf[0] = day;
3858af5734bSNikita Kiryanov field->buf[1] = month;
3868af5734bSNikita Kiryanov field->buf[2] = (unsigned char)year;
3878af5734bSNikita Kiryanov field->buf[3] = (unsigned char)(year >> 8);
3888af5734bSNikita Kiryanov
3898af5734bSNikita Kiryanov return 0;
3908af5734bSNikita Kiryanov }
3918af5734bSNikita Kiryanov
3928af5734bSNikita Kiryanov #define LAYOUT_VERSION_LEGACY 1
3938af5734bSNikita Kiryanov #define LAYOUT_VERSION_VER1 2
3948af5734bSNikita Kiryanov #define LAYOUT_VERSION_VER2 3
3958af5734bSNikita Kiryanov #define LAYOUT_VERSION_VER3 4
3968af5734bSNikita Kiryanov
3978af5734bSNikita Kiryanov extern struct eeprom_field layout_unknown[1];
3988af5734bSNikita Kiryanov
3998af5734bSNikita Kiryanov #define DEFINE_PRINT_UPDATE(x) eeprom_field_print_##x, eeprom_field_update_##x
4008af5734bSNikita Kiryanov
4018af5734bSNikita Kiryanov #ifdef CONFIG_CM_T3X
4028af5734bSNikita Kiryanov struct eeprom_field layout_legacy[5] = {
4038af5734bSNikita Kiryanov { "MAC address", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4048af5734bSNikita Kiryanov { "Board Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin) },
4058af5734bSNikita Kiryanov { "Serial Number", 8, NULL, DEFINE_PRINT_UPDATE(bin) },
4068af5734bSNikita Kiryanov { "Board Configuration", 64, NULL, DEFINE_PRINT_UPDATE(ascii) },
4078af5734bSNikita Kiryanov { RESERVED_FIELDS, 176, NULL, eeprom_field_print_reserved,
4088af5734bSNikita Kiryanov eeprom_field_update_ascii },
4098af5734bSNikita Kiryanov };
4108af5734bSNikita Kiryanov #else
4118af5734bSNikita Kiryanov #define layout_legacy layout_unknown
4128af5734bSNikita Kiryanov #endif
4138af5734bSNikita Kiryanov
4148af5734bSNikita Kiryanov #if defined(CONFIG_CM_T3X) || defined(CONFIG_CM_T3517)
4158af5734bSNikita Kiryanov struct eeprom_field layout_v1[12] = {
4168af5734bSNikita Kiryanov { "Major Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
4178af5734bSNikita Kiryanov { "Minor Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
4188af5734bSNikita Kiryanov { "1st MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4198af5734bSNikita Kiryanov { "2nd MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4208af5734bSNikita Kiryanov { "Production Date", 4, NULL, DEFINE_PRINT_UPDATE(date) },
4218af5734bSNikita Kiryanov { "Serial Number", 12, NULL, DEFINE_PRINT_UPDATE(bin_rev) },
4228af5734bSNikita Kiryanov { RESERVED_FIELDS, 96, NULL, DEFINE_PRINT_UPDATE(reserved) },
4238af5734bSNikita Kiryanov { "Product Name", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4248af5734bSNikita Kiryanov { "Product Options #1", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4258af5734bSNikita Kiryanov { "Product Options #2", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4268af5734bSNikita Kiryanov { "Product Options #3", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4278af5734bSNikita Kiryanov { RESERVED_FIELDS, 64, NULL, eeprom_field_print_reserved,
4288af5734bSNikita Kiryanov eeprom_field_update_ascii },
4298af5734bSNikita Kiryanov };
4308af5734bSNikita Kiryanov #else
4318af5734bSNikita Kiryanov #define layout_v1 layout_unknown
4328af5734bSNikita Kiryanov #endif
4338af5734bSNikita Kiryanov
4348af5734bSNikita Kiryanov struct eeprom_field layout_v2[15] = {
4358af5734bSNikita Kiryanov { "Major Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
4368af5734bSNikita Kiryanov { "Minor Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
4378af5734bSNikita Kiryanov { "1st MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4388af5734bSNikita Kiryanov { "2nd MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4398af5734bSNikita Kiryanov { "Production Date", 4, NULL, DEFINE_PRINT_UPDATE(date) },
4408af5734bSNikita Kiryanov { "Serial Number", 12, NULL, DEFINE_PRINT_UPDATE(bin_rev) },
4418af5734bSNikita Kiryanov { "3rd MAC Address (WIFI)", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4428af5734bSNikita Kiryanov { "4th MAC Address (Bluetooth)", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4438af5734bSNikita Kiryanov { "Layout Version", 1, NULL, DEFINE_PRINT_UPDATE(bin) },
4448af5734bSNikita Kiryanov { RESERVED_FIELDS, 83, NULL, DEFINE_PRINT_UPDATE(reserved) },
4458af5734bSNikita Kiryanov { "Product Name", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4468af5734bSNikita Kiryanov { "Product Options #1", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4478af5734bSNikita Kiryanov { "Product Options #2", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4488af5734bSNikita Kiryanov { "Product Options #3", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4498af5734bSNikita Kiryanov { RESERVED_FIELDS, 64, NULL, eeprom_field_print_reserved,
4508af5734bSNikita Kiryanov eeprom_field_update_ascii },
4518af5734bSNikita Kiryanov };
4528af5734bSNikita Kiryanov
4538af5734bSNikita Kiryanov struct eeprom_field layout_v3[16] = {
4548af5734bSNikita Kiryanov { "Major Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
4558af5734bSNikita Kiryanov { "Minor Revision", 2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
4568af5734bSNikita Kiryanov { "1st MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4578af5734bSNikita Kiryanov { "2nd MAC Address", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4588af5734bSNikita Kiryanov { "Production Date", 4, NULL, DEFINE_PRINT_UPDATE(date) },
4598af5734bSNikita Kiryanov { "Serial Number", 12, NULL, DEFINE_PRINT_UPDATE(bin_rev) },
4608af5734bSNikita Kiryanov { "3rd MAC Address (WIFI)", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4618af5734bSNikita Kiryanov { "4th MAC Address (Bluetooth)", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
4628af5734bSNikita Kiryanov { "Layout Version", 1, NULL, DEFINE_PRINT_UPDATE(bin) },
4638af5734bSNikita Kiryanov { "CompuLab EEPROM ID", 3, NULL, DEFINE_PRINT_UPDATE(bin) },
4648af5734bSNikita Kiryanov { RESERVED_FIELDS, 80, NULL, DEFINE_PRINT_UPDATE(reserved) },
4658af5734bSNikita Kiryanov { "Product Name", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4668af5734bSNikita Kiryanov { "Product Options #1", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4678af5734bSNikita Kiryanov { "Product Options #2", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4688af5734bSNikita Kiryanov { "Product Options #3", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
4698af5734bSNikita Kiryanov { RESERVED_FIELDS, 64, NULL, eeprom_field_print_reserved,
4708af5734bSNikita Kiryanov eeprom_field_update_ascii },
4718af5734bSNikita Kiryanov };
4728af5734bSNikita Kiryanov
eeprom_layout_assign(struct eeprom_layout * layout,int layout_version)4738af5734bSNikita Kiryanov void eeprom_layout_assign(struct eeprom_layout *layout, int layout_version)
4748af5734bSNikita Kiryanov {
4758af5734bSNikita Kiryanov switch (layout->layout_version) {
4768af5734bSNikita Kiryanov case LAYOUT_VERSION_LEGACY:
4778af5734bSNikita Kiryanov layout->fields = layout_legacy;
4788af5734bSNikita Kiryanov layout->num_of_fields = ARRAY_SIZE(layout_legacy);
4798af5734bSNikita Kiryanov break;
4808af5734bSNikita Kiryanov case LAYOUT_VERSION_VER1:
4818af5734bSNikita Kiryanov layout->fields = layout_v1;
4828af5734bSNikita Kiryanov layout->num_of_fields = ARRAY_SIZE(layout_v1);
4838af5734bSNikita Kiryanov break;
4848af5734bSNikita Kiryanov case LAYOUT_VERSION_VER2:
4858af5734bSNikita Kiryanov layout->fields = layout_v2;
4868af5734bSNikita Kiryanov layout->num_of_fields = ARRAY_SIZE(layout_v2);
4878af5734bSNikita Kiryanov break;
4888af5734bSNikita Kiryanov case LAYOUT_VERSION_VER3:
4898af5734bSNikita Kiryanov layout->fields = layout_v3;
4908af5734bSNikita Kiryanov layout->num_of_fields = ARRAY_SIZE(layout_v3);
4918af5734bSNikita Kiryanov break;
4928af5734bSNikita Kiryanov default:
4938af5734bSNikita Kiryanov __eeprom_layout_assign(layout, layout_version);
4948af5734bSNikita Kiryanov }
4958af5734bSNikita Kiryanov }
4968af5734bSNikita Kiryanov
eeprom_parse_layout_version(char * str)4978af5734bSNikita Kiryanov int eeprom_parse_layout_version(char *str)
4988af5734bSNikita Kiryanov {
4998af5734bSNikita Kiryanov if (!strcmp(str, "legacy"))
5008af5734bSNikita Kiryanov return LAYOUT_VERSION_LEGACY;
5018af5734bSNikita Kiryanov else if (!strcmp(str, "v1"))
5028af5734bSNikita Kiryanov return LAYOUT_VERSION_VER1;
5038af5734bSNikita Kiryanov else if (!strcmp(str, "v2"))
5048af5734bSNikita Kiryanov return LAYOUT_VERSION_VER2;
5058af5734bSNikita Kiryanov else if (!strcmp(str, "v3"))
5068af5734bSNikita Kiryanov return LAYOUT_VERSION_VER3;
5078af5734bSNikita Kiryanov else
5088af5734bSNikita Kiryanov return LAYOUT_VERSION_UNRECOGNIZED;
5098af5734bSNikita Kiryanov }
5108af5734bSNikita Kiryanov
eeprom_layout_detect(unsigned char * data)5118af5734bSNikita Kiryanov int eeprom_layout_detect(unsigned char *data)
5128af5734bSNikita Kiryanov {
5138af5734bSNikita Kiryanov switch (data[EEPROM_LAYOUT_VER_OFFSET]) {
5148af5734bSNikita Kiryanov case 0xff:
5158af5734bSNikita Kiryanov case 0:
5168af5734bSNikita Kiryanov return LAYOUT_VERSION_VER1;
5178af5734bSNikita Kiryanov case 2:
5188af5734bSNikita Kiryanov return LAYOUT_VERSION_VER2;
5198af5734bSNikita Kiryanov case 3:
5208af5734bSNikita Kiryanov return LAYOUT_VERSION_VER3;
5218af5734bSNikita Kiryanov }
5228af5734bSNikita Kiryanov
5238af5734bSNikita Kiryanov if (data[EEPROM_LAYOUT_VER_OFFSET] >= 0x20)
5248af5734bSNikita Kiryanov return LAYOUT_VERSION_LEGACY;
5258af5734bSNikita Kiryanov
5268af5734bSNikita Kiryanov return LAYOUT_VERSION_UNRECOGNIZED;
5278af5734bSNikita Kiryanov }
5288af5734bSNikita Kiryanov #endif
529