xref: /openbmc/u-boot/env/eeprom.c (revision 0649cd0d4908d9b983a0361b8665938ef25701be)
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 env_t *env_ptr;
25 
26 char *env_name_spec = "EEPROM";
27 
28 static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
29 			   uchar *buffer, unsigned cnt)
30 {
31 	int rcode;
32 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
33 	int old_bus = i2c_get_bus_num();
34 
35 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
36 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
37 #endif
38 
39 	rcode = eeprom_read(dev_addr, offset, buffer, cnt);
40 
41 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
42 	i2c_set_bus_num(old_bus);
43 #endif
44 
45 	return rcode;
46 }
47 
48 static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
49 			    uchar *buffer, unsigned cnt)
50 {
51 	int rcode;
52 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
53 	int old_bus = i2c_get_bus_num();
54 
55 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
56 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
57 #endif
58 
59 	rcode = eeprom_write(dev_addr, offset, buffer, cnt);
60 
61 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
62 	i2c_set_bus_num(old_bus);
63 #endif
64 
65 	return rcode;
66 }
67 
68 uchar env_get_char_spec(int index)
69 {
70 	uchar c;
71 	unsigned int off = CONFIG_ENV_OFFSET;
72 
73 #ifdef CONFIG_ENV_OFFSET_REDUND
74 	if (gd->env_valid == 2)
75 		off = CONFIG_ENV_OFFSET_REDUND;
76 #endif
77 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
78 			off + index + offsetof(env_t, data), &c, 1);
79 
80 	return c;
81 }
82 
83 void env_relocate_spec(void)
84 {
85 	char buf_env[CONFIG_ENV_SIZE];
86 	unsigned int off = CONFIG_ENV_OFFSET;
87 
88 #ifdef CONFIG_ENV_OFFSET_REDUND
89 	ulong len, crc[2], crc_tmp;
90 	unsigned int off_env[2];
91 	uchar rdbuf[64], flags[2];
92 	int i, crc_ok[2] = {0, 0};
93 
94 	eeprom_init(-1);	/* prepare for EEPROM read/write */
95 
96 	off_env[0] = CONFIG_ENV_OFFSET;
97 	off_env[1] = CONFIG_ENV_OFFSET_REDUND;
98 
99 	for (i = 0; i < 2; i++) {
100 		/* read CRC */
101 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
102 				off_env[i] + offsetof(env_t, crc),
103 				(uchar *)&crc[i], sizeof(ulong));
104 		/* read FLAGS */
105 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
106 				off_env[i] + offsetof(env_t, flags),
107 				(uchar *)&flags[i], sizeof(uchar));
108 
109 		crc_tmp = 0;
110 		len = ENV_SIZE;
111 		off = off_env[i] + offsetof(env_t, data);
112 		while (len > 0) {
113 			int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
114 
115 			eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
116 					rdbuf, n);
117 
118 			crc_tmp = crc32(crc_tmp, rdbuf, n);
119 			len -= n;
120 			off += n;
121 		}
122 
123 		if (crc_tmp == crc[i])
124 			crc_ok[i] = 1;
125 	}
126 
127 	if (!crc_ok[0] && !crc_ok[1]) {
128 		gd->env_addr	= 0;
129 		gd->env_valid	= 0;
130 	} else if (crc_ok[0] && !crc_ok[1]) {
131 		gd->env_valid = 1;
132 	} else if (!crc_ok[0] && crc_ok[1]) {
133 		gd->env_valid = 2;
134 	} else {
135 		/* both ok - check serial */
136 		if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
137 			gd->env_valid = 1;
138 		else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
139 			gd->env_valid = 2;
140 		else if (flags[0] == 0xFF && flags[1] == 0)
141 			gd->env_valid = 2;
142 		else if (flags[1] == 0xFF && flags[0] == 0)
143 			gd->env_valid = 1;
144 		else /* flags are equal - almost impossible */
145 			gd->env_valid = 1;
146 	}
147 
148 #else /* CONFIG_ENV_OFFSET_REDUND */
149 	ulong crc, len, new;
150 	uchar rdbuf[64];
151 
152 	eeprom_init(-1);	/* prepare for EEPROM read/write */
153 
154 	/* read old CRC */
155 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
156 			CONFIG_ENV_OFFSET + offsetof(env_t, crc),
157 			(uchar *)&crc, sizeof(ulong));
158 
159 	new = 0;
160 	len = ENV_SIZE;
161 	off = offsetof(env_t, data);
162 	while (len > 0) {
163 		int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
164 
165 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
166 				CONFIG_ENV_OFFSET + off, rdbuf, n);
167 		new = crc32(new, rdbuf, n);
168 		len -= n;
169 		off += n;
170 	}
171 
172 	if (crc == new) {
173 		gd->env_valid	= 1;
174 	} else {
175 		gd->env_valid	= 0;
176 	}
177 #endif /* CONFIG_ENV_OFFSET_REDUND */
178 
179 	off = CONFIG_ENV_OFFSET;
180 #ifdef CONFIG_ENV_OFFSET_REDUND
181 	if (gd->env_valid == 2)
182 		off = CONFIG_ENV_OFFSET_REDUND;
183 #endif
184 
185 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
186 		off, (uchar *)buf_env, CONFIG_ENV_SIZE);
187 
188 	env_import(buf_env, 1);
189 }
190 
191 int saveenv(void)
192 {
193 	env_t	env_new;
194 	int	rc;
195 	unsigned int off	= CONFIG_ENV_OFFSET;
196 #ifdef CONFIG_ENV_OFFSET_REDUND
197 	unsigned int off_red	= CONFIG_ENV_OFFSET_REDUND;
198 	char flag_obsolete	= OBSOLETE_FLAG;
199 #endif
200 
201 	BUG_ON(env_ptr != NULL);
202 
203 	rc = env_export(&env_new);
204 	if (rc)
205 		return rc;
206 
207 #ifdef CONFIG_ENV_OFFSET_REDUND
208 	if (gd->env_valid == 1) {
209 		off	= CONFIG_ENV_OFFSET_REDUND;
210 		off_red	= CONFIG_ENV_OFFSET;
211 	}
212 
213 	env_new.flags = ACTIVE_FLAG;
214 #endif
215 
216 	rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
217 			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
218 
219 #ifdef CONFIG_ENV_OFFSET_REDUND
220 	if (rc == 0) {
221 		eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
222 				 off_red + offsetof(env_t, flags),
223 				 (uchar *)&flag_obsolete, 1);
224 
225 		if (gd->env_valid == 1)
226 			gd->env_valid = 2;
227 		else
228 			gd->env_valid = 1;
229 	}
230 #endif
231 	return rc;
232 }
233 
234 /*
235  * Initialize Environment use
236  *
237  * We are still running from ROM, so data use is limited.
238  * Use a (moderately small) buffer on the stack
239  */
240 int env_init(void)
241 {
242 	gd->env_addr = (ulong)&default_environment[0];
243 	gd->env_valid = 1;
244 	return 0;
245 }
246