env_eeprom.c 6.67 KB
Newer Older
wdenk's avatar
wdenk committed
1
/*
2
 * (C) Copyright 2000-2010
wdenk's avatar
wdenk committed
3 4 5 6
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
 * Andreas Heppel <aheppel@sysgo.de>
7
 *
wdenk's avatar
wdenk committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>
#include <environment.h>
#include <linux/stddef.h>
Heiko Schocher's avatar
Heiko Schocher committed
31 32 33
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
#include <i2c.h>
#endif
34 35 36
#include <search.h>
#include <errno.h>
#include <linux/compiler.h>	/* for BUG_ON */
wdenk's avatar
wdenk committed
37

38 39
DECLARE_GLOBAL_DATA_PTR;

40
env_t *env_ptr;
wdenk's avatar
wdenk committed
41

42
char *env_name_spec = "EEPROM";
Heiko Schocher's avatar
Heiko Schocher committed
43 44
int env_eeprom_bus = -1;

45
static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
46
			   uchar *buffer, unsigned cnt)
Heiko Schocher's avatar
Heiko Schocher committed
47 48 49 50 51 52 53 54 55 56
{
	int rcode;
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
	int old_bus = i2c_get_bus_num();

	if (gd->flags & GD_FLG_RELOC) {
		if (env_eeprom_bus == -1) {
			I2C_MUX_DEVICE *dev = NULL;
			dev = i2c_mux_ident_muxstring(
				(uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
57
			if (dev != NULL)
Heiko Schocher's avatar
Heiko Schocher committed
58
				env_eeprom_bus = dev->busid;
59
			else
60
				printf("error adding env eeprom bus.\n");
Heiko Schocher's avatar
Heiko Schocher committed
61 62 63 64 65 66 67 68 69 70 71
		}
		if (old_bus != env_eeprom_bus) {
			i2c_set_bus_num(env_eeprom_bus);
			old_bus = env_eeprom_bus;
		}
	} else {
		rcode = i2c_mux_ident_muxstring_f(
				(uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
	}
#endif

72
	rcode = eeprom_read(dev_addr, offset, buffer, cnt);
73

Heiko Schocher's avatar
Heiko Schocher committed
74 75 76 77 78 79 80
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
	if (old_bus != env_eeprom_bus)
		i2c_set_bus_num(old_bus);
#endif
	return rcode;
}

81
static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
82
			    uchar *buffer, unsigned cnt)
Heiko Schocher's avatar
Heiko Schocher committed
83 84 85 86 87 88 89
{
	int rcode;
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
	int old_bus = i2c_get_bus_num();

	rcode = i2c_mux_ident_muxstring_f((uchar *)CONFIG_I2C_ENV_EEPROM_BUS);
#endif
90
	rcode = eeprom_write(dev_addr, offset, buffer, cnt);
Heiko Schocher's avatar
Heiko Schocher committed
91 92 93 94 95
#if defined(CONFIG_I2C_ENV_EEPROM_BUS)
	i2c_set_bus_num(old_bus);
#endif
	return rcode;
}
wdenk's avatar
wdenk committed
96

97
uchar env_get_char_spec(int index)
wdenk's avatar
wdenk committed
98 99
{
	uchar c;
100
	unsigned int off = CONFIG_ENV_OFFSET;
101

102 103 104 105
#ifdef CONFIG_ENV_OFFSET_REDUND
	if (gd->env_valid == 2)
		off = CONFIG_ENV_OFFSET_REDUND;
#endif
106
	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
107
			off + index + offsetof(env_t, data), &c, 1);
wdenk's avatar
wdenk committed
108

109
	return c;
wdenk's avatar
wdenk committed
110 111
}

112
void env_relocate_spec(void)
wdenk's avatar
wdenk committed
113
{
114
	char buf[CONFIG_ENV_SIZE];
115
	unsigned int off = CONFIG_ENV_OFFSET;
116

117 118 119 120
#ifdef CONFIG_ENV_OFFSET_REDUND
	if (gd->env_valid == 2)
		off = CONFIG_ENV_OFFSET_REDUND;
#endif
121
	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
122
			off, (uchar *)buf, CONFIG_ENV_SIZE);
123 124

	env_import(buf, 1);
wdenk's avatar
wdenk committed
125 126 127 128
}

int saveenv(void)
{
129 130 131
	env_t	env_new;
	ssize_t	len;
	char	*res;
132 133
	int	rc;
	unsigned int off	= CONFIG_ENV_OFFSET;
134
#ifdef CONFIG_ENV_OFFSET_REDUND
135 136
	unsigned int off_red	= CONFIG_ENV_OFFSET_REDUND;
	char flag_obsolete	= OBSOLETE_FLAG;
137 138 139 140 141
#endif

	BUG_ON(env_ptr != NULL);

	res = (char *)&env_new.data;
142
	len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL);
143 144 145 146 147 148 149
	if (len < 0) {
		error("Cannot export environment: errno = %d\n", errno);
		return 1;
	}
	env_new.crc = crc32(0, env_new.data, ENV_SIZE);

#ifdef CONFIG_ENV_OFFSET_REDUND
150
	if (gd->env_valid == 1) {
151 152
		off	= CONFIG_ENV_OFFSET_REDUND;
		off_red	= CONFIG_ENV_OFFSET;
153 154
	}

155
	env_new.flags = ACTIVE_FLAG;
156 157
#endif

158
	rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
159
			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
160 161 162

#ifdef CONFIG_ENV_OFFSET_REDUND
	if (rc == 0) {
163
		eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
164 165 166
				 off_red + offsetof(env_t, flags),
				 (uchar *)&flag_obsolete, 1);

167 168 169 170 171 172 173
		if (gd->env_valid == 1)
			gd->env_valid = 2;
		else
			gd->env_valid = 1;
	}
#endif
	return rc;
wdenk's avatar
wdenk committed
174 175
}

176
/*
wdenk's avatar
wdenk committed
177 178
 * Initialize Environment use
 *
179
 * We are still running from ROM, so data use is limited.
wdenk's avatar
wdenk committed
180 181
 * Use a (moderately small) buffer on the stack
 */
182 183 184
#ifdef CONFIG_ENV_OFFSET_REDUND
int env_init(void)
{
185
	ulong len, crc[2], crc_tmp;
186
	unsigned int off, off_env[2];
187 188
	uchar buf[64], flags[2];
	int i, crc_ok[2] = {0, 0};
189

190
	eeprom_init();	/* prepare for EEPROM read/write */
191 192 193 194 195 196

	off_env[0] = CONFIG_ENV_OFFSET;
	off_env[1] = CONFIG_ENV_OFFSET_REDUND;

	for (i = 0; i < 2; i++) {
		/* read CRC */
197
		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
198 199
				off_env[i] + offsetof(env_t, crc),
				(uchar *)&crc[i], sizeof(ulong));
200
		/* read FLAGS */
201
		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
202 203
				off_env[i] + offsetof(env_t, flags),
				(uchar *)&flags[i], sizeof(uchar));
204

205
		crc_tmp = 0;
206
		len = ENV_SIZE;
207
		off = off_env[i] + offsetof(env_t, data);
208 209 210
		while (len > 0) {
			int n = (len > sizeof(buf)) ? sizeof(buf) : len;

211
			eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
212
					buf, n);
213

214
			crc_tmp = crc32(crc_tmp, buf, n);
215 216 217
			len -= n;
			off += n;
		}
218

219 220 221 222 223
		if (crc_tmp == crc[i])
			crc_ok[i] = 1;
	}

	if (!crc_ok[0] && !crc_ok[1]) {
224 225
		gd->env_addr	= 0;
		gd->env_valid	= 0;
226 227 228 229

		return 0;
	} else if (crc_ok[0] && !crc_ok[1]) {
		gd->env_valid = 1;
230
	} else if (!crc_ok[0] && crc_ok[1]) {
231 232 233 234 235 236 237 238 239
		gd->env_valid = 2;
	} else {
		/* both ok - check serial */
		if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
			gd->env_valid = 1;
		else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
			gd->env_valid = 2;
		else if (flags[0] == 0xFF && flags[1] == 0)
			gd->env_valid = 2;
240
		else if (flags[1] == 0xFF && flags[0] == 0)
241 242 243 244 245 246
			gd->env_valid = 1;
		else /* flags are equal - almost impossible */
			gd->env_valid = 1;
	}

	if (gd->env_valid == 2)
247
		gd->env_addr = off_env[1] + offsetof(env_t, data);
248
	else if (gd->env_valid == 1)
249
		gd->env_addr = off_env[0] + offsetof(env_t, data);
250

251
	return 0;
252 253
}
#else
wdenk's avatar
wdenk committed
254 255 256 257 258 259
int env_init(void)
{
	ulong crc, len, new;
	unsigned off;
	uchar buf[64];

260
	eeprom_init();	/* prepare for EEPROM read/write */
wdenk's avatar
wdenk committed
261 262

	/* read old CRC */
263
	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
264 265
			CONFIG_ENV_OFFSET + offsetof(env_t, crc),
			(uchar *)&crc, sizeof(ulong));
wdenk's avatar
wdenk committed
266 267 268

	new = 0;
	len = ENV_SIZE;
269
	off = offsetof(env_t, data);
270

wdenk's avatar
wdenk committed
271 272 273
	while (len > 0) {
		int n = (len > sizeof(buf)) ? sizeof(buf) : len;

274
		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
Heiko Schocher's avatar
Heiko Schocher committed
275
				CONFIG_ENV_OFFSET + off, buf, n);
276
		new = crc32(new, buf, n);
wdenk's avatar
wdenk committed
277 278 279 280 281
		len -= n;
		off += n;
	}

	if (crc == new) {
282 283
		gd->env_addr	= offsetof(env_t, data);
		gd->env_valid	= 1;
wdenk's avatar
wdenk committed
284
	} else {
285 286
		gd->env_addr	= 0;
		gd->env_valid	= 0;
wdenk's avatar
wdenk committed
287 288
	}

289
	return 0;
wdenk's avatar
wdenk committed
290
}
291
#endif