/*
 * arch/arm/mach-cxd4115/cxd4115.c
 *
 * CXD4115 devices
 *
 * Copyright 2007,2008,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; 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.
 * 
 */
#include <linux/device.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/amba/bus.h>
#include <asm/hardware.h>
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
#include <asm/arch/uart.h>
#include <asm/arch/regs-pci.h>
#include <asm/arch/regs-emc.h>
#include <asm/io.h>

static struct uart_platform_data uart0_data = {
	.chan		= 0,
	.fifosize	= 32,
	.clock		= "UARTCLK0",
};

#if defined(CONFIG_CXD4115_ENABLE_UART1)
static struct uart_platform_data uart1_data = {
	.chan		= 1,
	.fifosize	= 32,
	.clock		= "UARTCLK1",
};
#endif /* CONFIG_CXD4115_ENABLE_UART1 */

#if defined(CONFIG_CXD4115_ENABLE_UART2)
static struct uart_platform_data uart2_data = {
	.chan		= 2,
	.fifosize	= 32,
	.clock		= "UARTCLK2",
};
#endif /* CONFIG_CXD4115_ENABLE_UART2 */

#ifdef CONFIG_ARCH_CXD4120
static struct uart_platform_data uart3_data = {
	.chan		= 3,
	.fifosize	= 32,
	.clock		= "UARTCLK3",
};
#endif /* CONFIG_ARCH_CXD4120 */

static struct amba_device uart0_device = {
	.dev = {
		.bus_id = "apb:050",
		.platform_data	= &uart0_data,
	},
	.res = {
		 .start = CXD4115_UART(0),
		 .end   = CXD4115_UART(0)+SZ_4K-1,
		 .flags = IORESOURCE_MEM,
	 },
	.irq = { IRQ_UART(0), },
};

#if defined(CONFIG_CXD4115_ENABLE_UART1)
static struct amba_device uart1_device = {
	.dev = {
		.bus_id = "apb:051",
		.platform_data	= &uart1_data,
	},
	.res = {
		 .start = CXD4115_UART(1),
		 .end   = CXD4115_UART(1)+SZ_4K-1,
		 .flags = IORESOURCE_MEM,
	 },
	.irq = { IRQ_UART(1), },
};
#endif /* CONFIG_CXD4115_ENABLE_UART1 */

#if defined(CONFIG_CXD4115_ENABLE_UART2)
static struct amba_device uart2_device = {
	.dev = {
		.bus_id = "apb:052",
		.platform_data	= &uart2_data,
	},
	.res = {
		 .start = CXD4115_UART(2),
		 .end   = CXD4115_UART(2)+SZ_4K-1,
		 .flags = IORESOURCE_MEM,
	 },
	.irq = { IRQ_UART(2), },
};
#endif /* CONFIG_CXD4115_ENABLE_UART2 */

#ifdef CONFIG_ARCH_CXD4120
static struct amba_device uart3_device = {
	.dev = {
		.bus_id = "apb:053",
		.platform_data	= &uart3_data,
	},
	.res = {
		 .start = CXD4115_UART(3),
		 .end   = CXD4115_UART(3)+SZ_4K-1,
		 .flags = IORESOURCE_MEM,
	 },
	.irq = { IRQ_UART(3), },
};
#endif /* CONFIG_ARCH_CXD4120 */

#ifdef CONFIG_CXD4115_PCIBRIDGE_ENABLE
static struct amba_device pcibridge_device = {
	.dev = {
		.bus_id = "ahb:6",
	},
	.res = {
		 .start = CXD4115_PCI_BASE,
		 .end   = CXD4115_PCI_BASE+CXD4115_PCI_SIZE-1,
		 .flags = IORESOURCE_MEM,
	 },
	.irq = { IRQ_PCI_BRIDGE, },
	.periphid = CXD4115_PCI_BASE,
};
#endif /* CONFIG_CXD4115_PCIBRIDGE_ENABLE */

static struct amba_device *amba_devs[] __initdata = {
	&uart0_device,
#ifdef CONFIG_CXD4115_ENABLE_UART1
	&uart1_device,
#endif /* CONFIG_CXD4115_ENABLE_UART1 */
#ifdef CONFIG_CXD4115_ENABLE_UART2
	&uart2_device,
#endif /* CONFIG_CXD4115_ENABLE_UART2 */
#ifdef CONFIG_ARCH_CXD4120
	&uart3_device,
#endif /* CONFIG_ARCH_CXD4120 */

#ifdef CONFIG_CXD4115_PCIBRIDGE_ENABLE
	&pcibridge_device,
#endif
};

#if defined(CONFIG_SMC9118)

#ifdef CONFIG_CXD4115_SMC9118_ON_EMC_CS1
# define SMC911X_INT  2 /* IRQ = GPIOSYS 2 */
# define SMC9118_MEM_START  EMC_CS1_BASE
# define SMC9118_EMC_CS 1
#else
# define SMC911X_INT 13 /* IRQ = GPIOSYS13 */
# define SMC9118_MEM_START  EMC_CS2_BASE
# define SMC9118_EMC_CS 2
#endif

static struct resource smc9118_resources[] = {
	[0] = {
		.start	= SMC9118_MEM_START,
		.end	= SMC9118_MEM_START + SZ_4K-1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start  = IRQ_GPIO_FALL(SMC911X_INT),
		.end	= IRQ_GPIO_FALL(SMC911X_INT),
		.flags	= IORESOURCE_IRQ,
	}
};

static struct platform_device smc9118_device = {
	.name		= "smc911x",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(smc9118_resources),
	.resource	= smc9118_resources,
};
#endif /* CONFIG_SMC9118 */

#ifdef CONFIG_ARCH_CXD4120
# define EMCSYSCLK 129000000
#else /* CONFIG_ARCH_CXD4115 */
# define EMCSYSCLK (CONFIG_CXD4115_CPUCLK/2)
#endif

#if EMCSYSCLK == 129000000
 /* period=7.75ns */
 /* AM2 PRM1 (TWEA=0x02, TWEN=0x06, TWDH=0x02, TWWAIT=0x04) */
 /*                15.5,      46.5,      15.5,        31.0  */
# define EMC_PARAM1 0x02060204
 /* AM2 PRM2 (TOEA=0x02, TRDLAT=0x08, TRAH=0x00, TRWAIT=0x04) */
 /*                15.5,      62.0,           0,        31.0  */
# define EMC_PARAM2 0x02080004
#elif EMCSYSCLK == 78000000
 /* period=12.8ns */
 /* AM2 PRM1 (TWEA=0x01, TWEN=0x04, TWDH=0x01, TWWAIT=0x02) */
 /*                12.8,      51.2,      12.8,        25.6  */
# define EMC_PARAM1 0x01040102
 /* AM2 PRM2 (TOEA=0x01, TRDLAT=0x05, TRAH=0x00, TRWAIT=0x02) */
 /*                12.8,        64.0,         0,        25.6  */
# define EMC_PARAM2 0x01050002
#elif EMCSYSCLK == 66000000
 /* period=15ns */
 /* AM2 PRM1 (TWEA=0x01, TWEN=0x03, TWDH=0x01, TWWAIT=0x02) */
 /*                15.0,      45.0,        15,        30.0  */
# define EMC_PARAM1 0x01030102
 /* AM2 PRM2 (TOEA=0x01, TRDLAT=0x04, TRAH=0x00, TRWAIT=0x02) */
 /*                15.0,        60.0,         0,        30.0  */
# define EMC_PARAM2 0x01040002
#else
# error "Unknown EMCSYSCLK"
#endif

#if defined(CONFIG_SMC9118)
static void emc_init(void)
{
	u32 dat;

	/* bind EMC CSx to AM2 */
	dat = readl(VA_EMC + EMC_CS_PHY_BIND);
	dat &= ~(EMC_CS_MASK << EMC_CS_SHIFT(SMC9118_EMC_CS));
	dat |= EMC_PHY_AM2 << EMC_CS_SHIFT(SMC9118_EMC_CS);
	writel(dat, VA_EMC + EMC_CS_PHY_BIND);

	/* config AM2 as PageMode:OFF,DW=16bit */
	writel(EMC_PHY_AM_DW16, VA_EMC + EMC_PHY_AM2_CFG);
	writel(EMC_PARAM1, VA_EMC + EMC_PHY_AM2_PRM1);
	writel(EMC_PARAM2, VA_EMC + EMC_PHY_AM2_PRM2);
}
#else /* CONFIG_SMC9118 */
# define emc_init() do {} while (0)
#endif /* CONFIG_SMC9118 */

static void __init cxd4115_init(void)
{
	int i, ret;
	struct amba_device **dev;

	emc_init();
	ret = cxd4115_core_init();
	for (i = 0, dev = amba_devs; i < ARRAY_SIZE(amba_devs); i++, dev++) {
		ret = amba_device_register(*dev, &iomem_resource);
		if (ret) {
			printk(KERN_ERR "cxd4115_init: amba_device_register failed.\n");
		}
	}

#if defined(CONFIG_SMC9118)
	ret = platform_device_register(&smc9118_device);
	if (ret) {
		printk(KERN_ERR "cxd4115_init: platform_device_register failed.\n");
	}
#endif /* CONFIG_SMC9118 */
}

MACHINE_START(CXD4115, "ARM-CXD4115")
	/* Maintainer: Sony Corporation */
	.phys_io	= CXD4115_APB_BASE,
	.io_pg_offst	= (IO_ADDRESS(CXD4115_APB_BASE) >> 18) & 0xfffc,
	.map_io		= cxd4115_map_io,
	.init_irq	= cxd4115_init_irq,
	.timer		= &cxd4115_timer,
	.init_machine	= cxd4115_init,
MACHINE_END
