/*
 * Copyright 2010 Allied Telesis Labs
 *
 * 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 <asm/addrspace.h>
#include <epi3.h>
#include <bootmenu.h>
#include <i2c.h>

extern uint x200_x400_fe_get_cpu_cap(void);
extern uint x200_x400_fe_get_chipid(void);

int bootflash_attached = 0;

/* atl_bootrom_board_attached()
 *
 * used by bootmenu to determine weather or not to print
 * option '5' (EPROM) in upgrade bootloader menu
 */

int atl_bootrom_board_attached(void)
{
	return bootflash_attached;
}

/* atl_get_offset_from_base()
 *
 * not needed on this product
 */

u32 atl_get_offset_from_base(void)
{
	return 0;
}

/* atl_get_env_addr()
 *
 * return address of env for places where a macro can't be used.
 */

u32 atl_get_env_addr(void)
{
	return CONFIG_ENV_ADDR;
}

phys_size_t initdram(int board_type)
{
	/* Sdram is setup by assembler code */
	/* If memory could be changed, we should return the true value here */
	return 128 * 1024 * 1024; /* should be 128M must fix */
}

u32 x200_x400_fe_serial_base_clock(void)
{
    uint32_t cap;
    uint32_t pid;
    /*
     * Read cap register to determine the system clks for c31x.
     */
#define X200_X400_FE_CHIPID_REG		0x18000000
#define X200_X400_FE_CAP_REG		0x18000004
#define CAP_REG_CORECAP_CORE_CLK_FREQ_SEL	(1<<22)
    pid = *(u32 *)KSEG1ADDR(X200_X400_FE_CHIPID_REG);
    cap = *(u32 *)KSEG1ADDR(X200_X400_FE_CAP_REG);
    if ((pid & 0x0ff0) == 0x0310) {
        if (cap & CAP_REG_CORECAP_CORE_CLK_FREQ_SEL) {
            return 133000000;
        } else {
            return 100000000;
        }
    }
    return 133000000;
}

/* n ( tick/1 sec ) , 2:1 = MIPS CPU Core freq : counter-reg(SPR#9) freq */
u32 x200_x400_fe_system_counter_freq(void)
{
	uint ratio;
	uint rate;

	rate = x200_x400_fe_serial_base_clock();
	ratio = (x200_x400_fe_get_cpu_cap() >> 4) & 3;
	switch (ratio) {
	    case 0: // R 266 H 200 ( * 2 )
		rate = rate*2;
		break;
	    case 1: // R 300 H 300 ( * 3 )
		if( rate == 100000000 ){
		    rate = rate * 3;
		} else {
		    rate = (rate*9)/4;
		}
		break;
	    case 2: // R 200 H 250 ( / 2 * 5 )
		if( rate == 100000000 ){
		    rate = (rate*5)/2;
		} else {
		    rate = (rate*6)/4;
		}
		break;
	    case 3: // R 133 H 100 ( * 1 )
		break;
	}

	return (rate)/2;
	/* 1 counter = 2 cpu core clock */
}

int checkboard (void)
{
	uint chipid;

	printf("Board: x200_x400-FE\n");

	chipid = x200_x400_fe_get_chipid();
	printf ("Chip revision %04x device %04x, ", (chipid >> 16), (chipid & 0xffff));
	printf("CPU Speed %d MHz\n", (int)(x200_x400_fe_serial_base_clock() * 2/1000000));

	return 0;
}


int misc_init_r (void)
{
	/* GPIO Initial State
	 * 0 : OUT : SYSTEM LED (def 0)
	 * 1 : INT : SFP Interrupt
	 * 2 : OUT : (OPEN) ( Hi )
	 * 3 : IN? : MODE_SW_N
	 * 4 : OUT : SFP1 Media Type
	 * 5 : OUT : SFP2 Media Type
	 * 6 : OUT : SFP3 Media Type
	 * 7 : OUT : SFP4 Media Type
	 */
	/* Output register */
	*(u32 *)0xb8000064 = 0x00000000;
	/* Output Enable */
	*(u32 *)0xb8000068 = 0x000000fe;
	/* Control */
	*(u32 *)0xb800006c = 0x00000001;
	/* Interrupt Mask */
	*(u32 *)0xb8000074 = 0x00000000;
	/* Interrupt polarity */
	*(u32 *)0xb8000070 = 0x00000000;

	/* CPU Soft Reset */
	*(unsigned long *)0xb8000110 = 0x00000000;

	/* Adjust pointers in epi3 name arrays
	 */
	diag_epi3_reloc_names();
	setenv("ethact", "switch");
#ifdef CONFIG_DIAG_EPI3
	epi3_setmacaddr();
#endif
	return 0;
}

u32 x200_x400_fe_flash_init(void)
{
	u32 size;

	while (!(size = flash_init()))
	{
		bootflash_attached = 1;

		/* Grab a copy of the ROM image now before the user removes the jumper -
		 * removing our access to the original copy of the code in the boot flash
		 * chip - then store it on the 8MB boundary (CONFIG_ROMIMAGE_BASE) for
		 * use by the booloader upgrade menu later on.
		 */

		memcpy((void *)CONFIG_ROMIMAGE_BASE, (void *)CONFIG_UBOOT_FLASH_BASE, CONFIG_UBOOT_SIZE);

		bootmenu_printf("System appears to have been booted from EPROM.\n");
		bootmenu_printf("Remove JP530 now then press any key to continue...");

		serial_getc();

		bootmenu_printf("\n");
	}

	return size;
}

int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	/* set reset bit */
	*(unsigned long *)0xb8000110 = 0x00000001;
	return 0;
}
