xref: /openbmc/u-boot/env/eeprom.c (revision fb740646)
1 /*
2  * (C) Copyright 2000-2010
3  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4  *
5  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
6  * Andreas Heppel <aheppel@sysgo.de>
7  *
8  * SPDX-License-Identifier:	GPL-2.0+
9  */
10 
11 #include <common.h>
12 #include <command.h>
13 #include <environment.h>
14 #include <linux/stddef.h>
15 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
16 #include <i2c.h>
17 #endif
18 #include <search.h>
19 #include <errno.h>
20 #include <linux/compiler.h>	/* for BUG_ON */
21 
22 DECLARE_GLOBAL_DATA_PTR;
23 
24 static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
25 			   uchar *buffer, unsigned cnt)
26 {
27 	int rcode;
28 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
29 	int old_bus = i2c_get_bus_num();
30 
31 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
32 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
33 #endif
34 
35 	rcode = eeprom_read(dev_addr, offset, buffer, cnt);
36 
37 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
38 	i2c_set_bus_num(old_bus);
39 #endif
40 
41 	return rcode;
42 }
43 
44 static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
45 			    uchar *buffer, unsigned cnt)
46 {
47 	int rcode;
48 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
49 	int old_bus = i2c_get_bus_num();
50 
51 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
52 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
53 #endif
54 
55 	rcode = eeprom_write(dev_addr, offset, buffer, cnt);
56 
57 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
58 	i2c_set_bus_num(old_bus);
59 #endif
60 
61 	return rcode;
62 }
63 
64 static int env_eeprom_get_char(int index)
65 {
66 	uchar c;
67 	unsigned int off = CONFIG_ENV_OFFSET;
68 
69 #ifdef CONFIG_ENV_OFFSET_REDUND
70 	if (gd->env_valid == ENV_REDUND)
71 		off = CONFIG_ENV_OFFSET_REDUND;
72 #endif
73 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
74 			off + index + offsetof(env_t, data), &c, 1);
75 
76 	return c;
77 }
78 
79 static int env_eeprom_load(void)
80 {
81 	char buf_env[CONFIG_ENV_SIZE];
82 	unsigned int off = CONFIG_ENV_OFFSET;
83 
84 #ifdef CONFIG_ENV_OFFSET_REDUND
85 	ulong len, crc[2], crc_tmp;
86 	unsigned int off_env[2];
87 	uchar rdbuf[64], flags[2];
88 	int i, crc_ok[2] = {0, 0};
89 
90 	eeprom_init(-1);	/* prepare for EEPROM read/write */
91 
92 	off_env[0] = CONFIG_ENV_OFFSET;
93 	off_env[1] = CONFIG_ENV_OFFSET_REDUND;
94 
95 	for (i = 0; i < 2; i++) {
96 		/* read CRC */
97 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
98 				off_env[i] + offsetof(env_t, crc),
99 				(uchar *)&crc[i], sizeof(ulong));
100 		/* read FLAGS */
101 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
102 				off_env[i] + offsetof(env_t, flags),
103 				(uchar *)&flags[i], sizeof(uchar));
104 
105 		crc_tmp = 0;
106 		len = ENV_SIZE;
107 		off = off_env[i] + offsetof(env_t, data);
108 		while (len > 0) {
109 			int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
110 
111 			eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
112 					rdbuf, n);
113 
114 			crc_tmp = crc32(crc_tmp, rdbuf, n);
115 			len -= n;
116 			off += n;
117 		}
118 
119 		if (crc_tmp == crc[i])
120 			crc_ok[i] = 1;
121 	}
122 
123 	if (!crc_ok[0] && !crc_ok[1]) {
124 		gd->env_addr	= 0;
125 		gd->env_valid = ENV_INVALID;
126 	} else if (crc_ok[0] && !crc_ok[1]) {
127 		gd->env_valid = ENV_VALID;
128 	} else if (!crc_ok[0] && crc_ok[1]) {
129 		gd->env_valid = ENV_REDUND;
130 	} else {
131 		/* both ok - check serial */
132 		if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
133 			gd->env_valid = ENV_VALID;
134 		else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
135 			gd->env_valid = ENV_REDUND;
136 		else if (flags[0] == 0xFF && flags[1] == 0)
137 			gd->env_valid = ENV_REDUND;
138 		else if (flags[1] == 0xFF && flags[0] == 0)
139 			gd->env_valid = ENV_VALID;
140 		else /* flags are equal - almost impossible */
141 			gd->env_valid = ENV_VALID;
142 	}
143 
144 #else /* CONFIG_ENV_OFFSET_REDUND */
145 	ulong crc, len, new;
146 	uchar rdbuf[64];
147 
148 	eeprom_init(-1);	/* prepare for EEPROM read/write */
149 
150 	/* read old CRC */
151 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
152 			CONFIG_ENV_OFFSET + offsetof(env_t, crc),
153 			(uchar *)&crc, sizeof(ulong));
154 
155 	new = 0;
156 	len = ENV_SIZE;
157 	off = offsetof(env_t, data);
158 	while (len > 0) {
159 		int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
160 
161 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
162 				CONFIG_ENV_OFFSET + off, rdbuf, n);
163 		new = crc32(new, rdbuf, n);
164 		len -= n;
165 		off += n;
166 	}
167 
168 	if (crc == new) {
169 		gd->env_valid = ENV_VALID;
170 	} else {
171 		gd->env_valid = ENV_INVALID;
172 	}
173 #endif /* CONFIG_ENV_OFFSET_REDUND */
174 
175 	off = CONFIG_ENV_OFFSET;
176 #ifdef CONFIG_ENV_OFFSET_REDUND
177 	if (gd->env_valid == ENV_REDUND)
178 		off = CONFIG_ENV_OFFSET_REDUND;
179 #endif
180 
181 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
182 		off, (uchar *)buf_env, CONFIG_ENV_SIZE);
183 
184 	env_import(buf_env, 1);
185 
186 	return 0;
187 }
188 
189 static int env_eeprom_save(void)
190 {
191 	env_t	env_new;
192 	int	rc;
193 	unsigned int off	= CONFIG_ENV_OFFSET;
194 #ifdef CONFIG_ENV_OFFSET_REDUND
195 	unsigned int off_red	= CONFIG_ENV_OFFSET_REDUND;
196 	char flag_obsolete	= OBSOLETE_FLAG;
197 #endif
198 
199 	rc = env_export(&env_new);
200 	if (rc)
201 		return rc;
202 
203 #ifdef CONFIG_ENV_OFFSET_REDUND
204 	if (gd->env_valid == ENV_VALID) {
205 		off	= CONFIG_ENV_OFFSET_REDUND;
206 		off_red	= CONFIG_ENV_OFFSET;
207 	}
208 
209 	env_new.flags = ACTIVE_FLAG;
210 #endif
211 
212 	rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
213 			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
214 
215 #ifdef CONFIG_ENV_OFFSET_REDUND
216 	if (rc == 0) {
217 		eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
218 				 off_red + offsetof(env_t, flags),
219 				 (uchar *)&flag_obsolete, 1);
220 
221 		if (gd->env_valid == ENV_VALID)
222 			gd->env_valid = ENV_REDUND;
223 		else
224 			gd->env_valid = ENV_VALID;
225 	}
226 #endif
227 	return rc;
228 }
229 
230 U_BOOT_ENV_LOCATION(eeprom) = {
231 	.location	= ENVL_EEPROM,
232 	ENV_NAME("EEPROM")
233 	.get_char	= env_eeprom_get_char,
234 	.load		= env_eeprom_load,
235 	.save		= env_save_ptr(env_eeprom_save),
236 };
237