/*
 * (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 <asm/arch/kirkwood.h>
#include <asm/arch/mpp.h>
#include <asm/arch/gpio.h>
#include <idprom.h>
#include "sbx81lifkw.h"
#include "../SBx8100/mpi.h"

DECLARE_GLOBAL_DATA_PTR;

struct led {
	u32 reg;
	u32 value;
	u32 mask;
};


struct led amber_solid = {
	KW_GPIO0_BASE,
	(1 << 10),
	(1 << 18) | (1 << 10)
};

struct led green_solid = {
	KW_GPIO0_BASE,
	(1 << 18) | (1 << 10),
	(1 << 18) | (1 << 10)
};

struct led amber_flash = {
	KW_GPIO0_BASE,
	0,
	(1 << 18) | (1 << 10)
};

struct led green_flash = {
	KW_GPIO0_BASE,
	(1 << 18),
	(1 << 18) | (1 << 10)
};


static void status_led_set(struct led *led)
{
	u32 val;
	val = readl(led->reg);
	val &= ~led->mask;
	val |= led->value;
	writel(val,led->reg);
}

int board_init(void)
{
	/* Power-down unused subsystems. The required
	 * subsystems are:
	 *
	 *  GE0         b0
	 *  PEX0 PHY    b1
	 *  PEX0.0      b2
	 *  TSU         b5
	 *  SDRAM       b6
	 *  RUNIT       b7
	 */
	writel(((1 << 0) | (1 << 1) | (1 << 2) |
		(1 << 5) | (1 << 6) | (1 << 7) ),
		KW_CPU_REG_BASE + 0x1c );

	/*
	 * 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(SBX81LIFKW_OE_VAL_LOW,
		       SBX81LIFKW_OE_VAL_HIGH,
		       SBX81LIFKW_OE_LOW, SBX81LIFKW_OE_HIGH);

	/* Multi-Purpose Pins Functionality configuration */
	u32 kwmpp_config[] = {
		MPP0_SPI_SCn,
		MPP1_SPI_MOSI,
		MPP2_SPI_SCK,
		MPP3_SPI_MISO,
		MPP4_UART0_RXD,
		MPP5_UART0_TXD,
		MPP6_SYSRST_OUTn,
		MPP7_PEX_RST_OUTn,
		MPP8_TW_SDA,
		MPP9_TW_SCK,
		MPP10_GPO,
		MPP11_GPIO,
		MPP12_GPO,
		MPP13_GPIO,
		MPP14_GPIO,
		MPP15_UART0_RTS,
		MPP16_UART0_CTS,
		MPP17_GPIO,
		MPP18_GPO,
		MPP19_GPO,
		MPP20_GPIO,
		MPP21_GPIO,
		MPP22_GPIO,
		MPP23_GPIO,
		MPP24_GPIO,
		MPP25_GPIO,
		MPP26_GPIO,
		MPP27_GPIO,
		MPP28_GPIO,
		MPP29_GPIO,
		MPP30_GPIO,
		MPP31_GPIO,
		MPP32_GPIO,
		MPP33_GPIO,
		MPP34_GPIO,
		MPP35_GPIO,
		MPP36_GPIO,
		MPP37_GPIO,
		MPP38_GPIO,
		MPP39_GPIO,
		MPP40_GPIO,
		MPP41_GPIO,
		MPP42_GPIO,
		MPP43_GPIO,
		MPP44_GPIO,
		MPP45_GPIO,
		MPP46_GPIO,
		MPP47_GPIO,
		MPP48_GPIO,
		MPP49_GPIO,
		0
	};
	kirkwood_mpp_conf(kwmpp_config);

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

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

	status_led_set(&amber_solid);

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

	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_DIS,
		.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;
	}

	/* Ensure the 88e6097 gets at least 10ms Reset
	 */
	kw_gpio_set_value(MV88E6097_RESET, 0);
	udelay(20000);
	kw_gpio_set_value(MV88E6097_RESET, 1);

	mv88e60xx_switch_initialize(&swcfg);
}
#endif

static int get_slot_id(void)
{
#define SID0_3_SHIFT (46-32)
#define SID0_3_MASK  (0x0f << SID0_3_SHIFT)
#define SID4_SHIFT (45-32)
#define SID4_MASK  (1 << SID4_SHIFT)

	int slot = 0;
	uint data_h = readl(KW_GPIO1_BASE + 0x10);

	slot |= ((data_h & SID0_3_MASK) >> SID0_3_SHIFT);
	/* slot |= (data_h & SID4_MASK) >> (SID4_SHIFT - 4); */

	return slot;
}

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
	status_led_set(&green_flash);

	return 0;
}
#endif

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

/* atl_get_offset_from_base()
 */
u32 atl_get_offset_from_base(void)
{
	/* SBx81GP24 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 environment. ENV code was hacked a little to make this work
 */

u32 atl_get_env_addr(void)
{
	return CONFIG_ENV_ADDR;
}
