1 /*
2  * (C) Copyright 2013
3  * Corscience GmbH & Co. KG, <www.corscience.de>
4  * Andreas Bießmann <andreas.biessmann@corscience.de>
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 #include <common.h>
9 #include <i2c.h>
10 
11 #include "tricorder-eeprom.h"
12 
13 static inline void warn_wrong_value(const char *msg, unsigned int a,
14 		unsigned int b)
15 {
16 	printf("Expected EEPROM %s %08x, got %08x\n", msg, a, b);
17 }
18 
19 static int handle_eeprom_v0(struct tricorder_eeprom *eeprom)
20 {
21 	struct tricorder_eeprom_v0 {
22 		uint32_t magic;
23 		uint16_t length;
24 		uint16_t version;
25 		char board_name[TRICORDER_BOARD_NAME_LENGTH];
26 		char board_version[TRICORDER_BOARD_VERSION_LENGTH];
27 		char board_serial[TRICORDER_BOARD_SERIAL_LENGTH];
28 		uint32_t crc32;
29 	} __packed eepromv0;
30 	uint32_t crc;
31 
32 	printf("Old EEPROM (v0), consider rewrite!\n");
33 
34 	if (be16_to_cpu(eeprom->length) != sizeof(eepromv0)) {
35 		warn_wrong_value("length", sizeof(eepromv0),
36 				 be16_to_cpu(eeprom->length));
37 		return 1;
38 	}
39 
40 	memcpy(&eepromv0, eeprom, sizeof(eepromv0));
41 
42 	crc = crc32(0L, (unsigned char *)&eepromv0,
43 		    sizeof(eepromv0) - sizeof(eepromv0.crc32));
44 	if (be32_to_cpu(eepromv0.crc32) != crc) {
45 		warn_wrong_value("CRC", be32_to_cpu(eepromv0.crc32),
46 				 crc);
47 		return 1;
48 	}
49 
50 	/* Ok the content is correct, do the conversion */
51 	memset(eeprom->interface_version, 0x0,
52 	       TRICORDER_INTERFACE_VERSION_LENGTH);
53 	crc = crc32(0L, (unsigned char *)eeprom, TRICORDER_EEPROM_CRC_SIZE);
54 	eeprom->crc32 = cpu_to_be32(crc);
55 
56 	return 0;
57 }
58 
59 static int handle_eeprom_v1(struct tricorder_eeprom *eeprom)
60 {
61 	uint32_t crc;
62 
63 	if (be16_to_cpu(eeprom->length) != TRICORDER_EEPROM_SIZE) {
64 		warn_wrong_value("length", TRICORDER_EEPROM_SIZE,
65 				 be16_to_cpu(eeprom->length));
66 		return 1;
67 	}
68 
69 	crc = crc32(0L, (unsigned char *)eeprom, TRICORDER_EEPROM_CRC_SIZE);
70 	if (be32_to_cpu(eeprom->crc32) != crc) {
71 		warn_wrong_value("CRC", be32_to_cpu(eeprom->crc32), crc);
72 		return 1;
73 	}
74 
75 	return 0;
76 }
77 
78 int tricorder_get_eeprom(int addr, struct tricorder_eeprom *eeprom)
79 {
80 #ifdef CONFIG_SYS_EEPROM_BUS_NUM
81 	unsigned int bus = i2c_get_bus_num();
82 	i2c_set_bus_num(CONFIG_SYS_EEPROM_BUS_NUM);
83 #endif
84 
85 	memset(eeprom, 0, TRICORDER_EEPROM_SIZE);
86 
87 	i2c_read(addr, 0, 2, (unsigned char *)eeprom, TRICORDER_EEPROM_SIZE);
88 #ifdef CONFIG_SYS_EEPROM_BUS_NUM
89 	i2c_set_bus_num(bus);
90 #endif
91 
92 	if (be32_to_cpu(eeprom->magic) != TRICORDER_EEPROM_MAGIC) {
93 		warn_wrong_value("magic", TRICORDER_EEPROM_MAGIC,
94 				 be32_to_cpu(eeprom->magic));
95 		return 1;
96 	}
97 
98 	switch (be16_to_cpu(eeprom->version)) {
99 	case 0:
100 		return handle_eeprom_v0(eeprom);
101 	case 1:
102 		return handle_eeprom_v1(eeprom);
103 	default:
104 		warn_wrong_value("version", TRICORDER_EEPROM_VERSION,
105 				 be16_to_cpu(eeprom->version));
106 		return 1;
107 	}
108 }
109 
110 #if !defined(CONFIG_SPL)
111 int tricorder_eeprom_read(unsigned devaddr)
112 {
113 	struct tricorder_eeprom eeprom;
114 	int ret = tricorder_get_eeprom(devaddr, &eeprom);
115 
116 	if (ret)
117 		return ret;
118 
119 	printf("Board type:               %.*s\n",
120 	       sizeof(eeprom.board_name), eeprom.board_name);
121 	printf("Board version:            %.*s\n",
122 	       sizeof(eeprom.board_version), eeprom.board_version);
123 	printf("Board serial:             %.*s\n",
124 	       sizeof(eeprom.board_serial), eeprom.board_serial);
125 	printf("Board interface version:  %.*s\n",
126 	       sizeof(eeprom.interface_version),
127 	       eeprom.interface_version);
128 
129 	return ret;
130 }
131 
132 int tricorder_eeprom_write(unsigned devaddr, const char *name,
133 		const char *version, const char *serial, const char *interface)
134 {
135 	struct tricorder_eeprom eeprom, eeprom_verify;
136 	size_t length;
137 	uint32_t crc;
138 	int ret;
139 	unsigned char *p;
140 	int i;
141 #ifdef CONFIG_SYS_EEPROM_BUS_NUM
142 	unsigned int bus;
143 #endif
144 
145 	memset(eeprom, 0, TRICORDER_EEPROM_SIZE);
146 	memset(eeprom_verify, 0, TRICORDER_EEPROM_SIZE);
147 
148 	eeprom.magic = cpu_to_be32(TRICORDER_EEPROM_MAGIC);
149 	eeprom.length = cpu_to_be16(TRICORDER_EEPROM_SIZE);
150 	eeprom.version = cpu_to_be16(TRICORDER_EEPROM_VERSION);
151 
152 	length = min(sizeof(eeprom.board_name), strlen(name));
153 	strncpy(eeprom.board_name, name, length);
154 
155 	length = min(sizeof(eeprom.board_version), strlen(version));
156 	strncpy(eeprom.board_version, version, length);
157 
158 	length = min(sizeof(eeprom.board_serial), strlen(serial));
159 	strncpy(eeprom.board_serial, serial, length);
160 
161 	if (interface) {
162 		length = min(sizeof(eeprom.interface_version),
163 				strlen(interface));
164 		strncpy(eeprom.interface_version, interface, length);
165 	}
166 
167 	crc = crc32(0L, (unsigned char *)&eeprom, TRICORDER_EEPROM_CRC_SIZE);
168 	eeprom.crc32 = cpu_to_be32(crc);
169 
170 #if defined(DEBUG)
171 	puts("Tricorder EEPROM content:\n");
172 	print_buffer(0, &eeprom, 1, sizeof(eeprom), 16);
173 #endif
174 
175 #ifdef CONFIG_SYS_EEPROM_BUS_NUM
176 	bus = i2c_get_bus_num();
177 	i2c_set_bus_num(CONFIG_SYS_EEPROM_BUS_NUM);
178 #endif
179 
180 	/* do page write to the eeprom */
181 	for (i = 0, p = (unsigned char *)&eeprom;
182 	     i < sizeof(eeprom);
183 	     i += 32, p += 32) {
184 		ret = i2c_write(devaddr, i, CONFIG_SYS_I2C_EEPROM_ADDR_LEN,
185 				p, min(sizeof(eeprom) - i, 32));
186 		if (ret)
187 			break;
188 		udelay(5000); /* 5ms write cycle timing */
189 	}
190 
191 	ret = i2c_read(devaddr, 0, 2, (unsigned char *)&eeprom_verify,
192 			TRICORDER_EEPROM_SIZE);
193 
194 	if (memcmp(&eeprom, &eeprom_verify, sizeof(eeprom)) != 0) {
195 		printf("Tricorder: Could not verify EEPROM content!\n");
196 		ret = 1;
197 	}
198 
199 #ifdef CONFIG_SYS_EEPROM_BUS_NUM
200 	i2c_set_bus_num(bus);
201 #endif
202 	return ret;
203 }
204 
205 int do_tricorder_eeprom(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
206 {
207 	if (argc == 3) {
208 		ulong dev_addr = simple_strtoul(argv[2], NULL, 16);
209 		eeprom_init();
210 		if (strcmp(argv[1], "read") == 0) {
211 			int rcode;
212 
213 			rcode = tricorder_eeprom_read(dev_addr);
214 
215 			return rcode;
216 		}
217 	} else if (argc == 6 || argc == 7) {
218 		ulong dev_addr = simple_strtoul(argv[2], NULL, 16);
219 		char *name = argv[3];
220 		char *version = argv[4];
221 		char *serial = argv[5];
222 		char *interface = NULL;
223 		eeprom_init();
224 
225 		if (argc == 7)
226 			interface = argv[6];
227 
228 		if (strcmp(argv[1], "write") == 0) {
229 			int rcode;
230 
231 			rcode = tricorder_eeprom_write(dev_addr, name, version,
232 					serial, interface);
233 
234 			return rcode;
235 		}
236 	}
237 
238 	return CMD_RET_USAGE;
239 }
240 
241 U_BOOT_CMD(
242 	tricordereeprom,	7,	1,	do_tricorder_eeprom,
243 	"Tricorder EEPROM",
244 	"read  devaddr\n"
245 	"       - read Tricorder EEPROM at devaddr and print content\n"
246 	"tricordereeprom write devaddr name version serial [interface]\n"
247 	"       - write Tricorder EEPROM at devaddr with 'name', 'version'"
248 	"and 'serial'\n"
249 	"         optional add an HW interface parameter"
250 );
251 #endif /* CONFIG_SPL */
252