blob: a4935847b40f5142faa1100019396ee01ae986d6 [file] [log] [blame]
/*
* Copyright (C) 2005 Texas Instruments.
*
* (C) Copyright 2004
* Jian Zhang, Texas Instruments, jzhang@ti.com.
*
* (C) Copyright 2002-2006
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*
* 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 <bootm.h>
#include <part.h>
#include <fat.h>
#include <image.h>
#include <mmc.h>
#include <timestamp.h>
#include <version.h>
#include <asm/arch/mem.h>
/*
* Preprocessor Definitions
*/
#ifndef CONFIG_IDENT_STRING
#define CONFIG_IDENT_STRING ""
#endif
/* Type Definition */
typedef int (init_fnc_t)(void);
/*
* Function Prototypes
*/
extern int misc_init_r (void);
extern int mmc_init(int verbose);
extern block_dev_desc_t *mmc_get_dev(int dev);
extern int get_boot_devices_list(const u32** devices_list);
/*
* Global Variables
*/
const char version_string[] =
X_LOADER_VERSION" (" X_LOADER_DATE " - " X_LOADER_TIME ")"CONFIG_IDENT_STRING;
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
#ifdef CFG_PRINTF
serial_init, /* serial communications setup */
display_options,
#endif
nand_init, /* board specific nand init */
NULL,
};
static inline int check_load_params(const uint8_t *buffer, const int *index, int count)
{
if (buffer == NULL || index == NULL || count == 0 || *index < BOOTM_INDEX_START || *index >= count) {
return (BOOTM_STATUS_FAILURE);
}
return (BOOTM_STATUS_SUCCESS);
}
#if (defined(CFG_MMC_ONENAND) || defined (CFG_MMC_NAND)) && defined(CFG_CMD_FAT)
/*
* int fat_load()
*
* Description:
* This routine attempts to read from the first initialized
* Multimedia Card (MMC) device, one of one or more designated
* secondary program loaders (SPL) from the a FAT file system on
* the first partition of a Master Boot Record volume.
*
* Input(s):
* buffer - A pointer to storage for the secondary program loader.
* index - A pointer to the index of boot file to attempt to load.
*
* Output(s):
* buffer - A pointer to the secondary program loader.
* index - A pointer to the index of the next boot file to
* attempt to load.
*
* Returns:
* BOOTM_STATUS_SUCCESS on success; otherwise, BOOTM_STATUS_FAILURE
* on error.
*
*/
static int fat_load(uint8_t *buffer, int *index)
{
#if defined(CONFIG_BOOTFILE)
const char * const files[] = { CONFIG_BOOTFILE };
#elif defined(CONFIG_BOOTFILE_LIST) && defined(CONFIG_BOOTM_IMAGE)
const char * const files[] = CONFIG_BOOTFILE_LIST;
#else
const char * const files[] = { NULL };
#endif /* defined(CONFIG_BOOTFILE) */
const char * file = NULL;
const char * failure = NULL;
const int count = ARRAY_SIZE(files);
const int device = 0;
const int partition = 1;
const int maxsize = 0;
const int verbose = 1;
long size = 0;
block_dev_desc_t *dev_desc = NULL;
unsigned char status = 0;
if (!check_load_params(buffer, index, count)) {
return (BOOTM_STATUS_FAILURE);
}
file = files[(*index)++];
if (*index >= count) {
*index = BOOTM_INDEX_STOP;
}
status = mmc_init(verbose);
if (status == 0) {
failure = "MMC initialization failed\n";
goto fail;
}
dev_desc = mmc_get_dev(device);
if (dev_desc == NULL) {
failure = "Could not access MMC device\n";
goto fail;
}
fat_register_device(dev_desc, partition);
size = file_fat_read(file, buffer, maxsize);
if (size == -1) {
printf("Could not read file \"%s\"\n", file);
goto fail;
}
printf("\n%ld bytes read\n", size);
printf("Loading from MMC%d, file \"%s\"\n", device, file);
return (BOOTM_STATUS_SUCCESS);
fail:
if (failure != NULL) {
puts(failure);
}
return (BOOTM_STATUS_FAILURE);
}
#endif /* (defined(CFG_MMC_ONENAND) || defined (CFG_MMC_NAND)) && defined(CFG_CMD_FAT) */
/*
* int block_load()
*
* Description:
* This routine attempts to read from a block device one of one or
* more designated secondary program loaders (SPL) specified by the
* array of block extents.
*
* Input(s):
* buffer - A pointer to storage for the secondary program
* loader.
* index - A pointer to the index of boot extent to attempt to
* load.
* count - The total number of extents.
* extents - The array of boot file extents to attempt to load.
* device - A pointer to a C string describing the block device
* being loaded from.
* read - A pointer to the device-specific function that will
* read the blocks to load.
* block_size - The size, in bytes, of the device's fundamental read
* block size.
*
*
* Output(s):
* buffer - A pointer to the secondary program loader.
* index - A pointer to the index of the next boot extent to
* attempt to load.
*
* Returns:
* BOOTM_STATUS_SUCCESS on success; otherwise, BOOTM_STATUS_FAILURE
* on error.
*
*/
static int block_load(uint8_t *buffer, int *index, int count, const boot_extent_t extents[], const char *device, int (*read)(unsigned char *, ulong), unsigned long block_size)
{
const boot_extent_t *extent = NULL;
unsigned long address;
unsigned long end;
int read_status;
if (!check_load_params(buffer, index, count)) {
return (BOOTM_STATUS_FAILURE);
}
extent = &extents[(*index)++];
if (*index >= count) {
*index = BOOTM_INDEX_STOP;
}
end = extent->start + extent->size + 1;
for (address = extent->start; address < end; address += block_size) {
read_status = read(buffer, address);
/* If successful, move the data pointer. If ECC fails,
* report failure to caller. If skipping bad block,
* just continue looping without moving the data pointer.
*/
if (read_status == NAND_READ_SUCCESS)
{
buffer += block_size;
}
else if (read_status == NAND_READ_ECC_FAILURE)
{
return (BOOTM_STATUS_FAILURE);
}
}
printf("Loading from %s, offset %#08lx\n", device, extent->start);
return (BOOTM_STATUS_SUCCESS);
}
#if defined(CFG_GPMC_ONENAND)
/*
* int onenand_load()
*
* Description:
* This routine attempts to read from a OneNAND device one of one or
* more designated secondary program loaders (SPL) specified by the
* array of block extents.
*
* Input(s):
* buffer - A pointer to storage for the secondary program
* loader.
* index - A pointer to the index of boot extent to attempt to
* load.
*
* Output(s):
* buffer - A pointer to the secondary program loader.
* index - A pointer to the index of the next boot extent to
* attempt to load.
*
* Returns:
* BOOTM_STATUS_SUCCESS on success; otherwise, BOOTM_STATUS_FAILURE
* on error.
*
*/
static int onenand_load(uint8_t *buffer, int *index)
{
#if defined(CONFIG_ONENAND_BOOTEXTENT)
const boot_extent_t extents[] = { CONFIG_ONENAND_BOOTEXTENT };
#elif defined(CONFIG_ONENAND_BOOTEXTENT_LIST) && defined(CONFIG_BOOTM_IMAGE)
const boot_extent_t extents[] = CONFIG_ONENAND_BOOTEXTENT_LIST;
#else
const boot_extent_t extents[] = { NULL };
#endif /* defined(CONFIG_ONENAND_BOOTEXTENT) */
return block_load(buffer,
index,
ARRAY_SIZE(extents),
extents,
"OneNAND",
onenand_read_block,
ONENAND_BLOCK_SIZE);
}
#endif /* defined(CFG_GPMC_ONENAND) */
#if defined(CFG_GPMC_NAND)
/*
* int nand_load()
*
* Description:
* This routine attempts to read from a NAND device one of one or
* more designated secondary program loaders (SPL) specified by the
* array of block extents.
*
* Input(s):
* buffer - A pointer to storage for the secondary program
* loader.
* index - A pointer to the index of boot extent to attempt to
* load.
*
* Output(s):
* buffer - A pointer to the secondary program loader.
* index - A pointer to the index of the next boot extent to
* attempt to load.
*
* Returns:
* BOOTM_STATUS_SUCCESS on success; otherwise, BOOTM_STATUS_FAILURE
* on error.
*
*/
static int nand_load(uint8_t *buffer, int *index)
{
#if defined(CONFIG_NAND_BOOTEXTENT)
const boot_extent_t extents[] = { CONFIG_NAND_BOOTEXTENT };
#elif defined(CONFIG_NAND_BOOTEXTENT_LIST) && defined(CONFIG_BOOTM_IMAGE)
const boot_extent_t extents[] = CONFIG_NAND_BOOTEXTENT_LIST;
#else
const boot_extent_t extents[] = { NULL };
#endif /* defined(CONFIG_NAND_BOOTEXTENT) */
return block_load(buffer,
index,
ARRAY_SIZE(extents),
extents,
"NAND",
nand_read_block,
NAND_BLOCK_SIZE);
}
#endif /* defined(CFG_GPMC_NAND) */
/*
* void do_arm_boot()
*
* Description:
* This routine attempts to boot the system from the specified
* boot device after copying the secondary-program loader into
* the specified memory buffer.
*
* Input(s):
* buffer - A pointer to storage for the secondary program
* loader.
* type - The boot device to boot from.
*
* Output(s):
* buffer - A pointer to the secondary program loader.
*
* Returns:
* N/A
*
*/
static void do_arm_boot(uint8_t *buffer, u32 type)
{
int index = BOOTM_INDEX_START;
int status = BOOTM_STATUS_FAILURE;
device_load load_image = NULL;
/* Determine, based on the boot setting, the source we are going
* to attempt to boot from.
*/
switch (type) {
#if ((defined(CFG_MMC_ONENAND) || defined (CFG_MMC_NAND)) && defined(CFG_CMD_FAT))
case MMC_NAND:
case MMC_ONENAND:
load_image = fat_load;
break;
#endif
#if defined(CFG_GPMC_ONENAND)
case GPMC_ONENAND:
load_image = onenand_load;
break;
#endif
#if defined(CFG_GPMC_NAND)
case GPMC_NAND:
load_image = nand_load;
break;
#endif
default:
goto fail;
}
/* Attempt to load and boot any configured images until we
* successfully boot or until we've exhausted all possible images.
*/
do {
status = load_image(buffer, &index);
if (bootm_load_successful(status)) {
status = bootm(buffer);
}
} while (bootm_load_continue(status, index));
fail:
return;
}
void start_armboot (void)
{
const u32* devices_list = NULL;
int devIdx;
int boot_devices_count = get_boot_devices_list(&devices_list);
const char * boot_name = NULL;
init_fnc_t **init_fnc_ptr;
uint8_t *buffer = (uint8_t *)CFG_LOADADDR;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
goto fail;
}
}
misc_init_r();
for( devIdx = 0; devIdx < boot_devices_count; devIdx++ )
{
u32 boot_dev = devices_list[devIdx];
switch (boot_dev) {
case MMC_NAND:
case MMC_ONENAND:
boot_name = "MMC";
break;
case GPMC_ONENAND:
boot_name = "OneNAND";
break;
case GPMC_NAND:
boot_name = "NAND";
break;
case USB_PERIPHERAL:
boot_name = "";
printf("Trying load from USB\n");
do_usb(); /* check for usb download */
break;
default:
boot_name = "<unknown>";
break;
}
debug("Trying X-Loader on %s\n", boot_name);
/* This should never return. */
do_arm_boot(buffer, boot_dev);
}
/* If for some reason it does, we have only one choice, hang. */
puts("Images exhausted\n");
fail:
hang();
}
void hang (void)
{
/* call board specific hang function */
board_hang();
/* if board_hang() returns, hange here */
puts("X-Loader hangs\n");
for (;;);
}