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