xref: /openbmc/u-boot/env/sf.c (revision 66c433ed4342e5761ee9b048c85fe47d31130b2e)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
20649cd0dSSimon Glass /*
30649cd0dSSimon Glass  * (C) Copyright 2000-2010
40649cd0dSSimon Glass  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
50649cd0dSSimon Glass  *
60649cd0dSSimon Glass  * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
70649cd0dSSimon Glass  * Andreas Heppel <aheppel@sysgo.de>
80649cd0dSSimon Glass  *
90649cd0dSSimon Glass  * (C) Copyright 2008 Atmel Corporation
100649cd0dSSimon Glass  */
110649cd0dSSimon Glass #include <common.h>
120649cd0dSSimon Glass #include <dm.h>
130649cd0dSSimon Glass #include <environment.h>
140649cd0dSSimon Glass #include <malloc.h>
150649cd0dSSimon Glass #include <spi.h>
160649cd0dSSimon Glass #include <spi_flash.h>
170649cd0dSSimon Glass #include <search.h>
180649cd0dSSimon Glass #include <errno.h>
190649cd0dSSimon Glass #include <dm/device-internal.h>
200649cd0dSSimon Glass 
214415f1d1SSimon Glass #ifndef CONFIG_SPL_BUILD
224415f1d1SSimon Glass #define CMD_SAVEENV
23b500c92bSAshish Kumar #define INITENV
244415f1d1SSimon Glass #endif
254415f1d1SSimon Glass 
260649cd0dSSimon Glass #ifdef CONFIG_ENV_OFFSET_REDUND
274415f1d1SSimon Glass #ifdef CMD_SAVEENV
280649cd0dSSimon Glass static ulong env_offset		= CONFIG_ENV_OFFSET;
290649cd0dSSimon Glass static ulong env_new_offset	= CONFIG_ENV_OFFSET_REDUND;
304415f1d1SSimon Glass #endif
310649cd0dSSimon Glass 
320649cd0dSSimon Glass #define ACTIVE_FLAG	1
330649cd0dSSimon Glass #define OBSOLETE_FLAG	0
340649cd0dSSimon Glass #endif /* CONFIG_ENV_OFFSET_REDUND */
350649cd0dSSimon Glass 
360649cd0dSSimon Glass DECLARE_GLOBAL_DATA_PTR;
370649cd0dSSimon Glass 
380649cd0dSSimon Glass static struct spi_flash *env_flash;
390649cd0dSSimon Glass 
setup_flash_device(void)400649cd0dSSimon Glass static int setup_flash_device(void)
410649cd0dSSimon Glass {
420649cd0dSSimon Glass #ifdef CONFIG_DM_SPI_FLASH
430649cd0dSSimon Glass 	struct udevice *new;
440649cd0dSSimon Glass 	int	ret;
450649cd0dSSimon Glass 
460649cd0dSSimon Glass 	/* speed and mode will be read from DT */
470649cd0dSSimon Glass 	ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
4825a17652SKonstantin Porotchkin 				     CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE,
4925a17652SKonstantin Porotchkin 				     &new);
500649cd0dSSimon Glass 	if (ret) {
51c5d548a9SYaniv Levinsky 		set_default_env("spi_flash_probe_bus_cs() failed", 0);
52c5951991SSimon Glass 		return ret;
530649cd0dSSimon Glass 	}
540649cd0dSSimon Glass 
550649cd0dSSimon Glass 	env_flash = dev_get_uclass_priv(new);
560649cd0dSSimon Glass #else
570649cd0dSSimon Glass 
580649cd0dSSimon Glass 	if (!env_flash) {
590649cd0dSSimon Glass 		env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS,
600649cd0dSSimon Glass 			CONFIG_ENV_SPI_CS,
610649cd0dSSimon Glass 			CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
620649cd0dSSimon Glass 		if (!env_flash) {
63c5d548a9SYaniv Levinsky 			set_default_env("spi_flash_probe() failed", 0);
64c5951991SSimon Glass 			return -EIO;
650649cd0dSSimon Glass 		}
660649cd0dSSimon Glass 	}
670649cd0dSSimon Glass #endif
680649cd0dSSimon Glass 	return 0;
690649cd0dSSimon Glass }
700649cd0dSSimon Glass 
710649cd0dSSimon Glass #if defined(CONFIG_ENV_OFFSET_REDUND)
724415f1d1SSimon Glass #ifdef CMD_SAVEENV
env_sf_save(void)73e5bce247SSimon Glass static int env_sf_save(void)
740649cd0dSSimon Glass {
750649cd0dSSimon Glass 	env_t	env_new;
760649cd0dSSimon Glass 	char	*saved_buffer = NULL, flag = OBSOLETE_FLAG;
770649cd0dSSimon Glass 	u32	saved_size, saved_offset, sector;
780649cd0dSSimon Glass 	int	ret;
790649cd0dSSimon Glass 
800649cd0dSSimon Glass 	ret = setup_flash_device();
810649cd0dSSimon Glass 	if (ret)
820649cd0dSSimon Glass 		return ret;
830649cd0dSSimon Glass 
840649cd0dSSimon Glass 	ret = env_export(&env_new);
850649cd0dSSimon Glass 	if (ret)
86c5951991SSimon Glass 		return -EIO;
870649cd0dSSimon Glass 	env_new.flags	= ACTIVE_FLAG;
880649cd0dSSimon Glass 
89203e94f6SSimon Glass 	if (gd->env_valid == ENV_VALID) {
900649cd0dSSimon Glass 		env_new_offset = CONFIG_ENV_OFFSET_REDUND;
910649cd0dSSimon Glass 		env_offset = CONFIG_ENV_OFFSET;
920649cd0dSSimon Glass 	} else {
930649cd0dSSimon Glass 		env_new_offset = CONFIG_ENV_OFFSET;
940649cd0dSSimon Glass 		env_offset = CONFIG_ENV_OFFSET_REDUND;
950649cd0dSSimon Glass 	}
960649cd0dSSimon Glass 
970649cd0dSSimon Glass 	/* Is the sector larger than the env (i.e. embedded) */
980649cd0dSSimon Glass 	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
990649cd0dSSimon Glass 		saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
1000649cd0dSSimon Glass 		saved_offset = env_new_offset + CONFIG_ENV_SIZE;
1010649cd0dSSimon Glass 		saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
1020649cd0dSSimon Glass 		if (!saved_buffer) {
103c5951991SSimon Glass 			ret = -ENOMEM;
1040649cd0dSSimon Glass 			goto done;
1050649cd0dSSimon Glass 		}
106*9ba5e5bcSHeiko Schocher 		ret = spi_flash_read(env_flash, saved_offset,
1070649cd0dSSimon Glass 					saved_size, saved_buffer);
1080649cd0dSSimon Glass 		if (ret)
1090649cd0dSSimon Glass 			goto done;
1100649cd0dSSimon Glass 	}
1110649cd0dSSimon Glass 
1120649cd0dSSimon Glass 	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
1130649cd0dSSimon Glass 
1140649cd0dSSimon Glass 	puts("Erasing SPI flash...");
1150649cd0dSSimon Glass 	ret = spi_flash_erase(env_flash, env_new_offset,
1160649cd0dSSimon Glass 				sector * CONFIG_ENV_SECT_SIZE);
1170649cd0dSSimon Glass 	if (ret)
1180649cd0dSSimon Glass 		goto done;
1190649cd0dSSimon Glass 
1200649cd0dSSimon Glass 	puts("Writing to SPI flash...");
1210649cd0dSSimon Glass 
1220649cd0dSSimon Glass 	ret = spi_flash_write(env_flash, env_new_offset,
1230649cd0dSSimon Glass 		CONFIG_ENV_SIZE, &env_new);
1240649cd0dSSimon Glass 	if (ret)
1250649cd0dSSimon Glass 		goto done;
1260649cd0dSSimon Glass 
1270649cd0dSSimon Glass 	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
1280649cd0dSSimon Glass 		ret = spi_flash_write(env_flash, saved_offset,
1290649cd0dSSimon Glass 					saved_size, saved_buffer);
1300649cd0dSSimon Glass 		if (ret)
1310649cd0dSSimon Glass 			goto done;
1320649cd0dSSimon Glass 	}
1330649cd0dSSimon Glass 
1340649cd0dSSimon Glass 	ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
1350649cd0dSSimon Glass 				sizeof(env_new.flags), &flag);
1360649cd0dSSimon Glass 	if (ret)
1370649cd0dSSimon Glass 		goto done;
1380649cd0dSSimon Glass 
1390649cd0dSSimon Glass 	puts("done\n");
1400649cd0dSSimon Glass 
141203e94f6SSimon Glass 	gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
1420649cd0dSSimon Glass 
1430649cd0dSSimon Glass 	printf("Valid environment: %d\n", (int)gd->env_valid);
1440649cd0dSSimon Glass 
1450649cd0dSSimon Glass  done:
1460649cd0dSSimon Glass 	if (saved_buffer)
1470649cd0dSSimon Glass 		free(saved_buffer);
1480649cd0dSSimon Glass 
1490649cd0dSSimon Glass 	return ret;
1500649cd0dSSimon Glass }
1514415f1d1SSimon Glass #endif /* CMD_SAVEENV */
1520649cd0dSSimon Glass 
env_sf_load(void)153c5951991SSimon Glass static int env_sf_load(void)
1540649cd0dSSimon Glass {
1550649cd0dSSimon Glass 	int ret;
15680719938SSimon Goldschmidt 	int read1_fail, read2_fail;
15780719938SSimon Goldschmidt 	env_t *tmp_env1, *tmp_env2;
1580649cd0dSSimon Glass 
1590649cd0dSSimon Glass 	tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
1600649cd0dSSimon Glass 			CONFIG_ENV_SIZE);
1610649cd0dSSimon Glass 	tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
1620649cd0dSSimon Glass 			CONFIG_ENV_SIZE);
1630649cd0dSSimon Glass 	if (!tmp_env1 || !tmp_env2) {
164c5d548a9SYaniv Levinsky 		set_default_env("malloc() failed", 0);
165c5951991SSimon Glass 		ret = -EIO;
1660649cd0dSSimon Glass 		goto out;
1670649cd0dSSimon Glass 	}
1680649cd0dSSimon Glass 
1690649cd0dSSimon Glass 	ret = setup_flash_device();
1700649cd0dSSimon Glass 	if (ret)
1710649cd0dSSimon Glass 		goto out;
1720649cd0dSSimon Glass 
173*9ba5e5bcSHeiko Schocher 	read1_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
1740649cd0dSSimon Glass 				    CONFIG_ENV_SIZE, tmp_env1);
175*9ba5e5bcSHeiko Schocher 	read2_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
1760649cd0dSSimon Glass 				    CONFIG_ENV_SIZE, tmp_env2);
1770649cd0dSSimon Glass 
17880719938SSimon Goldschmidt 	ret = env_import_redund((char *)tmp_env1, read1_fail, (char *)tmp_env2,
17980719938SSimon Goldschmidt 				read2_fail);
1800649cd0dSSimon Glass 
1810649cd0dSSimon Glass 	spi_flash_free(env_flash);
1820649cd0dSSimon Glass 	env_flash = NULL;
1830649cd0dSSimon Glass out:
1840649cd0dSSimon Glass 	free(tmp_env1);
1850649cd0dSSimon Glass 	free(tmp_env2);
186c5951991SSimon Glass 
187c5951991SSimon Glass 	return ret;
1880649cd0dSSimon Glass }
1890649cd0dSSimon Glass #else
1904415f1d1SSimon Glass #ifdef CMD_SAVEENV
env_sf_save(void)191e5bce247SSimon Glass static int env_sf_save(void)
1920649cd0dSSimon Glass {
1930649cd0dSSimon Glass 	u32	saved_size, saved_offset, sector;
1940649cd0dSSimon Glass 	char	*saved_buffer = NULL;
1950649cd0dSSimon Glass 	int	ret = 1;
1960649cd0dSSimon Glass 	env_t	env_new;
1970649cd0dSSimon Glass 
1980649cd0dSSimon Glass 	ret = setup_flash_device();
1990649cd0dSSimon Glass 	if (ret)
2000649cd0dSSimon Glass 		return ret;
2010649cd0dSSimon Glass 
2020649cd0dSSimon Glass 	/* Is the sector larger than the env (i.e. embedded) */
2030649cd0dSSimon Glass 	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
2040649cd0dSSimon Glass 		saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
2050649cd0dSSimon Glass 		saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
2060649cd0dSSimon Glass 		saved_buffer = malloc(saved_size);
2070649cd0dSSimon Glass 		if (!saved_buffer)
2080649cd0dSSimon Glass 			goto done;
2090649cd0dSSimon Glass 
210*9ba5e5bcSHeiko Schocher 		ret = spi_flash_read(env_flash, saved_offset,
2110649cd0dSSimon Glass 			saved_size, saved_buffer);
2120649cd0dSSimon Glass 		if (ret)
2130649cd0dSSimon Glass 			goto done;
2140649cd0dSSimon Glass 	}
2150649cd0dSSimon Glass 
2160649cd0dSSimon Glass 	ret = env_export(&env_new);
2170649cd0dSSimon Glass 	if (ret)
2180649cd0dSSimon Glass 		goto done;
2190649cd0dSSimon Glass 
2200649cd0dSSimon Glass 	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
2210649cd0dSSimon Glass 
2220649cd0dSSimon Glass 	puts("Erasing SPI flash...");
2230649cd0dSSimon Glass 	ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
2240649cd0dSSimon Glass 		sector * CONFIG_ENV_SECT_SIZE);
2250649cd0dSSimon Glass 	if (ret)
2260649cd0dSSimon Glass 		goto done;
2270649cd0dSSimon Glass 
2280649cd0dSSimon Glass 	puts("Writing to SPI flash...");
2290649cd0dSSimon Glass 	ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
2300649cd0dSSimon Glass 		CONFIG_ENV_SIZE, &env_new);
2310649cd0dSSimon Glass 	if (ret)
2320649cd0dSSimon Glass 		goto done;
2330649cd0dSSimon Glass 
2340649cd0dSSimon Glass 	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
2350649cd0dSSimon Glass 		ret = spi_flash_write(env_flash, saved_offset,
2360649cd0dSSimon Glass 			saved_size, saved_buffer);
2370649cd0dSSimon Glass 		if (ret)
2380649cd0dSSimon Glass 			goto done;
2390649cd0dSSimon Glass 	}
2400649cd0dSSimon Glass 
2410649cd0dSSimon Glass 	ret = 0;
2420649cd0dSSimon Glass 	puts("done\n");
2430649cd0dSSimon Glass 
2440649cd0dSSimon Glass  done:
2450649cd0dSSimon Glass 	if (saved_buffer)
2460649cd0dSSimon Glass 		free(saved_buffer);
2470649cd0dSSimon Glass 
2480649cd0dSSimon Glass 	return ret;
2490649cd0dSSimon Glass }
2504415f1d1SSimon Glass #endif /* CMD_SAVEENV */
2510649cd0dSSimon Glass 
env_sf_load(void)252c5951991SSimon Glass static int env_sf_load(void)
2530649cd0dSSimon Glass {
2540649cd0dSSimon Glass 	int ret;
2550649cd0dSSimon Glass 	char *buf = NULL;
2560649cd0dSSimon Glass 
2570649cd0dSSimon Glass 	buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
2580649cd0dSSimon Glass 	if (!buf) {
259c5d548a9SYaniv Levinsky 		set_default_env("malloc() failed", 0);
260c5951991SSimon Glass 		return -EIO;
2610649cd0dSSimon Glass 	}
2620649cd0dSSimon Glass 
2630649cd0dSSimon Glass 	ret = setup_flash_device();
2640649cd0dSSimon Glass 	if (ret)
2650649cd0dSSimon Glass 		goto out;
2660649cd0dSSimon Glass 
267*9ba5e5bcSHeiko Schocher 	ret = spi_flash_read(env_flash,
268*9ba5e5bcSHeiko Schocher 		CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
2690649cd0dSSimon Glass 	if (ret) {
270*9ba5e5bcSHeiko Schocher 		set_default_env("spi_flash_read() failed", 0);
2710649cd0dSSimon Glass 		goto err_read;
2720649cd0dSSimon Glass 	}
2730649cd0dSSimon Glass 
2740649cd0dSSimon Glass 	ret = env_import(buf, 1);
27542a1820bSSimon Goldschmidt 	if (!ret)
276203e94f6SSimon Glass 		gd->env_valid = ENV_VALID;
2770649cd0dSSimon Glass 
2780649cd0dSSimon Glass err_read:
2790649cd0dSSimon Glass 	spi_flash_free(env_flash);
2800649cd0dSSimon Glass 	env_flash = NULL;
2810649cd0dSSimon Glass out:
2820649cd0dSSimon Glass 	free(buf);
283c5951991SSimon Glass 
284c5951991SSimon Glass 	return ret;
2850649cd0dSSimon Glass }
2860649cd0dSSimon Glass #endif
2870649cd0dSSimon Glass 
288119c01c2SRajesh Bhagat #ifdef CONFIG_ENV_ADDR
env_sf_get_env_addr(void)289119c01c2SRajesh Bhagat __weak void *env_sf_get_env_addr(void)
290119c01c2SRajesh Bhagat {
291119c01c2SRajesh Bhagat 	return (void *)CONFIG_ENV_ADDR;
292119c01c2SRajesh Bhagat }
293119c01c2SRajesh Bhagat #endif
294119c01c2SRajesh Bhagat 
295b500c92bSAshish Kumar #if defined(INITENV) && defined(CONFIG_ENV_ADDR)
env_sf_init(void)296b500c92bSAshish Kumar static int env_sf_init(void)
297b500c92bSAshish Kumar {
298119c01c2SRajesh Bhagat 	env_t *env_ptr = (env_t *)env_sf_get_env_addr();
299b500c92bSAshish Kumar 
300b500c92bSAshish Kumar 	if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
301b500c92bSAshish Kumar 		gd->env_addr	= (ulong)&(env_ptr->data);
302b500c92bSAshish Kumar 		gd->env_valid	= 1;
303b500c92bSAshish Kumar 	} else {
304b500c92bSAshish Kumar 		gd->env_addr = (ulong)&default_environment[0];
305b500c92bSAshish Kumar 		gd->env_valid = 1;
306b500c92bSAshish Kumar 	}
307b500c92bSAshish Kumar 
308b500c92bSAshish Kumar 	return 0;
309b500c92bSAshish Kumar }
310b500c92bSAshish Kumar #endif
311b500c92bSAshish Kumar 
3124415f1d1SSimon Glass U_BOOT_ENV_LOCATION(sf) = {
3134415f1d1SSimon Glass 	.location	= ENVL_SPI_FLASH,
314ac358bebSSimon Glass 	ENV_NAME("SPI Flash")
315e5bce247SSimon Glass 	.load		= env_sf_load,
3164415f1d1SSimon Glass #ifdef CMD_SAVEENV
317e5bce247SSimon Glass 	.save		= env_save_ptr(env_sf_save),
3184415f1d1SSimon Glass #endif
319b500c92bSAshish Kumar #if defined(INITENV) && defined(CONFIG_ENV_ADDR)
320b500c92bSAshish Kumar 	.init		= env_sf_init,
321b500c92bSAshish Kumar #endif
3224415f1d1SSimon Glass };
323