/*
 * Copyright 2008-2009 Freescale Semiconductor, Inc.
 * 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 <pci.h>
#include <asm/processor.h>
#include <asm/mmu.h>
#include <asm/cache.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_pci.h>
#include <asm/fsl_ddr_sdram.h>
#include <asm/io.h>
#include <mtd/cfi_flash.h>
#include <spd.h>
#include <miiphy.h>
#include <libfdt.h>
#include <spd_sdram.h>
#include <fdt_support.h>
#include <tsec.h>
#include <netdev.h>
#include <sata.h>
#include <i2c.h>
#include <epi3.h>
#include "cpld.h"

/* Global variable used to store detected number of banks */
int cfi_flash_num_flash_banks;

ulong flash_get_size (phys_addr_t base, int banknum);
extern flash_info_t  flash_info[];

phys_size_t fixed_sdram(void);
static int detect_num_flash_banks(void);

int board_early_init_f (void)
{
	volatile ccsr_gpio_t *gpio =  (void *)(CONFIG_SYS_MPC85xx_GPIO_ADDR);
	volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
	volatile unsigned char *cpld = (void *)(CONFIG_SYS_CPLD_BASE);

	/* Don't take the PHYs out of reset until the ports are actually needed */
	clrbits_8(cpld+CPLD_CR4,
			CPLD_CR4_RSTPHY_GE1 | \
			CPLD_CR4_RSTPHY_GE2 | \
			CPLD_CR4_RSTPHY_SFPP);
	/* Un-reset switch chip */
	setbits_8(cpld+CPLD_CR4, CPLD_CR4_SW_RST);

	/* Un-reset LM81 and LM87 chip */
	setbits_8(cpld+CPLD_CR3, CPLD_CR3_LM81_RST);

	/* enable writing to flash */
	setbits_8(cpld+CPLD_CR1, CPLD_CR1_FLASH_WP | CPLD_CR1_FLASH_PRGM);

#ifdef CONFIG_MMC
	setbits_be32(&gur->pmuxcr,
			(MPC85xx_PMUXCR_SD_DATA |
			 MPC85xx_PMUXCR_SDHC_CD |
			 MPC85xx_PMUXCR_SDHC_WP));

#endif

	/* Power-down the unused blocks to save power */
	out_be32 (&gur->devdisr, MPC85xx_DEVDISR_SEC |
			MPC85xx_DEVDISR_USB1 | 
			MPC85xx_DEVDISR_USB2 |
			MPC85xx_DEVDISR_USB3 | 
			MPC85xx_DEVDISR_SATA1 |
			MPC85xx_DEVDISR_SATA2 |
		       	MPC85xx_DEVDISR_SERDES2 |
			MPC85xx_DEVDISR_SERDES1);

	/* Setup GPIO pins*/
	out_be32(&gpio->gpdir, CONFIG_SYS_GPDIR_VAL);
	out_be32(&gpio->gpodr, CONFIG_SYS_GPODR_VAL);
	out_be32(&gpio->gpimr, CONFIG_SYS_GPIMR_VAL);
	out_be32(&gpio->gpicr, CONFIG_SYS_GPICR_VAL);

	/* Set initial values for GPIOS configured as outputs */
	out_be32(&gpio->gpdat, CONFIG_SYS_GPDAT_INIT_VAL);

	return 0;
}

static u8 get_fan_dac (void)
{
	uint dac = simple_strtoul(getenv("fancontrol"), NULL, 16);

	// ensure the DAC is set within an acceptable range
	if (dac < 0x60)
		dac = 0x60; // ~5v output on the DAC
	if (dac > 0xff)
		dac = 0xff; // 12v output on the DAC

	return dac;
}

struct i2c_addr {
	uint bus;
	uchar dev;
};

static void set_fan_cntl (u8 val)
{
	struct i2c_addr fan_controllers[] = {
			{2, 0x2c},
			{4, 0x2e},
			{0, 0}
	};
	struct i2c_addr *controller;

	controller = fan_controllers;
	while (controller->bus !=0 && controller->dev !=0) {
		i2c_set_bus_num(controller->bus);
		i2c_write (controller->dev, 0x19, 1, &val, sizeof(val));
		controller++;
	}
}

int misc_init_r (void)
{
	u8 val = 0xff;

	/* Setup fans.*/
	if (!getenv("fancontrol")) {
		/* Normally these should be forced to full speed */
		set_fan_cntl (val);
	} else {
		/* For development/test we allow slowing down the fan */
		set_fan_cntl (get_fan_dac());
	}

#ifdef CONFIG_DIAG_EPI3
	epi3_setmacaddr();
#endif

	return 0;
}

int checkboard (void)
{
	puts("Board: x610 ");

	return 0;
}

phys_size_t
initdram(int board_type)
{
	phys_size_t dram_size = 0;

	puts("Initializing....");

#ifdef CONFIG_SPD_EEPROM
	dram_size = fsl_ddr_sdram();
#else
	dram_size = fixed_sdram();

	if (set_ddr_laws(CONFIG_SYS_DDR_SDRAM_BASE,
			 dram_size,
			 LAW_TRGT_IF_DDR_1) < 0) {
		printf("ERROR setting Local Access Windows for DDR\n");
		return 0;
	}
#endif
	dram_size = setup_ddr_tlbs(dram_size / 0x100000);
	dram_size *= 0x100000;

	puts("    DDR: ");
	return dram_size;
}

#if !defined(CONFIG_SPD_EEPROM)
/*
 * Fixed sdram init -- doesn't use serial presence detect.
 */

phys_size_t fixed_sdram (void)
{
	volatile ccsr_ddr_t *ddr = (void *)(CONFIG_SYS_MPC85xx_DDR_ADDR);
	volatile u32 *ddrclkdr = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR + 0xB28);

	u32 temp_sdram_cfg = CONFIG_SYS_DDR_SDRAM_CFG & ~SDRAM_CFG_MEM_EN;

	/* Disable the unused DDR clock outputs, we're using MCK0,
	 * the rest can be disabled to reduce power consumption */
	setbits_be32(ddrclkdr, 0x1f);

	out_be32(&ddr->cs2_bnds, CONFIG_SYS_DDR_CS2_BNDS);
	out_be32(&ddr->cs2_config, CONFIG_SYS_DDR_CS2_CONFIG);
	out_be32(&ddr->cs2_config_2, CONFIG_SYS_DDR_CS2_CONFIG_2);

	out_be32(&ddr->timing_cfg_3, CONFIG_SYS_DDR_TIMING_3);
	out_be32(&ddr->timing_cfg_0, CONFIG_SYS_DDR_TIMING_0);
	out_be32(&ddr->timing_cfg_1, CONFIG_SYS_DDR_TIMING_1);
	out_be32(&ddr->timing_cfg_2, CONFIG_SYS_DDR_TIMING_2);

	out_be32(&ddr->sdram_mode, CONFIG_SYS_DDR_MODE_1);
	out_be32(&ddr->sdram_mode_2, CONFIG_SYS_DDR_MODE_2);
	out_be32(&ddr->sdram_interval, CONFIG_SYS_DDR_INTERVAL);
	out_be32(&ddr->sdram_cfg_2, CONFIG_SYS_DDR_SDRAM_CFG_2);
	out_be32(&ddr->sdram_data_init, CONFIG_SYS_DDR_DATA_INIT);
	out_be32(&ddr->sdram_clk_cntl, CONFIG_SYS_DDR_CLK_CTRL);

	out_be32(&ddr->timing_cfg_4, CONFIG_SYS_DDR_TIMING_4);
	out_be32(&ddr->timing_cfg_5, CONFIG_SYS_DDR_TIMING_5);
	out_be32(&ddr->ddr_zq_cntl, CONFIG_SYS_DDR_ZQ_CNTL);
	out_be32(&ddr->ddr_wrlvl_cntl, CONFIG_SYS_DDR_WRLVL_CNTL);
	out_be32(&ddr->sdram_cfg_2, CONFIG_SYS_DDR_SDRAM_CFG_2);
#if defined (CONFIG_DDR_ECC)
	out_be32(&ddr->err_int_en, CONFIG_SYS_DDR_ERR_INT_EN);
	out_be32(&ddr->err_disable, CONFIG_SYS_DDR_ERR_DIS);
	out_be32(&ddr->err_sbe, CONFIG_SYS_DDR_SBE);
	temp_sdram_cfg |= SDRAM_CFG_ECC_EN;
#endif
	/* Set, but do not enable the memory */
	out_be32(&ddr->sdram_cfg, temp_sdram_cfg);
	udelay(500);
	asm("sync;isync");

	/* Let the controller go */
	out_be32(&ddr->sdram_cfg, temp_sdram_cfg | SDRAM_CFG_MEM_EN);

	/* Poll DDR_SDRAM_CFG_2[D_INIT] bit until auto-data init is done.  */
	while (in_be32(&ddr->sdram_cfg_2) & 0x10) {
		udelay(1000);		/* throttle polling rate */
	}

	return CONFIG_SYS_SDRAM_SIZE * 1024 * 1024;
}

#endif

#ifdef CONFIG_PCI1
static struct pci_controller pci1_hose;
#endif

#ifdef CONFIG_PCIE1
static struct pci_controller pcie1_hose;
#endif

#ifdef CONFIG_PCIE2
static struct pci_controller pcie2_hose;
#endif

#ifdef CONFIG_PCIE3
static struct pci_controller pcie3_hose;
#endif

#ifdef CONFIG_PCI
void pci_init_board(void)
{
	ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
	struct fsl_pci_info pci_info[4];
	u32 devdisr, pordevsr, io_sel, sdrs2_io_sel;
	u32 porpllsr, pci_agent, pci_speed, pci_32, pci_arb, pci_clk_sel;
	int first_free_busno = 0;
	int num = 0;

#if defined(CONFIG_PCIE3) || defined(CONFIG_PCIE2) || defined(CONFIG_PCIE1)
	int pcie_ep, pcie_configured;
#endif

	devdisr = in_be32(&gur->devdisr);
	pordevsr = in_be32(&gur->pordevsr);
	porpllsr = in_be32(&gur->porpllsr);
	io_sel = (pordevsr & MPC85xx_PORDEVSR_IO_SEL) >> 19;
	sdrs2_io_sel = (pordevsr & MPC85xx_PORDEVSR_SRDS2_IO_SEL) >> 27;

	debug("   pci_init_board: devdisr=%x, sdrs2_io_sel=%x, io_sel=%x\n",
		devdisr, sdrs2_io_sel, io_sel);

	if (sdrs2_io_sel == 7)
		printf("    Serdes2 disalbed\n");
	else if (sdrs2_io_sel == 4) {
		printf("    eTSEC1 is in sgmii mode.\n");
		printf("    eTSEC3 is in sgmii mode.\n");
	} else if (sdrs2_io_sel == 6)
		printf("    eTSEC1 is in sgmii mode.\n");

	puts("\n");
#ifdef CONFIG_PCIE3
	pcie_configured = is_fsl_pci_cfg(LAW_TRGT_IF_PCIE_3, io_sel);

	if (pcie_configured && !(devdisr & MPC85xx_DEVDISR_PCIE3)){
		SET_STD_PCIE_INFO(pci_info[num], 3);
		pcie_ep = fsl_setup_hose(&pcie3_hose, pci_info[num].regs);
		printf ("    PCIE3 connected to Slot3 as %s (base address %lx)\n",
			pcie_ep ? "Endpoint" : "Root Complex",
			pci_info[num].regs);
		first_free_busno = fsl_pci_init_port(&pci_info[num++],
					&pcie3_hose, first_free_busno);
	} else {
		printf ("    PCIE3: disabled\n");
	}

	puts("\n");
#else
	setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_PCIE3); /* disable */
#endif

#ifdef CONFIG_PCIE1
	pcie_configured = is_fsl_pci_cfg(LAW_TRGT_IF_PCIE_1, io_sel);

	if (pcie_configured && !(devdisr & MPC85xx_DEVDISR_PCIE)){
		SET_STD_PCIE_INFO(pci_info[num], 1);
		pcie_ep = fsl_setup_hose(&pcie1_hose, pci_info[num].regs);
		printf ("    PCIE1 connected to Slot1 as %s (base address %lx)\n",
			pcie_ep ? "Endpoint" : "Root Complex",
			pci_info[num].regs);
		first_free_busno = fsl_pci_init_port(&pci_info[num++],
					&pcie1_hose, first_free_busno);
	} else {
		printf ("    PCIE1: disabled\n");
	}

	puts("\n");
#else
	setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_PCIE); /* disable */
#endif

#ifdef CONFIG_PCIE2
	pcie_configured = is_fsl_pci_cfg(LAW_TRGT_IF_PCIE_2, io_sel);

	if (pcie_configured && !(devdisr & MPC85xx_DEVDISR_PCIE2)){
		SET_STD_PCIE_INFO(pci_info[num], 2);
		pcie_ep = fsl_setup_hose(&pcie2_hose, pci_info[num].regs);
		printf ("    PCIE2 connected to Slot 2 as %s (base address %lx)\n",
			pcie_ep ? "Endpoint" : "Root Complex",
			pci_info[num].regs);
		first_free_busno = fsl_pci_init_port(&pci_info[num++],
					&pcie2_hose, first_free_busno);
	} else {
		printf ("    PCIE2: disabled\n");
	}

	puts("\n");
#else
	setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_PCIE2); /* disable */
#endif

#ifdef CONFIG_PCI1
	pci_speed = 66666000;
	pci_32 = 1;
	pci_arb = pordevsr & MPC85xx_PORDEVSR_PCI1_ARB;
	pci_clk_sel = porpllsr & MPC85xx_PORDEVSR_PCI1_SPD;

	if (!(devdisr & MPC85xx_DEVDISR_PCI1)) {
		SET_STD_PCI_INFO(pci_info[num], 1);
		pci_agent = fsl_setup_hose(&pci1_hose, pci_info[num].regs);
		printf ("\n    PCI: %d bit, %s MHz, %s, %s, %s (base address %lx)\n",
			(pci_32) ? 32 : 64,
			(pci_speed == 33333000) ? "33" :
			(pci_speed == 66666000) ? "66" : "unknown",
			pci_clk_sel ? "sync" : "async",
			pci_agent ? "agent" : "host",
			pci_arb ? "arbiter" : "external-arbiter",
			pci_info[num].regs);

		first_free_busno = fsl_pci_init_port(&pci_info[num++],
					&pci1_hose, first_free_busno);
	} else {
		printf ("    PCI: disabled\n");
	}

	puts("\n");
#else
	setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_PCI1); /* disable */
#endif
}
#endif

int board_early_init_r(void)
{
	const unsigned int flashbase = CONFIG_SYS_FLASH_BASE;
	const u8 flash_esel = find_tlb_idx((void *)flashbase, 1);

	/*
	 * Remap Boot flash + PROMJET region to caching-inhibited
	 * so that flash can be erased properly.
	 */

	/* Flush d-cache and invalidate i-cache of any FLASH data */
	flush_dcache();
	invalidate_icache();

	/* invalidate existing TLB entry for flash + promjet */
	disable_tlb(flash_esel);

	set_tlb(1, flashbase, CONFIG_SYS_FLASH_BASE_PHYS,	/* tlb, epn, rpn */
		MAS3_SX|MAS3_SW|MAS3_SR, MAS2_I|MAS2_G,	/* perms, wimge */
		0, flash_esel, BOOKE_PAGESZ_256M, 1);	/* ts, esel, tsize, iprot */

	/* detect the number of Flash banks */
	detect_num_flash_banks();

	return 0;
}

int board_eth_init(bd_t *bis)
{
#ifdef CONFIG_TSEC_ENET
	volatile unsigned char *cpld = (void *)(CONFIG_SYS_CPLD_BASE);
	struct tsec_info_struct tsec_info[2];
	volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
	int num = 0;
	uint sdrs2_io_sel =
		(gur->pordevsr & MPC85xx_PORDEVSR_SRDS2_IO_SEL) >> 27;

#ifdef CONFIG_TSEC1
	SET_STD_TSEC_INFO(tsec_info[num], 1);
	if ((sdrs2_io_sel == 4) || (sdrs2_io_sel == 6)) {
		tsec_info[num].phyaddr = 0;
		tsec_info[num].flags |= TSEC_SGMII;
	}
	num++;
#endif
#ifdef CONFIG_TSEC3
	SET_STD_TSEC_INFO(tsec_info[num], 3);
	if (sdrs2_io_sel == 4) {
		tsec_info[num].phyaddr = 1;
		tsec_info[num].flags |= TSEC_SGMII;
	}
	num++;
#endif

	if (!num) {
		printf("No TSECs initialized\n");
		return 0;
	}

	/* Bring 88E1111 PHY out of reset */
	setbits_8(cpld+CPLD_CR2, CPLD_CR2_TSEC_PHY_RES);

	tsec_eth_init(bis, tsec_info, num);
#endif
	return pci_eth_init(bis);
}

#if defined(CONFIG_OF_BOARD_SETUP)
void ft_board_setup(void *blob, bd_t *bd)
{
	ft_cpu_setup(blob, bd);

#ifdef CONFIG_PCI1
	ft_fsl_pci_setup(blob, "pci0", &pci1_hose);
#endif
#ifdef CONFIG_PCIE2
	ft_fsl_pci_setup(blob, "pci1", &pcie2_hose);
#endif
#ifdef CONFIG_PCIE2
	ft_fsl_pci_setup(blob, "pci2", &pcie1_hose);
#endif
#ifdef CONFIG_PCIE1
	ft_fsl_pci_setup(blob, "pci3", &pcie3_hose);
#endif
#ifdef CONFIG_FSL_SGMII_RISER
	fsl_sgmii_riser_fdt_fixup(blob);
#endif
}
#endif

/**
 * Detect single die vs dual die flash.
 *
 * For this board we expect 64MB of flash. The Intel/Numonyx flash parts can
 * be either 2x32MB or 1x64MB depending on the batch. This sets the number
 * of banks to 1 if the 1x64MB configuration is detected otherwise the number
 * of banks is set to 2.
 */
static int
detect_num_flash_banks (void)
{
	ulong bank1_size;
	ulong bank2_size;
	ulong total_size;

	cfi_flash_num_flash_banks = 2;

	bank1_size = flash_get_size(CONFIG_SYS_FLASH_BASE, 0);
	debug("Bank1 size: %lu\n", bank1_size);
	if (bank1_size >= (64 << 20)) {
		cfi_flash_num_flash_banks = 1;
		debug("Total size: %lu\n", bank1_size);
		return 0;
	}

	bank2_size = flash_get_size(CONFIG_SYS_FLASH_BASE + bank1_size, 1);
	debug("Bank2 size: %lu\n", bank2_size);
	total_size = bank1_size + bank2_size;

	if (bank2_size > 0) {
		cfi_flash_num_flash_banks = 2;
	} else {
		cfi_flash_num_flash_banks = 1;
	}

	debug("Total size: %lu\n", total_size);
	return 0;
}


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_UBOOT_SIZE + \
				CONFIG_ENV_SECT_SIZE + \
				CONFIG_EPI3_SIZE);
	}
	return partition_size;
}

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

#ifdef CONFIG_MMC_SPI
/* SD card functions */

u8 sdcard_is_present(void)
{
	u8 sdcard_present;
	sdcard_present = !((*(volatile u8 *)(CONFIG_SYS_CPLD_BASE + CPLD_SR2)) & CPLD_SR2_SD_DET);
	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 + CPLD_SR2)) & CPLD_SR2_SD_PROT) != 0;
	return protect_on;
}

extern u8 espi_select(u8 select);

void sdcard_select(u8 select)
{
	volatile unsigned char *cpld = (void *)(CONFIG_SYS_CPLD_BASE);

	if (select) {
		/* Pull the chip select low (active) and turn on the green LED */
		clrsetbits_8(cpld + CPLD_CR4, CPLD_CR4_SD_CS|CPLD_CR4_SD_LED_MASK, CPLD_CR4_SD_LED_GREEN);
	} else {
		/* Pull the chip select high (inactive) and turn off the LED */
		clrsetbits_8(cpld + CPLD_CR4, CPLD_CR4_SD_LED_MASK, CPLD_CR4_SD_CS|CPLD_CR4_SD_LED_OFF);
	}
	/* Ensure some compatability with prototype boards */
	espi_select(select);
}
#endif /* CONFIG_MMC_SPI */
