xref: /openbmc/u-boot/env/eeprom.c (revision 203e94f6c9ca03e260175ce240f5856507395585)
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 == ENV_REDUND)
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 = ENV_VALID;
132  	} else if (!crc_ok[0] && crc_ok[1]) {
133  		gd->env_valid = ENV_REDUND;
134  	} else {
135  		/* both ok - check serial */
136  		if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
137  			gd->env_valid = ENV_VALID;
138  		else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
139  			gd->env_valid = ENV_REDUND;
140  		else if (flags[0] == 0xFF && flags[1] == 0)
141  			gd->env_valid = ENV_REDUND;
142  		else if (flags[1] == 0xFF && flags[0] == 0)
143  			gd->env_valid = ENV_VALID;
144  		else /* flags are equal - almost impossible */
145  			gd->env_valid = ENV_VALID;
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	= ENV_VALID;
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 == ENV_REDUND)
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 == ENV_VALID) {
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 == ENV_VALID)
226  			gd->env_valid = ENV_REDUND;
227  		else
228  			gd->env_valid = ENV_VALID;
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 = ENV_VALID;
244  	return 0;
245  }
246