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