/*
 * 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 <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 <asm/mp.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"
#ifdef CONFIG_FSL_ESDHC
#include <mmc.h>
#include <fsl_esdhc.h>
#endif
#ifdef CONFIG_USB_STORAGE
#include <usb.h>
#endif


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

	/* take out of reset hw required by u-boot */
	clrbits_8(cpld+CPLD_CR3, CPLD_CR3_PHY_WAN1_RST );
	clrbits_8(cpld+CPLD_CR3, CPLD_CR3_PCIE_RST );

	/* setup the I2C devices so they can be used */
	clrbits_8(cpld+CPLD_CR5, CPLD_CR5_I2C_1_ENABLE);

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

#endif

#if 0
	/* Power-down the unused blocks to save power */
	out_be32 (&gur->devdisr,
			MPC85xx_DEVDISR_USB2 |
			MPC85xx_DEVDISR_SATA1);
#endif

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

	return 0;
}

#ifdef CONFIG_USB_STORAGE
/*
 * initialize USB hub via I2C[1] (I2C-2)
 */
int usbhub_init(void)
{
	uint reg;
	int ret = 0;
	unsigned int oldbus;
	uint i;
	volatile unsigned char *cpld = (void *)(CONFIG_SYS_CPLD_BASE);
	uchar u[] = {0x20, 0x24, 0x04, 0x12, 0x25, 0xa0, 0x0b, 0x9b, 0x20, 0x02, 0x00, 0x00, 0x00, 0x01, 0x32, 0x01, 0x32,
		     0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
		    };

	printf("Hub:   ");

	/* disable I2C-2 */
	setbits_8(cpld+CPLD_CR5, CPLD_CR5_I2C_2_ENABLE);
	udelay(10);

	/* take the USB hub out of reset */
	clrbits_8(cpld+CPLD_CR3, CPLD_CR3_USB_RST);
	udelay(600);

	/* select device 2 (USB hub) on I2C-2 */
	clrbits_8(cpld+CPLD_CR5, CPLD_CR5_I2C_2_SELECT);
	setbits_8(cpld+CPLD_CR5, CPLD_CR5_I2C_2_CH2);

	/* enable I2C-2 */
	clrbits_8(cpld+CPLD_CR5, CPLD_CR5_I2C_2_ENABLE);

	oldbus = I2C_GET_BUS();
	I2C_SET_BUS(1);

	/* write the default HUB registers */
	for (reg = 0, i = 0; i < 264; i += 33, reg += 32) {
		ret = i2c_write (IIC1_USB2512_ADDR, reg, 1, &u[i], 33);
		udelay(100);
	}

	if (ret == 0)
		printf("initialized\n");
	else
		printf("failed - cannot initialize USB hub\n");

	I2C_SET_BUS(oldbus);
	return ret;
}
#endif

int misc_init_r (void)
{
#ifdef CONFIG_USB_STORAGE
	usbhub_init();
	usb_init();
#endif

#ifdef CONFIG_DIAG_EPI3
	epi3_setmacaddr();
#endif

	return 0;
}

int checkboard (void)
{
#if defined(CONFIG_MK_HYDROGEN0)
	puts("Board: Hydrogen-0 ");
#else
	puts("Board: AR660S ");
#endif

	return 0;
}

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

	puts("Initializing....");

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

	dram_size = setup_ddr_tlbs(dram_size / 0x100000);
	dram_size *= 0x100000;

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

phys_size_t fixed_sdram(void)
{
	volatile ccsr_ddr_t *ddr = (ccsr_ddr_t *)CONFIG_SYS_MPC85xx_DDR_ADDR;

	u32 temp_sdram_cfg = CONFIG_SYS_DDR_SDRAM_CFG & ~SDRAM_CFG_MEM_EN;

	out_be32(&ddr->cs0_bnds, CONFIG_SYS_DDR_CS0_BNDS);
	out_be32(&ddr->cs0_config, CONFIG_SYS_DDR_CS0_CONFIG);
	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_cfg, CONFIG_SYS_DDR_SDRAM_CFG);
	out_be32(&ddr->sdram_cfg_2, CONFIG_SYS_DDR_SDRAM_CFG_2);
	out_be32(&ddr->sdram_mode, CONFIG_SYS_DDR_SDRAM_MODE);
	out_be32(&ddr->sdram_mode_2, CONFIG_SYS_DDR_SDRAM_MODE_2);
	out_be32(&ddr->sdram_interval, CONFIG_SYS_DDR_SDRAM_INTERVAL);
	out_be32(&ddr->sdram_data_init, CONFIG_SYS_DDR_DATA_INIT);
	out_be32(&ddr->sdram_clk_cntl, CONFIG_SYS_DDR_SDRAM_CLK_CNTL);
	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;
}

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_64M, 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[3];
	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 PHY out of reset */
	clrbits_8(cpld+CPLD_CR3, CPLD_CR3_PHY_WAN1_RST);

	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);
}
#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;
}

int atl_bootrom_board_attached(void)
{
    return 0; /* no such thing on this product */
}

u32 atl_get_env_addr(void)
{
	return CONFIG_ENV_ADDR;
}

u32 atl_get_offset_from_base(void)
{
    return 0x0; /* Not used by this product */
}

#ifdef CONFIG_FSL_ESDHC
int board_mmc_init(bd_t *bd)
{
	return fsl_esdhc_mmc_init(bd);
}
#endif

#ifdef CONFIG_MMC
/* SD card functions */
u8 sdcard_is_present(void)
{
	u8 sdcard_present;
	volatile u32 *reg = (u32 *)(CONFIG_SYS_IMMR + PRSSTAT);

	sdcard_present = (esdhc_read32(reg) & PRSSTAT_CINS) == PRSSTAT_CINS;
	return sdcard_present;
}
#endif

#ifdef CONFIG_MP
void board_lmb_reserve(struct lmb *lmb)
{
	cpu_mp_lmb_reserve(lmb);
}
#endif
