xref: /rk3399_rockchip-uboot/board/compulab/common/eeprom.c (revision 8af5734b4e9322a9af8640ba1af48ebe14ebf6c3)
1689be5f8SIgor Grinberg /*
2689be5f8SIgor Grinberg  * (C) Copyright 2011 CompuLab, Ltd. <www.compulab.co.il>
3689be5f8SIgor Grinberg  *
4689be5f8SIgor Grinberg  * Authors: Nikita Kiryanov <nikita@compulab.co.il>
5689be5f8SIgor Grinberg  *	    Igor Grinberg <grinberg@compulab.co.il>
6689be5f8SIgor Grinberg  *
7689be5f8SIgor Grinberg  * SPDX-License-Identifier:	GPL-2.0+
8689be5f8SIgor Grinberg  */
9689be5f8SIgor Grinberg 
10689be5f8SIgor Grinberg #include <common.h>
11689be5f8SIgor Grinberg #include <i2c.h>
12*8af5734bSNikita Kiryanov #include <eeprom_layout.h>
13*8af5734bSNikita Kiryanov #include <eeprom_field.h>
14*8af5734bSNikita Kiryanov #include <linux/kernel.h>
1553af877fSNikita Kiryanov #include "eeprom.h"
16689be5f8SIgor Grinberg 
17d3f041c0SIgor Grinberg #ifndef CONFIG_SYS_I2C_EEPROM_ADDR
18d3f041c0SIgor Grinberg # define CONFIG_SYS_I2C_EEPROM_ADDR	0x50
19d3f041c0SIgor Grinberg # define CONFIG_SYS_I2C_EEPROM_ADDR_LEN	1
20d3f041c0SIgor Grinberg #endif
21d3f041c0SIgor Grinberg 
227d2f669bSNikita Kiryanov #ifndef CONFIG_SYS_I2C_EEPROM_BUS
237d2f669bSNikita Kiryanov #define CONFIG_SYS_I2C_EEPROM_BUS	0
247d2f669bSNikita Kiryanov #endif
257d2f669bSNikita Kiryanov 
26689be5f8SIgor Grinberg #define EEPROM_LAYOUT_VER_OFFSET	44
27689be5f8SIgor Grinberg #define BOARD_SERIAL_OFFSET		20
28689be5f8SIgor Grinberg #define BOARD_SERIAL_OFFSET_LEGACY	8
29689be5f8SIgor Grinberg #define BOARD_REV_OFFSET		0
30689be5f8SIgor Grinberg #define BOARD_REV_OFFSET_LEGACY		6
31689be5f8SIgor Grinberg #define BOARD_REV_SIZE			2
3253af877fSNikita Kiryanov #define PRODUCT_NAME_OFFSET		128
3353af877fSNikita Kiryanov #define PRODUCT_NAME_SIZE		16
34689be5f8SIgor Grinberg #define MAC_ADDR_OFFSET			4
35689be5f8SIgor Grinberg #define MAC_ADDR_OFFSET_LEGACY		0
36689be5f8SIgor Grinberg 
37689be5f8SIgor Grinberg #define LAYOUT_INVALID	0
38689be5f8SIgor Grinberg #define LAYOUT_LEGACY	0xff
39689be5f8SIgor Grinberg 
40e7a2447bSNikita Kiryanov static int cl_eeprom_bus;
41689be5f8SIgor Grinberg static int cl_eeprom_layout; /* Implicitly LAYOUT_INVALID */
42689be5f8SIgor Grinberg 
43689be5f8SIgor Grinberg static int cl_eeprom_read(uint offset, uchar *buf, int len)
44689be5f8SIgor Grinberg {
4552658fdaSNikita Kiryanov 	int res;
4652658fdaSNikita Kiryanov 	unsigned int current_i2c_bus = i2c_get_bus_num();
4752658fdaSNikita Kiryanov 
48e7a2447bSNikita Kiryanov 	res = i2c_set_bus_num(cl_eeprom_bus);
4952658fdaSNikita Kiryanov 	if (res < 0)
5052658fdaSNikita Kiryanov 		return res;
5152658fdaSNikita Kiryanov 
5252658fdaSNikita Kiryanov 	res = i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, offset,
53689be5f8SIgor Grinberg 			CONFIG_SYS_I2C_EEPROM_ADDR_LEN, buf, len);
5452658fdaSNikita Kiryanov 
5552658fdaSNikita Kiryanov 	i2c_set_bus_num(current_i2c_bus);
5652658fdaSNikita Kiryanov 
5752658fdaSNikita Kiryanov 	return res;
58689be5f8SIgor Grinberg }
59689be5f8SIgor Grinberg 
60e7a2447bSNikita Kiryanov static int cl_eeprom_setup(uint eeprom_bus)
61689be5f8SIgor Grinberg {
62689be5f8SIgor Grinberg 	int res;
63689be5f8SIgor Grinberg 
64e7a2447bSNikita Kiryanov 	/*
65e7a2447bSNikita Kiryanov 	 * We know the setup was already done when the layout is set to a valid
66e7a2447bSNikita Kiryanov 	 * value and we're using the same bus as before.
67e7a2447bSNikita Kiryanov 	 */
68e7a2447bSNikita Kiryanov 	if (cl_eeprom_layout != LAYOUT_INVALID && eeprom_bus == cl_eeprom_bus)
69689be5f8SIgor Grinberg 		return 0;
70689be5f8SIgor Grinberg 
71e7a2447bSNikita Kiryanov 	cl_eeprom_bus = eeprom_bus;
72689be5f8SIgor Grinberg 	res = cl_eeprom_read(EEPROM_LAYOUT_VER_OFFSET,
73689be5f8SIgor Grinberg 			     (uchar *)&cl_eeprom_layout, 1);
74689be5f8SIgor Grinberg 	if (res) {
75689be5f8SIgor Grinberg 		cl_eeprom_layout = LAYOUT_INVALID;
76689be5f8SIgor Grinberg 		return res;
77689be5f8SIgor Grinberg 	}
78689be5f8SIgor Grinberg 
79689be5f8SIgor Grinberg 	if (cl_eeprom_layout == 0 || cl_eeprom_layout >= 0x20)
80689be5f8SIgor Grinberg 		cl_eeprom_layout = LAYOUT_LEGACY;
81689be5f8SIgor Grinberg 
82689be5f8SIgor Grinberg 	return 0;
83689be5f8SIgor Grinberg }
84689be5f8SIgor Grinberg 
85689be5f8SIgor Grinberg void get_board_serial(struct tag_serialnr *serialnr)
86689be5f8SIgor Grinberg {
87689be5f8SIgor Grinberg 	u32 serial[2];
88689be5f8SIgor Grinberg 	uint offset;
89689be5f8SIgor Grinberg 
90689be5f8SIgor Grinberg 	memset(serialnr, 0, sizeof(*serialnr));
91689be5f8SIgor Grinberg 
92e7a2447bSNikita Kiryanov 	if (cl_eeprom_setup(CONFIG_SYS_I2C_EEPROM_BUS))
93689be5f8SIgor Grinberg 		return;
94689be5f8SIgor Grinberg 
95689be5f8SIgor Grinberg 	offset = (cl_eeprom_layout != LAYOUT_LEGACY) ?
96689be5f8SIgor Grinberg 		BOARD_SERIAL_OFFSET : BOARD_SERIAL_OFFSET_LEGACY;
97689be5f8SIgor Grinberg 
98689be5f8SIgor Grinberg 	if (cl_eeprom_read(offset, (uchar *)serial, 8))
99689be5f8SIgor Grinberg 		return;
100689be5f8SIgor Grinberg 
101689be5f8SIgor Grinberg 	if (serial[0] != 0xffffffff && serial[1] != 0xffffffff) {
102689be5f8SIgor Grinberg 		serialnr->low = serial[0];
103689be5f8SIgor Grinberg 		serialnr->high = serial[1];
104689be5f8SIgor Grinberg 	}
105689be5f8SIgor Grinberg }
106689be5f8SIgor Grinberg 
107689be5f8SIgor Grinberg /*
108689be5f8SIgor Grinberg  * Routine: cl_eeprom_read_mac_addr
109689be5f8SIgor Grinberg  * Description: read mac address and store it in buf.
110689be5f8SIgor Grinberg  */
111e7a2447bSNikita Kiryanov int cl_eeprom_read_mac_addr(uchar *buf, uint eeprom_bus)
112689be5f8SIgor Grinberg {
113689be5f8SIgor Grinberg 	uint offset;
114e93e809fSNikita Kiryanov 	int err;
115689be5f8SIgor Grinberg 
116e93e809fSNikita Kiryanov 	err = cl_eeprom_setup(eeprom_bus);
117e93e809fSNikita Kiryanov 	if (err)
118e93e809fSNikita Kiryanov 		return err;
119689be5f8SIgor Grinberg 
120689be5f8SIgor Grinberg 	offset = (cl_eeprom_layout != LAYOUT_LEGACY) ?
121689be5f8SIgor Grinberg 			MAC_ADDR_OFFSET : MAC_ADDR_OFFSET_LEGACY;
122689be5f8SIgor Grinberg 
123689be5f8SIgor Grinberg 	return cl_eeprom_read(offset, buf, 6);
124689be5f8SIgor Grinberg }
125689be5f8SIgor Grinberg 
126a937fd16SIgor Grinberg static u32 board_rev;
127a937fd16SIgor Grinberg 
128689be5f8SIgor Grinberg /*
129689be5f8SIgor Grinberg  * Routine: cl_eeprom_get_board_rev
130689be5f8SIgor Grinberg  * Description: read system revision from eeprom
131689be5f8SIgor Grinberg  */
13272898ac7SNikita Kiryanov u32 cl_eeprom_get_board_rev(uint eeprom_bus)
133689be5f8SIgor Grinberg {
134689be5f8SIgor Grinberg 	char str[5]; /* Legacy representation can contain at most 4 digits */
135689be5f8SIgor Grinberg 	uint offset = BOARD_REV_OFFSET_LEGACY;
136689be5f8SIgor Grinberg 
137a937fd16SIgor Grinberg 	if (board_rev)
138a937fd16SIgor Grinberg 		return board_rev;
139a937fd16SIgor Grinberg 
14072898ac7SNikita Kiryanov 	if (cl_eeprom_setup(eeprom_bus))
141689be5f8SIgor Grinberg 		return 0;
142689be5f8SIgor Grinberg 
143689be5f8SIgor Grinberg 	if (cl_eeprom_layout != LAYOUT_LEGACY)
144689be5f8SIgor Grinberg 		offset = BOARD_REV_OFFSET;
145689be5f8SIgor Grinberg 
146a937fd16SIgor Grinberg 	if (cl_eeprom_read(offset, (uchar *)&board_rev, BOARD_REV_SIZE))
147689be5f8SIgor Grinberg 		return 0;
148689be5f8SIgor Grinberg 
149689be5f8SIgor Grinberg 	/*
150689be5f8SIgor Grinberg 	 * Convert legacy syntactic representation to semantic
151689be5f8SIgor Grinberg 	 * representation. i.e. for rev 1.00: 0x100 --> 0x64
152689be5f8SIgor Grinberg 	 */
153689be5f8SIgor Grinberg 	if (cl_eeprom_layout == LAYOUT_LEGACY) {
154a937fd16SIgor Grinberg 		sprintf(str, "%x", board_rev);
155a937fd16SIgor Grinberg 		board_rev = simple_strtoul(str, NULL, 10);
156689be5f8SIgor Grinberg 	}
157689be5f8SIgor Grinberg 
158a937fd16SIgor Grinberg 	return board_rev;
159689be5f8SIgor Grinberg };
16053af877fSNikita Kiryanov 
16153af877fSNikita Kiryanov /*
16253af877fSNikita Kiryanov  * Routine: cl_eeprom_get_board_rev
16353af877fSNikita Kiryanov  * Description: read system revision from eeprom
16453af877fSNikita Kiryanov  *
16553af877fSNikita Kiryanov  * @buf: buffer to store the product name
16653af877fSNikita Kiryanov  * @eeprom_bus: i2c bus num of the eeprom
16753af877fSNikita Kiryanov  *
16853af877fSNikita Kiryanov  * @return: 0 on success, < 0 on failure
16953af877fSNikita Kiryanov  */
17053af877fSNikita Kiryanov int cl_eeprom_get_product_name(uchar *buf, uint eeprom_bus)
17153af877fSNikita Kiryanov {
17253af877fSNikita Kiryanov 	int err;
17353af877fSNikita Kiryanov 
17453af877fSNikita Kiryanov 	if (buf == NULL)
17553af877fSNikita Kiryanov 		return -EINVAL;
17653af877fSNikita Kiryanov 
17753af877fSNikita Kiryanov 	err = cl_eeprom_setup(eeprom_bus);
17853af877fSNikita Kiryanov 	if (err)
17953af877fSNikita Kiryanov 		return err;
18053af877fSNikita Kiryanov 
18153af877fSNikita Kiryanov 	err = cl_eeprom_read(PRODUCT_NAME_OFFSET, buf, PRODUCT_NAME_SIZE);
18253af877fSNikita Kiryanov 	if (!err) /* Protect ourselves from invalid data (unterminated str) */
18353af877fSNikita Kiryanov 		buf[PRODUCT_NAME_SIZE - 1] = '\0';
18453af877fSNikita Kiryanov 
18553af877fSNikita Kiryanov 	return err;
18653af877fSNikita Kiryanov }
187*8af5734bSNikita Kiryanov 
188*8af5734bSNikita Kiryanov #ifdef CONFIG_CMD_EEPROM_LAYOUT
189*8af5734bSNikita Kiryanov /**
190*8af5734bSNikita Kiryanov  * eeprom_field_print_bin_ver() - print a "version field" which contains binary
191*8af5734bSNikita Kiryanov  *				  data
192*8af5734bSNikita Kiryanov  *
193*8af5734bSNikita Kiryanov  * Treat the field data as simple binary data, and print it formatted as a
194*8af5734bSNikita Kiryanov  * version number (2 digits after decimal point).
195*8af5734bSNikita Kiryanov  * The field size must be exactly 2 bytes.
196*8af5734bSNikita Kiryanov  *
197*8af5734bSNikita Kiryanov  * Sample output:
198*8af5734bSNikita Kiryanov  *      Field Name      123.45
199*8af5734bSNikita Kiryanov  *
200*8af5734bSNikita Kiryanov  * @field:	an initialized field to print
201*8af5734bSNikita Kiryanov  */
202*8af5734bSNikita Kiryanov void eeprom_field_print_bin_ver(const struct eeprom_field *field)
203*8af5734bSNikita Kiryanov {
204*8af5734bSNikita Kiryanov 	if ((field->buf[0] == 0xff) && (field->buf[1] == 0xff)) {
205*8af5734bSNikita Kiryanov 		field->buf[0] = 0;
206*8af5734bSNikita Kiryanov 		field->buf[1] = 0;
207*8af5734bSNikita Kiryanov 	}
208*8af5734bSNikita Kiryanov 
209*8af5734bSNikita Kiryanov 	printf(PRINT_FIELD_SEGMENT, field->name);
210*8af5734bSNikita Kiryanov 	int major = (field->buf[1] << 8 | field->buf[0]) / 100;
211*8af5734bSNikita Kiryanov 	int minor = (field->buf[1] << 8 | field->buf[0]) - major * 100;
212*8af5734bSNikita Kiryanov 	printf("%d.%02d\n", major, minor);
213*8af5734bSNikita Kiryanov }
214*8af5734bSNikita Kiryanov 
215*8af5734bSNikita Kiryanov /**
216*8af5734bSNikita Kiryanov  * eeprom_field_update_bin_ver() - update a "version field" which contains
217*8af5734bSNikita Kiryanov  *				   binary data
218*8af5734bSNikita Kiryanov  *
219*8af5734bSNikita Kiryanov  * This function takes a version string in the form of x.y (x and y are both
220*8af5734bSNikita Kiryanov  * decimal values, y is limited to two digits), translates it to the binary
221*8af5734bSNikita Kiryanov  * form, then writes it to the field. The field size must be exactly 2 bytes.
222*8af5734bSNikita Kiryanov  *
223*8af5734bSNikita Kiryanov  * This function strictly enforces the data syntax, and will not update the
224*8af5734bSNikita Kiryanov  * field if there's any deviation from it. It also protects from overflow.
225*8af5734bSNikita Kiryanov  *
226*8af5734bSNikita Kiryanov  * @field:	an initialized field
227*8af5734bSNikita Kiryanov  * @value:	a version string
228*8af5734bSNikita Kiryanov  *
229*8af5734bSNikita Kiryanov  * Returns 0 on success, -1 on failure.
230*8af5734bSNikita Kiryanov  */
231*8af5734bSNikita Kiryanov int eeprom_field_update_bin_ver(struct eeprom_field *field, char *value)
232*8af5734bSNikita Kiryanov {
233*8af5734bSNikita Kiryanov 	char *endptr;
234*8af5734bSNikita Kiryanov 	char *tok = strtok(value, ".");
235*8af5734bSNikita Kiryanov 	if (tok == NULL)
236*8af5734bSNikita Kiryanov 		return -1;
237*8af5734bSNikita Kiryanov 
238*8af5734bSNikita Kiryanov 	int num = simple_strtol(tok, &endptr, 0);
239*8af5734bSNikita Kiryanov 	if (*endptr != '\0')
240*8af5734bSNikita Kiryanov 		return -1;
241*8af5734bSNikita Kiryanov 
242*8af5734bSNikita Kiryanov 	tok = strtok(NULL, "");
243*8af5734bSNikita Kiryanov 	if (tok == NULL)
244*8af5734bSNikita Kiryanov 		return -1;
245*8af5734bSNikita Kiryanov 
246*8af5734bSNikita Kiryanov 	int remainder = simple_strtol(tok, &endptr, 0);
247*8af5734bSNikita Kiryanov 	if (*endptr != '\0')
248*8af5734bSNikita Kiryanov 		return -1;
249*8af5734bSNikita Kiryanov 
250*8af5734bSNikita Kiryanov 	num = num * 100 + remainder;
251*8af5734bSNikita Kiryanov 	if (num >> 16)
252*8af5734bSNikita Kiryanov 		return -1;
253*8af5734bSNikita Kiryanov 
254*8af5734bSNikita Kiryanov 	field->buf[0] = (unsigned char)num;
255*8af5734bSNikita Kiryanov 	field->buf[1] = num >> 8;
256*8af5734bSNikita Kiryanov 
257*8af5734bSNikita Kiryanov 	return 0;
258*8af5734bSNikita Kiryanov }
259*8af5734bSNikita Kiryanov 
260*8af5734bSNikita Kiryanov char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
261*8af5734bSNikita Kiryanov 		    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
262*8af5734bSNikita Kiryanov 
263*8af5734bSNikita Kiryanov /**
264*8af5734bSNikita Kiryanov  * eeprom_field_print_date() - print a field which contains date data
265*8af5734bSNikita Kiryanov  *
266*8af5734bSNikita Kiryanov  * Treat the field data as simple binary data, and print it formatted as a date.
267*8af5734bSNikita Kiryanov  * Sample output:
268*8af5734bSNikita Kiryanov  *      Field Name      07/Feb/2014
269*8af5734bSNikita Kiryanov  *      Field Name      56/BAD/9999
270*8af5734bSNikita Kiryanov  *
271*8af5734bSNikita Kiryanov  * @field:	an initialized field to print
272*8af5734bSNikita Kiryanov  */
273*8af5734bSNikita Kiryanov void eeprom_field_print_date(const struct eeprom_field *field)
274*8af5734bSNikita Kiryanov {
275*8af5734bSNikita Kiryanov 	printf(PRINT_FIELD_SEGMENT, field->name);
276*8af5734bSNikita Kiryanov 	printf("%02d/", field->buf[0]);
277*8af5734bSNikita Kiryanov 	if (field->buf[1] >= 1 && field->buf[1] <= 12)
278*8af5734bSNikita Kiryanov 		printf("%s", months[field->buf[1] - 1]);
279*8af5734bSNikita Kiryanov 	else
280*8af5734bSNikita Kiryanov 		printf("BAD");
281*8af5734bSNikita Kiryanov 
282*8af5734bSNikita Kiryanov 	printf("/%d\n", field->buf[3] << 8 | field->buf[2]);
283*8af5734bSNikita Kiryanov }
284*8af5734bSNikita Kiryanov 
285*8af5734bSNikita Kiryanov static int validate_date(unsigned char day, unsigned char month,
286*8af5734bSNikita Kiryanov 			unsigned int year)
287*8af5734bSNikita Kiryanov {
288*8af5734bSNikita Kiryanov 	int days_in_february;
289*8af5734bSNikita Kiryanov 
290*8af5734bSNikita Kiryanov 	switch (month) {
291*8af5734bSNikita Kiryanov 	case 0:
292*8af5734bSNikita Kiryanov 	case 2:
293*8af5734bSNikita Kiryanov 	case 4:
294*8af5734bSNikita Kiryanov 	case 6:
295*8af5734bSNikita Kiryanov 	case 7:
296*8af5734bSNikita Kiryanov 	case 9:
297*8af5734bSNikita Kiryanov 	case 11:
298*8af5734bSNikita Kiryanov 		if (day > 31)
299*8af5734bSNikita Kiryanov 			return -1;
300*8af5734bSNikita Kiryanov 		break;
301*8af5734bSNikita Kiryanov 	case 3:
302*8af5734bSNikita Kiryanov 	case 5:
303*8af5734bSNikita Kiryanov 	case 8:
304*8af5734bSNikita Kiryanov 	case 10:
305*8af5734bSNikita Kiryanov 		if (day > 30)
306*8af5734bSNikita Kiryanov 			return -1;
307*8af5734bSNikita Kiryanov 		break;
308*8af5734bSNikita Kiryanov 	case 1:
309*8af5734bSNikita Kiryanov 		days_in_february = 28;
310*8af5734bSNikita Kiryanov 		if (year % 4 == 0) {
311*8af5734bSNikita Kiryanov 			if (year % 100 != 0)
312*8af5734bSNikita Kiryanov 				days_in_february = 29;
313*8af5734bSNikita Kiryanov 			else if (year % 400 == 0)
314*8af5734bSNikita Kiryanov 				days_in_february = 29;
315*8af5734bSNikita Kiryanov 		}
316*8af5734bSNikita Kiryanov 
317*8af5734bSNikita Kiryanov 		if (day > days_in_february)
318*8af5734bSNikita Kiryanov 			return -1;
319*8af5734bSNikita Kiryanov 
320*8af5734bSNikita Kiryanov 		break;
321*8af5734bSNikita Kiryanov 	default:
322*8af5734bSNikita Kiryanov 		return -1;
323*8af5734bSNikita Kiryanov 	}
324*8af5734bSNikita Kiryanov 
325*8af5734bSNikita Kiryanov 	return 0;
326*8af5734bSNikita Kiryanov }
327*8af5734bSNikita Kiryanov 
328*8af5734bSNikita Kiryanov /**
329*8af5734bSNikita Kiryanov  * eeprom_field_update_date() - update a date field which contains binary data
330*8af5734bSNikita Kiryanov  *
331*8af5734bSNikita Kiryanov  * This function takes a date string in the form of x/Mon/y (x and y are both
332*8af5734bSNikita Kiryanov  * decimal values), translates it to the binary representation, then writes it
333*8af5734bSNikita Kiryanov  * to the field.
334*8af5734bSNikita Kiryanov  *
335*8af5734bSNikita Kiryanov  * This function strictly enforces the data syntax, and will not update the
336*8af5734bSNikita Kiryanov  * field if there's any deviation from it. It also protects from overflow in the
337*8af5734bSNikita Kiryanov  * year value, and checks the validity of the date.
338*8af5734bSNikita Kiryanov  *
339*8af5734bSNikita Kiryanov  * @field:	an initialized field
340*8af5734bSNikita Kiryanov  * @value:	a date string
341*8af5734bSNikita Kiryanov  *
342*8af5734bSNikita Kiryanov  * Returns 0 on success, -1 on failure.
343*8af5734bSNikita Kiryanov  */
344*8af5734bSNikita Kiryanov int eeprom_field_update_date(struct eeprom_field *field, char *value)
345*8af5734bSNikita Kiryanov {
346*8af5734bSNikita Kiryanov 	char *endptr;
347*8af5734bSNikita Kiryanov 	char *tok1 = strtok(value, "/");
348*8af5734bSNikita Kiryanov 	char *tok2 = strtok(NULL, "/");
349*8af5734bSNikita Kiryanov 	char *tok3 = strtok(NULL, "/");
350*8af5734bSNikita Kiryanov 
351*8af5734bSNikita Kiryanov 	if (tok1 == NULL || tok2 == NULL || tok3 == NULL) {
352*8af5734bSNikita Kiryanov 		printf("%s: syntax error\n", field->name);
353*8af5734bSNikita Kiryanov 		return -1;
354*8af5734bSNikita Kiryanov 	}
355*8af5734bSNikita Kiryanov 
356*8af5734bSNikita Kiryanov 	unsigned char day = (unsigned char)simple_strtol(tok1, &endptr, 0);
357*8af5734bSNikita Kiryanov 	if (*endptr != '\0' || day == 0) {
358*8af5734bSNikita Kiryanov 		printf("%s: invalid day\n", field->name);
359*8af5734bSNikita Kiryanov 		return -1;
360*8af5734bSNikita Kiryanov 	}
361*8af5734bSNikita Kiryanov 
362*8af5734bSNikita Kiryanov 	unsigned char month;
363*8af5734bSNikita Kiryanov 	for (month = 1; month <= 12; month++)
364*8af5734bSNikita Kiryanov 		if (!strcmp(tok2, months[month - 1]))
365*8af5734bSNikita Kiryanov 			break;
366*8af5734bSNikita Kiryanov 
367*8af5734bSNikita Kiryanov 	unsigned int year = simple_strtol(tok3, &endptr, 0);
368*8af5734bSNikita Kiryanov 	if (*endptr != '\0') {
369*8af5734bSNikita Kiryanov 		printf("%s: invalid year\n", field->name);
370*8af5734bSNikita Kiryanov 		return -1;
371*8af5734bSNikita Kiryanov 	}
372*8af5734bSNikita Kiryanov 
373*8af5734bSNikita Kiryanov 	if (validate_date(day, month - 1, year)) {
374*8af5734bSNikita Kiryanov 		printf("%s: invalid date\n", field->name);
375*8af5734bSNikita Kiryanov 		return -1;
376*8af5734bSNikita Kiryanov 	}
377*8af5734bSNikita Kiryanov 
378*8af5734bSNikita Kiryanov 	if (year >> 16) {
379*8af5734bSNikita Kiryanov 		printf("%s: year overflow\n", field->name);
380*8af5734bSNikita Kiryanov 		return -1;
381*8af5734bSNikita Kiryanov 	}
382*8af5734bSNikita Kiryanov 
383*8af5734bSNikita Kiryanov 	field->buf[0] = day;
384*8af5734bSNikita Kiryanov 	field->buf[1] = month;
385*8af5734bSNikita Kiryanov 	field->buf[2] = (unsigned char)year;
386*8af5734bSNikita Kiryanov 	field->buf[3] = (unsigned char)(year >> 8);
387*8af5734bSNikita Kiryanov 
388*8af5734bSNikita Kiryanov 	return 0;
389*8af5734bSNikita Kiryanov }
390*8af5734bSNikita Kiryanov 
391*8af5734bSNikita Kiryanov #define	LAYOUT_VERSION_LEGACY 1
392*8af5734bSNikita Kiryanov #define	LAYOUT_VERSION_VER1 2
393*8af5734bSNikita Kiryanov #define	LAYOUT_VERSION_VER2 3
394*8af5734bSNikita Kiryanov #define	LAYOUT_VERSION_VER3 4
395*8af5734bSNikita Kiryanov 
396*8af5734bSNikita Kiryanov extern struct eeprom_field layout_unknown[1];
397*8af5734bSNikita Kiryanov 
398*8af5734bSNikita Kiryanov #define DEFINE_PRINT_UPDATE(x) eeprom_field_print_##x, eeprom_field_update_##x
399*8af5734bSNikita Kiryanov 
400*8af5734bSNikita Kiryanov #ifdef CONFIG_CM_T3X
401*8af5734bSNikita Kiryanov struct eeprom_field layout_legacy[5] = {
402*8af5734bSNikita Kiryanov 	{ "MAC address",          6, NULL, DEFINE_PRINT_UPDATE(mac) },
403*8af5734bSNikita Kiryanov 	{ "Board Revision",       2, NULL, DEFINE_PRINT_UPDATE(bin) },
404*8af5734bSNikita Kiryanov 	{ "Serial Number",        8, NULL, DEFINE_PRINT_UPDATE(bin) },
405*8af5734bSNikita Kiryanov 	{ "Board Configuration", 64, NULL, DEFINE_PRINT_UPDATE(ascii) },
406*8af5734bSNikita Kiryanov 	{ RESERVED_FIELDS,      176, NULL, eeprom_field_print_reserved,
407*8af5734bSNikita Kiryanov 					   eeprom_field_update_ascii },
408*8af5734bSNikita Kiryanov };
409*8af5734bSNikita Kiryanov #else
410*8af5734bSNikita Kiryanov #define layout_legacy layout_unknown
411*8af5734bSNikita Kiryanov #endif
412*8af5734bSNikita Kiryanov 
413*8af5734bSNikita Kiryanov #if defined(CONFIG_CM_T3X) || defined(CONFIG_CM_T3517)
414*8af5734bSNikita Kiryanov struct eeprom_field layout_v1[12] = {
415*8af5734bSNikita Kiryanov 	{ "Major Revision",      2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
416*8af5734bSNikita Kiryanov 	{ "Minor Revision",      2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
417*8af5734bSNikita Kiryanov 	{ "1st MAC Address",     6, NULL, DEFINE_PRINT_UPDATE(mac) },
418*8af5734bSNikita Kiryanov 	{ "2nd MAC Address",     6, NULL, DEFINE_PRINT_UPDATE(mac) },
419*8af5734bSNikita Kiryanov 	{ "Production Date",     4, NULL, DEFINE_PRINT_UPDATE(date) },
420*8af5734bSNikita Kiryanov 	{ "Serial Number",      12, NULL, DEFINE_PRINT_UPDATE(bin_rev) },
421*8af5734bSNikita Kiryanov 	{ RESERVED_FIELDS,      96, NULL, DEFINE_PRINT_UPDATE(reserved) },
422*8af5734bSNikita Kiryanov 	{ "Product Name",       16, NULL, DEFINE_PRINT_UPDATE(ascii) },
423*8af5734bSNikita Kiryanov 	{ "Product Options #1", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
424*8af5734bSNikita Kiryanov 	{ "Product Options #2", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
425*8af5734bSNikita Kiryanov 	{ "Product Options #3", 16, NULL, DEFINE_PRINT_UPDATE(ascii) },
426*8af5734bSNikita Kiryanov 	{ RESERVED_FIELDS,      64, NULL, eeprom_field_print_reserved,
427*8af5734bSNikita Kiryanov 					  eeprom_field_update_ascii },
428*8af5734bSNikita Kiryanov };
429*8af5734bSNikita Kiryanov #else
430*8af5734bSNikita Kiryanov #define layout_v1 layout_unknown
431*8af5734bSNikita Kiryanov #endif
432*8af5734bSNikita Kiryanov 
433*8af5734bSNikita Kiryanov struct eeprom_field layout_v2[15] = {
434*8af5734bSNikita Kiryanov 	{ "Major Revision",            2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
435*8af5734bSNikita Kiryanov 	{ "Minor Revision",            2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
436*8af5734bSNikita Kiryanov 	{ "1st MAC Address",           6, NULL, DEFINE_PRINT_UPDATE(mac) },
437*8af5734bSNikita Kiryanov 	{ "2nd MAC Address",           6, NULL, DEFINE_PRINT_UPDATE(mac) },
438*8af5734bSNikita Kiryanov 	{ "Production Date",           4, NULL, DEFINE_PRINT_UPDATE(date) },
439*8af5734bSNikita Kiryanov 	{ "Serial Number",            12, NULL, DEFINE_PRINT_UPDATE(bin_rev) },
440*8af5734bSNikita Kiryanov 	{ "3rd MAC Address (WIFI)",    6, NULL, DEFINE_PRINT_UPDATE(mac) },
441*8af5734bSNikita Kiryanov 	{ "4th MAC Address (Bluetooth)", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
442*8af5734bSNikita Kiryanov 	{ "Layout Version",            1, NULL, DEFINE_PRINT_UPDATE(bin) },
443*8af5734bSNikita Kiryanov 	{ RESERVED_FIELDS,            83, NULL, DEFINE_PRINT_UPDATE(reserved) },
444*8af5734bSNikita Kiryanov 	{ "Product Name",             16, NULL, DEFINE_PRINT_UPDATE(ascii) },
445*8af5734bSNikita Kiryanov 	{ "Product Options #1",       16, NULL, DEFINE_PRINT_UPDATE(ascii) },
446*8af5734bSNikita Kiryanov 	{ "Product Options #2",       16, NULL, DEFINE_PRINT_UPDATE(ascii) },
447*8af5734bSNikita Kiryanov 	{ "Product Options #3",       16, NULL, DEFINE_PRINT_UPDATE(ascii) },
448*8af5734bSNikita Kiryanov 	{ RESERVED_FIELDS,            64, NULL, eeprom_field_print_reserved,
449*8af5734bSNikita Kiryanov 						eeprom_field_update_ascii },
450*8af5734bSNikita Kiryanov };
451*8af5734bSNikita Kiryanov 
452*8af5734bSNikita Kiryanov struct eeprom_field layout_v3[16] = {
453*8af5734bSNikita Kiryanov 	{ "Major Revision",            2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
454*8af5734bSNikita Kiryanov 	{ "Minor Revision",            2, NULL, DEFINE_PRINT_UPDATE(bin_ver) },
455*8af5734bSNikita Kiryanov 	{ "1st MAC Address",           6, NULL, DEFINE_PRINT_UPDATE(mac) },
456*8af5734bSNikita Kiryanov 	{ "2nd MAC Address",           6, NULL, DEFINE_PRINT_UPDATE(mac) },
457*8af5734bSNikita Kiryanov 	{ "Production Date",           4, NULL, DEFINE_PRINT_UPDATE(date) },
458*8af5734bSNikita Kiryanov 	{ "Serial Number",            12, NULL, DEFINE_PRINT_UPDATE(bin_rev) },
459*8af5734bSNikita Kiryanov 	{ "3rd MAC Address (WIFI)",    6, NULL, DEFINE_PRINT_UPDATE(mac) },
460*8af5734bSNikita Kiryanov 	{ "4th MAC Address (Bluetooth)", 6, NULL, DEFINE_PRINT_UPDATE(mac) },
461*8af5734bSNikita Kiryanov 	{ "Layout Version",            1, NULL, DEFINE_PRINT_UPDATE(bin) },
462*8af5734bSNikita Kiryanov 	{ "CompuLab EEPROM ID",        3, NULL, DEFINE_PRINT_UPDATE(bin) },
463*8af5734bSNikita Kiryanov 	{ RESERVED_FIELDS,            80, NULL, DEFINE_PRINT_UPDATE(reserved) },
464*8af5734bSNikita Kiryanov 	{ "Product Name",             16, NULL, DEFINE_PRINT_UPDATE(ascii) },
465*8af5734bSNikita Kiryanov 	{ "Product Options #1",       16, NULL, DEFINE_PRINT_UPDATE(ascii) },
466*8af5734bSNikita Kiryanov 	{ "Product Options #2",       16, NULL, DEFINE_PRINT_UPDATE(ascii) },
467*8af5734bSNikita Kiryanov 	{ "Product Options #3",       16, NULL, DEFINE_PRINT_UPDATE(ascii) },
468*8af5734bSNikita Kiryanov 	{ RESERVED_FIELDS,            64, NULL, eeprom_field_print_reserved,
469*8af5734bSNikita Kiryanov 						eeprom_field_update_ascii },
470*8af5734bSNikita Kiryanov };
471*8af5734bSNikita Kiryanov 
472*8af5734bSNikita Kiryanov void eeprom_layout_assign(struct eeprom_layout *layout, int layout_version)
473*8af5734bSNikita Kiryanov {
474*8af5734bSNikita Kiryanov 	switch (layout->layout_version) {
475*8af5734bSNikita Kiryanov 	case LAYOUT_VERSION_LEGACY:
476*8af5734bSNikita Kiryanov 		layout->fields = layout_legacy;
477*8af5734bSNikita Kiryanov 		layout->num_of_fields = ARRAY_SIZE(layout_legacy);
478*8af5734bSNikita Kiryanov 		break;
479*8af5734bSNikita Kiryanov 	case LAYOUT_VERSION_VER1:
480*8af5734bSNikita Kiryanov 		layout->fields = layout_v1;
481*8af5734bSNikita Kiryanov 		layout->num_of_fields = ARRAY_SIZE(layout_v1);
482*8af5734bSNikita Kiryanov 		break;
483*8af5734bSNikita Kiryanov 	case LAYOUT_VERSION_VER2:
484*8af5734bSNikita Kiryanov 		layout->fields = layout_v2;
485*8af5734bSNikita Kiryanov 		layout->num_of_fields = ARRAY_SIZE(layout_v2);
486*8af5734bSNikita Kiryanov 		break;
487*8af5734bSNikita Kiryanov 	case LAYOUT_VERSION_VER3:
488*8af5734bSNikita Kiryanov 		layout->fields = layout_v3;
489*8af5734bSNikita Kiryanov 		layout->num_of_fields = ARRAY_SIZE(layout_v3);
490*8af5734bSNikita Kiryanov 		break;
491*8af5734bSNikita Kiryanov 	default:
492*8af5734bSNikita Kiryanov 		__eeprom_layout_assign(layout, layout_version);
493*8af5734bSNikita Kiryanov 	}
494*8af5734bSNikita Kiryanov }
495*8af5734bSNikita Kiryanov 
496*8af5734bSNikita Kiryanov int eeprom_parse_layout_version(char *str)
497*8af5734bSNikita Kiryanov {
498*8af5734bSNikita Kiryanov 	if (!strcmp(str, "legacy"))
499*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_LEGACY;
500*8af5734bSNikita Kiryanov 	else if (!strcmp(str, "v1"))
501*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_VER1;
502*8af5734bSNikita Kiryanov 	else if (!strcmp(str, "v2"))
503*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_VER2;
504*8af5734bSNikita Kiryanov 	else if (!strcmp(str, "v3"))
505*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_VER3;
506*8af5734bSNikita Kiryanov 	else
507*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_UNRECOGNIZED;
508*8af5734bSNikita Kiryanov }
509*8af5734bSNikita Kiryanov 
510*8af5734bSNikita Kiryanov int eeprom_layout_detect(unsigned char *data)
511*8af5734bSNikita Kiryanov {
512*8af5734bSNikita Kiryanov 	switch (data[EEPROM_LAYOUT_VER_OFFSET]) {
513*8af5734bSNikita Kiryanov 	case 0xff:
514*8af5734bSNikita Kiryanov 	case 0:
515*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_VER1;
516*8af5734bSNikita Kiryanov 	case 2:
517*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_VER2;
518*8af5734bSNikita Kiryanov 	case 3:
519*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_VER3;
520*8af5734bSNikita Kiryanov 	}
521*8af5734bSNikita Kiryanov 
522*8af5734bSNikita Kiryanov 	if (data[EEPROM_LAYOUT_VER_OFFSET] >= 0x20)
523*8af5734bSNikita Kiryanov 		return LAYOUT_VERSION_LEGACY;
524*8af5734bSNikita Kiryanov 
525*8af5734bSNikita Kiryanov 	return LAYOUT_VERSION_UNRECOGNIZED;
526*8af5734bSNikita Kiryanov }
527*8af5734bSNikita Kiryanov #endif
528