blob: 721faebd8f2ddb399e76bf5f784d1708a244c4d3 [file] [log] [blame]
Christian Marangif13466e2025-04-07 20:59:51 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Author: Christian Marangi <ansuelsmth@gmail.com>
4 */
5#include <env_internal.h>
6#include <errno.h>
7#include <malloc.h>
8#include <mtd.h>
9#include <asm/cache.h>
10#include <asm/global_data.h>
11#include <linux/mtd/mtd.h>
12#include <u-boot/crc.h>
13
14DECLARE_GLOBAL_DATA_PTR;
15
16static int setup_mtd_device(struct mtd_info **mtd_env)
17{
18 struct mtd_info *mtd;
19
20 mtd_probe_devices();
21
22 mtd = get_mtd_device_nm(CONFIG_ENV_MTD_DEV);
23 if (IS_ERR_OR_NULL(mtd)) {
24 env_set_default("get_mtd_device_nm() failed", 0);
25 return mtd ? PTR_ERR(mtd) : -EINVAL;
26 }
27
28 *mtd_env = mtd;
29
30 return 0;
31}
32
33static int env_mtd_save(void)
34{
35 char *saved_buf, *write_buf, *tmp;
36 struct erase_info ei = { };
37 struct mtd_info *mtd_env;
38 u32 sect_size, sect_num;
39 size_t ret_len = 0;
40 u32 write_size;
41 env_t env_new;
42 int remaining;
43 u32 offset;
44 int ret;
45
46 ret = setup_mtd_device(&mtd_env);
47 if (ret)
48 return ret;
49
50 sect_size = mtd_env->erasesize;
51
52 /* Is the sector larger than the env (i.e. embedded) */
53 if (sect_size > CONFIG_ENV_SIZE) {
54 saved_buf = malloc(sect_size);
55 if (!saved_buf) {
56 ret = -ENOMEM;
57 goto done;
58 }
59
60 offset = CONFIG_ENV_OFFSET;
61 remaining = sect_size;
62 tmp = saved_buf;
63
64 while (remaining) {
65 /* Skip the block if it is bad */
66 if (!(offset % sect_size) &&
67 mtd_block_isbad(mtd_env, offset)) {
68 offset += sect_size;
69 continue;
70 }
71
72 ret = mtd_read(mtd_env, offset, mtd_env->writesize,
73 &ret_len, tmp);
74 if (ret)
75 goto done;
76
77 tmp += ret_len;
78 offset += ret_len;
79 remaining -= ret_len;
80 }
81 }
82
83 ret = env_export(&env_new);
84 if (ret)
85 goto done;
86
87 sect_num = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
88
89 ei.mtd = mtd_env;
90 ei.addr = CONFIG_ENV_OFFSET;
91 ei.len = sect_num * sect_size;
92
93 puts("Erasing MTD...");
94 ret = mtd_erase(mtd_env, &ei);
95 if (ret)
96 goto done;
97
98 if (sect_size > CONFIG_ENV_SIZE) {
99 memcpy(saved_buf, &env_new, CONFIG_ENV_SIZE);
100 write_size = sect_size;
101 write_buf = saved_buf;
102 } else {
103 write_size = sect_num * sect_size;
104 write_buf = (char *)&env_new;
105 }
106
107 offset = CONFIG_ENV_OFFSET;
108 remaining = sect_size;
109 tmp = write_buf;
110
111 puts("Writing to MTD...");
112 while (remaining) {
113 /* Skip the block if it is bad */
114 if (!(offset % sect_size) &&
115 mtd_block_isbad(mtd_env, offset)) {
116 offset += sect_size;
117 continue;
118 }
119
120 ret = mtd_write(mtd_env, offset, mtd_env->writesize,
121 &ret_len, tmp);
122 if (ret)
123 goto done;
124
125 offset += mtd_env->writesize;
126 remaining -= ret_len;
127 tmp += ret_len;
128 }
129
130 ret = 0;
131 puts("done\n");
132
133done:
134 if (saved_buf)
135 free(saved_buf);
136
137 return ret;
138}
139
140static int env_mtd_load(void)
141{
142 struct mtd_info *mtd_env;
143 char *buf, *tmp;
144 size_t ret_len;
145 int remaining;
146 u32 sect_size;
147 u32 offset;
148 int ret;
149
150 buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
151 if (!buf) {
152 env_set_default("memalign() failed", 0);
153 return -EIO;
154 }
155
156 ret = setup_mtd_device(&mtd_env);
157 if (ret)
158 goto out;
159
160 sect_size = mtd_env->erasesize;
161
162 offset = CONFIG_ENV_OFFSET;
163 remaining = CONFIG_ENV_SIZE;
164 tmp = buf;
165
166 while (remaining) {
167 /* Skip the block if it is bad */
168 if (!(offset % sect_size) &&
169 mtd_block_isbad(mtd_env, offset)) {
170 offset += sect_size;
171 continue;
172 }
173
174 ret = mtd_read(mtd_env, offset, mtd_env->writesize,
175 &ret_len, tmp);
176 if (ret) {
177 env_set_default("mtd_read() failed", 1);
178 goto out;
179 }
180
181 tmp += ret_len;
182 offset += ret_len;
183 remaining -= ret_len;
184 }
185
186 ret = env_import(buf, 1, H_EXTERNAL);
187 if (!ret)
188 gd->env_valid = ENV_VALID;
189
190out:
191 free(buf);
192
193 return ret;
194}
195
196static int env_mtd_erase(void)
197{
198 struct mtd_info *mtd_env;
199 u32 sect_size, sect_num;
200 char *saved_buf, *tmp;
201 struct erase_info ei;
202 size_t ret_len;
203 int remaining;
204 u32 offset;
205 int ret;
206
207 ret = setup_mtd_device(&mtd_env);
208 if (ret)
209 return ret;
210
211 sect_size = mtd_env->erasesize;
212
213 /* Is the sector larger than the env (i.e. embedded) */
214 if (sect_size > CONFIG_ENV_SIZE) {
215 saved_buf = malloc(sect_size);
216 if (!saved_buf) {
217 ret = -ENOMEM;
218 goto done;
219 }
220
221 offset = CONFIG_ENV_OFFSET;
222 remaining = sect_size;
223 tmp = saved_buf;
224
225 while (remaining) {
226 /* Skip the block if it is bad */
227 if (!(offset % sect_size) &&
228 mtd_block_isbad(mtd_env, offset)) {
229 offset += sect_size;
230 continue;
231 }
232
233 ret = mtd_read(mtd_env, offset, mtd_env->writesize,
234 &ret_len, tmp);
235 if (ret)
236 goto done;
237
238 tmp += ret_len;
239 offset += ret_len;
240 remaining -= ret_len;
241 }
242 }
243
244 sect_num = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
245
246 ei.mtd = mtd_env;
247 ei.addr = CONFIG_ENV_OFFSET;
248 ei.len = sect_num * sect_size;
249
250 ret = mtd_erase(mtd_env, &ei);
251 if (ret)
252 goto done;
253
254 if (sect_size > CONFIG_ENV_SIZE) {
255 memset(saved_buf, 0, CONFIG_ENV_SIZE);
256
257 offset = CONFIG_ENV_OFFSET;
258 remaining = sect_size;
259 tmp = saved_buf;
260
261 while (remaining) {
262 /* Skip the block if it is bad */
263 if (!(offset % sect_size) &&
264 mtd_block_isbad(mtd_env, offset)) {
265 offset += sect_size;
266 continue;
267 }
268
269 ret = mtd_write(mtd_env, offset, mtd_env->writesize,
270 &ret_len, tmp);
271 if (ret)
272 goto done;
273
274 offset += mtd_env->writesize;
275 remaining -= ret_len;
276 tmp += ret_len;
277 }
278 }
279
280 ret = 0;
281
282done:
283 if (saved_buf)
284 free(saved_buf);
285
286 return ret;
287}
288
289__weak void *env_mtd_get_env_addr(void)
290{
291 return (void *)CONFIG_ENV_ADDR;
292}
293
294/*
295 * Check if Environment on CONFIG_ENV_ADDR is valid.
296 */
297static int env_mtd_init_addr(void)
298{
299 env_t *env_ptr = (env_t *)env_mtd_get_env_addr();
300
301 if (!env_ptr)
302 return -ENOENT;
303
304 if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
305 gd->env_addr = (ulong)&env_ptr->data;
306 gd->env_valid = ENV_VALID;
307 } else {
308 gd->env_valid = ENV_INVALID;
309 }
310
311 return 0;
312}
313
314static int env_mtd_init(void)
315{
316 int ret;
317
318 ret = env_mtd_init_addr();
319 if (ret != -ENOENT)
320 return ret;
321
322 /*
323 * return here -ENOENT, so env_init()
324 * can set the init bit and later if no
325 * other Environment storage is defined
326 * can set the default environment
327 */
328 return -ENOENT;
329}
330
331U_BOOT_ENV_LOCATION(mtd) = {
332 .location = ENVL_MTD,
333 ENV_NAME("MTD")
334 .load = env_mtd_load,
335 .save = ENV_SAVE_PTR(env_mtd_save),
336 .erase = ENV_ERASE_PTR(env_mtd_erase),
337 .init = env_mtd_init,
338};