/*
 * (C) Copyright 2000
 * Wolfgang Denk, DENX Software Engineering, wd@denx.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 <ppcboot.h>
#include <command.h>
#include <malloc.h>
#include <devices.h>
#ifdef CONFIG_8xx
#include <mpc8xx.h>
#endif
#if (CONFIG_COMMANDS & CFG_CMD_IDE)
#include <ide.h>
#endif
#if (CONFIG_COMMANDS & CFG_CMD_SCSI)
#include <scsi.h>
#endif
#if (CONFIG_COMMANDS & CFG_CMD_KGDB)
#include <kgdb.h>
#endif
#ifdef CONFIG_STATUS_LED
#include <status_led.h>
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#include <net.h>
#endif
#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG)
#include <cmd_bedbug.h>
#endif
#ifdef CFG_ALLOC_DPRAM
#include <commproc.h>
#endif
#include <version.h>

static char *failed = "*** failed ***\n";

#ifdef	CONFIG_PCU_E
extern flash_info_t flash_info[];
#endif

#if defined(CFG_ENV_IS_IN_FLASH)
# ifndef  CFG_ENV_ADDR
#  define CFG_ENV_ADDR	(CFG_FLASH_BASE + CFG_ENV_OFFSET)
# endif
# ifndef  CFG_ENV_SIZE
#  define CFG_ENV_SIZE	CFG_ENV_SECT_SIZE
# endif
# if (CFG_ENV_ADDR >= CFG_MONITOR_BASE) && \
     (CFG_ENV_ADDR+CFG_ENV_SIZE) < (CFG_MONITOR_BASE + CFG_MONITOR_LEN)
#  define ENV_IS_EMBEDDED
# endif
#endif	/* CFG_ENV_IS_IN_FLASH */
#if ( ((CFG_ENV_ADDR+CFG_ENV_SIZE) < CFG_MONITOR_BASE) || \
      (CFG_ENV_ADDR >= (CFG_MONITOR_BASE + CFG_MONITOR_LEN)) ) || \
    defined(CFG_ENV_IS_IN_NVRAM)
#define	TOTAL_MALLOC_LEN	(CFG_MALLOC_LEN + CFG_ENV_SIZE)
#else
#define	TOTAL_MALLOC_LEN	CFG_MALLOC_LEN
#endif

/*
 * Begin and End of memory area for malloc(), and current "brk"
 */
static	ulong	mem_malloc_start = 0;
static	ulong	mem_malloc_end	 = 0;
static	ulong	mem_malloc_brk	 = 0;

/*
 * The Malloc area is immediately below the monitor copy in DRAM
 */
static void mem_malloc_init (ulong dest_addr)
{
	mem_malloc_end	  = dest_addr;
	mem_malloc_start  = dest_addr - TOTAL_MALLOC_LEN;
	mem_malloc_brk    = mem_malloc_start;

	memset ((void *)mem_malloc_start, 0, mem_malloc_end - mem_malloc_start);
}

void *sbrk (ptrdiff_t increment)
{
	ulong old = mem_malloc_brk;
	ulong new = old + increment;

	if ((new < mem_malloc_start) ||
	    (new > mem_malloc_end) ) {
		return (NULL);
	}
	mem_malloc_brk = new;
	return ((void *)old);
}

char *
strmhz(char *buf, long hz)
{
    long l, n;
    long m;

    n = hz / 1000000L;

    l = sprintf (buf, "%ld", n);

    m = (hz % 1000000L) / 1000L;

    if (m != 0)
	sprintf (buf+l, ".%03ld", m);

    return (buf);
}

/*
 * Breathe some life into the board...
 *
 * Initialize an SMC for serial comms, and carry out some hardware
 * tests.
 *
 * The first part of initialization is running from Flash memory;
 * its main purpose is to initialize the RAM so that we
 * can relocate the monitor code to RAM.
 */
void
board_init_f (ulong bootflag)
{
    bd_t	*bd;
    ulong	reg, len;
    int		board_type;
    ulong	addr_moni, addr_sp;
    ulong	dram_size;
    int		i, baudrate;
    char	*s, *e;
    uchar	tmp[64];	/* long enough for environment variables */
    /* Pointer to initial global data area */
    init_data_t *idata = (init_data_t *)(CFG_INIT_RAM_ADDR + CFG_INIT_DATA_OFFSET);

#if defined(CONFIG_BOARD_PRE_INIT) || \
    defined(CONFIG_ADCIOP)    || \
    defined(CONFIG_AR405)     || \
    defined(CONFIG_CANBT)     || \
    defined(CONFIG_CPCI405)   || \
    defined(CONFIG_CPCIISER4) || \
    defined(CONFIG_CU824)     || \
    defined(CONFIG_DASA_SIM)  || \
    defined(CONFIG_EVB64260)  || \
    defined(CONFIG_LWMON)     || \
    defined(CONFIG_MPC8260ADS)|| \
    defined(CONFIG_OCRTC)     || \
    defined(CONFIG_PIP405)    || \
    defined(CONFIG_RPXSUPER)  || \
    defined(CONFIG_WALNUT405)
    board_pre_init();        /* very early board init code (fpga boot, etc.) */
#endif

#if defined(CONFIG_8260)

    get_8260_clks();

#else	/* !CONFIG_8260 */

    /* CPU Clock Speed */
    idata->cpu_clk = get_gclk_freq ();		/* in  Hz */

#endif	/* CONFIG_8260 */

    /*
     * we need the timebase for udelay() in DRAM setup,
     * and in case we need to read the environment from  EEPROM
     */
    init_timebase ();

#ifdef CFG_ALLOC_DPRAM
    /*
     * set up dual port ram alloc area
     * if used, it is needed by serial, scc and i2c.
     */
    dpram_init ();
#endif

#if defined(CONFIG_HYMOD)
    /*
     * This initialisation is for very early stuff that requires the i2c
     * driver (which needs to know the clocks, to calculate the dividers,
     * and timebase, for udelay(), and Dual Port RAM on the 8xx platform)
     */
    board_postclk_init();
#endif

    env_init (idata);

    i = getenv_r ("baudrate", tmp, sizeof(tmp));
    baudrate = (i > 0) ? (int)simple_strtoul(tmp, NULL, 10) : CONFIG_BAUDRATE;

    /* set up serial port */
    serial_init (idata->cpu_clk, baudrate);

    idata->have_console = 1;

    /* Initialize the console (before the relocation) */
    console_init_f ();

    /* now we can use standard printf/puts/getc/tstc functions */
    display_options();

#if defined(CONFIG_8260)
    prt_8260_rsr();
    prt_8260_clks();
#endif	/* CONFIG_8260 */

    puts ("CPU:   ");					/* Check CPU		*/

    if (checkcpu(idata->cpu_clk) < 0) {
    	puts (failed);
	hang();
    }

#if defined(CONFIG_WATCHDOG)
    puts ("       Watchdog enabled\n");
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    puts ("Board: ");					/* Check Board		*/

    if ((board_type = checkboard()) < 0) {
    	puts (failed);
	hang();
    }

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

#if defined(CONFIG_COGENT) || defined(CONFIG_SXNI855T) || \
    defined(CONFIG_RSD_PROTO) || defined(CONFIG_HYMOD)
    /* miscellaneous platform dependent initialisations */
    if (misc_init_f() < 0) {
	puts (failed);
	hang();
    }
#endif

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    puts ("DRAM:  ");

    if ((dram_size = initdram (board_type)) > 0) {
	printf ("%2ld MB\n", dram_size >> 20);
    } else {
	puts (failed);
	hang();
    }

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    /*
     * Now that we have DRAM mapped and working, we can
     * relocate the code and continue running from DRAM.
     *
     * First reserve memory for monitor code at end of DRAM.
     */
    len = get_endaddr() - CFG_MONITOR_BASE;

    if (len > CFG_MONITOR_LEN) {
	printf ("*** PPCBoot size %ld > reserved memory (%d)\n",
		len, CFG_MONITOR_LEN);
	hang();
    }

    if (CFG_MONITOR_LEN > len)
	len = CFG_MONITOR_LEN;

#ifdef CONFIG_PRAM		/* reserve protected RAM at top of memory */
    i = getenv_r ("pram", tmp, sizeof(tmp));
    reg = (i > 0) ? simple_strtoul(tmp, NULL, 10) : CONFIG_PRAM;
# ifdef DEBUG
    printf ("Reserving %ldk for protected RAM\n", reg);
# endif
    len += (reg << 10);		/* size is in kB */
#endif	/* CONFIG_PRAM */

    /* round up to next 4 kB limit */
    len = (len + (4096 - 1)) & ~(4096 - 1);

    addr_moni = CFG_SDRAM_BASE + dram_size - len;

#ifdef DEBUG
    printf ("Relocating to: %08lx, %d bytes for malloc()\n",
    	addr_moni, TOTAL_MALLOC_LEN);
#endif

    /*
     * Then we (permanently) allocate a Board Info struct.
     *
     * We leave room for the malloc() arena.
     */
    len = sizeof(bd_t) + TOTAL_MALLOC_LEN;

    bd = (bd_t *)(addr_moni - len);

#ifdef DEBUG
    printf ("Board Info at: %08lx\n", (ulong)bd);
#endif

    /*
     * Finally, we set up a new (bigger) stack.
     *
     * Leave some safety gap for SP, force alignment on 16 byte boundary
     */
    addr_sp  = (ulong)bd - 128;
    addr_sp &= ~0xF;

    /*
     * Save local variables to board info struct
     */

    bd->bi_memstart    = CFG_SDRAM_BASE;  /* start of  DRAM memory		*/
    bd->bi_memsize     = dram_size;	  /* size  of  DRAM memory in bytes	*/

#ifdef CONFIG_IP860
    bd->bi_sramstart   = SRAM_BASE;	  /* start of  SRAM memory		*/
    bd->bi_sramsize    = SRAM_SIZE;	  /* size  of  SRAM memory		*/
#else
    bd->bi_sramstart   = 0; /* FIXME */	  /* start of  SRAM memory		*/
    bd->bi_sramsize    = 0; /* FIXME */	  /* size  of  SRAM memory		*/
#endif

#if defined(CONFIG_8xx) || defined(CONFIG_8260)
    bd->bi_immr_base   = CFG_IMMR;	  /* base  of IMMR register		*/
#endif

    bd->bi_bootflags   = bootflag;	  /* boot / reboot flag (for LynxOS)	*/

    /* IP Address */
    bd->bi_ip_addr = 0;
    i = getenv_r ("ipaddr", tmp, sizeof(tmp));
    s = (i > 0) ? tmp : NULL;
    for (reg=0; reg<4; ++reg) {
    	ulong val = s ? simple_strtoul(s, &e, 10) : 0;
	bd->bi_ip_addr <<= 8;
	bd->bi_ip_addr  |= (val & 0xFF);
	if (s)
		s = (*e) ? e+1 : e;
    }

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    i = getenv_r ("ethaddr", tmp, sizeof(tmp));
    s = (i > 0) ? tmp : NULL;

#ifdef	CONFIG_MBX
    if (s == NULL)
	board_get_enetaddr(bd->bi_enetaddr);
    else
#endif
    for (reg=0; reg<6; ++reg) {
	bd->bi_enetaddr[reg] = s ? simple_strtoul(s, &e, 16) : 0;
	if (s)
		s = (*e) ? e+1 : e;
    }
#ifdef	CONFIG_HERMES
    if ((board_type >> 16) == 2)
	bd->bi_ethspeed = board_type & 0xFFFF;
    else
        bd->bi_ethspeed = 0xFFFF;
#endif

#ifdef CONFIG_NX823
	load_sernum_ethaddr(bd);
#endif

#ifdef CONFIG_EVB64260
    /* handle the 2nd and 3rd ethernet addresses */

    i = getenv_r ("eth1addr", tmp, sizeof(tmp));
    s = (i > 0) ? tmp : NULL;

    for (reg = 0; reg < 6; ++reg) {
	bd->bi_enet1addr[reg] = s ? simple_strtoul(s, &e, 16) : 0;
	if (s)
		s = (*e) ? e+1 : e;
    }

    i = getenv_r ("eth2addr", tmp, sizeof(tmp));
    s = (i > 0) ? tmp : NULL;

    for (reg = 0; reg < 6; ++reg) {
	bd->bi_enet2addr[reg] = s ? simple_strtoul(s, &e, 16) : 0;
	if (s)
		s = (*e) ? e+1 : e;
    }
#endif

    bd->bi_intfreq  = idata->cpu_clk;			/* Internal Freq, in Hz	*/
    bd->bi_busfreq  = get_bus_freq(idata->cpu_clk);	/* Bus Freq,      in Hz	*/
#if defined(CONFIG_8260)
    bd->bi_cpmfreq = idata->cpm_clk;
    bd->bi_brgfreq = idata->brg_clk;
    bd->bi_sccfreq = idata->scc_clk;
    bd->bi_vco	   = idata->vco_out;
#endif /* CONFIG_8260 */

    bd->bi_baudrate = baudrate;			/* Console Baudrate		*/

#ifdef CFG_EXTBDINFO
    strncpy(bd->bi_s_version, "1.2", sizeof(bd->bi_s_version));
    strncpy(bd->bi_r_version, PPCBOOT_VERSION, sizeof(bd->bi_r_version));

    bd->bi_procfreq    = idata->cpu_clk; /* Processor Speed, In Hz */
    bd->bi_plb_busfreq = bd->bi_busfreq;
#ifdef CONFIG_405GP
    bd->bi_pci_busfreq = get_PCI_freq();
#endif
#endif

    /* Function pointers must be added after code relocation */
#if 0
    bd->bi_mon_fnc.getc   = NULL;	/* Addr of getc() from Console	*/
    bd->bi_mon_fnc.tstc   = NULL;	/* Addr of tstc() from Console	*/
    bd->bi_mon_fnc.putc   = NULL;	/* Addr of putc()   to Console	*/
    bd->bi_mon_fnc.putstr = NULL;	/* Addr of putstr() to Console	*/
    bd->bi_mon_fnc.printf = NULL;	/* Addr of printf() to Console	*/
    bd->bi_mon_fnc.install_hdlr = NULL;
    bd->bi_mon_fnc.free_hdlr    = NULL;
    bd->bi_mon_fnc.malloc	= NULL;
    bd->bi_mon_fnc.free		= NULL;
#endif

#ifdef DEBUG
    printf ("New Stack Pointer is: %08lx\n", addr_sp);
#endif

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    relocate_code (addr_sp, bd, addr_moni);

    /* NOTREACHED - relocate_code() does not return */
}

void    board_init_r  (bd_t *bd, ulong dest_addr)
{
    char	*s;
    cmd_tbl_t	*cmdtp;
    ulong	flash_size;
    ulong	reloc_off = dest_addr - CFG_MONITOR_BASE;
    /* Pointer to initial global data area */
    init_data_t *idata = (init_data_t *)(CFG_INIT_RAM_ADDR + CFG_INIT_DATA_OFFSET);

    extern void malloc_bin_reloc (ulong);

#ifdef DEBUG
    printf ("Now running in RAM - dest_addr = 0x%08lx\n", dest_addr);
#endif

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    /*
     * Save a global pointer to the board info struct
     */
    bd_ptr = bd ;
    /*
     * Set the monitor function pointer to DPRAM init data
     * and relocate at least the I/O function pointers
     */
    bd->bi_mon_fnc = &idata->bi_mon_fnc;
    {	ulong addr;

	addr = (ulong)(idata->bi_mon_fnc.getc  ) + reloc_off;
	idata->bi_mon_fnc.getc   = (int  (*)(void))addr;

	addr = (ulong)(idata->bi_mon_fnc.tstc  ) + reloc_off;
	idata->bi_mon_fnc.tstc   = (int  (*)(void))addr;

	addr = (ulong)(idata->bi_mon_fnc.putc  ) + reloc_off;
	idata->bi_mon_fnc.putc   = (void (*)(const char c))addr;

	addr = (ulong)(idata->bi_mon_fnc.puts  ) + reloc_off;
	idata->bi_mon_fnc.puts   = (void (*)(const char *s))addr;

	addr = (ulong)(idata->bi_mon_fnc.printf) + reloc_off;
	idata->bi_mon_fnc.printf = (void (*)(const char *fmt, ...))addr;
    }

    /*
     * We have to relocate the command table manually
     */
    for (cmdtp=&cmd_tbl[0]; cmdtp->name; cmdtp++) {
	ulong addr;

	addr = (ulong)(cmdtp->cmd) + reloc_off;
#if 0
	printf ("Command \"%s\": 0x%08lx => 0x%08lx\n",
	    cmdtp->name, (ulong)(cmdtp->cmd), addr);
#endif
	cmdtp->cmd = (void (*)(struct cmd_tbl_s*,bd_t*,int,int,char*[]))addr;

	addr = (ulong)(cmdtp->name) + reloc_off;
	cmdtp->name = (char *)addr;

	if (cmdtp->usage) {
		addr = (ulong)(cmdtp->usage) + reloc_off;
		cmdtp->usage = (char *)addr;
	}
#ifdef	CFG_LONGHELP
	if (cmdtp->help) {
		addr = (ulong)(cmdtp->help) + reloc_off;
		cmdtp->help = (char *)addr;
	}
#endif
    }

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

#if defined(CONFIG_IP860) || defined(CONFIG_PCU_E)
    icache_enable();	/* it's time to enable the instruction cache */
#endif

    asm ("sync ; isync");

    /*
     * Setup trap handlers
     */
    trap_init(dest_addr);

    puts ("FLASH: ");

    if ((flash_size = flash_init ()) > 0) {
    	if (flash_size >= (1 << 20)) {
		printf ("%2ld MB\n", flash_size >> 20);
	} else {
		printf ("%2ld kB\n", flash_size >> 10);
	}
    } else {
	puts (failed);
	hang();
    }
    bd->bi_flashstart = CFG_FLASH_BASE;  /* update start of FLASH memory	*/
    bd->bi_flashsize  = flash_size;   /* size of FLASH memory (final value)	*/
#if defined(CONFIG_PCU_E)
    bd->bi_flashoffset = 0;
#elif CFG_MONITOR_BASE == CFG_FLASH_BASE
    bd->bi_flashoffset = CFG_MONITOR_LEN; /* reserved area for startup monitor	*/
#else
    bd->bi_flashoffset = 0;
#endif

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    /* initialize higher level parts of CPU like time base and timers */
    cpu_init_r (bd);

    bd->bi_mon_fnc->malloc = malloc;
    bd->bi_mon_fnc->free   = free;

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    /* initialize malloc() area */
    mem_malloc_init (dest_addr);
    malloc_bin_reloc (reloc_off);

#ifdef CONFIG_SPI
# if !defined(CFG_ENV_IS_IN_EEPROM)
    spi_init_f ();
# endif
    spi_init_r();
#endif

    /* relocate environment function pointers etc. */
    env_relocate (reloc_off);

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

#if defined(CONFIG_COGENT)	|| \
    defined(CONFIG_CPCI405)	|| \
    defined(CONFIG_EVB64260)	|| \
    defined(CONFIG_HYMOD)	|| \
    defined(CONFIG_LWMON)	|| \
    defined(CONFIG_PCU_E) || \
    defined(CONFIG_MISC_INIT_R)
    /* miscellaneous platform dependent initialisations */
    misc_init_r(bd);
#endif

#ifdef	CONFIG_HERMES
    if (bd->bi_ethspeed != 0xFFFF)
    	hermes_start_lxt980((int)bd->bi_ethspeed);
#endif

#if (CONFIG_COMMANDS & CFG_CMD_NET) && ( \
    defined(CONFIG_CCM)		|| \
    defined(CONFIG_IP860)	|| \
    defined(CONFIG_IVML24)	|| \
    defined(CONFIG_IVMS8)	|| \
    defined(CONFIG_LWMON)	|| \
    defined(CONFIG_MPC8260ADS)	|| \
    defined(CONFIG_PCU_E)	|| \
    defined(CONFIG_RPXSUPER)	|| \
    defined(CONFIG_SPD823TS)	)

# if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
# endif	/* CONFIG_WATCHDOG */
# ifdef DEBUG
    puts ("Reset Ethernet PHY\n");
# endif
    reset_phy ();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) && !(CONFIG_COMMANDS & CFG_CMD_IDE)
# if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
# endif	/* CONFIG_WATCHDOG */
    puts ("PCMCIA:");
    pcmcia_init();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_KGDB)
# if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
# endif	/* CONFIG_WATCHDOG */
    puts ("KGDB:  ");
    kgdb_init();
#endif

#ifdef DEBUG
    printf ("Monitor relocated to 0x%08lx\n", dest_addr);
#endif

    /*
     * Enable Interrupts
     */
    interrupt_init (bd);
#ifdef CONFIG_STATUS_LED
    status_led_set (STATUS_LED_BOOT, STATUS_LED_BLINKING);
#endif

    udelay(20);

    set_timer(0);

    /* Insert function pointers now that we have relocated the code */

    bd->bi_mon_fnc->install_hdlr = irq_install_handler;
    bd->bi_mon_fnc->free_hdlr    = irq_free_handler;

    /* Initialize from environment */
    if ((s = getenv("loadaddr")) != NULL) {
    	load_addr = simple_strtoul(s, NULL, 16);
    }
#if (CONFIG_COMMANDS & CFG_CMD_NET)
    if ((s = getenv("bootfile")) != NULL) {
	copy_filename (BootFile, s, sizeof(BootFile));
    }
#endif	/* CFG_CMD_NET */

#if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */

    /* Initialize other board modules */
#ifdef CONFIG_PCI
    /*
     * Do pci configuration
     */
    pci_init(bd);
#endif

#if (CONFIG_COMMANDS & CFG_CMD_IDE)
# if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
# endif	/* CONFIG_WATCHDOG */
# ifdef	CONFIG_IDE_PCCARD
    puts ("PCMCIA:");
# else
    puts ("IDE:   ");
#endif
    ide_init(bd);
#endif	/* CFG_CMD_IDE */

#if (CONFIG_COMMANDS & CFG_CMD_SCSI)
# if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
# endif	/* CONFIG_WATCHDOG */
    scsi_init();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG)
# if defined(CONFIG_WATCHDOG)
    watchdog_reset ();
# endif	/* CONFIG_WATCHDOG */
    bedbug_init();
#endif

/** LEAVE THIS HERE **/
    /* Initialize devices */
    devices_init (reloc_off);

    /* Initialize the console (after the relocation and devices init) */
    console_init_r (reloc_off);
    putc('\n');
/**********************/

#ifdef CONFIG_PRAM
    /*
     * Export available size of memory for Linux,
     * taking into account the protected RAM at top of memory
     */
    {
	ulong	pram;
	char	*s;
	uchar	memsz[32];

	if ((s = getenv("pram")) != NULL) {
		pram = simple_strtoul(s, NULL, 10);
	} else {
		pram = CONFIG_PRAM;
	}
	sprintf (memsz, "%ldk", (bd->bi_memsize / 1024) - pram);
	setenv ("mem", memsz);
    }
#endif

    /* Initialization complete - start the monitor */

    /* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
#if defined(CONFIG_WATCHDOG)
	watchdog_reset ();
#endif	/* CONFIG_WATCHDOG */
	main_loop (bd);
    }

    /* NOTREACHED - no way out of command loop except booting */
}

void hang(void)
{
	puts ("### ERROR ### Please RESET the board ###\n");
	for (;;);
}
