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