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