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