Home | History | Annotate | Download | only in env
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * (C) Copyright 2000-2010
      4  * Wolfgang Denk, DENX Software Engineering, wd (at) denx.de.
      5  *
      6  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
      7  * Andreas Heppel <aheppel (at) sysgo.de>
      8  */
      9 
     10 #include <common.h>
     11 #include <command.h>
     12 #include <environment.h>
     13 #include <linux/stddef.h>
     14 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
     15 #include <i2c.h>
     16 #endif
     17 #include <search.h>
     18 #include <errno.h>
     19 #include <linux/compiler.h>	/* for BUG_ON */
     20 
     21 DECLARE_GLOBAL_DATA_PTR;
     22 
     23 static int eeprom_bus_read(unsigned dev_addr, unsigned offset,
     24 			   uchar *buffer, unsigned cnt)
     25 {
     26 	int rcode;
     27 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
     28 	int old_bus = i2c_get_bus_num();
     29 
     30 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
     31 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
     32 #endif
     33 
     34 	rcode = eeprom_read(dev_addr, offset, buffer, cnt);
     35 
     36 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
     37 	i2c_set_bus_num(old_bus);
     38 #endif
     39 
     40 	return rcode;
     41 }
     42 
     43 static int eeprom_bus_write(unsigned dev_addr, unsigned offset,
     44 			    uchar *buffer, unsigned cnt)
     45 {
     46 	int rcode;
     47 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
     48 	int old_bus = i2c_get_bus_num();
     49 
     50 	if (old_bus != CONFIG_I2C_ENV_EEPROM_BUS)
     51 		i2c_set_bus_num(CONFIG_I2C_ENV_EEPROM_BUS);
     52 #endif
     53 
     54 	rcode = eeprom_write(dev_addr, offset, buffer, cnt);
     55 
     56 #if defined(CONFIG_I2C_ENV_EEPROM_BUS)
     57 	i2c_set_bus_num(old_bus);
     58 #endif
     59 
     60 	return rcode;
     61 }
     62 
     63 /** Call this function from overridden env_get_char_spec() if you need
     64  * this functionality.
     65  */
     66 int env_eeprom_get_char(int index)
     67 {
     68 	uchar c;
     69 	unsigned int off = CONFIG_ENV_OFFSET;
     70 
     71 #ifdef CONFIG_ENV_OFFSET_REDUND
     72 	if (gd->env_valid == ENV_REDUND)
     73 		off = CONFIG_ENV_OFFSET_REDUND;
     74 #endif
     75 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
     76 			off + index + offsetof(env_t, data), &c, 1);
     77 
     78 	return c;
     79 }
     80 
     81 static int env_eeprom_load(void)
     82 {
     83 	char buf_env[CONFIG_ENV_SIZE];
     84 	unsigned int off = CONFIG_ENV_OFFSET;
     85 
     86 #ifdef CONFIG_ENV_OFFSET_REDUND
     87 	ulong len, crc[2], crc_tmp;
     88 	unsigned int off_env[2];
     89 	uchar rdbuf[64], flags[2];
     90 	int i, crc_ok[2] = {0, 0};
     91 
     92 	eeprom_init(-1);	/* prepare for EEPROM read/write */
     93 
     94 	off_env[0] = CONFIG_ENV_OFFSET;
     95 	off_env[1] = CONFIG_ENV_OFFSET_REDUND;
     96 
     97 	for (i = 0; i < 2; i++) {
     98 		/* read CRC */
     99 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
    100 				off_env[i] + offsetof(env_t, crc),
    101 				(uchar *)&crc[i], sizeof(ulong));
    102 		/* read FLAGS */
    103 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
    104 				off_env[i] + offsetof(env_t, flags),
    105 				(uchar *)&flags[i], sizeof(uchar));
    106 
    107 		crc_tmp = 0;
    108 		len = ENV_SIZE;
    109 		off = off_env[i] + offsetof(env_t, data);
    110 		while (len > 0) {
    111 			int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
    112 
    113 			eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR, off,
    114 					rdbuf, n);
    115 
    116 			crc_tmp = crc32(crc_tmp, rdbuf, n);
    117 			len -= n;
    118 			off += n;
    119 		}
    120 
    121 		if (crc_tmp == crc[i])
    122 			crc_ok[i] = 1;
    123 	}
    124 
    125 	if (!crc_ok[0] && !crc_ok[1]) {
    126 		gd->env_addr	= 0;
    127 		gd->env_valid = ENV_INVALID;
    128 	} else if (crc_ok[0] && !crc_ok[1]) {
    129 		gd->env_valid = ENV_VALID;
    130 	} else if (!crc_ok[0] && crc_ok[1]) {
    131 		gd->env_valid = ENV_REDUND;
    132 	} else {
    133 		/* both ok - check serial */
    134 		if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
    135 			gd->env_valid = ENV_VALID;
    136 		else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
    137 			gd->env_valid = ENV_REDUND;
    138 		else if (flags[0] == 0xFF && flags[1] == 0)
    139 			gd->env_valid = ENV_REDUND;
    140 		else if (flags[1] == 0xFF && flags[0] == 0)
    141 			gd->env_valid = ENV_VALID;
    142 		else /* flags are equal - almost impossible */
    143 			gd->env_valid = ENV_VALID;
    144 	}
    145 
    146 #else /* CONFIG_ENV_OFFSET_REDUND */
    147 	ulong crc, len, new;
    148 	uchar rdbuf[64];
    149 
    150 	eeprom_init(-1);	/* prepare for EEPROM read/write */
    151 
    152 	/* read old CRC */
    153 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
    154 			CONFIG_ENV_OFFSET + offsetof(env_t, crc),
    155 			(uchar *)&crc, sizeof(ulong));
    156 
    157 	new = 0;
    158 	len = ENV_SIZE;
    159 	off = offsetof(env_t, data);
    160 	while (len > 0) {
    161 		int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
    162 
    163 		eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
    164 				CONFIG_ENV_OFFSET + off, rdbuf, n);
    165 		new = crc32(new, rdbuf, n);
    166 		len -= n;
    167 		off += n;
    168 	}
    169 
    170 	if (crc == new) {
    171 		gd->env_valid = ENV_VALID;
    172 	} else {
    173 		gd->env_valid = ENV_INVALID;
    174 	}
    175 #endif /* CONFIG_ENV_OFFSET_REDUND */
    176 
    177 	off = CONFIG_ENV_OFFSET;
    178 #ifdef CONFIG_ENV_OFFSET_REDUND
    179 	if (gd->env_valid == ENV_REDUND)
    180 		off = CONFIG_ENV_OFFSET_REDUND;
    181 #endif
    182 
    183 	eeprom_bus_read(CONFIG_SYS_DEF_EEPROM_ADDR,
    184 		off, (uchar *)buf_env, CONFIG_ENV_SIZE);
    185 
    186 	return env_import(buf_env, 1);
    187 }
    188 
    189 static int env_eeprom_save(void)
    190 {
    191 	env_t	env_new;
    192 	int	rc;
    193 	unsigned int off	= CONFIG_ENV_OFFSET;
    194 #ifdef CONFIG_ENV_OFFSET_REDUND
    195 	unsigned int off_red	= CONFIG_ENV_OFFSET_REDUND;
    196 	char flag_obsolete	= OBSOLETE_FLAG;
    197 #endif
    198 
    199 	rc = env_export(&env_new);
    200 	if (rc)
    201 		return rc;
    202 
    203 #ifdef CONFIG_ENV_OFFSET_REDUND
    204 	if (gd->env_valid == ENV_VALID) {
    205 		off	= CONFIG_ENV_OFFSET_REDUND;
    206 		off_red	= CONFIG_ENV_OFFSET;
    207 	}
    208 
    209 	env_new.flags = ACTIVE_FLAG;
    210 #endif
    211 
    212 	rc = eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
    213 			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
    214 
    215 #ifdef CONFIG_ENV_OFFSET_REDUND
    216 	if (rc == 0) {
    217 		eeprom_bus_write(CONFIG_SYS_DEF_EEPROM_ADDR,
    218 				 off_red + offsetof(env_t, flags),
    219 				 (uchar *)&flag_obsolete, 1);
    220 
    221 		if (gd->env_valid == ENV_VALID)
    222 			gd->env_valid = ENV_REDUND;
    223 		else
    224 			gd->env_valid = ENV_VALID;
    225 	}
    226 #endif
    227 	return rc;
    228 }
    229 
    230 U_BOOT_ENV_LOCATION(eeprom) = {
    231 	.location	= ENVL_EEPROM,
    232 	ENV_NAME("EEPROM")
    233 	.load		= env_eeprom_load,
    234 	.save		= env_save_ptr(env_eeprom_save),
    235 };
    236