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 * (C) Copyright 2008 Atmel Corporation
10 */
11#include <common.h>
12#include <dm.h>
13#include <env.h>
14#include <env_internal.h>
15#include <malloc.h>
16#include <spi.h>
17#include <spi_flash.h>
18#include <search.h>
19#include <errno.h>
20#include <uuid.h>
21#include <asm/cache.h>
22#include <asm/global_data.h>
23#include <dm/device-internal.h>
24#include <u-boot/crc.h>
25
26#define	OFFSET_INVALID		(~(u32)0)
27
28#ifdef CONFIG_ENV_OFFSET_REDUND
29#define ENV_OFFSET_REDUND	CONFIG_ENV_OFFSET_REDUND
30
31static ulong env_offset		= CONFIG_ENV_OFFSET;
32static ulong env_new_offset	= CONFIG_ENV_OFFSET_REDUND;
33
34#else
35
36#define ENV_OFFSET_REDUND	OFFSET_INVALID
37
38#endif /* CONFIG_ENV_OFFSET_REDUND */
39
40DECLARE_GLOBAL_DATA_PTR;
41
42static int setup_flash_device(struct spi_flash **env_flash)
43{
44#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
45	struct udevice *new;
46	int	ret;
47
48	/* speed and mode will be read from DT */
49	ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
50				     &new);
51	if (ret) {
52		env_set_default("spi_flash_probe_bus_cs() failed", 0);
53		return ret;
54	}
55
56	*env_flash = dev_get_uclass_priv(new);
57#else
58	*env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
59				     CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
60	if (!*env_flash) {
61		env_set_default("spi_flash_probe() failed", 0);
62		return -EIO;
63	}
64#endif
65	return 0;
66}
67
68#if defined(CONFIG_ENV_OFFSET_REDUND)
69static int env_sf_save(void)
70{
71	env_t	env_new;
72	char	*saved_buffer = NULL, flag = ENV_REDUND_OBSOLETE;
73	u32	saved_size = 0, saved_offset = 0, sector;
74	u32	sect_size = CONFIG_ENV_SECT_SIZE;
75	int	ret;
76	struct spi_flash *env_flash;
77
78	ret = setup_flash_device(&env_flash);
79	if (ret)
80		return ret;
81
82	if (IS_ENABLED(CONFIG_ENV_SECT_SIZE_AUTO))
83		sect_size = env_flash->mtd.erasesize;
84
85	ret = env_export(&env_new);
86	if (ret)
87		return -EIO;
88	env_new.flags	= ENV_REDUND_ACTIVE;
89
90	if (gd->env_valid == ENV_VALID) {
91		env_new_offset = CONFIG_ENV_OFFSET_REDUND;
92		env_offset = CONFIG_ENV_OFFSET;
93	} else {
94		env_new_offset = CONFIG_ENV_OFFSET;
95		env_offset = CONFIG_ENV_OFFSET_REDUND;
96	}
97
98	/* Is the sector larger than the env (i.e. embedded) */
99	if (sect_size > CONFIG_ENV_SIZE) {
100		saved_size = sect_size - CONFIG_ENV_SIZE;
101		saved_offset = env_new_offset + CONFIG_ENV_SIZE;
102		saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
103		if (!saved_buffer) {
104			ret = -ENOMEM;
105			goto done;
106		}
107		ret = spi_flash_read(env_flash, saved_offset,
108					saved_size, saved_buffer);
109		if (ret)
110			goto done;
111	}
112
113	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
114
115	puts("Erasing SPI flash...");
116	ret = spi_flash_erase(env_flash, env_new_offset,
117				sector * sect_size);
118	if (ret)
119		goto done;
120
121	puts("Writing to SPI flash...");
122
123	ret = spi_flash_write(env_flash, env_new_offset,
124		CONFIG_ENV_SIZE, &env_new);
125	if (ret)
126		goto done;
127
128	if (sect_size > CONFIG_ENV_SIZE) {
129		ret = spi_flash_write(env_flash, saved_offset,
130					saved_size, saved_buffer);
131		if (ret)
132			goto done;
133	}
134
135	ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
136				sizeof(env_new.flags), &flag);
137	if (ret)
138		goto done;
139
140	puts("done\n");
141
142	gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
143
144	printf("Valid environment: %d\n", (int)gd->env_valid);
145
146done:
147	spi_flash_free(env_flash);
148
149	if (saved_buffer)
150		free(saved_buffer);
151
152	return ret;
153}
154
155static int env_sf_load(void)
156{
157	int ret;
158	int read1_fail, read2_fail;
159	env_t *tmp_env1, *tmp_env2;
160	struct spi_flash *env_flash;
161
162	tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
163			CONFIG_ENV_SIZE);
164	tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
165			CONFIG_ENV_SIZE);
166	if (!tmp_env1 || !tmp_env2) {
167		env_set_default("malloc() failed", 0);
168		ret = -EIO;
169		goto out;
170	}
171
172	ret = setup_flash_device(&env_flash);
173	if (ret)
174		goto out;
175
176	read1_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
177				    CONFIG_ENV_SIZE, tmp_env1);
178	read2_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
179				    CONFIG_ENV_SIZE, tmp_env2);
180
181	ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
182				read2_fail, H_EXTERNAL);
183
184	spi_flash_free(env_flash);
185out:
186	free(tmp_env1);
187	free(tmp_env2);
188
189	return ret;
190}
191#else
192static int env_sf_save(void)
193{
194	u32	saved_size = 0, saved_offset = 0, sector;
195	u32	sect_size = CONFIG_ENV_SECT_SIZE;
196	char	*saved_buffer = NULL;
197	int	ret = 1;
198	env_t	env_new;
199	struct spi_flash *env_flash;
200
201	ret = setup_flash_device(&env_flash);
202	if (ret)
203		return ret;
204
205	if (IS_ENABLED(CONFIG_ENV_SECT_SIZE_AUTO))
206		sect_size = env_flash->mtd.erasesize;
207
208	/* Is the sector larger than the env (i.e. embedded) */
209	if (sect_size > CONFIG_ENV_SIZE) {
210		saved_size = sect_size - CONFIG_ENV_SIZE;
211		saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
212		saved_buffer = malloc(saved_size);
213		if (!saved_buffer) {
214			ret = -ENOMEM;
215			goto done;
216		}
217
218		ret = spi_flash_read(env_flash, saved_offset,
219			saved_size, saved_buffer);
220		if (ret)
221			goto done;
222	}
223
224	ret = env_export(&env_new);
225	if (ret)
226		goto done;
227
228	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, sect_size);
229
230	puts("Erasing SPI flash...");
231	ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
232		sector * sect_size);
233	if (ret)
234		goto done;
235
236	puts("Writing to SPI flash...");
237	ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
238		CONFIG_ENV_SIZE, &env_new);
239	if (ret)
240		goto done;
241
242	if (sect_size > CONFIG_ENV_SIZE) {
243		ret = spi_flash_write(env_flash, saved_offset,
244			saved_size, saved_buffer);
245		if (ret)
246			goto done;
247	}
248
249	ret = 0;
250	puts("done\n");
251
252done:
253	spi_flash_free(env_flash);
254
255	if (saved_buffer)
256		free(saved_buffer);
257
258	return ret;
259}
260
261static int env_sf_load(void)
262{
263	int ret;
264	char *buf = NULL;
265	struct spi_flash *env_flash;
266
267	buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
268	if (!buf) {
269		env_set_default("malloc() failed", 0);
270		return -EIO;
271	}
272
273	ret = setup_flash_device(&env_flash);
274	if (ret)
275		goto out;
276
277	ret = spi_flash_read(env_flash,
278		CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
279	if (ret) {
280		env_set_default("spi_flash_read() failed", 0);
281		goto err_read;
282	}
283
284	ret = env_import(buf, 1, H_EXTERNAL);
285	if (!ret)
286		gd->env_valid = ENV_VALID;
287
288err_read:
289	spi_flash_free(env_flash);
290out:
291	free(buf);
292
293	return ret;
294}
295#endif
296
297static int env_sf_erase(void)
298{
299	int ret;
300	env_t env;
301	struct spi_flash *env_flash;
302
303	ret = setup_flash_device(&env_flash);
304	if (ret)
305		return ret;
306
307	memset(&env, 0, sizeof(env_t));
308	ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, &env);
309	if (ret)
310		goto done;
311
312	if (ENV_OFFSET_REDUND != OFFSET_INVALID)
313		ret = spi_flash_write(env_flash, ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, &env);
314
315done:
316	spi_flash_free(env_flash);
317
318	return ret;
319}
320
321__weak void *env_sf_get_env_addr(void)
322{
323#ifndef CONFIG_SPL_BUILD
324	return (void *)CONFIG_ENV_ADDR;
325#else
326	return NULL;
327#endif
328}
329
330/*
331 * check if Environment on CONFIG_ENV_ADDR is valid.
332 */
333static int env_sf_init_addr(void)
334{
335	env_t *env_ptr = (env_t *)env_sf_get_env_addr();
336
337	if (!env_ptr)
338		return -ENOENT;
339
340	if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
341		gd->env_addr = (ulong)&(env_ptr->data);
342		gd->env_valid = ENV_VALID;
343	} else {
344		gd->env_valid = ENV_INVALID;
345	}
346
347	return 0;
348}
349
350#if defined(CONFIG_ENV_SPI_EARLY)
351/*
352 * early load environment from SPI flash (before relocation)
353 * and check if it is valid.
354 */
355static int env_sf_init_early(void)
356{
357	int ret;
358	int read1_fail;
359	int read2_fail;
360	int crc1_ok;
361	env_t *tmp_env2 = NULL;
362	env_t *tmp_env1;
363	struct spi_flash *env_flash;
364
365	/*
366	 * if malloc is not ready yet, we cannot use
367	 * this part yet.
368	 */
369	if (!gd->malloc_limit)
370		return -ENOENT;
371
372	tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
373			CONFIG_ENV_SIZE);
374	if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
375		tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
376					     CONFIG_ENV_SIZE);
377
378	if (!tmp_env1 || !tmp_env2)
379		goto out;
380
381	ret = setup_flash_device(&env_flash);
382	if (ret)
383		goto out;
384
385	read1_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
386				    CONFIG_ENV_SIZE, tmp_env1);
387
388	if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
389		read2_fail = spi_flash_read(env_flash,
390					    CONFIG_ENV_OFFSET_REDUND,
391					    CONFIG_ENV_SIZE, tmp_env2);
392		ret = env_check_redund((char *)tmp_env1, read1_fail,
393				       (char *)tmp_env2, read2_fail);
394
395		if (ret < 0)
396			goto err_read;
397
398		if (gd->env_valid == ENV_VALID)
399			gd->env_addr = (unsigned long)&tmp_env1->data;
400		else
401			gd->env_addr = (unsigned long)&tmp_env2->data;
402	} else {
403		if (read1_fail)
404			goto err_read;
405
406		crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
407				tmp_env1->crc;
408		if (!crc1_ok)
409			goto err_read;
410
411		/* if valid -> this is our env */
412		gd->env_valid = ENV_VALID;
413		gd->env_addr = (unsigned long)&tmp_env1->data;
414	}
415
416	spi_flash_free(env_flash);
417
418	return 0;
419err_read:
420	spi_flash_free(env_flash);
421
422	free(tmp_env1);
423	if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
424		free(tmp_env2);
425out:
426	/* env is not valid. always return 0 */
427	gd->env_valid = ENV_INVALID;
428	return 0;
429}
430#endif
431
432static int env_sf_init(void)
433{
434	int ret = env_sf_init_addr();
435	if (ret != -ENOENT)
436		return ret;
437#ifdef CONFIG_ENV_SPI_EARLY
438	return env_sf_init_early();
439#endif
440	/*
441	 * return here -ENOENT, so env_init()
442	 * can set the init bit and later if no
443	 * other Environment storage is defined
444	 * can set the default environment
445	 */
446	return -ENOENT;
447}
448
449U_BOOT_ENV_LOCATION(sf) = {
450	.location	= ENVL_SPI_FLASH,
451	ENV_NAME("SPIFlash")
452	.load		= env_sf_load,
453	.save		= ENV_SAVE_PTR(env_sf_save),
454	.erase		= ENV_ERASE_PTR(env_sf_erase),
455	.init		= env_sf_init,
456};
457