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 	unsigned int bus = i2c_get_bus_num();
81 	i2c_set_bus_num(CONFIG_SYS_EEPROM_BUS_NUM);
82 
83 	memset(eeprom, 0, TRICORDER_EEPROM_SIZE);
84 
85 	i2c_read(addr, 0, 2, (unsigned char *)eeprom, TRICORDER_EEPROM_SIZE);
86 	i2c_set_bus_num(bus);
87 
88 	if (be32_to_cpu(eeprom->magic) != TRICORDER_EEPROM_MAGIC) {
89 		warn_wrong_value("magic", TRICORDER_EEPROM_MAGIC,
90 				 be32_to_cpu(eeprom->magic));
91 		return 1;
92 	}
93 
94 	switch (be16_to_cpu(eeprom->version)) {
95 	case 0:
96 		return handle_eeprom_v0(eeprom);
97 	case 1:
98 		return handle_eeprom_v1(eeprom);
99 	default:
100 		warn_wrong_value("version", TRICORDER_EEPROM_VERSION,
101 				 be16_to_cpu(eeprom->version));
102 		return 1;
103 	}
104 }
105 
106 #if !defined(CONFIG_SPL)
107 int tricorder_eeprom_read(unsigned devaddr)
108 {
109 	struct tricorder_eeprom eeprom;
110 	int ret = tricorder_get_eeprom(devaddr, &eeprom);
111 
112 	if (ret)
113 		return ret;
114 
115 	printf("Board type:               %.*s\n",
116 	       sizeof(eeprom.board_name), eeprom.board_name);
117 	printf("Board version:            %.*s\n",
118 	       sizeof(eeprom.board_version), eeprom.board_version);
119 	printf("Board serial:             %.*s\n",
120 	       sizeof(eeprom.board_serial), eeprom.board_serial);
121 	printf("Board interface version:  %.*s\n",
122 	       sizeof(eeprom.interface_version),
123 	       eeprom.interface_version);
124 
125 	return ret;
126 }
127 
128 int tricorder_eeprom_write(unsigned devaddr, const char *name,
129 		const char *version, const char *serial, const char *interface)
130 {
131 	struct tricorder_eeprom eeprom, eeprom_verify;
132 	size_t length;
133 	uint32_t crc;
134 	int ret;
135 	unsigned char *p;
136 	int i;
137 
138 	memset(eeprom, 0, TRICORDER_EEPROM_SIZE);
139 	memset(eeprom_verify, 0, TRICORDER_EEPROM_SIZE);
140 
141 	eeprom.magic = cpu_to_be32(TRICORDER_EEPROM_MAGIC);
142 	eeprom.length = cpu_to_be16(TRICORDER_EEPROM_SIZE);
143 	eeprom.version = cpu_to_be16(TRICORDER_EEPROM_VERSION);
144 
145 	length = min(sizeof(eeprom.board_name), strlen(name));
146 	strncpy(eeprom.board_name, name, length);
147 
148 	length = min(sizeof(eeprom.board_version), strlen(version));
149 	strncpy(eeprom.board_version, version, length);
150 
151 	length = min(sizeof(eeprom.board_serial), strlen(serial));
152 	strncpy(eeprom.board_serial, serial, length);
153 
154 	if (interface) {
155 		length = min(sizeof(eeprom.interface_version),
156 				strlen(interface));
157 		strncpy(eeprom.interface_version, interface, length);
158 	}
159 
160 	crc = crc32(0L, (unsigned char *)&eeprom, TRICORDER_EEPROM_CRC_SIZE);
161 	eeprom.crc32 = cpu_to_be32(crc);
162 
163 #if defined(DEBUG)
164 	puts("Tricorder EEPROM content:\n");
165 	print_buffer(0, &eeprom, 1, sizeof(eeprom), 16);
166 #endif
167 
168 	eeprom_init(CONFIG_SYS_EEPROM_BUS_NUM);
169 
170 	ret = eeprom_write(devaddr, 0, (unsigned char *)&eeprom,
171 			TRICORDER_EEPROM_SIZE);
172 	if (ret)
173 		printf("Tricorder: Could not write EEPROM content!\n");
174 
175 	ret = eeprom_read(devaddr, 0, (unsigned char *)&eeprom_verify,
176 			TRICORDER_EEPROM_SIZE);
177 	if (ret)
178 		printf("Tricorder: Could not read EEPROM content!\n");
179 
180 	if (memcmp(&eeprom, &eeprom_verify, sizeof(eeprom)) != 0) {
181 		printf("Tricorder: Could not verify EEPROM content!\n");
182 		ret = 1;
183 	}
184 
185 	return ret;
186 }
187 
188 int do_tricorder_eeprom(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
189 {
190 	if (argc == 3) {
191 		ulong dev_addr = simple_strtoul(argv[2], NULL, 16);
192 
193 		if (strcmp(argv[1], "read") == 0)
194 			return tricorder_eeprom_read(dev_addr);
195 	} else if (argc == 6 || argc == 7) {
196 		ulong dev_addr = simple_strtoul(argv[2], NULL, 16);
197 		char *name = argv[3];
198 		char *version = argv[4];
199 		char *serial = argv[5];
200 		char *interface = NULL;
201 
202 		if (argc == 7)
203 			interface = argv[6];
204 
205 		if (strcmp(argv[1], "write") == 0)
206 			return tricorder_eeprom_write(dev_addr, name, version,
207 						      serial, interface);
208 	}
209 
210 	return CMD_RET_USAGE;
211 }
212 
213 U_BOOT_CMD(
214 	tricordereeprom,	7,	1,	do_tricorder_eeprom,
215 	"Tricorder EEPROM",
216 	"read  devaddr\n"
217 	"       - read Tricorder EEPROM at devaddr and print content\n"
218 	"tricordereeprom write devaddr name version serial [interface]\n"
219 	"       - write Tricorder EEPROM at devaddr with 'name', 'version'"
220 	"and 'serial'\n"
221 	"         optional add an HW interface parameter"
222 );
223 #endif /* CONFIG_SPL */
224