/*
 * (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 <watchdog.h>
#include <command.h>
#include <malloc.h>
#include <devices.h>
#include <syscall.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
#include <net.h>
#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG)
#include <cmd_bedbug.h>
#endif
#ifdef CFG_ALLOC_DPRAM
#include <commproc.h>
#endif
#include <version.h>
#if defined(CONFIG_BAB750)
#include <w83c553f.h>
#endif

#if (CONFIG_COMMANDS & CFG_CMD_DOC)
void doc_init (void);
#endif

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;

/************************************************************************
 * Utilities								*
 ************************************************************************
 */

/*
 * 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);
}

static void syscalls_init(int reloc_off)
{
    ulong *addr;
	
    addr = (ulong *)syscall_tbl;
    syscall_tbl[SYSCALL_MALLOC] = (void *)malloc;
    syscall_tbl[SYSCALL_FREE]   = (void *)free;

    syscall_tbl[SYSCALL_INSTALL_HDLR] = (void *)irq_install_handler;
    syscall_tbl[SYSCALL_FREE_HDLR]    = (void *)irq_free_handler;

    addr = (ulong*)0xc00;    /* syscall ISR addr */

    /* patch ISR code */
    *addr++ |= (ulong)syscall_tbl >> 16;
    *addr++ |= (ulong)syscall_tbl & 0xFFFF;
    *addr++ |= NR_SYSCALLS >> 16;
    *addr++ |= NR_SYSCALLS & 0xFFFF;
}

/************************************************************************
 *
 * This is the first part of the initialization sequence that is
 * implemented in C, but still running from ROM.
 * 
 * The main purpose is to provide a (serial) console interface as
 * soon as possible (so we can see any error messages), and to
 * initialize the RAM so that we can relocate the monitor code to
 * RAM.
 *
 * Be aware of the restrictions: global data is read-only, BSS is not
 * initialized, and stack space is limited to a few kB.
 *
 ************************************************************************
 */

void
board_init_f (ulong bootflag)
{
    bd_t	*bd;
    ulong	reg, len, addr, addr_sp, dram_size;
    int		i, baudrate, board_type;
    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_CU824)     || \
    defined(CONFIG_EVB64260)  || \
    defined(CONFIG_LWMON)     || \
    defined(CONFIG_MPC8260ADS)|| \
    defined(CONFIG_RPXSUPER)  || \
    defined(CONFIG_EP8260)    || \
    defined(CONFIG_WALNUT405) || \
    defined(CONFIG_W7O)
    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) || defined(CONFIG_BOARD_POSTCLK_INIT)
    /*
     * 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 ();

#ifdef DEBUG
    if(sizeof(init_data_t) > CFG_INIT_DATA_SIZE) {
	printf ("PANIC: sizeof(init_data_t)=%d > CFG_INIT_DATA_SIZE=%d\n",
		sizeof(init_data_t), CFG_INIT_DATA_SIZE);
	hang();
    }
#endif	/* DEBUG */

    /* 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_HW_WATCHDOG)
    puts ("       Hardware watchdog enabled\n");
#endif	/* CONFIG_HW_WATCHDOG */
    WATCHDOG_RESET();

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

    WATCHDOG_RESET();

    puts ("DRAM:  ");

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

#if defined(CFG_DRAM_TEST)
    if (testdram() != 0){
	hang();
    }
#endif /* CFG_DRAM_TEST */

    WATCHDOG_RESET();

    /*
     * Now that we have DRAM mapped and working, we can
     * relocate the code and continue running from DRAM.
     *
     * Reserve memory at end of RAM for (top down in that order):
     *	- protected RAM
     *	- LCD framebuffer
     *	- monitor code
     *	- board info struct
     */
    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;

    addr = CFG_SDRAM_BASE + dram_size;

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

    /* round down to next 4 kB limit */
    addr &= ~(4096 - 1);
#ifdef DEBUG
    printf ("Top of RAM usable for PPCBoot at: %08lx\n", addr);
#endif

#ifdef CONFIG_LCD	
    /*
     * reserve memory for LCD display (always full pages)
     */
    addr = lcd_setmem (addr);
    idata->lcd_base = addr;
#endif	/* CONFIG_LCD */

    /*
     * reserve memory for PPCBoot code, data & bss
     * round down to next 4 kB limit
     */
    addr -= len;
    addr &= ~(4096 - 1);

#ifdef DEBUG
    printf ("Reserving %ldk for PPCBoot at: %08lx\n",
	len>>10, addr);
#endif

    /*
     * reserve memory for malloc() arena
     */
    addr_sp = addr - TOTAL_MALLOC_LEN;
#ifdef DEBUG
    printf ("Reserving %dk for malloc() at: %08lx\n",
	TOTAL_MALLOC_LEN>>10, addr_sp);
#endif

    /*
     * (permanently) allocate a Board Info struct
     */
    addr_sp -= sizeof(bd_t);
    bd = (bd_t *)addr_sp;
#ifdef DEBUG
    printf ("Reserving %d Bytes for Board Info at: %08lx\n",
	sizeof(bd_t), addr_sp);
#endif

    /*
     * Finally, we set up a new (bigger) stack.
     *
     * Leave some safety gap for SP, force alignment on 16 byte boundary
     * Clear initial stack frame
     */
    addr_sp -=  16;
    addr_sp &= ~0xF;
    *((ulong *)addr_sp)-- = 0;
    *((ulong *)addr_sp)-- = 0;
#ifdef DEBUG
    printf ("Stack Pointer at: %08lx\n", addr_sp);
#endif

    /*
     * 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)	*/

    WATCHDOG_RESET();

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

#if defined (CONFIG_MBX) || defined (CONFIG_RPXCLASSIC) || defined(CONFIG_IAD210)
    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

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

    WATCHDOG_RESET();

    relocate_code (addr_sp, bd, addr);

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


/************************************************************************
 *
 * This is the next part if the initialization sequence: we are now
 * running from RAM and have a "normal" C environment, i. e. global
 * data can be written, BSS has been cleared, the stack size in not
 * that critical any more, etc.
 *
 ************************************************************************
 */

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;

    extern void malloc_bin_reloc (ulong);

#ifdef DEBUG
    printf ("Now running in RAM - PPCBoot at: %08lx\n", dest_addr);
#endif

    WATCHDOG_RESET();

    /*
     * Save a global pointer to the board info struct
     */
    bd_ptr = bd ;

    /*
     * 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 = (int (*)(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
    }

    WATCHDOG_RESET();

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

#if defined(CONFIG_BAB750)
    /*
     * Do pci configuration on BAB 750 _before_ the flash
     * is initialised, because we need the ISA bridge there.
     */
    pci_init(bd);
    /*
     * Initialise the ISA bridge
     */
    initialise_w83c553f();
#endif

    asm ("sync ; isync");

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

    puts ("FLASH: ");

    if ((flash_size = flash_init ()) > 0) {
#ifdef CFG_FLASH_CHECKSUM
	if (flash_size >= (1 << 20)) {
		printf ("%2ld MB", flash_size >> 20);
	} else {
		printf ("%2ld kB", flash_size >> 10);
	}
	/*
	 * Compute and print flash CRC if flashchecksum is set to 'y'
	 *
	 * NOTE: Maybe we should add some WATCHDOG_RESET()?	XXX
	 */
	s = getenv ("flashchecksum");
	if (s && (*s == 'y')) {
		printf ("  CRC: %08lX",
			crc32 ( 0,
				(const unsigned char *)CFG_FLASH_BASE,
				flash_size)
		);
	}
	putc('\n');
#else
	if (flash_size >= (1 << 20)) {
		printf ("%2ld MB\n", flash_size >> 20);
	} else {
		printf ("%2ld kB\n", flash_size >> 10);
	}
#endif	/* CFG_FLASH_CHECKSUM */
    } 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

    WATCHDOG_RESET();

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

    WATCHDOG_RESET();

    /* 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);

    /* IP Address */
    bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

    WATCHDOG_RESET();

#if defined(CONFIG_PCI) && !defined(CONFIG_BAB750)
    /*
     * Do pci configuration
     */
    pci_init(bd);
#endif

/** leave this here (after malloc(), environment and PCI are working) **/
    /* Initialize devices */
    devices_init (bd, reloc_off);

    /* allocate syscalls table (console_init_r will fill it in */
    syscall_tbl = (void**)malloc(NR_SYSCALLS*sizeof(void*));

    /* Initialize the console (after the relocation and devices init) */
    console_init_r (reloc_off);
/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
    syscalls_init(reloc_off);

#if defined(CONFIG_CCM)		|| \
    defined(CONFIG_COGENT)	|| \
    defined(CONFIG_CPCI405)	|| \
    defined(CONFIG_EVB64260)	|| \
    defined(CONFIG_HYMOD)	|| \
    defined(CONFIG_LWMON)	|| \
    defined(CONFIG_PCU_E)	|| \
    defined(CONFIG_W7O)		|| \
    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_EP8260)	|| \
    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)	)

    WATCHDOG_RESET();
# ifdef DEBUG
    puts ("Reset Ethernet PHY\n");
# endif
    reset_phy ();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) && !(CONFIG_COMMANDS & CFG_CMD_IDE)
    WATCHDOG_RESET();
    puts ("PCMCIA:");
    pcmcia_init();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_KGDB)
    WATCHDOG_RESET();
    puts ("KGDB:  ");
    kgdb_init();
#endif

#ifdef DEBUG
    printf ("PPCBoot relocated to %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 */

    /* 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 */

    WATCHDOG_RESET();

#if (CONFIG_COMMANDS & CFG_CMD_IDE)
    WATCHDOG_RESET();
# ifdef	CONFIG_IDE_8xx_PCCARD
    puts ("PCMCIA:");
# else
    puts ("IDE:   ");
#endif
    ide_init(bd);
#endif	/* CFG_CMD_IDE */

#if (CONFIG_COMMANDS & CFG_CMD_SCSI)
    WATCHDOG_RESET();
    puts ("SCSI:  ");
    scsi_init();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_DOC)
    WATCHDOG_RESET();
    puts ("DOC:   ");
    doc_init();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_NET) && defined(CONFIG_NET_MULTI)
    WATCHDOG_RESET();
    puts ("Net:   ");
    eth_initialize(bd);
#endif

#ifdef CONFIG_LAST_STAGE_INIT
    WATCHDOG_RESET();
    /*
     * Some parts can be only initialized if all others (like
     * Interrupts) are up and running (i.e. the PC-style ISA
     * keyboard).
     */
    last_stage_init ();
#endif

#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG)
    WATCHDOG_RESET();
    bedbug_init();
#endif

#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 (;;) {
	WATCHDOG_RESET();
	main_loop (bd);
    }

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

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