xref: /openbmc/u-boot/env/eeprom.c (revision 624656314f5684995fb9f499d38ad18d378802a5)
183d290c5STom 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>
12*62465631SSimon Glass #include <eeprom.h>
130649cd0dSSimon Glass #include <environment.h>
140649cd0dSSimon Glass #include <linux/stddef.h>
150649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
160649cd0dSSimon Glass #include <i2c.h>
170649cd0dSSimon Glass #endif
180649cd0dSSimon Glass #include <search.h>
190649cd0dSSimon Glass #include <errno.h>
200649cd0dSSimon Glass #include <linux/compiler.h>	/* for BUG_ON */
210649cd0dSSimon Glass 
220649cd0dSSimon Glass DECLARE_GLOBAL_DATA_PTR;
230649cd0dSSimon Glass 
eeprom_bus_read(unsigned dev_addr,unsigned offset,uchar * buffer,unsigned cnt)240649cd0dSSimon Glass static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
250649cd0dSSimon Glass 			   uchar *buffer, unsigned cnt)
260649cd0dSSimon Glass {
270649cd0dSSimon Glass 	int rcode;
280649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
290649cd0dSSimon Glass 	int old_bus = i2c_get_bus_num();
300649cd0dSSimon Glass 
310649cd0dSSimon Glass 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
320649cd0dSSimon Glass 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
330649cd0dSSimon Glass #endif
340649cd0dSSimon Glass 
350649cd0dSSimon Glass 	rcode = eeprom_read(dev_addr, offset, buffer, cnt);
360649cd0dSSimon Glass 
370649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
380649cd0dSSimon Glass 	i2c_set_bus_num(old_bus);
390649cd0dSSimon Glass #endif
400649cd0dSSimon Glass 
410649cd0dSSimon Glass 	return rcode;
420649cd0dSSimon Glass }
430649cd0dSSimon Glass 
eeprom_bus_write(unsigned dev_addr,unsigned offset,uchar * buffer,unsigned cnt)440649cd0dSSimon Glass static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
450649cd0dSSimon Glass 			    uchar *buffer, unsigned cnt)
460649cd0dSSimon Glass {
470649cd0dSSimon Glass 	int rcode;
480649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
490649cd0dSSimon Glass 	int old_bus = i2c_get_bus_num();
500649cd0dSSimon Glass 
510649cd0dSSimon Glass 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
520649cd0dSSimon Glass 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
530649cd0dSSimon Glass #endif
540649cd0dSSimon Glass 
550649cd0dSSimon Glass 	rcode = eeprom_write(dev_addr, offset, buffer, cnt);
560649cd0dSSimon Glass 
570649cd0dSSimon Glass #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
580649cd0dSSimon Glass 	i2c_set_bus_num(old_bus);
590649cd0dSSimon Glass #endif
600649cd0dSSimon Glass 
610649cd0dSSimon Glass 	return rcode;
620649cd0dSSimon Glass }
630649cd0dSSimon Glass 
64b2cdef48SGoldschmidt Simon /** Call this function from overridden env_get_char_spec() if you need
65b2cdef48SGoldschmidt Simon  * this functionality.
66b2cdef48SGoldschmidt Simon  */
env_eeprom_get_char(int index)67b2cdef48SGoldschmidt Simon int env_eeprom_get_char(int index)
680649cd0dSSimon Glass {
690649cd0dSSimon Glass 	uchar c;
700649cd0dSSimon Glass 	unsigned int off = CONFIG_ENV_OFFSET;
710649cd0dSSimon Glass 
720649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
73203e94f6SSimon Glass 	if (gd->env_valid == ENV_REDUND)
740649cd0dSSimon Glass 		off = CONFIG_ENV_OFFSET_REDUND;
750649cd0dSSimon Glass #endif
760649cd0dSSimon Glass 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
770649cd0dSSimon Glass 			off + index + offsetof(env_t, data), &c, 1);
780649cd0dSSimon Glass 
790649cd0dSSimon Glass 	return c;
800649cd0dSSimon Glass }
810649cd0dSSimon Glass 
env_eeprom_load(void)82c5951991SSimon Glass static int env_eeprom_load(void)
830649cd0dSSimon Glass {
840649cd0dSSimon Glass 	char buf_env[CONFIG_ENV_SIZE];
850649cd0dSSimon Glass 	unsigned int off = CONFIG_ENV_OFFSET;
860649cd0dSSimon Glass 
870649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
880649cd0dSSimon Glass 	ulong len, crc[2], crc_tmp;
890649cd0dSSimon Glass 	unsigned int off_env[2];
900649cd0dSSimon Glass 	uchar rdbuf[64], flags[2];
910649cd0dSSimon Glass 	int i, crc_ok[2] = {0, 0};
920649cd0dSSimon Glass 
930649cd0dSSimon Glass 	eeprom_init(-1);	/* prepare for EEPROM read/write */
940649cd0dSSimon Glass 
950649cd0dSSimon Glass 	off_env[0] = CONFIG_ENV_OFFSET;
960649cd0dSSimon Glass 	off_env[1] = CONFIG_ENV_OFFSET_REDUND;
970649cd0dSSimon Glass 
980649cd0dSSimon Glass 	for (i = 0; i < 2; i++) {
990649cd0dSSimon Glass 		/* read CRC */
1000649cd0dSSimon Glass 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1010649cd0dSSimon Glass 				off_env[i] + offsetof(env_t, crc),
1020649cd0dSSimon Glass 				(uchar *)&crc[i], sizeof(ulong));
1030649cd0dSSimon Glass 		/* read FLAGS */
1040649cd0dSSimon Glass 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1050649cd0dSSimon Glass 				off_env[i] + offsetof(env_t, flags),
1060649cd0dSSimon Glass 				(uchar *)&flags[i], sizeof(uchar));
1070649cd0dSSimon Glass 
1080649cd0dSSimon Glass 		crc_tmp = 0;
1090649cd0dSSimon Glass 		len = ENV_SIZE;
1100649cd0dSSimon Glass 		off = off_env[i] + offsetof(env_t, data);
1110649cd0dSSimon Glass 		while (len > 0) {
1120649cd0dSSimon Glass 			int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
1130649cd0dSSimon Glass 
1140649cd0dSSimon Glass 			eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
1150649cd0dSSimon Glass 					rdbuf, n);
1160649cd0dSSimon Glass 
1170649cd0dSSimon Glass 			crc_tmp = crc32(crc_tmp, rdbuf, n);
1180649cd0dSSimon Glass 			len -= n;
1190649cd0dSSimon Glass 			off += n;
1200649cd0dSSimon Glass 		}
1210649cd0dSSimon Glass 
1220649cd0dSSimon Glass 		if (crc_tmp == crc[i])
1230649cd0dSSimon Glass 			crc_ok[i] = 1;
1240649cd0dSSimon Glass 	}
1250649cd0dSSimon Glass 
1260649cd0dSSimon Glass 	if (!crc_ok[0] && !crc_ok[1]) {
1270649cd0dSSimon Glass 		gd->env_addr	= 0;
1282d7cb5b4SSimon Glass 		gd->env_valid = ENV_INVALID;
1290649cd0dSSimon Glass 	} else if (crc_ok[0] && !crc_ok[1]) {
130203e94f6SSimon Glass 		gd->env_valid = ENV_VALID;
1310649cd0dSSimon Glass 	} else if (!crc_ok[0] && crc_ok[1]) {
132203e94f6SSimon Glass 		gd->env_valid = ENV_REDUND;
1330649cd0dSSimon Glass 	} else {
1340649cd0dSSimon Glass 		/* both ok - check serial */
1350649cd0dSSimon Glass 		if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
136203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
1370649cd0dSSimon Glass 		else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
138203e94f6SSimon Glass 			gd->env_valid = ENV_REDUND;
1390649cd0dSSimon Glass 		else if (flags[0] == 0xFF && flags[1] == 0)
140203e94f6SSimon Glass 			gd->env_valid = ENV_REDUND;
1410649cd0dSSimon Glass 		else if (flags[1] == 0xFF && flags[0] == 0)
142203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
1430649cd0dSSimon Glass 		else /* flags are equal - almost impossible */
144203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
1450649cd0dSSimon Glass 	}
1460649cd0dSSimon Glass 
1470649cd0dSSimon Glass #else /* CONFIG_ENV_OFFSET_REDUND */
1480649cd0dSSimon Glass 	ulong crc, len, new;
1490649cd0dSSimon Glass 	uchar rdbuf[64];
1500649cd0dSSimon Glass 
1510649cd0dSSimon Glass 	eeprom_init(-1);	/* prepare for EEPROM read/write */
1520649cd0dSSimon Glass 
1530649cd0dSSimon Glass 	/* read old CRC */
1540649cd0dSSimon Glass 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1550649cd0dSSimon Glass 			CONFIG_ENV_OFFSET + offsetof(env_t, crc),
1560649cd0dSSimon Glass 			(uchar *)&crc, sizeof(ulong));
1570649cd0dSSimon Glass 
1580649cd0dSSimon Glass 	new = 0;
1590649cd0dSSimon Glass 	len = ENV_SIZE;
1600649cd0dSSimon Glass 	off = offsetof(env_t, data);
1610649cd0dSSimon Glass 	while (len > 0) {
1620649cd0dSSimon Glass 		int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
1630649cd0dSSimon Glass 
1640649cd0dSSimon Glass 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1650649cd0dSSimon Glass 				CONFIG_ENV_OFFSET + off, rdbuf, n);
1660649cd0dSSimon Glass 		new = crc32(new, rdbuf, n);
1670649cd0dSSimon Glass 		len -= n;
1680649cd0dSSimon Glass 		off += n;
1690649cd0dSSimon Glass 	}
1700649cd0dSSimon Glass 
1710649cd0dSSimon Glass 	if (crc == new) {
172203e94f6SSimon Glass 		gd->env_valid = ENV_VALID;
1730649cd0dSSimon Glass 	} else {
1742d7cb5b4SSimon Glass 		gd->env_valid = ENV_INVALID;
1750649cd0dSSimon Glass 	}
1760649cd0dSSimon Glass #endif /* CONFIG_ENV_OFFSET_REDUND */
1770649cd0dSSimon Glass 
1780649cd0dSSimon Glass 	off = CONFIG_ENV_OFFSET;
1790649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
180203e94f6SSimon Glass 	if (gd->env_valid == ENV_REDUND)
1810649cd0dSSimon Glass 		off = CONFIG_ENV_OFFSET_REDUND;
1820649cd0dSSimon Glass #endif
1830649cd0dSSimon Glass 
1840649cd0dSSimon Glass 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
1850649cd0dSSimon Glass 		off, (uchar *)buf_env, CONFIG_ENV_SIZE);
1860649cd0dSSimon Glass 
1872166ebf7SSimon Goldschmidt 	return env_import(buf_env, 1);
1880649cd0dSSimon Glass }
1890649cd0dSSimon Glass 
env_eeprom_save(void)190e5bce247SSimon Glass static int env_eeprom_save(void)
1910649cd0dSSimon Glass {
1920649cd0dSSimon Glass 	env_t	env_new;
1930649cd0dSSimon Glass 	int	rc;
1940649cd0dSSimon Glass 	unsigned int off	= CONFIG_ENV_OFFSET;
1950649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
1960649cd0dSSimon Glass 	unsigned int off_red	= CONFIG_ENV_OFFSET_REDUND;
1970649cd0dSSimon Glass 	char flag_obsolete	= OBSOLETE_FLAG;
1980649cd0dSSimon Glass #endif
1990649cd0dSSimon Glass 
2000649cd0dSSimon Glass 	rc = env_export(&env_new);
2010649cd0dSSimon Glass 	if (rc)
2020649cd0dSSimon Glass 		return rc;
2030649cd0dSSimon Glass 
2040649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
205203e94f6SSimon Glass 	if (gd->env_valid == ENV_VALID) {
2060649cd0dSSimon Glass 		off	= CONFIG_ENV_OFFSET_REDUND;
2070649cd0dSSimon Glass 		off_red	= CONFIG_ENV_OFFSET;
2080649cd0dSSimon Glass 	}
2090649cd0dSSimon Glass 
2100649cd0dSSimon Glass 	env_new.flags = ACTIVE_FLAG;
2110649cd0dSSimon Glass #endif
2120649cd0dSSimon Glass 
2130649cd0dSSimon Glass 	rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
2140649cd0dSSimon Glass 			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
2150649cd0dSSimon Glass 
2160649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
2170649cd0dSSimon Glass 	if (rc == 0) {
2180649cd0dSSimon Glass 		eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
2190649cd0dSSimon Glass 				 off_red + offsetof(env_t, flags),
2200649cd0dSSimon Glass 				 (uchar *)&flag_obsolete, 1);
2210649cd0dSSimon Glass 
222203e94f6SSimon Glass 		if (gd->env_valid == ENV_VALID)
223203e94f6SSimon Glass 			gd->env_valid = ENV_REDUND;
2240649cd0dSSimon Glass 		else
225203e94f6SSimon Glass 			gd->env_valid = ENV_VALID;
2260649cd0dSSimon Glass 	}
2270649cd0dSSimon Glass #endif
2280649cd0dSSimon Glass 	return rc;
2290649cd0dSSimon Glass }
2300649cd0dSSimon Glass 
2314415f1d1SSimon Glass U_BOOT_ENV_LOCATION(eeprom) = {
2324415f1d1SSimon Glass 	.location	= ENVL_EEPROM,
233ac358bebSSimon Glass 	ENV_NAME("EEPROM")
234e5bce247SSimon Glass 	.load		= env_eeprom_load,
235e5bce247SSimon Glass 	.save		= env_save_ptr(env_eeprom_save),
2364415f1d1SSimon Glass };
237