/*
 * TiVo initialization and setup code
 *
 * Copyright (C) 2000-2003 TiVo Inc. All Rights Reserved.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/bootmem.h>
#include <linux/tivoconfig.h>
#include <asm/bootinfo.h>
#include <asm/mipsregs.h>
#include <asm/reboot.h>
#include <asm/traps.h>
#include <asm/page.h>
#include <asm/delay.h>
#include <asm/bcache.h>
#include <asm/bcm7315.h>

extern void tivo_restart(char *command);
extern void tivo_halt(void);
extern void tivo_power_off(void);
extern void dump_tlb_all(void);

extern int panic_timeout;
extern struct ide_ops bcm_ide_ops;
extern struct bcache_ops bcm_bcops;
extern int _end;

char arcs_cmdline[CL_SIZE];
#define MTN_RB_RAC_PREFETCH_LIMIT       __preg32(0xFFE00410)

static void bcm_gpio0_reset(void)
{
	*BCM_GPIO_SEL0 &= 0xdf;
	barrier();
	*BCM_GIO_DIRLSB &= 0xfe;
	barrier();
	*BCM_GIO_OUTLSB &= 0xfe;
	barrier();
	mdelay(500);
}

static void bcm_gpio10_reset(void)
{
	*BCM_GPIO_SEL0 &= 0xbf;
	barrier();
	*BCM_GIO_DIRHSB &= 0xfb;
	barrier();
	*BCM_GIO_OUTHSB &= 0xfb;
	barrier();
	mdelay(500);
}

static void config5_reset(void)
{
	__asm__ __volatile__(
	       	"   .set    noreorder\n"
	       	"   .set    noat\n"
                "   .word   0x4001b005\n"
                "   nop\n"
                "   ori     $1,3\n"
                "   .word   0x4081b005\n"
	       	"   .set    at\n"
	       	"   .set    reorder\n"
		: : : "$1", "memory" );

	mdelay(500);
}

static void bcm_reboot(void)
{
	cli();
	/*
	 * Try to reset the board by guessing which GPIO controls the reset
	 * As long as the following GPIOs are always congigured as output there shouldn't be 
	 * any problem with trying one after the other till the reset happens.
	 */
	bcm_gpio0_reset();	// P1 reset, board reset

	bcm_gpio10_reset(); // P0 reset, board reset

	config5_reset();	// PX reset. bcm7315 reset
}

void __init tivo_setup(void)
{
#ifdef CONFIG_BLK_DEV_IDE
	ide_ops = &bcm_ide_ops;
#endif
	// uh huh... sure, it's const...
	*(unsigned long *)&mips_io_port_base = 0;
	_machine_restart = tivo_restart;
	_machine_halt = tivo_halt;
	_machine_power_off = tivo_power_off;
	panic_timeout = 1;
}

// Note the arguments are listed here to match up with what init_arch()
// thinks it passes.  Really the arguments come from the bootprom, and
// only the first argument is valid, as a pointer to a single
// NULL-terminated string
//
int __init prom_init(int argc, char **argv, char **envp, int *prom_vec)
{
	unsigned long mem_end, mem_start = 0;
	unsigned long *bblock;
	TivoConfigValue brev, brid;
	unsigned long magic = (unsigned long)envp;
	extern void tivo_generic_setup(char *, int);
	extern void (*tivo_board_reboot)(void);
	char *brev_param;
        unsigned int prefetch_roof = 0;
        int i = 0;

	// mips-linux expects FR bit cleared, this also makes sure the other
	// bits are in a sane state, but leave BEV on until we get our
	// exception handler installed, so that PROM gets any (unexpected)
	// exceptions
	write_32bit_cp0_register(CP0_STATUS, ST0_CU3 | ST0_BEV);
	write_32bit_cp0_register(CP0_WATCHLO, 0);
	write_32bit_cp0_register(CP0_WATCHHI, 0);

	write_32bit_cp0_register($22, read_32bit_cp0_register($22) | (1 << 20));

	mips_machgroup = MACH_GROUP_TIVO;

	// set this up now, in case of early panic
	tivo_board_reboot = bcm_reboot;

        if (argc != 0) {
            const char *cl = (const char *)argc;
            strncpy(arcs_cmdline, cl, CL_SIZE - 1);
        }

        if (magic == 0x5469566f) {
            int fCanOverride = 0;
#ifdef CONFIG_TIVO_DEVEL
            fCanOverride = 1;
#endif
            brev = (TivoConfigValue)((unsigned long)argv & 0xffffff00);
            brid = (TivoConfigValue)((unsigned long)argv & 0x000000ff);
            if (brev == 0 || brev == 0xffffff00) {
                prefetch_roof = (*MTN_RB_RAC_PREFETCH_LIMIT >> 8)& 0x000000FF;
                barrier();
                for (i = 128; i; i = i/2) {
                    if (prefetch_roof & i) {
                        prefetch_roof &= i;
                        break;
                    }
                }

                switch (prefetch_roof) {
                  case 32:
                    brev = kTivoConfigBoardIDUnknown32M;
                    break;
                  case 64:
                    brev = kTivoConfigBoardIDUnknown64M;
                    break;
                  default:
                    brev = kTivoConfigBoardIDUnknown;
                    break;
                }
                fCanOverride = 1;
            }
            if (fCanOverride) {
                brev_param = strstr(arcs_cmdline, "brev=");
                if (brev_param) {
                    brev = simple_strtoul(brev_param + 5, NULL, 0) << 8;
                }
            }

            bblock = (unsigned long *)prom_vec;
#ifdef CONFIG_BLK_DEV_INITRD
            if (bblock[6] > bblock[5] && !(bblock[5] & 3)) {
                initrd_start = PAGE_ALIGN((unsigned long)&_end);
                initrd_end = initrd_start + bblock[6] - bblock[5];
                mem_start = virt_to_phys((void *)PAGE_ALIGN(initrd_end));
            }
#endif
        } else {
            // the console is not up yet, so no point in trying to
            // print a panic message...
            bcm_reboot();
            // not reached
            return 0;
        }

	InitTivoConfig( brev );
	SetTivoConfig( kTivoConfigBoardID, brev | brid );

	if( GetTivoConfig( kTivoConfigMemSize, &mem_end ) != 0 )
	{
	    printk( "Couldn't get the memory size configuration\n" );
	    // Use a reasonable default of 32MB
	    mem_end = 32 * 1024 * 1024;
	}

	// Use /dev/ttyS1 as the default kernel console if none specified
	if (!strstr(arcs_cmdline, "console=")) {
		size_t clsize = strlen(arcs_cmdline);

		if (clsize && clsize < CL_SIZE - 1)
			*(arcs_cmdline + clsize++) = ' ';
		strncat(arcs_cmdline, "console=1", CL_SIZE - 1 - clsize);
	}
#ifdef CONFIG_TIVO_DEVEL
	if (!strstr(arcs_cmdline, "dsscon=")) {
		size_t clsize = strlen(arcs_cmdline);
		strncat(arcs_cmdline, " dsscon=true", CL_SIZE - 1 - clsize);
	}

	if (!strstr(arcs_cmdline, "HpkImpl=")) {
		size_t clsize = strlen(arcs_cmdline);
		strncat(arcs_cmdline, " HpkImpl=Gen04", CL_SIZE - 1 - clsize);
	}
#endif

	add_memory_region(mem_start, mem_end - mem_start, BOOT_MEM_RAM);

	tivo_generic_setup(arcs_cmdline, CL_SIZE);

	return 0;
}

void __init prom_fixup_mem_map(unsigned long start, unsigned long end)
{
}

// Free the 2nd page of RAM.  The 1st is reserved for exception vectors,
// and the kernel loads starting at 0x80002000, leaving an unused page
// at 0x80001000.  The kernel cannot load at 0x80001000 because it has
// a 2-page alignment restriction, due to one of the sections being
// 2-page aligned.
//
// It would be more correct to add a linker script symbol for where the
// kernel loads, but for now we always load at 0x80002000, and the linker
// script is already complex enough...
//
void __init prom_free_prom_memory(void)
{
	unsigned long page2 = 0x80001000;
	
	ClearPageReserved(virt_to_page(page2));
	set_page_count(virt_to_page(page2), 1);
	free_page(page2);
}

int __init page_is_ram(unsigned long pagenr)
{
	return 1;
}

static int bcm_be_board_handler(struct pt_regs *regs, int is_fixup)
{
	dump_tlb_all();
	return MIPS_BE_FATAL;
}

void __init bus_error_init(void)
{
	be_board_handler = bcm_be_board_handler;
}

const char *get_system_type(void)
{
#if defined(CONFIG_TIVO_UMA2C)
	return "TiVo UMA2C board";
#else
	return "TiVo unknown platform";
#endif
}

