xref: /openbmc/u-boot/env/eeprom.c (revision e8f80a5a)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
20649cd0dSSimon Glass /*
30649cd0dSSimon Glass  * (C) Copyright 2000-2010
40649cd0dSSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
50649cd0dSSimon Glass  *
60649cd0dSSimon Glass  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
70649cd0dSSimon Glass  * Andreas Heppel <aheppel@sysgo.de>
80649cd0dSSimon Glass  */
90649cd0dSSimon Glass 
100649cd0dSSimon Glass #include <common.h>
110649cd0dSSimon Glass #include <command.h>
120649cd0dSSimon Glass #include <environment.h>
130649cd0dSSimon Glass #include <linux/stddef.h>
140649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
150649cd0dSSimon Glass #include <i2c.h>
160649cd0dSSimon Glass #endif
170649cd0dSSimon Glass #include <search.h>
180649cd0dSSimon Glass #include <errno.h>
190649cd0dSSimon Glass #include <linux/compiler.h>	/* for BUG_ON */
200649cd0dSSimon Glass 
210649cd0dSSimon Glass DECLARE_GLOBAL_DATA_PTR;
220649cd0dSSimon Glass 
eeprom_bus_read(unsigned dev_addr,unsigned offset,uchar * buffer,unsigned cnt)230649cd0dSSimon Glass static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
240649cd0dSSimon Glass 			   uchar *buffer, unsigned cnt)
250649cd0dSSimon Glass {
260649cd0dSSimon Glass 	int rcode;
270649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
280649cd0dSSimon Glass 	int old_bus = i2c_get_bus_num();
290649cd0dSSimon Glass 
300649cd0dSSimon Glass 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
310649cd0dSSimon Glass 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
320649cd0dSSimon Glass #endif
330649cd0dSSimon Glass 
340649cd0dSSimon Glass 	rcode = eeprom_read(dev_addr, offset, buffer, cnt);
350649cd0dSSimon Glass 
360649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
370649cd0dSSimon Glass 	i2c_set_bus_num(old_bus);
380649cd0dSSimon Glass #endif
390649cd0dSSimon Glass 
400649cd0dSSimon Glass 	return rcode;
410649cd0dSSimon Glass }
420649cd0dSSimon Glass 
eeprom_bus_write(unsigned dev_addr,unsigned offset,uchar * buffer,unsigned cnt)430649cd0dSSimon Glass static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
440649cd0dSSimon Glass 			    uchar *buffer, unsigned cnt)
450649cd0dSSimon Glass {
460649cd0dSSimon Glass 	int rcode;
470649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
480649cd0dSSimon Glass 	int old_bus = i2c_get_bus_num();
490649cd0dSSimon Glass 
500649cd0dSSimon Glass 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
510649cd0dSSimon Glass 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
520649cd0dSSimon Glass #endif
530649cd0dSSimon Glass 
540649cd0dSSimon Glass 	rcode = eeprom_write(dev_addr, offset, buffer, cnt);
550649cd0dSSimon Glass 
560649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
570649cd0dSSimon Glass 	i2c_set_bus_num(old_bus);
580649cd0dSSimon Glass #endif
590649cd0dSSimon Glass 
600649cd0dSSimon Glass 	return rcode;
610649cd0dSSimon Glass }
620649cd0dSSimon Glass 
63b2cdef48SGoldschmidt Simon /** Call this function from overridden env_get_char_spec() if you need
64b2cdef48SGoldschmidt Simon  * this functionality.
65b2cdef48SGoldschmidt Simon  */
env_eeprom_get_char(int index)66b2cdef48SGoldschmidt Simon int env_eeprom_get_char(int index)
670649cd0dSSimon Glass {
680649cd0dSSimon Glass 	uchar c;
690649cd0dSSimon Glass 	unsigned int off = CONFIG_ENV_OFFSET;
700649cd0dSSimon Glass 
710649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
72203e94f6SSimon Glass 	if (gd->env_valid == ENV_REDUND)
730649cd0dSSimon Glass 		off = CONFIG_ENV_OFFSET_REDUND;
740649cd0dSSimon Glass #endif
750649cd0dSSimon Glass 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
760649cd0dSSimon Glass 			off + index + offsetof(env_t, data), &c, 1);
770649cd0dSSimon Glass 
780649cd0dSSimon Glass 	return c;
790649cd0dSSimon Glass }
800649cd0dSSimon Glass 
env_eeprom_load(void)81c5951991SSimon Glass static int env_eeprom_load(void)
820649cd0dSSimon Glass {
830649cd0dSSimon Glass 	char buf_env[CONFIG_ENV_SIZE];
840649cd0dSSimon Glass 	unsigned int off = CONFIG_ENV_OFFSET;
850649cd0dSSimon Glass 
860649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
870649cd0dSSimon Glass 	ulong len, crc[2], crc_tmp;
880649cd0dSSimon Glass 	unsigned int off_env[2];
890649cd0dSSimon Glass 	uchar rdbuf[64], flags[2];
900649cd0dSSimon Glass 	int i, crc_ok[2] = {0, 0};
910649cd0dSSimon Glass 
920649cd0dSSimon Glass 	eeprom_init(-1);	/* prepare for EEPROM read/write */
930649cd0dSSimon Glass 
940649cd0dSSimon Glass 	off_env[0] = CONFIG_ENV_OFFSET;
950649cd0dSSimon Glass 	off_env[1] = CONFIG_ENV_OFFSET_REDUND;
960649cd0dSSimon Glass 
970649cd0dSSimon Glass 	for (i = 0; i < 2; i++) {
980649cd0dSSimon Glass 		/* read CRC */
990649cd0dSSimon Glass 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1000649cd0dSSimon Glass 				off_env[i] + offsetof(env_t, crc),
1010649cd0dSSimon Glass 				(uchar *)&crc[i], sizeof(ulong));
1020649cd0dSSimon Glass 		/* read FLAGS */
1030649cd0dSSimon Glass 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1040649cd0dSSimon Glass 				off_env[i] + offsetof(env_t, flags),
1050649cd0dSSimon Glass 				(uchar *)&flags[i], sizeof(uchar));
1060649cd0dSSimon Glass 
1070649cd0dSSimon Glass 		crc_tmp = 0;
1080649cd0dSSimon Glass 		len = ENV_SIZE;
1090649cd0dSSimon Glass 		off = off_env[i] + offsetof(env_t, data);
1100649cd0dSSimon Glass 		while (len > 0) {
1110649cd0dSSimon Glass 			int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
1120649cd0dSSimon Glass 
1130649cd0dSSimon Glass 			eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
1140649cd0dSSimon Glass 					rdbuf, n);
1150649cd0dSSimon Glass 
1160649cd0dSSimon Glass 			crc_tmp = crc32(crc_tmp, rdbuf, n);
1170649cd0dSSimon Glass 			len -= n;
1180649cd0dSSimon Glass 			off += n;
1190649cd0dSSimon Glass 		}
1200649cd0dSSimon Glass 
1210649cd0dSSimon Glass 		if (crc_tmp == crc[i])
1220649cd0dSSimon Glass 			crc_ok[i] = 1;
1230649cd0dSSimon Glass 	}
1240649cd0dSSimon Glass 
1250649cd0dSSimon Glass 	if (!crc_ok[0] && !crc_ok[1]) {
1260649cd0dSSimon Glass 		gd->env_addr	= 0;
1272d7cb5b4SSimon Glass 		gd->env_valid = ENV_INVALID;
1280649cd0dSSimon Glass 	} else if (crc_ok[0] && !crc_ok[1]) {
129203e94f6SSimon Glass 		gd->env_valid = ENV_VALID;
1300649cd0dSSimon Glass 	} else if (!crc_ok[0] && crc_ok[1]) {
131203e94f6SSimon Glass 		gd->env_valid = ENV_REDUND;
1320649cd0dSSimon Glass 	} else {
1330649cd0dSSimon Glass 		/* both ok - check serial */
1340649cd0dSSimon Glass 		if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
135203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
1360649cd0dSSimon Glass 		else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
137203e94f6SSimon Glass 			gd->env_valid = ENV_REDUND;
1380649cd0dSSimon Glass 		else if (flags[0] == 0xFF && flags[1] == 0)
139203e94f6SSimon Glass 			gd->env_valid = ENV_REDUND;
1400649cd0dSSimon Glass 		else if (flags[1] == 0xFF && flags[0] == 0)
141203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
1420649cd0dSSimon Glass 		else /* flags are equal - almost impossible */
143203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
1440649cd0dSSimon Glass 	}
1450649cd0dSSimon Glass 
1460649cd0dSSimon Glass #else /* CONFIG_ENV_OFFSET_REDUND */
1470649cd0dSSimon Glass 	ulong crc, len, new;
1480649cd0dSSimon Glass 	uchar rdbuf[64];
1490649cd0dSSimon Glass 
1500649cd0dSSimon Glass 	eeprom_init(-1);	/* prepare for EEPROM read/write */
1510649cd0dSSimon Glass 
1520649cd0dSSimon Glass 	/* read old CRC */
1530649cd0dSSimon Glass 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1540649cd0dSSimon Glass 			CONFIG_ENV_OFFSET + offsetof(env_t, crc),
1550649cd0dSSimon Glass 			(uchar *)&crc, sizeof(ulong));
1560649cd0dSSimon Glass 
1570649cd0dSSimon Glass 	new = 0;
1580649cd0dSSimon Glass 	len = ENV_SIZE;
1590649cd0dSSimon Glass 	off = offsetof(env_t, data);
1600649cd0dSSimon Glass 	while (len > 0) {
1610649cd0dSSimon Glass 		int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
1620649cd0dSSimon Glass 
1630649cd0dSSimon Glass 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1640649cd0dSSimon Glass 				CONFIG_ENV_OFFSET + off, rdbuf, n);
1650649cd0dSSimon Glass 		new = crc32(new, rdbuf, n);
1660649cd0dSSimon Glass 		len -= n;
1670649cd0dSSimon Glass 		off += n;
1680649cd0dSSimon Glass 	}
1690649cd0dSSimon Glass 
1700649cd0dSSimon Glass 	if (crc == new) {
171203e94f6SSimon Glass 		gd->env_valid = ENV_VALID;
1720649cd0dSSimon Glass 	} else {
1732d7cb5b4SSimon Glass 		gd->env_valid = ENV_INVALID;
1740649cd0dSSimon Glass 	}
1750649cd0dSSimon Glass #endif /* CONFIG_ENV_OFFSET_REDUND */
1760649cd0dSSimon Glass 
1770649cd0dSSimon Glass 	off = CONFIG_ENV_OFFSET;
1780649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
179203e94f6SSimon Glass 	if (gd->env_valid == ENV_REDUND)
1800649cd0dSSimon Glass 		off = CONFIG_ENV_OFFSET_REDUND;
1810649cd0dSSimon Glass #endif
1820649cd0dSSimon Glass 
1830649cd0dSSimon Glass 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1840649cd0dSSimon Glass 		off, (uchar *)buf_env, CONFIG_ENV_SIZE);
1850649cd0dSSimon Glass 
1862166ebf7SSimon Goldschmidt 	return env_import(buf_env, 1);
1870649cd0dSSimon Glass }
1880649cd0dSSimon Glass 
env_eeprom_save(void)189e5bce247SSimon Glass static int env_eeprom_save(void)
1900649cd0dSSimon Glass {
1910649cd0dSSimon Glass 	env_t	env_new;
1920649cd0dSSimon Glass 	int	rc;
1930649cd0dSSimon Glass 	unsigned int off	= CONFIG_ENV_OFFSET;
1940649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
1950649cd0dSSimon Glass 	unsigned int off_red	= CONFIG_ENV_OFFSET_REDUND;
1960649cd0dSSimon Glass 	char flag_obsolete	= OBSOLETE_FLAG;
1970649cd0dSSimon Glass #endif
1980649cd0dSSimon Glass 
1990649cd0dSSimon Glass 	rc = env_export(&env_new);
2000649cd0dSSimon Glass 	if (rc)
2010649cd0dSSimon Glass 		return rc;
2020649cd0dSSimon Glass 
2030649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
204203e94f6SSimon Glass 	if (gd->env_valid == ENV_VALID) {
2050649cd0dSSimon Glass 		off	= CONFIG_ENV_OFFSET_REDUND;
2060649cd0dSSimon Glass 		off_red	= CONFIG_ENV_OFFSET;
2070649cd0dSSimon Glass 	}
2080649cd0dSSimon Glass 
2090649cd0dSSimon Glass 	env_new.flags = ACTIVE_FLAG;
2100649cd0dSSimon Glass #endif
2110649cd0dSSimon Glass 
2120649cd0dSSimon Glass 	rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
2130649cd0dSSimon Glass 			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
2140649cd0dSSimon Glass 
2150649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
2160649cd0dSSimon Glass 	if (rc == 0) {
2170649cd0dSSimon Glass 		eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
2180649cd0dSSimon Glass 				 off_red + offsetof(env_t, flags),
2190649cd0dSSimon Glass 				 (uchar *)&flag_obsolete, 1);
2200649cd0dSSimon Glass 
221203e94f6SSimon Glass 		if (gd->env_valid == ENV_VALID)
222203e94f6SSimon Glass 			gd->env_valid = ENV_REDUND;
2230649cd0dSSimon Glass 		else
224203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
2250649cd0dSSimon Glass 	}
2260649cd0dSSimon Glass #endif
2270649cd0dSSimon Glass 	return rc;
2280649cd0dSSimon Glass }
2290649cd0dSSimon Glass 
2304415f1d1SSimon Glass U_BOOT_ENV_LOCATION(eeprom) = {
2314415f1d1SSimon Glass 	.location	= ENVL_EEPROM,
232ac358bebSSimon Glass 	ENV_NAME("EEPROM")
233e5bce247SSimon Glass 	.load		= env_eeprom_load,
234e5bce247SSimon Glass 	.save		= env_save_ptr(env_eeprom_save),
2354415f1d1SSimon Glass };
236