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