xref: /openbmc/u-boot/env/sf.c (revision 203e94f6c9ca03e260175ce240f5856507395585)
1  /*
2   * (C) Copyright 2000-2010
3   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4   *
5   * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
6   * Andreas Heppel <aheppel@sysgo.de>
7   *
8   * (C) Copyright 2008 Atmel Corporation
9   *
10   * SPDX-License-Identifier:	GPL-2.0+
11   */
12  #include <common.h>
13  #include <dm.h>
14  #include <environment.h>
15  #include <malloc.h>
16  #include <spi.h>
17  #include <spi_flash.h>
18  #include <search.h>
19  #include <errno.h>
20  #include <dm/device-internal.h>
21  
22  #ifndef CONFIG_ENV_SPI_BUS
23  # define CONFIG_ENV_SPI_BUS	CONFIG_SF_DEFAULT_BUS
24  #endif
25  #ifndef CONFIG_ENV_SPI_CS
26  # define CONFIG_ENV_SPI_CS	CONFIG_SF_DEFAULT_CS
27  #endif
28  #ifndef CONFIG_ENV_SPI_MAX_HZ
29  # define CONFIG_ENV_SPI_MAX_HZ	CONFIG_SF_DEFAULT_SPEED
30  #endif
31  #ifndef CONFIG_ENV_SPI_MODE
32  # define CONFIG_ENV_SPI_MODE	CONFIG_SF_DEFAULT_MODE
33  #endif
34  
35  #ifdef CONFIG_ENV_OFFSET_REDUND
36  static ulong env_offset		= CONFIG_ENV_OFFSET;
37  static ulong env_new_offset	= CONFIG_ENV_OFFSET_REDUND;
38  
39  #define ACTIVE_FLAG	1
40  #define OBSOLETE_FLAG	0
41  #endif /* CONFIG_ENV_OFFSET_REDUND */
42  
43  DECLARE_GLOBAL_DATA_PTR;
44  
45  char *env_name_spec = "SPI Flash";
46  
47  static struct spi_flash *env_flash;
48  
49  static int setup_flash_device(void)
50  {
51  #ifdef CONFIG_DM_SPI_FLASH
52  	struct udevice *new;
53  	int	ret;
54  
55  	/* speed and mode will be read from DT */
56  	ret = spi_flash_probe_bus_cs(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
57  				     0, 0, &new);
58  	if (ret) {
59  		set_default_env("!spi_flash_probe_bus_cs() failed");
60  		return 1;
61  	}
62  
63  	env_flash = dev_get_uclass_priv(new);
64  #else
65  
66  	if (!env_flash) {
67  		env_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS,
68  			CONFIG_ENV_SPI_CS,
69  			CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
70  		if (!env_flash) {
71  			set_default_env("!spi_flash_probe() failed");
72  			return 1;
73  		}
74  	}
75  #endif
76  	return 0;
77  }
78  
79  #if defined(CONFIG_ENV_OFFSET_REDUND)
80  int saveenv(void)
81  {
82  	env_t	env_new;
83  	char	*saved_buffer = NULL, flag = OBSOLETE_FLAG;
84  	u32	saved_size, saved_offset, sector;
85  	int	ret;
86  
87  	ret = setup_flash_device();
88  	if (ret)
89  		return ret;
90  
91  	ret = env_export(&env_new);
92  	if (ret)
93  		return ret;
94  	env_new.flags	= ACTIVE_FLAG;
95  
96  	if (gd->env_valid == ENV_VALID) {
97  		env_new_offset = CONFIG_ENV_OFFSET_REDUND;
98  		env_offset = CONFIG_ENV_OFFSET;
99  	} else {
100  		env_new_offset = CONFIG_ENV_OFFSET;
101  		env_offset = CONFIG_ENV_OFFSET_REDUND;
102  	}
103  
104  	/* Is the sector larger than the env (i.e. embedded) */
105  	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
106  		saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
107  		saved_offset = env_new_offset + CONFIG_ENV_SIZE;
108  		saved_buffer = memalign(ARCH_DMA_MINALIGN, saved_size);
109  		if (!saved_buffer) {
110  			ret = 1;
111  			goto done;
112  		}
113  		ret = spi_flash_read(env_flash, saved_offset,
114  					saved_size, saved_buffer);
115  		if (ret)
116  			goto done;
117  	}
118  
119  	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
120  
121  	puts("Erasing SPI flash...");
122  	ret = spi_flash_erase(env_flash, env_new_offset,
123  				sector * CONFIG_ENV_SECT_SIZE);
124  	if (ret)
125  		goto done;
126  
127  	puts("Writing to SPI flash...");
128  
129  	ret = spi_flash_write(env_flash, env_new_offset,
130  		CONFIG_ENV_SIZE, &env_new);
131  	if (ret)
132  		goto done;
133  
134  	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
135  		ret = spi_flash_write(env_flash, saved_offset,
136  					saved_size, saved_buffer);
137  		if (ret)
138  			goto done;
139  	}
140  
141  	ret = spi_flash_write(env_flash, env_offset + offsetof(env_t, flags),
142  				sizeof(env_new.flags), &flag);
143  	if (ret)
144  		goto done;
145  
146  	puts("done\n");
147  
148  	gd->env_valid = gd->env_valid == ENV_REDUND ? ENV_VALID : ENV_REDUND;
149  
150  	printf("Valid environment: %d\n", (int)gd->env_valid);
151  
152   done:
153  	if (saved_buffer)
154  		free(saved_buffer);
155  
156  	return ret;
157  }
158  
159  void env_relocate_spec(void)
160  {
161  	int ret;
162  	int crc1_ok = 0, crc2_ok = 0;
163  	env_t *tmp_env1 = NULL;
164  	env_t *tmp_env2 = NULL;
165  	env_t *ep = NULL;
166  
167  	tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
168  			CONFIG_ENV_SIZE);
169  	tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
170  			CONFIG_ENV_SIZE);
171  	if (!tmp_env1 || !tmp_env2) {
172  		set_default_env("!malloc() failed");
173  		goto out;
174  	}
175  
176  	ret = setup_flash_device();
177  	if (ret)
178  		goto out;
179  
180  	ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
181  				CONFIG_ENV_SIZE, tmp_env1);
182  	if (ret) {
183  		set_default_env("!spi_flash_read() failed");
184  		goto err_read;
185  	}
186  
187  	if (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc)
188  		crc1_ok = 1;
189  
190  	ret = spi_flash_read(env_flash, CONFIG_ENV_OFFSET_REDUND,
191  				CONFIG_ENV_SIZE, tmp_env2);
192  	if (!ret) {
193  		if (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc)
194  			crc2_ok = 1;
195  	}
196  
197  	if (!crc1_ok && !crc2_ok) {
198  		set_default_env("!bad CRC");
199  		goto err_read;
200  	} else if (crc1_ok && !crc2_ok) {
201  		gd->env_valid = ENV_VALID;
202  	} else if (!crc1_ok && crc2_ok) {
203  		gd->env_valid = ENV_REDUND;
204  	} else if (tmp_env1->flags == ACTIVE_FLAG &&
205  		   tmp_env2->flags == OBSOLETE_FLAG) {
206  		gd->env_valid = ENV_VALID;
207  	} else if (tmp_env1->flags == OBSOLETE_FLAG &&
208  		   tmp_env2->flags == ACTIVE_FLAG) {
209  		gd->env_valid = ENV_REDUND;
210  	} else if (tmp_env1->flags == tmp_env2->flags) {
211  		gd->env_valid = ENV_VALID;
212  	} else if (tmp_env1->flags == 0xFF) {
213  		gd->env_valid = ENV_VALID;
214  	} else if (tmp_env2->flags == 0xFF) {
215  		gd->env_valid = ENV_REDUND;
216  	} else {
217  		/*
218  		 * this differs from code in env_flash.c, but I think a sane
219  		 * default path is desirable.
220  		 */
221  		gd->env_valid = ENV_VALID;
222  	}
223  
224  	if (gd->env_valid == ENV_VALID)
225  		ep = tmp_env1;
226  	else
227  		ep = tmp_env2;
228  
229  	ret = env_import((char *)ep, 0);
230  	if (!ret) {
231  		error("Cannot import environment: errno = %d\n", errno);
232  		set_default_env("!env_import failed");
233  	}
234  
235  err_read:
236  	spi_flash_free(env_flash);
237  	env_flash = NULL;
238  out:
239  	free(tmp_env1);
240  	free(tmp_env2);
241  }
242  #else
243  int saveenv(void)
244  {
245  	u32	saved_size, saved_offset, sector;
246  	char	*saved_buffer = NULL;
247  	int	ret = 1;
248  	env_t	env_new;
249  
250  	ret = setup_flash_device();
251  	if (ret)
252  		return ret;
253  
254  	/* Is the sector larger than the env (i.e. embedded) */
255  	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
256  		saved_size = CONFIG_ENV_SECT_SIZE - CONFIG_ENV_SIZE;
257  		saved_offset = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
258  		saved_buffer = malloc(saved_size);
259  		if (!saved_buffer)
260  			goto done;
261  
262  		ret = spi_flash_read(env_flash, saved_offset,
263  			saved_size, saved_buffer);
264  		if (ret)
265  			goto done;
266  	}
267  
268  	ret = env_export(&env_new);
269  	if (ret)
270  		goto done;
271  
272  	sector = DIV_ROUND_UP(CONFIG_ENV_SIZE, CONFIG_ENV_SECT_SIZE);
273  
274  	puts("Erasing SPI flash...");
275  	ret = spi_flash_erase(env_flash, CONFIG_ENV_OFFSET,
276  		sector * CONFIG_ENV_SECT_SIZE);
277  	if (ret)
278  		goto done;
279  
280  	puts("Writing to SPI flash...");
281  	ret = spi_flash_write(env_flash, CONFIG_ENV_OFFSET,
282  		CONFIG_ENV_SIZE, &env_new);
283  	if (ret)
284  		goto done;
285  
286  	if (CONFIG_ENV_SECT_SIZE > CONFIG_ENV_SIZE) {
287  		ret = spi_flash_write(env_flash, saved_offset,
288  			saved_size, saved_buffer);
289  		if (ret)
290  			goto done;
291  	}
292  
293  	ret = 0;
294  	puts("done\n");
295  
296   done:
297  	if (saved_buffer)
298  		free(saved_buffer);
299  
300  	return ret;
301  }
302  
303  void env_relocate_spec(void)
304  {
305  	int ret;
306  	char *buf = NULL;
307  
308  	buf = (char *)memalign(ARCH_DMA_MINALIGN, CONFIG_ENV_SIZE);
309  	if (!buf) {
310  		set_default_env("!malloc() failed");
311  		return;
312  	}
313  
314  	ret = setup_flash_device();
315  	if (ret)
316  		goto out;
317  
318  	ret = spi_flash_read(env_flash,
319  		CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, buf);
320  	if (ret) {
321  		set_default_env("!spi_flash_read() failed");
322  		goto err_read;
323  	}
324  
325  	ret = env_import(buf, 1);
326  	if (ret)
327  		gd->env_valid = ENV_VALID;
328  
329  err_read:
330  	spi_flash_free(env_flash);
331  	env_flash = NULL;
332  out:
333  	free(buf);
334  }
335  #endif
336  
337  int env_init(void)
338  {
339  	/* SPI flash isn't usable before relocation */
340  	gd->env_addr = (ulong)&default_environment[0];
341  	gd->env_valid = ENV_VALID;
342  
343  	return 0;
344  }
345