mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-10-30 19:48:19 +00:00 
			
		
		
		
	rename CFG_NAND_LEGACY to CONFIG_NAND_LEGACY Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
		
			
				
	
	
		
			557 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			557 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * (C) Copyright 2003-2004
 | |
|  * Gary Jennejohn, DENX Software Engineering, gj@denx.de.
 | |
|  * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com
 | |
|  *
 | |
|  * See file CREDITS for list of people who contributed to this
 | |
|  * project.
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License as
 | |
|  * published by the Free Software Foundation; either version 2 of
 | |
|  * the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 | |
|  * MA 02111-1307 USA
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| 
 | |
| #include <command.h>
 | |
| #include <image.h>
 | |
| #include <asm/byteorder.h>
 | |
| #if defined(CONFIG_NAND_LEGACY)
 | |
| #include <linux/mtd/nand_legacy.h>
 | |
| #endif
 | |
| #include <fat.h>
 | |
| #include <part.h>
 | |
| 
 | |
| #include "auto_update.h"
 | |
| 
 | |
| #ifdef CONFIG_AUTO_UPDATE
 | |
| 
 | |
| #if !defined(CONFIG_CMD_FAT)
 | |
| #error "must define CONFIG_CMD_FAT"
 | |
| #endif
 | |
| 
 | |
| extern au_image_t au_image[];
 | |
| extern int N_AU_IMAGES;
 | |
| 
 | |
| /* where to load files into memory */
 | |
| #define LOAD_ADDR ((unsigned char *)0x100000)
 | |
| #define MAX_LOADSZ 0x1c00000
 | |
| 
 | |
| /* externals */
 | |
| extern int fat_register_device(block_dev_desc_t *, int);
 | |
| extern int file_fat_detectfs(void);
 | |
| extern long file_fat_read(const char *, void *, unsigned long);
 | |
| long do_fat_read (const char *filename, void *buffer,
 | |
| 		  unsigned long maxsize, int dols);
 | |
| extern int flash_sect_erase(ulong, ulong);
 | |
| extern int flash_sect_protect (int, ulong, ulong);
 | |
| extern int flash_write (char *, ulong, ulong);
 | |
| 
 | |
| #if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_LEGACY)
 | |
| /* references to names in cmd_nand.c */
 | |
| #define NANDRW_READ	0x01
 | |
| #define NANDRW_WRITE	0x00
 | |
| #define NANDRW_JFFS2	0x02
 | |
| #define NANDRW_JFFS2_SKIP	0x04
 | |
| extern struct nand_chip nand_dev_desc[];
 | |
| extern int nand_legacy_rw(struct nand_chip* nand, int cmd,
 | |
| 			  size_t start, size_t len,
 | |
| 			  size_t * retlen, u_char * buf);
 | |
| extern int nand_legacy_erase(struct nand_chip* nand, size_t ofs,
 | |
| 			     size_t len, int clean);
 | |
| #endif
 | |
| 
 | |
| extern block_dev_desc_t ide_dev_desc[CFG_IDE_MAXDEVICE];
 | |
| 
 | |
| int au_check_cksum_valid(int i, long nbytes)
 | |
| {
 | |
| 	image_header_t *hdr;
 | |
| 
 | |
| 	hdr = (image_header_t *)LOAD_ADDR;
 | |
| #if defined(CONFIG_FIT)
 | |
| 	if (genimg_get_format ((void *)hdr) != IMAGE_FORMAT_LEGACY) {
 | |
| 		puts ("Non legacy image format not supported\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	if ((au_image[i].type == AU_FIRMWARE) &&
 | |
| 	    (au_image[i].size != image_get_data_size (hdr))) {
 | |
| 		printf ("Image %s has wrong size\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (nbytes != (image_get_image_size (hdr))) {
 | |
| 		printf ("Image %s bad total SIZE\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* check the data CRC */
 | |
| 	if (!image_check_dcrc (hdr)) {
 | |
| 		printf ("Image %s bad data checksum\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int au_check_header_valid(int i, long nbytes)
 | |
| {
 | |
| 	image_header_t *hdr;
 | |
| 	unsigned long checksum;
 | |
| 
 | |
| 	hdr = (image_header_t *)LOAD_ADDR;
 | |
| #if defined(CONFIG_FIT)
 | |
| 	if (genimg_get_format ((void *)hdr) != IMAGE_FORMAT_LEGACY) {
 | |
| 		puts ("Non legacy image format not supported\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	/* check the easy ones first */
 | |
| 	if (nbytes < image_get_header_size ()) {
 | |
| 		printf ("Image %s bad header SIZE\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (!image_check_magic (hdr) || !image_check_arch (hdr, IH_ARCH_PPC)) {
 | |
| 		printf ("Image %s bad MAGIC or ARCH\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (!image_check_hcrc (hdr)) {
 | |
| 		printf ("Image %s bad header checksum\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* check the type - could do this all in one gigantic if() */
 | |
| 	if (((au_image[i].type & AU_TYPEMASK) == AU_FIRMWARE) &&
 | |
| 	    !image_check_type (hdr, IH_TYPE_FIRMWARE)) {
 | |
| 		printf ("Image %s wrong type\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	if (((au_image[i].type & AU_TYPEMASK) == AU_SCRIPT) &&
 | |
| 	    !image_check_type (hdr, IH_TYPE_SCRIPT)) {
 | |
| 		printf ("Image %s wrong type\n", au_image[i].name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* recycle checksum */
 | |
| 	checksum = image_get_data_size (hdr);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int au_do_update(int i, long sz)
 | |
| {
 | |
| 	image_header_t *hdr;
 | |
| 	char *addr;
 | |
| 	long start, end;
 | |
| 	int off, rc;
 | |
| 	uint nbytes;
 | |
| 	int k;
 | |
| #if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_LEGACY)
 | |
| 	int total;
 | |
| #endif
 | |
| 
 | |
| 	hdr = (image_header_t *)LOAD_ADDR;
 | |
| #if defined(CONFIG_FIT)
 | |
| 	if (genimg_get_format ((void *)hdr) != IMAGE_FORMAT_LEGACY) {
 | |
| 		puts ("Non legacy image format not supported\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	switch (au_image[i].type & AU_TYPEMASK) {
 | |
| 	case AU_SCRIPT:
 | |
| 		printf("Executing script %s\n", au_image[i].name);
 | |
| 
 | |
| 		/* execute a script */
 | |
| 		if (image_check_type (hdr, IH_TYPE_SCRIPT)) {
 | |
| 			addr = (char *)((char *)hdr + image_get_header_size ());
 | |
| 			/* stick a NULL at the end of the script, otherwise */
 | |
| 			/* parse_string_outer() runs off the end. */
 | |
| 			addr[image_get_data_size (hdr)] = 0;
 | |
| 			addr += 8;
 | |
| 
 | |
| 			/*
 | |
| 			 * Replace cr/lf with ;
 | |
| 			 */
 | |
| 			k = 0;
 | |
| 			while (addr[k] != 0) {
 | |
| 				if ((addr[k] == 10) || (addr[k] == 13)) {
 | |
| 					addr[k] = ';';
 | |
| 				}
 | |
| 				k++;
 | |
| 			}
 | |
| 
 | |
| 			run_command(addr, 0);
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	case AU_FIRMWARE:
 | |
| 	case AU_NOR:
 | |
| 	case AU_NAND:
 | |
| 		start = au_image[i].start;
 | |
| 		end = au_image[i].start + au_image[i].size - 1;
 | |
| 
 | |
| 		/*
 | |
| 		 * do not update firmware when image is already in flash.
 | |
| 		 */
 | |
| 		if (au_image[i].type == AU_FIRMWARE) {
 | |
| 			char *orig = (char*)start;
 | |
| 			char *new  = (char *)((char *)hdr +
 | |
| 					      image_get_header_size ());
 | |
| 			nbytes = image_get_data_size (hdr);
 | |
| 
 | |
| 			while (--nbytes) {
 | |
| 				if (*orig++ != *new++) {
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 			if (!nbytes) {
 | |
| 				printf ("Skipping firmware update - "
 | |
| 					"images are identical\n");
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/* unprotect the address range */
 | |
| 		if (((au_image[i].type & AU_FLAGMASK) == AU_PROTECT) ||
 | |
| 		    (au_image[i].type == AU_FIRMWARE)) {
 | |
| 			flash_sect_protect (0, start, end);
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * erase the address range.
 | |
| 		 */
 | |
| 		if (au_image[i].type != AU_NAND) {
 | |
| 			printf ("Updating NOR FLASH with image %s\n",
 | |
| 				au_image[i].name);
 | |
| 			debug ("flash_sect_erase(%lx, %lx);\n", start, end);
 | |
| 			flash_sect_erase (start, end);
 | |
| 		} else {
 | |
| #if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_LEGACY)
 | |
| 			printf ("Updating NAND FLASH with image %s\n",
 | |
| 				au_image[i].name);
 | |
| 			debug ("nand_legacy_erase(%lx, %lx);\n", start, end);
 | |
| 			rc = nand_legacy_erase (nand_dev_desc, start,
 | |
| 						end - start + 1, 0);
 | |
| 			debug ("nand_legacy_erase returned %x\n", rc);
 | |
| #endif
 | |
| 		}
 | |
| 
 | |
| 		udelay(10000);
 | |
| 
 | |
| 		/* strip the header - except for the kernel and ramdisk */
 | |
| 		if (au_image[i].type != AU_FIRMWARE) {
 | |
| 			addr = (char *)hdr;
 | |
| 			off = image_get_header_size ();
 | |
| 			nbytes = image_get_image_size (hdr);
 | |
| 		} else {
 | |
| 			addr = (char *)((char *)hdr + image_get_header_size ());
 | |
| 			off = 0;
 | |
| 			nbytes = image_get_data_size (hdr);
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * copy the data from RAM to FLASH
 | |
| 		 */
 | |
| 		if (au_image[i].type != AU_NAND) {
 | |
| 			debug ("flash_write(%p, %lx, %x)\n",
 | |
| 			       addr, start, nbytes);
 | |
| 			rc = flash_write ((char *)addr, start,
 | |
| 					  (nbytes + 1) & ~1);
 | |
| 		} else {
 | |
| #if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_LEGACY)
 | |
| 			debug ("nand_legacy_rw(%p, %lx, %x)\n",
 | |
| 			       addr, start, nbytes);
 | |
| 			rc = nand_legacy_rw (nand_dev_desc,
 | |
| 					     NANDRW_WRITE | NANDRW_JFFS2,
 | |
| 					     start, nbytes, (size_t *)&total,
 | |
| 					     (uchar *)addr);
 | |
| 			debug ("nand_legacy_rw: ret=%x total=%d nbytes=%d\n",
 | |
| 			       rc, total, nbytes);
 | |
| #else
 | |
| 			rc = -1;
 | |
| #endif
 | |
| 		}
 | |
| 		if (rc != 0) {
 | |
| 			printf ("Flashing failed due to error %d\n", rc);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * check the dcrc of the copy
 | |
| 		 */
 | |
| 		if (au_image[i].type != AU_NAND) {
 | |
| 			rc = crc32 (0, (uchar *)(start + off),
 | |
| 				    image_get_data_size (hdr));
 | |
| 		} else {
 | |
| #if defined(CONFIG_CMD_NAND) && defined(CONFIG_NAND_LEGACY)
 | |
| 			rc = nand_legacy_rw (nand_dev_desc,
 | |
| 					     NANDRW_READ | NANDRW_JFFS2 |
 | |
| 					     NANDRW_JFFS2_SKIP,
 | |
| 					     start, nbytes, (size_t *)&total,
 | |
| 					     (uchar *)addr);
 | |
| 			rc = crc32 (0, (uchar *)(addr + off),
 | |
| 				    image_get_data_size (hdr));
 | |
| #endif
 | |
| 		}
 | |
| 		if (rc != image_get_dcrc (hdr)) {
 | |
| 			printf ("Image %s Bad Data Checksum After COPY\n",
 | |
| 				au_image[i].name);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		/* protect the address range */
 | |
| 		/* this assumes that ONLY the firmware is protected! */
 | |
| 		if (((au_image[i].type & AU_FLAGMASK) == AU_PROTECT) ||
 | |
| 		    (au_image[i].type == AU_FIRMWARE)) {
 | |
| 			flash_sect_protect (1, start, end);
 | |
| 		}
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		printf("Wrong image type selected!\n");
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void process_macros (const char *input, char *output)
 | |
| {
 | |
| 	char c, prev;
 | |
| 	const char *varname_start = NULL;
 | |
| 	int inputcnt  = strlen (input);
 | |
| 	int outputcnt = CFG_CBSIZE;
 | |
| 	int state = 0;	/* 0 = waiting for '$'	*/
 | |
| 			/* 1 = waiting for '(' or '{' */
 | |
| 			/* 2 = waiting for ')' or '}' */
 | |
| 			/* 3 = waiting for '''  */
 | |
| #ifdef DEBUG_PARSER
 | |
| 	char *output_start = output;
 | |
| 
 | |
| 	printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n",
 | |
| 		strlen(input), input);
 | |
| #endif
 | |
| 
 | |
| 	prev = '\0';			/* previous character */
 | |
| 
 | |
| 	while (inputcnt && outputcnt) {
 | |
| 	    c = *input++;
 | |
| 	    inputcnt--;
 | |
| 
 | |
| 	    if (state != 3) {
 | |
| 	    /* remove one level of escape characters */
 | |
| 	    if ((c == '\\') && (prev != '\\')) {
 | |
| 		if (inputcnt-- == 0)
 | |
| 			break;
 | |
| 		prev = c;
 | |
| 		c = *input++;
 | |
| 	    }
 | |
| 	    }
 | |
| 
 | |
| 	    switch (state) {
 | |
| 	    case 0:			/* Waiting for (unescaped) $ */
 | |
| 		if ((c == '\'') && (prev != '\\')) {
 | |
| 			state = 3;
 | |
| 			break;
 | |
| 		}
 | |
| 		if ((c == '$') && (prev != '\\')) {
 | |
| 			state++;
 | |
| 		} else {
 | |
| 			*(output++) = c;
 | |
| 			outputcnt--;
 | |
| 		}
 | |
| 		break;
 | |
| 	    case 1:			/* Waiting for ( */
 | |
| 		if (c == '(' || c == '{') {
 | |
| 			state++;
 | |
| 			varname_start = input;
 | |
| 		} else {
 | |
| 			state = 0;
 | |
| 			*(output++) = '$';
 | |
| 			outputcnt--;
 | |
| 
 | |
| 			if (outputcnt) {
 | |
| 				*(output++) = c;
 | |
| 				outputcnt--;
 | |
| 			}
 | |
| 		}
 | |
| 		break;
 | |
| 	    case 2:			/* Waiting for )	*/
 | |
| 		if (c == ')' || c == '}') {
 | |
| 			int i;
 | |
| 			char envname[CFG_CBSIZE], *envval;
 | |
| 			/* Varname # of chars */
 | |
| 			int envcnt = input - varname_start - 1;
 | |
| 
 | |
| 			/* Get the varname */
 | |
| 			for (i = 0; i < envcnt; i++) {
 | |
| 				envname[i] = varname_start[i];
 | |
| 			}
 | |
| 			envname[i] = 0;
 | |
| 
 | |
| 			/* Get its value */
 | |
| 			envval = getenv (envname);
 | |
| 
 | |
| 			/* Copy into the line if it exists */
 | |
| 			if (envval != NULL)
 | |
| 				while ((*envval) && outputcnt) {
 | |
| 					*(output++) = *(envval++);
 | |
| 					outputcnt--;
 | |
| 				}
 | |
| 			/* Look for another '$' */
 | |
| 			state = 0;
 | |
| 		}
 | |
| 		break;
 | |
| 	    case 3:			/* Waiting for '	*/
 | |
| 		if ((c == '\'') && (prev != '\\')) {
 | |
| 			state = 0;
 | |
| 		} else {
 | |
| 			*(output++) = c;
 | |
| 			outputcnt--;
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	    prev = c;
 | |
| 	}
 | |
| 
 | |
| 	if (outputcnt)
 | |
| 		*output = 0;
 | |
| 
 | |
| #ifdef DEBUG_PARSER
 | |
| 	printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
 | |
| 		strlen (output_start), output_start);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * this is called from board_init() after the hardware has been set up
 | |
|  * and is usable. That seems like a good time to do this.
 | |
|  * Right now the return value is ignored.
 | |
|  */
 | |
| int do_auto_update(void)
 | |
| {
 | |
| 	block_dev_desc_t *stor_dev = NULL;
 | |
| 	long sz;
 | |
| 	int i, res, cnt, old_ctrlc, got_ctrlc;
 | |
| 	char buffer[32];
 | |
| 	char str[80];
 | |
| 	int n;
 | |
| 
 | |
| 	if  (ide_dev_desc[0].type != DEV_TYPE_UNKNOWN) {
 | |
| 		stor_dev = get_dev ("ide", 0);
 | |
| 		if (stor_dev == NULL) {
 | |
| 			debug ("ide: unknown device\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (fat_register_device (stor_dev, 1) != 0) {
 | |
| 		debug ("Unable to register ide disk 0:1\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Check if magic file is present
 | |
| 	 */
 | |
| 	if ((n = do_fat_read (AU_MAGIC_FILE, buffer,
 | |
| 			      sizeof(buffer), LS_NO)) <= 0) {
 | |
| 		debug ("No auto_update magic file (n=%d)\n", n);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| #ifdef CONFIG_AUTO_UPDATE_SHOW
 | |
| 	board_auto_update_show (1);
 | |
| #endif
 | |
| 	puts("\nAutoUpdate Disk detected! Trying to update system...\n");
 | |
| 
 | |
| 	/* make sure that we see CTRL-C and save the old state */
 | |
| 	old_ctrlc = disable_ctrlc (0);
 | |
| 
 | |
| 	/* just loop thru all the possible files */
 | |
| 	for (i = 0; i < N_AU_IMAGES; i++) {
 | |
| 		/*
 | |
| 		 * Try to expand the environment var in the fname
 | |
| 		 */
 | |
| 		process_macros (au_image[i].name, str);
 | |
| 		strcpy (au_image[i].name, str);
 | |
| 
 | |
| 		printf("Reading %s ...", au_image[i].name);
 | |
| 		/* just read the header */
 | |
| 		sz = do_fat_read (au_image[i].name, LOAD_ADDR,
 | |
| 				  image_get_header_size (), LS_NO);
 | |
| 		debug ("read %s sz %ld hdr %d\n",
 | |
| 			au_image[i].name, sz, image_get_header_size ());
 | |
| 		if (sz <= 0 || sz < image_get_header_size ()) {
 | |
| 			puts(" not found\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (au_check_header_valid (i, sz) < 0) {
 | |
| 			puts(" header not valid\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 		sz = do_fat_read (au_image[i].name, LOAD_ADDR,
 | |
| 				  MAX_LOADSZ, LS_NO);
 | |
| 		debug ("read %s sz %ld hdr %d\n",
 | |
| 			au_image[i].name, sz, image_get_header_size ());
 | |
| 		if (sz <= 0 || sz <= image_get_header_size ()) {
 | |
| 			puts(" not found\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (au_check_cksum_valid (i, sz) < 0) {
 | |
| 			puts(" checksum not valid\n");
 | |
| 			continue;
 | |
| 		}
 | |
| 		puts(" done\n");
 | |
| 
 | |
| 		do {
 | |
| 			res = au_do_update (i, sz);
 | |
| 			/* let the user break out of the loop */
 | |
| 			if (ctrlc() || had_ctrlc ()) {
 | |
| 				clear_ctrlc ();
 | |
| 				if (res < 0)
 | |
| 					got_ctrlc = 1;
 | |
| 				break;
 | |
| 			}
 | |
| 			cnt++;
 | |
| 		} while (res < 0);
 | |
| 	}
 | |
| 
 | |
| 	/* restore the old state */
 | |
| 	disable_ctrlc (old_ctrlc);
 | |
| 
 | |
| 	puts("AutoUpdate finished\n\n");
 | |
| #ifdef CONFIG_AUTO_UPDATE_SHOW
 | |
| 	board_auto_update_show (0);
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int auto_update(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 | |
| {
 | |
| 	do_auto_update();
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| U_BOOT_CMD(
 | |
| 	autoupd,	1,	1,	auto_update,
 | |
| 	"autoupd - Automatically update images\n",
 | |
| 	NULL
 | |
| );
 | |
| #endif /* CONFIG_AUTO_UPDATE */
 |