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