/*
 * (C) Copyright 2010
 * Allied Telesis <www.alliedtelesis.com>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA
 */

/* Based on board/Marvell/rd6281a/rd6281a.c */

#include <common.h>
#include <miiphy.h>
#include <netdev.h>
#include <i2c.h>
#include <pca953x.h>
#include <asm/arch/kirkwood.h>
#include <asm/arch/mpp.h>
#include <idprom.h>
#include "sbx81lifxcat.h"
#include "../SBx8100/mpi.h"
#include <rtc.h>

DECLARE_GLOBAL_DATA_PTR;

#define PCA9539_ADDR 0x76

int board_init(void)
{
	/*
	 * default gpio configuration
	 * There are maximum 64 gpios controlled through 2 sets of registers
	 * the  below configuration configures mainly initial LED status
	 */
	kw_config_gpio(SBX81LIFXCAT_OE_VAL_LOW,
		       SBX81LIFXCAT_OE_VAL_HIGH,
		       SBX81LIFXCAT_OE_LOW, SBX81LIFXCAT_OE_HIGH);

	/* Multi-Purpose Pins Functionality configuration */
	u32 kwmpp_config[] = {
		MPP0_SPI_SCn,
		MPP1_SPI_MOSI,
		MPP2_SPI_SCK,
		MPP3_SPI_MISO,
		MPP4_NF_IO6,
		MPP5_NF_IO7,
		MPP6_SYSRST_OUTn,
		MPP7_GPO,
		MPP8_TW_SDA,
		MPP9_TW_SCK,
		MPP10_UART0_TXD,
		MPP11_UART0_RXD,
		MPP12_GPO,		/* No connected */
		MPP13_UART1_TXD,
		MPP14_UART1_RXD,
		MPP15_GPIO,
		MPP16_GPIO,
		MPP17_GPIO,		/* No connected */
		MPP18_NF_IO0,
		MPP19_NF_IO1,
		MPP20_GE1_0,
		MPP21_GE1_1,
		MPP22_GE1_2,
		MPP23_GE1_3,
		MPP24_GE1_4,
		MPP25_GE1_5,
		MPP26_GE1_6,
		MPP27_GE1_7,
		MPP28_GE1_8,		/* Mirrors MPP6 */
		MPP29_GE1_9,		/* Mirrors MPP7 */
		MPP30_GE1_10,
		MPP31_GE1_11,
		MPP32_GE1_12,
		MPP33_GE1_13,
		MPP34_GPIO,		/* RGPP0 */
		MPP35_GPIO,
		MPP36_GPIO,
		MPP37_GPIO,
		MPP38_GPIO,
		MPP39_GPIO,
		MPP40_GPIO,
		MPP41_GPIO,
		MPP42_GPIO,
		MPP43_GPIO,
		MPP44_GPIO,
		MPP45_GPIO,		/* RGPP11 */
		MPP46_GPIO,		/* No connected */
		MPP47_GPIO,		/* No connected */
		MPP48_GPIO,		/* No connected */
		MPP49_GPIO,		/* Connected to SW_INT_OUTn for interrupts */
		0
	};
	kirkwood_mpp_conf(kwmpp_config);

	/*
	 * arch number of board
	 */
	gd->bd->bi_arch_number = MACH_TYPE_SBX81GP24;

	/* adress of boot parameters */
	gd->bd->bi_boot_params = kw_sdram_bar(0) + 0x100;

	return 0;
}

int dram_init(void)
{
	int i;

	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
		gd->bd->bi_dram[i].start = kw_sdram_bar(i);
		gd->bd->bi_dram[i].size = kw_sdram_bs(i);
	}

	/* set the status LED solid amber */
	pca953x_set_dir(PCA9539_ADDR, 0xf800, 0x0000);
	pca953x_set_val(PCA9539_ADDR, 0xc000, 0x4000);

	/* ensure the poe-dis line is driven low */
	pca953x_set_val(PCA9539_ADDR, 0x0800, 0x0000);

	mpi_board_init();

	return 0;
}

#ifdef CONFIG_MV88E60XX_SWITCH
/* Configure and enable Switch and PHY */
void reset_phy(void)
{
	int test_jig;
	char *env;

	/* configure and initialize switch */
	struct mv88e61xx_config swcfg = {
		.name = "egiga0",
		.vlancfg = MV88E61XX_VLANCFG_ROUTER,
		.rgmii_delay = MV88E61XX_RGMII_DELAY_EN,
		.portstate = MV88E61XX_PORTSTT_FORWARDING,
		.led_init = MV88E61XX_LED_INIT_EN,
		.mdip = MV88E61XX_MDIP_NOCHANGE,
		.ports_enabled = 0x703, /* Pt0,1,8,9,10(CPU) */
		.cpuport = (1 << 10),
	};

	env = getenv("test_jig");
	if (!env)
		test_jig = atl_bootrom_board_attached();
	else
		test_jig = simple_strtoul(env, NULL, 0);

	/* If we're running in the test jig disable ports 8 and 9 because
	 * they are looped back and prevent us from using ports 0 and 1
	 */
	if (test_jig) {
		swcfg.ports_enabled &= ~0x0300;
	}

	mv88e60xx_switch_initialize(&swcfg);
}
#endif

static int get_slot_id(void)
{
#define SLOT_ID_MASK 0x0f
	u8 gpio_reg = 0;

	if ((gpio_reg = pca953x_get_val(PCA9539_ADDR)) < 0)
		return -1;

	return gpio_reg & SLOT_ID_MASK;
}

static void setenv_slot_id(void)
{
	int slot;
	char buf[16];

	if ((slot = get_slot_id()) < 0) {
		fprintf(stderr, "Error: could not determine slot number\n");
		slot = 0;
	}

	sprintf(buf, "%d", slot);
	setenv("slot_id", buf);
}

#ifdef CONFIG_MISC_INIT_R
int misc_init_r(void)
{
	/* TODO: check our list of "reserved" MAC addresses
	 */
	uchar mac[6] = {0x02, 0x00, 0xcd, 0x00, 0x00, 0x00};

	setenv_slot_id();
	mac[5] = simple_strtoul(getenv("slot_id"), NULL, 10);
	eth_setenv_enetaddr("ethaddr", mac);
#ifdef CONFIG_DIAG_IDPROM
	idprom_setmachid();
#endif
#ifdef MTDPARTS_DEFAULT
	setenv("mtdparts", MTDPARTS_DEFAULT);
#endif
#ifdef MTDPARTS_MTDOOPS
	setenv("mtdoops", MTDPARTS_MTDOOPS);
#endif
	/* set the status LED flashing green */
	pca953x_set_val(PCA9539_ADDR, 0xc000, 0x8000);
	return 0;
}
#endif

//Not really an RTC, but enough functionality to interact with SNTP
#ifdef CONFIG_CMD_DATE
extern int mk_date (char *, struct rtc_time *);

int rtc_get(struct rtc_time *tmp)
{
	char *strtime = getenv("rtctime");
	if (strtime) {
		mk_date(strtime, tmp);
	}
	return 0;
}

int rtc_set(struct rtc_time *tmp)
{
	char strtime[16];

	//MMDDhhmmCCYY.ss
	sprintf(strtime, "%02d%02d%02d%02d%4d.%02d",
	        tmp->tm_mon, tmp->tm_mday, tmp->tm_hour,
	        tmp->tm_min, tmp->tm_year, tmp->tm_sec);
	setenv("rtctime", strtime);
	return 0;
}

void rtc_reset(void)
{
	return;
}
#endif

/*********************************************************************
 * ATL specific extensions
 *********************************************************************/

/* atl_get_offset_from_base()
 */
u32 atl_get_offset_from_base(void)
{
	/* SBx81LIFXCAT always has the special blocks at the
	 * *start* of SPI flash, so the offset is always 0.
	 */
	return 0;
}

/* atl_get_env_addr()
 *
 * Get location of U-Boot environement. ENV code was hacked a little to make this work
 */

u32 atl_get_env_addr(void)
{
	return CONFIG_ENV_ADDR;
}
