/*
 * board/alliedtelesis/SBx8100/mpi.c
 *
 * Copyright (c) 2010 Allied Telesis Labs
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2. This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
 */
#include <common.h>
#include <i2c.h>
#include <pca953x.h>
#include "mpi.h"

enum {
	DIR_OUT = 0,
	DIR_IN,
};

enum {
	MPI_SPI_EN = 0,
	SD_EN,
	FLASH_EN,
	SPI_WP_N,
	OUT_EN_N,
	WE,
	SD_PROTECTED,
	SD_DETECT_N,
	CONF1,
	CONF2,
	CONF3,
	CONF4,
	LED1,
	LED2,
	LED3,
	LED4,
};

#define GPIO_DIR(pin, dir)	(dir << pin)
#define BIT(pin)		(1 << pin)

static const uint gpio_dirs =
	GPIO_DIR(MPI_SPI_EN, DIR_OUT) |
	GPIO_DIR(SD_EN, DIR_OUT) |
	GPIO_DIR(FLASH_EN, DIR_OUT) |
	GPIO_DIR(SPI_WP_N, DIR_OUT) |
	GPIO_DIR(OUT_EN_N, DIR_OUT) |
	GPIO_DIR(WE, DIR_OUT) |
	GPIO_DIR(SD_PROTECTED, DIR_IN) |
	GPIO_DIR(SD_DETECT_N, DIR_IN) |
	GPIO_DIR(CONF1, DIR_IN) |
	GPIO_DIR(CONF2, DIR_IN) |
	GPIO_DIR(CONF3, DIR_IN) |
	GPIO_DIR(CONF4, DIR_IN) |
	GPIO_DIR(LED1, DIR_IN) |
	GPIO_DIR(LED2, DIR_IN) |
	GPIO_DIR(LED3, DIR_IN) |
	GPIO_DIR(LED4, DIR_IN);

int mpi_board_attached(void)
{
	return i2c_probe(CONFIG_MPI_GPIO) == 0;
}

int mpi_board_init(void)
{
	if (!mpi_board_attached())
		return -1;

	printf("MPI board detected\n");

	/* set the outputs to a sensible value */
	pca953x_set_val(CONFIG_MPI_GPIO,
		BIT(MPI_SPI_EN) | BIT(SPI_WP_N) | BIT(SD_EN) |
		BIT(FLASH_EN) | BIT(SPI_WP_N) | BIT(WE),
		0);

	/* setup the GPIO direction */
	pca953x_set_dir(CONFIG_MPI_GPIO, 0xffff, gpio_dirs);

	/* enable id programming */
	pca953x_set_val(CONFIG_MPI_GPIO, BIT(OUT_EN_N)|BIT(WE),
			BIT(WE));

	return 0;
}

/**
 * Configure the MPI board for SPI flash access.
 * \param enable - enable(!0) or disable(0) access to the MPI's spi flash
 * \param write -  enable(!0) or disable(0) write access
 */
int mpi_spi_flash_select(int enable, int write)
{
	uint mask = BIT(MPI_SPI_EN) | BIT(SPI_WP_N) | BIT(FLASH_EN) | BIT(OUT_EN_N);
	uint data;

	if (write)
		mask |= (BIT(SPI_WP_N) | BIT(WE));
	if (enable) {
		data = mask;
		setenv("spi_clock", "0x100000");
	} else {
		data = 0;
		setenv("spi_clock", NULL);
	}

	return pca953x_set_val(CONFIG_MPI_GPIO, mask, data);
}

static int do_mpi_select (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int enable, write;

	if (argc < 3) {
		cmd_usage(cmdtp);
		return -1;
	}

	enable = simple_strtol(argv[1], NULL, 10);
	write = simple_strtoul(argv[2], NULL, 10);

	return mpi_spi_flash_select(enable, write);
}

U_BOOT_CMD(
	mpisel, 3, 1, do_mpi_select,
	"select MPI board flash",
	"enable write\n"
	"\tenable - enable(!0) or disable(0) access\n"
	"\twrite  - enable(!0) or disable(0)\n"
	);

#ifdef CONFIG_USE_MPI_BOARD_SD

u8 sdcard_is_present(void)
{
	return ! (pca953x_get_val(CONFIG_MPI_GPIO) & BIT(SD_DETECT_N));
}

static u8 sdcard_changed = 0;

u8 sdcard_has_changed(void)
{
	u8 ret = sdcard_changed;
	static u8 old_present = 0;
	u8 present = sdcard_is_present();
	if (present != old_present) {
		old_present = present;
		sdcard_changed = 1;
	} else {
		sdcard_changed = 0;
	}

	return ret;
}

void sdcard_check_changed(void)
{
	/* This is called from the serial getc routine (where the
	 * bootloader spends most of its idle time). Unfortunately
	 * the gpio that tells us about the presence is at the end of
	 * the i2c bus which is slow to access and makes us lose
	 * characters if we constantly poll it.
	 */
}

u8 sdcard_write_protected(void)
{
	return !! (pca953x_get_val(CONFIG_MPI_GPIO) & BIT(SD_PROTECTED));
}

void sdcard_select(u8 select)
{
	uint mask = BIT(MPI_SPI_EN) | BIT(SD_EN);
	uint data;

	data = select ? mask : 0;

	pca953x_set_val(CONFIG_MPI_GPIO, mask, data);
}
#endif
