/* x600.c
 * 
 * Matthew Millman
 */

#include <common.h>
#include <ioports.h>
#include <mpc83xx.h>
#include <asm/mpc8349_pci.h>
#include <i2c.h>
#include <spi.h>
#include <spd.h>
#include <miiphy.h>
#include <command.h>
#include <asm/mmu.h>
#if defined(CONFIG_SPD_EEPROM)
#include <spd_sdram.h>
#endif

#include <bootmenu.h>
#include <flash.h>
#include <epi3.h>

int fixed_sdram(void);
void sdram_init(void);

void ddr_enable_ecc(unsigned int dram_size);

#ifndef CONFIG_SYS_NO_FLASH
extern flash_info_t	flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
#endif /* CONFIG_SYS_NO_FLASH */

int board_early_init_f (void)
{
	u8 u44_port0_config = 
			/* 0x1	*/	/* IN  RSTXFP */
			0x2	|	/* IN  MSTR_SRST_N */
			/* 0x4 */	/* IN  RSYPHY_1 */
			/* 0x8 */	/* IN  RSTSW_1 */
			0x10	|	/* IN  IFLASH_WP2_N */
			0x20	|	/* IN  UNUSED */
			0x40	|	/* IN  UNUSED */
			0x80	 	/* IN  UNUSED */
			;
	
	u8 u49_port0_config = 
			/* 0x1 */	/* OUT TXDIS_SFP24 */
			/* 0x2 */	/* OUT TXDIS_SFP23 */
			/* 0x4 */	/* OUT TXDIS_SFP22 */
			/* 0x8 */	/* OUT TXDIS_SFP21 */
			0x10	|	/* IN  RXLOS_SFP24 */
			0x20 	|	/* IN  RXLOS_SFP23 */
			0x40	|	/* IN  RXLOS_SFP22 */
			0x80		/* IN  RXLOS_SFP21 */
			;	
	
	u8 u49_port1_config = 
			0x1	| 	/* IN  PRES_SFP24_N */
			0x2 	|	/* IN  PRES_SFP23_N */
			0x4	|	/* IN  PRES_SFP22_N */
			0x8	|	/* IN  PRES_SFP21_N */
			0x10	|	/* IN  TXFLT_SFP24 */
			0x20	|	/* IN  TXFLT_SFP23 */
			0x40	|	/* IN  TXFLT_SFP22 */
			0x80		/* IN  TXFLT_SFP21 */
			;
	
	u8 u44_port0_initial_state = 
			0x1	|	/* OUT RSTXFP */
			0x2	|	/* OUT MSTR_SRST_N */
			0x4	|	/* OUT RSYPHY_1 */
			/* 0x8 */	/* OUT RSTSW_1 */
			0x10	|	/* OUT IFLASH_WP2_N */
			0x20	|	/* IN  UNUSED */
			0x40	|	/* IN  UNUSED */
			0x80		/* IN  UNUSED */
			;
	
	i2c_write(0x18, 0x01, 0x01, &u44_port0_initial_state, 1); /* write u44.0 */
	i2c_write(0x18, 0x03, 0x01, &u44_port0_config, 1); /* u44 */
	
	i2c_write(0x21, 0x06, 0x01, &u49_port0_config, 1); /* u49 */
	i2c_write(0x21, 0x07, 0x01, &u49_port1_config, 1); /* u49 */
	
	/* Set GPIO GP1_DIR register */
	*(u32 *)((u32)CONFIG_SYS_IMMR + 0xC00) = 0x0000003E;
	/* Turn off all LEDs, except the POE reset line */
    /* This means the debug LED will be lit on x600s which have it fitted */
	*(u32 *)((u32)CONFIG_SYS_IMMR + 0xC08) = 0x0000003A;
	
	/* Don't take the PHYs out of reset until the ports are actually needed */
	*(u8 *)(CONFIG_SYS_CPLD_BASE + 3) &= ~0x0c;
	/* Un-reset switch chip */
	*(u8 *)(CONFIG_SYS_CPLD_BASE + 3) |= 0x01;

	/* Allow access to entire flash chip */
	*(u8 *)(CONFIG_SYS_CPLD_BASE + 0) &= ~0x80;
	
	return 0;
}

/* 
 * What we know about the ATI CPLD so far 
 * (based entirely on reverse engineering by experimentation)
 * 
 * The ATI x600 CPLD 'U6' appears to have four bytes of register space
 * which is writable *and* readable (what a luxary!). It is most likely
 * that the only thing this register space does is control the various I/O
 * pins connected to it (U6). We'll figure out what bits control what pins as 
 * we need them.
 *
 * Listings below use big endian bit numbering, names are Allegro net names.
 *
 * CPLD offset 0x0
 *
 * Bit 7 (0x01) OUT: FL_PRGM
 * Bit 6 (0x02) OUT: ~FL_WP
 * Bit 5 (0x04): UNKNOWN
 * Bit 4 (0x08): UNKNOWN
 * Bit 3 (0x10): UNKNOWN
 * Bit 2 (0x20): UNKNOWN
 * Bit 1 (0x40): UNKNOWN
 * Bit 0 (0x80) OUT: LCL_1M_SP
 *
 * CPLD offset 0x1
 *
 * Bit 7 (0x01): UNKNOWN
 * Bit 6 (0x02): UNKNOWN
 * Bit 5 (0x04) OUT: ~RST_FE_PHY
 * Bit 4 (0x08): UNKNOWN
 * Bit 3 (0x10): UNKNOWN
 * Bit 2 (0x20): UNKNOWN
 * Bit 1 (0x40): UNKNOWN
 * Bit 0 (0x80): UNKNOWN 
 *
 * CPLD offset 0x2
 *
 * Bit 7 (0x01): UNKNOWN
 * Bit 6 (0x02): UNKNOWN
 * Bit 5 (0x04): UNKNOWN
 * Bit 4 (0x08): UNKNOWN
 * Bit 3 (0x10): UNKNOWN
 * Bit 2 (0x20): UNKNOWN
 * Bit 1 (0x40): UNKNOWN
 * Bit 0 (0x80) OUT: System reset/MR of U14 (No net name)
 *
 * CPLD offset 0x3
 *
 * Bit 7 (0x01): UNKNOWN
 * Bit 6 (0x02): UNKNOWN
 * Bit 5 (0x04): UNKNOWN
 * Bit 4 (0x08): UNKNOWN
 * Bit 3 (0x10): UNKNOWN
 * Bit 2 (0x20) OUT: ~SPI_CS
 * Bit 1 (0x40): UNKNOWN
 * Bit 0 (0x80): UNKNOWN
 * 
 * CPLD offset 0x11
 *
 * Bit 7 (0x01): UNKNOWN
 * Bit 6 (0x02): UNKNOWN
 * Bit 5 (0x04) IN: SD_PROTECT
 * Bit 4 (0x08) IN: ~SD_DETECT
 * Bit 3 (0x10): UNKNOWN
 * Bit 2 (0x20): UNKNOWN
 * Bit 1 (0x40): UNKNOWN
 * Bit 0 (0x80): UNKNOWN 
*/

u32 atl_get_jffs2_part_size(void)
{
	u32 partition_size = 0;
	int i;

	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
		if (flash_info[i].flash_id != FLASH_UNKNOWN) {
			partition_size += flash_info[i].size;
		}
	}
	if (partition_size != 0) {
		partition_size -= CONFIG_JFFS2_PART_OFFSET;
	}
	return partition_size;
}

#ifdef CONFIG_MISC_INIT_R
int misc_init_r (void)
{
	int i,j;

	/* Remove 'soft' locking on relevant flash erase blocks */
	
	/* Enable local bus high order address bits (Initially disabled so system boots from start of flash) */
	*(u8 *)CONFIG_SYS_CPLD_BASE &= ~0x80;
	
	/* Remove flash write protection */
	*(u8 *)CONFIG_SYS_CPLD_BASE |= 0x02;
	
	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
		for (j = 0; j < flash_info[i].sector_count; j++) {
			if ((flash_info[i].start[j] >= CONFIG_JFFS2_PART_OFFSET + CONFIG_SYS_FLASH_BASE) &&
			    (flash_info[i].start[j] < CONFIG_JFFS2_PART_OFFSET + CONFIG_JFFS2_PART_SIZE + CONFIG_SYS_FLASH_BASE)) {
				flash_real_protect(&flash_info[i], j, 0);
			}
		}
	}

	/* Turn on flash VPP */
	*(u8 *)CONFIG_SYS_CPLD_BASE |= 0x01;
	
	/* Take management PHY out of reset */
	*(u8 *)(CONFIG_SYS_CPLD_BASE + 1) |= 0x04;
	
#ifdef CONFIG_DIAG_EPI3
	epi3_setmacaddr();
#endif

	return (0);
}
#endif /* CONFIG_MISC_INIT_R */

phys_size_t initdram (int board_type)
{
	volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
	u32 msize = 0;

	if ((im->sysconf.immrbar & IMMRBAR_BASE_ADDR) != (u32)im)
		return -1;

	puts("Initializing\n");

	/* DDR SDRAM - Main SODIMM */
	im->sysconf.ddrlaw[0].bar = CONFIG_SYS_DDR_BASE & LAWBAR_BAR;

	msize = fixed_sdram();

	/*
	 * Initialize and enable DDR ECC.
	 */
	
	bootmenu_printf("\nInitialising ECC Memory... ");
	
	ddr_enable_ecc(msize * 1024 * 1024);

	bootmenu_printf("Done\n");

	puts("   DDR RAM: ");
	/* return total bus SDRAM size(bytes)  -- DDR */
	
	return (msize * 1024 * 1024);
}

int fixed_sdram(void)
{
	volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
	
	/* 
	 * All of the values below were obtained by directly reverse engineering an
	 * x600-24Ts/XP running software by ATI. We have not taken the time ourselves
	 * to verify that these are completely correct which could be considered dangerous,
	 * however, at the time or writing we in a very rapid, clandestine prototyping phase
	 * and do not have the luxary of doing this - MJM.
	 *
	 * Ideally these will be optimised at a later date however I very much doubt that
	 * this will happen unless these values are found to be in error.
	 */
	
	im->sysconf.ddrlaw[0].ar = 0x8000001c;
	im->sysconf.ddrlaw[0].bar = 0x00000000;

	im->ddr.csbnds[0].csbnds = 0x0000001f;
	
	im->ddr.cs_config[0] = 0x80014102;
	
	im->ddr.timing_cfg_1 = 0x38352322;
	im->ddr.timing_cfg_2 = 0x1210490a;
	
	im->ddr.timing_cfg_3 = 0x00010000;
	im->ddr.timing_cfg_0 = 0x00260802;
	
	//printf("t_cfg_3: 0x%08X t_cfg_0: 0x%08X cfg_2: 0x%08X mode_2: 0x%08X\n",&(im->ddr.res1[0x70]),
	  //     &(im->ddr.res1[0x74]), &(im->ddr.res2[0x00]), &(im->ddr.res3[0x00]));
	
	im->ddr.sdram_cfg = 0x63000000;
	im->ddr.sdram_cfg2 = 0x20401000;
	
	im->ddr.sdram_mode = 0x240e0432;
	im->ddr.sdram_mode2 = 0x40006000;
	
	im->ddr.sdram_interval = 0x04100100;
	
	im->ddr.sdram_clk_cntl = 0x02000000;
	
	im->ddr.err_sbe = 0x00ff0000;
	
	asm("sync;isync;msync");
	udelay(500);

	/* enable DDR controller */
	im->ddr.sdram_cfg |= SDRAM_CFG_MEM_EN;
	
	return 512;
}

int checkboard (void)
{
	return 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 0; /* no such thing on this product */
}

/* atl_get_env_addr()
 *
 * Get location of U-Boot environement. ENV code was hacked a little to make this work
 *
 * This shouldn't be needed on x600 but it nastily crashes with out it for reasons I have
 * not yet had time to investigate.
 */

u32 atl_get_env_addr(void)
{
	return CONFIG_ENV_ADDR;
}

/* atl_get_offset_from_base()
 *
 * Not used by this product
 */

u32 atl_get_offset_from_base(void)
{
    return 0x0;
}

/* do_reset()
 *
 * We have our own reset code for this product
 *
 * Writes to CPLD to reset system
 */

int do_reset (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{	
	*(u8 *)(CONFIG_SYS_CPLD_BASE + 2) &= ~0x80;
	
	return 1;
}

#ifdef CONFIG_MMC_SPI
/* SD card functions */

u8 sdcard_is_present(void)
{
	u8 sdcard_present;
	sdcard_present = !((*(volatile u8 *)(CONFIG_SYS_CPLD_BASE + 0x11)) & 0x08);
	return sdcard_present;
}

static u8 sdcard_changed = 0;

u8 sdcard_has_changed(void)
{
	u8 ret = sdcard_changed;
	sdcard_changed = 0;
	return ret;
}

DECLARE_GLOBAL_DATA_PTR;

void sdcard_check_changed(void)
{
	static u8 old_present = 0;
	// Check gd->bd exists so we know uboot has relocated
	if (gd->bd) {
		u8 present = sdcard_is_present();
		if (present != old_present) {
			old_present = present;
			sdcard_changed = 1;
		}
	}
}

u8 sdcard_write_protected(void)
{
	u8 protect_on;
	protect_on = ((*(volatile u8 *)(CONFIG_SYS_CPLD_BASE + 0x11)) & 0x04) != 0;
	return protect_on;
}

void sdcard_select(u8 select)
{
	if (select) {
		*(u8 *) (CONFIG_SYS_CPLD_BASE + 3) &= ~0x20;
		*(u8 *) (CONFIG_SYS_CPLD_BASE + 3) &= ~0x80;
	} else {
		*(u8 *) (CONFIG_SYS_CPLD_BASE + 3) |= 0x20;
		*(u8 *) (CONFIG_SYS_CPLD_BASE + 3) |= 0x80;
	}
}

#endif
