1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2000-2010
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  *
6  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
7  * Andreas Heppel <aheppel@sysgo.de>
8  */
9 
10 #include <command.h>
11 #include <eeprom.h>
12 #include <env.h>
13 #include <env_internal.h>
14 #include <asm/global_data.h>
15 #include <linux/stddef.h>
16 #include <u-boot/crc.h>
17 #include <search.h>
18 #include <errno.h>
19 #include <linux/compiler.h>	/* for BUG_ON */
20 
21 DECLARE_GLOBAL_DATA_PTR;
22 
env_eeprom_load(void)23 static int env_eeprom_load(void)
24 {
25 	char buf_env[CONFIG_ENV_SIZE];
26 	unsigned int off = CONFIG_ENV_OFFSET;
27 
28 #ifdef CONFIG_ENV_OFFSET_REDUND
29 	ulong len, crc[2], crc_tmp;
30 	unsigned int off_env[2];
31 	uchar rdbuf[64], flags[2];
32 	int i, crc_ok[2] = {0, 0};
33 
34 	eeprom_init(-1);	/* prepare for EEPROM read/write */
35 
36 	off_env[0] = CONFIG_ENV_OFFSET;
37 	off_env[1] = CONFIG_ENV_OFFSET_REDUND;
38 
39 	for (i = 0; i < 2; i++) {
40 		/* read CRC */
41 		eeprom_read(CONFIG_SYS_I2C_EEPROM_ADDR,
42 				off_env[i] + offsetof(env_t, crc),
43 				(uchar *)&crc[i], sizeof(ulong));
44 		/* read FLAGS */
45 		eeprom_read(CONFIG_SYS_I2C_EEPROM_ADDR,
46 				off_env[i] + offsetof(env_t, flags),
47 				(uchar *)&flags[i], sizeof(uchar));
48 
49 		crc_tmp = 0;
50 		len = ENV_SIZE;
51 		off = off_env[i] + offsetof(env_t, data);
52 		while (len > 0) {
53 			int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
54 
55 			eeprom_read(CONFIG_SYS_I2C_EEPROM_ADDR, off,
56 					rdbuf, n);
57 
58 			crc_tmp = crc32(crc_tmp, rdbuf, n);
59 			len -= n;
60 			off += n;
61 		}
62 
63 		if (crc_tmp == crc[i])
64 			crc_ok[i] = 1;
65 	}
66 
67 	if (!crc_ok[0] && !crc_ok[1]) {
68 		gd->env_addr	= 0;
69 		gd->env_valid = ENV_INVALID;
70 	} else if (crc_ok[0] && !crc_ok[1]) {
71 		gd->env_valid = ENV_VALID;
72 	} else if (!crc_ok[0] && crc_ok[1]) {
73 		gd->env_valid = ENV_REDUND;
74 	} else {
75 		/* both ok - check serial */
76 		if (flags[0] == ENV_REDUND_ACTIVE &&
77 		    flags[1] == ENV_REDUND_OBSOLETE)
78 			gd->env_valid = ENV_VALID;
79 		else if (flags[0] == ENV_REDUND_OBSOLETE &&
80 			 flags[1] == ENV_REDUND_ACTIVE)
81 			gd->env_valid = ENV_REDUND;
82 		else if (flags[0] == 0xFF && flags[1] == 0)
83 			gd->env_valid = ENV_REDUND;
84 		else if (flags[1] == 0xFF && flags[0] == 0)
85 			gd->env_valid = ENV_VALID;
86 		else /* flags are equal - almost impossible */
87 			gd->env_valid = ENV_VALID;
88 	}
89 
90 #else /* CONFIG_ENV_OFFSET_REDUND */
91 	ulong crc, len, new;
92 	uchar rdbuf[64];
93 
94 	eeprom_init(-1);	/* prepare for EEPROM read/write */
95 
96 	/* read old CRC */
97 	eeprom_read(CONFIG_SYS_I2C_EEPROM_ADDR,
98 			CONFIG_ENV_OFFSET + offsetof(env_t, crc),
99 			(uchar *)&crc, sizeof(ulong));
100 
101 	new = 0;
102 	len = ENV_SIZE;
103 	off = offsetof(env_t, data);
104 	while (len > 0) {
105 		int n = (len > sizeof(rdbuf)) ? sizeof(rdbuf) : len;
106 
107 		eeprom_read(CONFIG_SYS_I2C_EEPROM_ADDR,
108 				CONFIG_ENV_OFFSET + off, rdbuf, n);
109 		new = crc32(new, rdbuf, n);
110 		len -= n;
111 		off += n;
112 	}
113 
114 	if (crc == new) {
115 		gd->env_valid = ENV_VALID;
116 	} else {
117 		gd->env_valid = ENV_INVALID;
118 	}
119 #endif /* CONFIG_ENV_OFFSET_REDUND */
120 
121 	off = CONFIG_ENV_OFFSET;
122 #ifdef CONFIG_ENV_OFFSET_REDUND
123 	if (gd->env_valid == ENV_REDUND)
124 		off = CONFIG_ENV_OFFSET_REDUND;
125 #endif
126 
127 	eeprom_read(CONFIG_SYS_I2C_EEPROM_ADDR,
128 		off, (uchar *)buf_env, CONFIG_ENV_SIZE);
129 
130 	return env_import(buf_env, 1, H_EXTERNAL);
131 }
132 
env_eeprom_save(void)133 static int env_eeprom_save(void)
134 {
135 	env_t	env_new;
136 	int	rc;
137 	unsigned int off	= CONFIG_ENV_OFFSET;
138 #ifdef CONFIG_ENV_OFFSET_REDUND
139 	unsigned int off_red	= CONFIG_ENV_OFFSET_REDUND;
140 	char flag_obsolete	= ENV_REDUND_OBSOLETE;
141 #endif
142 
143 	rc = env_export(&env_new);
144 	if (rc)
145 		return rc;
146 
147 #ifdef CONFIG_ENV_OFFSET_REDUND
148 	if (gd->env_valid == ENV_VALID) {
149 		off	= CONFIG_ENV_OFFSET_REDUND;
150 		off_red	= CONFIG_ENV_OFFSET;
151 	}
152 
153 	env_new.flags = ENV_REDUND_ACTIVE;
154 #endif
155 
156 	rc = eeprom_write(CONFIG_SYS_I2C_EEPROM_ADDR,
157 			      off, (uchar *)&env_new, CONFIG_ENV_SIZE);
158 
159 #ifdef CONFIG_ENV_OFFSET_REDUND
160 	if (rc == 0) {
161 		eeprom_write(CONFIG_SYS_I2C_EEPROM_ADDR,
162 				 off_red + offsetof(env_t, flags),
163 				 (uchar *)&flag_obsolete, 1);
164 
165 		if (gd->env_valid == ENV_VALID)
166 			gd->env_valid = ENV_REDUND;
167 		else
168 			gd->env_valid = ENV_VALID;
169 	}
170 #endif
171 	return rc;
172 }
173 
174 U_BOOT_ENV_LOCATION(eeprom) = {
175 	.location	= ENVL_EEPROM,
176 	ENV_NAME("EEPROM")
177 	.load		= env_eeprom_load,
178 	.save		= env_save_ptr(env_eeprom_save),
179 };
180