/*
 * sysmocom sysmobts v2 board support.
 * Copyright (C) 2012 sysmocom s.f.m.c GmbH
 * Copyright (C) 2011 Lyrtech <www.lyrtech.com>
 *
 * Based on SFF-SDR platform, original copyright follows:
 
 * Lyrtech SFFSDR board support.
 *
 * Copyright (C) 2008 Philip Balister, OpenSDR <philip@opensdr.com>
 * Copyright (C) 2008 Lyrtech <www.lyrtech.com>
 *
 * Based on DV-EVM platform, original copyright follows:
 *
 * Copyright (C) 2007 MontaVista Software, Inc.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/memblock.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/leds.h>

#include <linux/i2c.h>
#include <linux/i2c/at24.h>
#include <linux/regulator/machine.h>
#include <linux/etherdevice.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/io.h>
#include <linux/dspdl.h>
#include <linux/fpgadl.h>
#include <linux/serial_8250.h>

#include <asm/setup.h>
#include <asm/mach-types.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/flash.h>

#include <mach/common.h>
#include <linux/platform_data/i2c-davinci.h>
#include <mach/serial.h>
#include <mach/mux.h>
#include <linux/platform_data/mtd-davinci.h>
#include <linux/platform_data/mmc-davinci.h>

#include "davinci.h"

#define SYSMOBTS_V2_PHY_ID					"davinci_mdio-0:01"
#define SYSMOBTS_V2_PHY_MASK					(0x2)

#define DAVINCI_ASYNC_EMIF_CONTROL_BASE   	0x01e00000
#define DAVINCI_ASYNC_EMIF_DATA_CE0_BASE  	0x02000000

#define DAVINCI_DSP_L1D_EMIF_BASE  			0x11F04000
#define DAVINCI_DSP_L1D_EMIF_SIZE  			0x00014000
#define DAVINCI_DSP_L1P_EMIF_BASE  			0x11E08000
#define DAVINCI_DSP_L1P_EMIF_SIZE  			0x00008000
#define DAVINCI_DSP_L2_EMIF_BASE  			0x11800000
#define DAVINCI_DSP_L2_EMIF_SIZE  			0x00010000
#define DAVINCI_DSP_DDR_EMIF_BASE  			0x86D00000
#define DAVINCI_DSP_DDR_EMIF_SIZE  			0x00600000

#define SYSMOBTS_V2_GPIO_BOARD_REV_D0			GPIO(15)
#define SYSMOBTS_V2_GPIO_BOARD_REV_D1			GPIO(16)
#define SYSMOBTS_V2_GPIO_BOARD_REV_D2			GPIO(17)
#define SYSMOBTS_V2_GPIO_MMC_CD				GPIO(7)
#define SYSMOBTS_V2_GPIO_LED_RF_ACTIVITY		GPIO(29)
#define SYSMOBTS_V2_GPIO_LED_RF_ONLINE		GPIO(31)
#define SYSMOBTS_V2_GPIO_FPGA_BUSY			GPIO(45)
#define SYSMOBTS_V2_GPIO_FPGA_DONE			GPIO(46)
#define SYSMOBTS_V2_GPIO_FPGA_INIT_B			GPIO(50)
#define SYSMOBTS_V2_GPIO_FPGA_PROG_B			GPIO(51)

#define SYSMOBTS_V2_FPGA_SELECTMAP_BASE		0x04000000

#define FPGA_MEM_BASE						(0x04000000)
#define FPGA_MEM_SIZE						(0x02000000)
#define FPGA_UART0_MEM_BASE					(0x04000800)
#define FPGA_UART1_MEM_BASE					(0x04000810)
#define FPGA_UART0_IRQ						GPIO(6)
#define FPGA_UART1_IRQ						GPIO(7)

static struct plat_serial8250_port sysmobts_v2_multitrx_uart_platform_data[] = {
	{
		.mapbase	= FPGA_UART0_MEM_BASE,
		.irq		= 0,
		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
		.iotype		= UPIO_MEM,
		.regshift	= 1,
		.uartclk	= 54000000,
	},
	{
		.mapbase	= FPGA_UART1_MEM_BASE,
		.irq		= 0,
		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
		.iotype		= UPIO_MEM,
		.regshift	= 1,
		.uartclk	= 54000000,
	},
	{
		.flags		= 0
	},
};

static void sysmobts_v2_multitrx_uart_release(struct device *device)
{
}

static struct platform_device sysmobts_v2_multitrx_uart_device;
static struct platform_device sysmobts_v2_multitrx_uart_device_template =
{
	.name = "serial8250",
	.id = PLAT8250_DEV_PLATFORM1,
	.dev = {
		.platform_data = sysmobts_v2_multitrx_uart_platform_data,
		.release = sysmobts_v2_multitrx_uart_release,
	},
};

struct mtd_partition sysmobts_v2_nandflash_partition[] = {
	{
		.name		= "Please use the command-line",
		.offset		= 0,
		.size		= SZ_128K,
		.mask_flags	= MTD_WRITEABLE, /* force read-only */
	},
};

static struct davinci_nand_pdata sysmobts_v2_nandflash_data = {
	.parts		= sysmobts_v2_nandflash_partition,
	.nr_parts	= ARRAY_SIZE(sysmobts_v2_nandflash_partition),
	.ecc_mode	= NAND_ECC_HW,
	.bbt_options	= NAND_BBT_USE_FLASH,
	.ecc_bits	= 1,
};

static struct resource sysmobts_v2_nandflash_resource[] = {
	{
		.start		= DAVINCI_ASYNC_EMIF_DATA_CE0_BASE,
		.end		= DAVINCI_ASYNC_EMIF_DATA_CE0_BASE + 0x1F,
		.flags		= IORESOURCE_MEM,
	}, {
		.start		= DAVINCI_ASYNC_EMIF_CONTROL_BASE,
		.end		= DAVINCI_ASYNC_EMIF_CONTROL_BASE + SZ_4K - 1,
		.flags		= IORESOURCE_MEM,
	},
};

static struct platform_device sysmobts_v2_nandflash_device = {
	.name		= "davinci_nand", /* Name of driver */
	.id		= 0,
	.dev		= {
		.platform_data	= &sysmobts_v2_nandflash_data,
	},
	.num_resources	= ARRAY_SIZE(sysmobts_v2_nandflash_resource),
	.resource	= sysmobts_v2_nandflash_resource,
};

static struct at24_platform_data eeprom_info = {
	.byte_len	= (2*1024) / 8,
	.page_size	= 8,
	.flags		= 0,
	.setup      = davinci_get_mac_addr,
	.context	= (void *)0x0000,
};

static struct at24_platform_data eeprom_info_revD = {
	.byte_len	= (64*1024) / 8,
	.page_size	= 32,
	.flags		= AT24_FLAG_ADDR16,
	.setup		= davinci_get_mac_addr,
	.context        = (void *)0x0000,
};


/* TPS65023 voltage regulator support */
/* 1.2V Core */
static struct regulator_consumer_supply tps65023_dcdc1[] = {
	{
		.supply = "1.2V_core",
	},
};

/* 1.8V I/O */
static struct regulator_consumer_supply tps65023_dcdc2[] = {
	{
		.supply = "1.8V_io",
	},
};

/* 3.3V I/O */
static struct regulator_consumer_supply tps65023_dcdc3[] = {
	{
		.supply = "3.3V_io",
	},
};

/* 1.8V PLL */
static struct regulator_consumer_supply tps65023_ldo1[] = {
	{
		.supply = "1.8V_pll",
	},
};

/* Unused */
static struct regulator_consumer_supply tps65023_ldo2[] = {
	{
		.supply = "unused",
	},
};

static struct regulator_init_data tps65023_regulator_data[] = {
	/* dcdc1 */
	{
		.constraints = {
			.min_uV = 1150000,
			.max_uV = 1350000,
			.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
			.always_on = 1,
			.boot_on = 1,
		},
		.num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc1),
		.consumer_supplies = tps65023_dcdc1,
	},
	/* dcdc2 */
	{
		.constraints = {
			.min_uV = 1800000,
			.max_uV = 1800000,
			.always_on = 1,
			.boot_on = 1,
		},
		.num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc2),
		.consumer_supplies = tps65023_dcdc2,
	},
	/* dcdc3 */
	{
		.constraints = {
			.min_uV = 3300000,
			.max_uV = 3300000,
			.always_on = 1,
			.boot_on = 1,
		},
		.num_consumer_supplies = ARRAY_SIZE(tps65023_dcdc3),
		.consumer_supplies = tps65023_dcdc3,
	},
	/* ldo1 */
	{
		.constraints = {
			.min_uV = 1800000,
			.max_uV = 1800000,
			.always_on = 1,
			.boot_on = 1,
		},
		.num_consumer_supplies = ARRAY_SIZE(tps65023_ldo1),
		.consumer_supplies = tps65023_ldo1,
	},
	/* ldo2 */
	{
		.constraints = {
			.min_uV = 2500000,
			.max_uV = 3300000,
			.boot_on = 1,
			.always_on = 1,
		},
		.num_consumer_supplies = ARRAY_SIZE(tps65023_ldo2),
		.consumer_supplies = tps65023_ldo2,
	},
};

static struct i2c_board_info __initdata i2c_info[] =  {
	{
		/* Ethernet EEPROM */
		I2C_BOARD_INFO("24c02", 0x50),
		.platform_data	= &eeprom_info,
	},
	{
		/* Power Management IC */
		I2C_BOARD_INFO("tps65023", 0x48),
		.platform_data = &tps65023_regulator_data[0],
	},
	{
		/* Temperature Sensor */
		I2C_BOARD_INFO("tmp411", 0x4D),
	},
};

static struct i2c_board_info __initdata i2c_info_revD[] = {
	{
		/* Ethernet EEPROM */
		I2C_BOARD_INFO("24c64", 0x50),
		.platform_data  = &eeprom_info_revD,
	},
	{
		/* Power Management IC */
		I2C_BOARD_INFO("tps65023", 0x48),
		.platform_data = &tps65023_regulator_data[0],
	},
	{
		/* Temperature Sensor */
		I2C_BOARD_INFO("tmp411", 0x4D),
	},
};

static struct davinci_i2c_platform_data i2c_pdata = {
	.bus_freq	= 100 	/* kHz */,
	.bus_delay	= 2 	/* usec */,
	.sda_pin	= 44	/* for bus recovery */,
	.scl_pin	= 43	/* for bus recovery */,
};

static int __init sysmobts_v2_get_board_rev(void)
{
	int boardRev;

	gpio_request(SYSMOBTS_V2_GPIO_BOARD_REV_D0, "board rev (b0)");
	gpio_request(SYSMOBTS_V2_GPIO_BOARD_REV_D1, "board rev (b1)");
	gpio_request(SYSMOBTS_V2_GPIO_BOARD_REV_D2, "board rev (b2)");

	gpio_direction_input(SYSMOBTS_V2_GPIO_BOARD_REV_D0);
	gpio_direction_input(SYSMOBTS_V2_GPIO_BOARD_REV_D1);
	gpio_direction_input(SYSMOBTS_V2_GPIO_BOARD_REV_D2);

	boardRev = ((gpio_get_value(SYSMOBTS_V2_GPIO_BOARD_REV_D0) != 0) << 0)
		| ((gpio_get_value(SYSMOBTS_V2_GPIO_BOARD_REV_D1) != 0) << 1)
		| ((gpio_get_value(SYSMOBTS_V2_GPIO_BOARD_REV_D2) != 0) << 2);

	gpio_free(SYSMOBTS_V2_GPIO_BOARD_REV_D0);
	gpio_free(SYSMOBTS_V2_GPIO_BOARD_REV_D1);
	gpio_free(SYSMOBTS_V2_GPIO_BOARD_REV_D2);
	return boardRev;
}

static void __init sysmobts_v2_init_i2c(void)
{
	davinci_init_i2c(&i2c_pdata);

	/* Install the info with the right EEPROM address */
	if (sysmobts_v2_get_board_rev() <= 2)
		i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info));
	else
		i2c_register_board_info(1, i2c_info_revD,
						ARRAY_SIZE(i2c_info_revD));
}

static void sysmobts_v2_teardown_fpga_device(void)
{
	platform_device_unregister(&sysmobts_v2_multitrx_uart_device);

	gpio_free(FPGA_UART0_IRQ);
	gpio_free(FPGA_UART1_IRQ);
}
	
static void sysmobts_v2_setup_fpga_device(void)
{
	sysmobts_v2_multitrx_uart_device = sysmobts_v2_multitrx_uart_device_template;

	gpio_request(FPGA_UART0_IRQ, "multi-trx uart0 irq");
	gpio_direction_input(FPGA_UART0_IRQ);
	sysmobts_v2_multitrx_uart_platform_data[0].irq = gpio_to_irq(FPGA_UART0_IRQ);

	gpio_request(FPGA_UART1_IRQ, "multi-trx uart1 irq");
	gpio_direction_input(FPGA_UART1_IRQ);
	sysmobts_v2_multitrx_uart_platform_data[1].irq = gpio_to_irq(FPGA_UART1_IRQ);

	platform_device_register(&sysmobts_v2_multitrx_uart_device);
}	

static struct fpgadl_pdata_t sysmobts_v2_fpgadl_pdata = {
	.bitstream_max_size	= 0x200000,
	.program_b		= SYSMOBTS_V2_GPIO_FPGA_PROG_B,
	.done			= SYSMOBTS_V2_GPIO_FPGA_DONE,
	.busy			= SYSMOBTS_V2_GPIO_FPGA_BUSY,
	.init_b			= SYSMOBTS_V2_GPIO_FPGA_INIT_B,
	.setup			= sysmobts_v2_setup_fpga_device,
	.teardown		= sysmobts_v2_teardown_fpga_device,
};

static struct resource sysmobts_v2_fpgadl_resources[] = {
	{
		.name		= "selectmap",
		.start		= SYSMOBTS_V2_FPGA_SELECTMAP_BASE,
		.end		= SYSMOBTS_V2_FPGA_SELECTMAP_BASE + 4 - 1,
		.flags		= IORESOURCE_MEM,
	}
};

static struct platform_device sysmobts_v2_fpgadl_device = {
	.name		= "fpgadl_par",
	.id		= 0,
	.dev		= {
		.platform_data = &sysmobts_v2_fpgadl_pdata,
	},
	.num_resources	= ARRAY_SIZE(sysmobts_v2_fpgadl_resources),
	.resource 	= sysmobts_v2_fpgadl_resources, 
};

static struct resource sysmobts_v2_dspdl_resources[] = {
	{
		.name		= "l1d",
		.start		= DAVINCI_DSP_L1D_EMIF_BASE,
		.end		= DAVINCI_DSP_L1D_EMIF_BASE + DAVINCI_DSP_L1D_EMIF_SIZE - 1,
		.flags		= IORESOURCE_MEM,
	},
	{
		.name		= "l1p",
		.start		= DAVINCI_DSP_L1P_EMIF_BASE,
		.end		= DAVINCI_DSP_L1P_EMIF_BASE + DAVINCI_DSP_L1P_EMIF_SIZE - 1,
		.flags		= IORESOURCE_MEM,
	},
	{
		.name		= "l2",
		.start		= DAVINCI_DSP_L2_EMIF_BASE,
		.end		= DAVINCI_DSP_L2_EMIF_BASE + DAVINCI_DSP_L2_EMIF_SIZE - 1,
		.flags		= IORESOURCE_MEM,
	},
	{
		.name		= "ddr",
		.start		= DAVINCI_DSP_DDR_EMIF_BASE,
		.end		= DAVINCI_DSP_DDR_EMIF_BASE + DAVINCI_DSP_DDR_EMIF_SIZE - 1,
		.flags		= IORESOURCE_MEM,
	}
};

static struct platform_device sysmobts_v2_dspdl_device = {
	.name		= "dspdl_dm644x",
	.id		= 0,
	.num_resources	= ARRAY_SIZE(sysmobts_v2_dspdl_resources),
	.resource 	= sysmobts_v2_dspdl_resources, 
};

static int sysmobts_v2_mmc_get_cd(int module)
{
	/* low == card present */
	return !gpio_get_value_cansleep(SYSMOBTS_V2_GPIO_MMC_CD);
}

static int sysmobts_v2_mmc_get_ro(int module)
{
	/* high == card's write protect switch active */
	return 0;
}

static struct davinci_mmc_config sysmobts_v2_mmc_config = {
	.get_cd		= sysmobts_v2_mmc_get_cd,
	.get_ro		= sysmobts_v2_mmc_get_ro,
	.wires		= 4,
};

static struct gpio_led sysmobts_v2_gpio_leds[] = {
	{
		.name			= "activity_led",
		.gpio			= SYSMOBTS_V2_GPIO_LED_RF_ACTIVITY,
	},
	{
		.name			= "online_led",
		.gpio			= SYSMOBTS_V2_GPIO_LED_RF_ONLINE,
	},
};

static struct gpio_led_platform_data sysmobts_v2_gpio_leds_info = {
	.leds		= sysmobts_v2_gpio_leds,
	.num_leds	= ARRAY_SIZE(sysmobts_v2_gpio_leds),
};

static struct platform_device sysmobts_v2_led_device = {
	.name		= "leds-gpio",
	.id		= -1,
	.dev		= {
		.platform_data = &sysmobts_v2_gpio_leds_info,
	},
};

static struct platform_device *sysmobts_v2_devices[] __initdata = {
	&sysmobts_v2_led_device,
	&sysmobts_v2_fpgadl_device,
	&sysmobts_v2_dspdl_device,
	&sysmobts_v2_nandflash_device,
};

static struct davinci_uart_config uart_config __initdata = {
	.enabled_uarts = (1 << 0) | (1 << 1) | (1 << 2),
};

static void __init sysmobts_v2_map_io(void)
{
	dm644x_init();
}

static void __init sysmobts_v2_reserve(void)
{
	if (memblock_remove(DAVINCI_DSP_DDR_EMIF_BASE, DAVINCI_DSP_DDR_EMIF_SIZE)) {
		printk(KERN_ERR "Failed to reserve memory for DSP device (%d KB at 0x%08x)\n",
			       DAVINCI_DSP_DDR_EMIF_SIZE/1024, DAVINCI_DSP_DDR_EMIF_BASE);
	}
	else
	{
		printk(KERN_ERR "Reserved DSP memory: %d KB at 0x%08x\n",
			       DAVINCI_DSP_DDR_EMIF_SIZE/1024, DAVINCI_DSP_DDR_EMIF_BASE);
	}	
}

static __init void sysmobts_v2_init(void)
{
	struct davinci_soc_info *soc_info = &davinci_soc_info;

	platform_add_devices(sysmobts_v2_devices, ARRAY_SIZE(sysmobts_v2_devices));
	sysmobts_v2_init_i2c();
	davinci_serial_init(&uart_config);
	soc_info->emac_pdata->phy_id = SYSMOBTS_V2_PHY_ID;
	regulator_has_full_constraints();
	davinci_setup_mmc(0, &sysmobts_v2_mmc_config);
}

static const char *sysmobts_boards_compat[] __initdata = {
	"sysmocom,sysmobts",
	NULL,
};

MACHINE_START(SYSMOBTS_V2, "sysmocom sysmobts v2")
	.atag_offset  = 0x100,
	.reserve      = sysmobts_v2_reserve,
	.map_io	      = sysmobts_v2_map_io,
	.init_irq     = davinci_irq_init,
	.init_time	= davinci_timer_init,
	.init_machine = sysmobts_v2_init,
	.init_late	= davinci_init_late,
	.dma_zone_size	= SZ_128M,
	.restart      = davinci_restart,
	.dt_compat	= sysmobts_boards_compat,
MACHINE_END