/* 2009-08-18: File added and changed by Sony Corporation */
/*
 *  Copyright 2009 Sony Corporation.
 *
 *  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;  version 2 of the  License.
 *
 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  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.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <asm/io.h>
#include <asm/arch/smu.h>
#ifdef CONFIG_MXC_MC13783_POWER
#include <asm/arch/pmic_power.h>
#endif /* CONFIG_MXC_MC13783_POWER */

int __initdata pgpf_serial_ports[] = { 1, 1, 1, 0 };

extern struct platform_device pgpf_eth_device;
extern struct platform_device pgpf_bl_device;
extern struct platform_device pgpf_lcd_device;
extern struct platform_device pgpf_gpio_device;

static struct resource mc13783_resources[] = {
	{
		.start = PGPF_GPIO_ATLAS_INT + INT_GPIO_BASE,
		.end   = PGPF_GPIO_ATLAS_INT + INT_GPIO_BASE,
		.flags = IORESOURCE_IRQ,
	},
};

static struct platform_device mc13783_device = {
	.name = "navu2_mc13783",
	.id = -1,
	.num_resources = ARRAY_SIZE(mc13783_resources),
	.resource = mc13783_resources,
};

static struct platform_device ad7795_device ={
	.name = "xyz5-adc",
	.id   = -1,
};

static struct uart_port navu2_extuart_port = {
	.iobase = 0,
	.membase = 0,
	.irq = PGPF_GPIO_EXUART0_INT + INT_GPIO_BASE,
	.uartclk = NAVU2_REFCLKO_CLK * 4,
	.fifosize = 32,
	.regshift = 1,
	.iotype = UPIO_MEM,
	.flags = UPF_IOREMAP,
	.mapbase = (unsigned long)NAVU2_EXUART_BASE,
};
 
static struct platform_device *devs[] __initdata = {
	&pgpf_eth_device,
	&mc13783_device,
	&ad7795_device,
	&pgpf_bl_device,
	&pgpf_lcd_device,
	&pgpf_gpio_device,
};

void __init pgpf_device_init(void)
{
	platform_add_devices(devs, ARRAY_SIZE(devs));
}

#ifdef CONFIG_MXC_MC13783_POWER
static int __init navu2_regulator_init(void)
{
	t_regulator_config config;

	/* turn on VMMC1(2.8V) */
	config.voltage.vmmc1 = VMMC1_2_8V;
	config.lp_mode = LOW_POWER_CTRL_BY_PIN;
	pmic_power_regulator_set_config(REGU_VMMC1, &config);
	pmic_power_regulator_on(REGU_VMMC1);

	return 0;
}
late_initcall(navu2_regulator_init);
#endif /* CONFIG_MXC_MC13783_POWER */

static int __init navu2_gpio_fixup(void)
{
	gpio_direction_input(PGPF_GPIO_BT_RXD);
	gpio_direction_input(PGPF_GPIO_BT_TXD);
	gpio_direction_input(PGPF_GPIO_BT_XCTS);
	gpio_direction_input(PGPF_GPIO_BT_XRTS);
	gpio_direction_input(PGPF_GPIO_GPS_RXD);
	gpio_direction_input(PGPF_GPIO_GPS_TXD);

	/* set GPS_TXD / GPS_RXD to UART */
	__raw_writel(__raw_readl(CHG_PINSEL_G80) | 0x00002800, CHG_PINSEL_G80);
	
	return 0;
}
device_initcall(navu2_gpio_fixup);

static int __init navu2_exuart_fixup(void)
{
	void *exuart_base;
	u8 lcr, efr;
#define LCR 6
#define EFR 4
#define DLD 4

	exuart_base = ioremap(NAVU2_EXUART_BASE, 16);
	if (!exuart_base)
		return -1;

	/* set base baud x4 mode */
	lcr = __raw_readb(exuart_base + LCR); /* save LCR */
	__raw_writeb(0xbf, exuart_base + LCR); /* for EFR accesss */
	efr = __raw_readb(exuart_base + EFR); /* save EFR */
	__raw_writeb(efr | 0x10, exuart_base + EFR); /* for DLD access */
	__raw_writeb(0x80, exuart_base + LCR); /* for DLD access */
	__raw_writeb(0x20, exuart_base + DLD); /* set x4 mode */
	__raw_writeb(0xbf, exuart_base + LCR); /* for EFR access */
	__raw_writeb(efr, exuart_base + EFR); /* restore EFR */
	__raw_writeb(lcr, exuart_base + LCR); /* restore LCR */

	iounmap(exuart_base);

	serial8250_register_port(&navu2_extuart_port);

	return 0;
}
late_initcall(navu2_exuart_fixup);

void pgpf_reset_hook(void)
{
	extern int mp200_fb_suspend(struct platform_device *dev, pm_message_t state);
	pmic_power_regulator_off(REGU_GPO1);
	pmic_power_regulator_off(REGU_GPO2);
	mp200_fb_suspend(NULL, PMSG_SUSPEND);
}
