xref: /OK3568_Linux_fs/u-boot/env/eeprom.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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