xref: /openbmc/u-boot/board/compulab/common/eeprom.c (revision 624656314f5684995fb9f499d38ad18d378802a5)
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