/*
 * Platform device support for NXP PNX833X SoCs.
 * Written by Nikita Youshchenko <yoush@debian.org>, based on PNX8550 code.
 *
 * 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 <linux/device.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/resource.h>
#include <linux/serial.h>
#include <linux/serial_pnx8xxx.h>
#include <linux/i2c-pnx0105.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/dma-mapping.h>

#include <irq.h>
#include <gpio.h>
#include <pnx833x.h>
#include <adma.h>
#include <prjconfig.h>

extern void nand_data_write (unsigned long base, const unsigned char *src, unsigned int bytecount);
extern void nand_data_read (unsigned long base, unsigned char *dst, unsigned int bytecount);

#define MIU_NCS0_BASE_PHYS		0x18000000
#define MIU_NCS1_BASE_PHYS		0x1A000000
#define MIU_NCS2_BASE_PHYS		0x1C000000
#define MIU_NCS3_BASE_PHYS		0x1E000000

#define I2C_CLOCK_SELECT_25KHZ		7
#define I2C_CLOCK_SELECT_50KHZ		6
#define I2C_CLOCK_SELECT_75KHZ		5
#define I2C_CLOCK_SELECT_100KHZ		4
#define I2C_CLOCK_SELECT_150KHZ		3
#define I2C_CLOCK_SELECT_200KHZ		2
#define I2C_CLOCK_SELECT_300KHZ		1
#define I2C_CLOCK_SELECT_400KHZ		0

#if defined (CONFIG_I2C0_25KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_25KHZ
#elif defined (CONFIG_I2C0_50KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_50KHZ
#elif defined (CONFIG_I2C0_75KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_75KHZ
#elif defined (CONFIG_I2C0_100KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_100KHZ
#elif defined (CONFIG_I2C0_150KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_150KHZ
#elif defined (CONFIG_I2C0_200KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_200KHZ
#elif defined (CONFIG_I2C0_300KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_300KHZ
#elif defined (CONFIG_I2C0_400KHZ)
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_400KHZ
#else
#define I2C0_CLOCK_FREQ 		I2C_CLOCK_SELECT_50KHZ		/* 50khz : default value in case nothing is selected in the config */
#endif

#if defined (CONFIG_I2C1_25KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_25KHZ
#elif defined (CONFIG_I2C1_50KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_50KHZ
#elif defined (CONFIG_I2C1_75KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_75KHZ
#elif defined (CONFIG_I2C1_100KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_100KHZ
#elif defined (CONFIG_I2C1_150KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_150KHZ
#elif defined (CONFIG_I2C1_200KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_200KHZ
#elif defined (CONFIG_I2C1_300KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_300KHZ
#elif defined (CONFIG_I2C1_400KHZ)
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_400KHZ
#else
#define I2C1_CLOCK_FREQ 		I2C_CLOCK_SELECT_100KHZ 	/* 100khz : default value in case nothing is selected in the config */
#endif

#if defined(CONFIG_SERIAL_PNX8XXX) || defined(CONFIG_SERIAL_PNX8XXX_MODULE)
static u64 uart_dmamask = DMA_BIT_MASK(32);

static struct resource pnx833x_uart_resources[] = {
	[0] = {
		.start	= PNX833X_UART0_PORTS_START,
		.end	= PNX833X_UART0_PORTS_END,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= PNX833X_PIC_UART0_INT,
		.end	= PNX833X_PIC_UART0_INT,
		.flags	= IORESOURCE_IRQ,
	},
	[2] = {
		.start	= PNX833X_UART1_PORTS_START,
		.end	= PNX833X_UART1_PORTS_END,
		.flags	= IORESOURCE_MEM,
	},
	[3] = {
		.start	= PNX833X_PIC_UART1_INT,
		.end	= PNX833X_PIC_UART1_INT,
		.flags	= IORESOURCE_IRQ,
	},
};

struct pnx8xxx_port pnx8xxx_ports[] = {
	[0] = {
		.port   = {
			.type		= PORT_PNX8XXX,
			.iotype		= UPIO_MEM,
			.membase	= (void __iomem *)PNX833X_UART0_PORTS_START,
			.mapbase	= PNX833X_UART0_PORTS_START,
			.irq		= PNX833X_PIC_UART0_INT,
			.uartclk	= 3692300,
			.fifosize	= 16,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 0,
#ifdef CONFIG_SERIAL_PNX8XXX_TTYS0
			.not_ttys	= 0,
#elif defined(CONFIG_SERIAL_PNX8XXX_UART0)
			.not_ttys	= 1,	/* serial port not used as ttyS */
#else
			.not_ttys	= 2,	/* serial port not available */
#endif
		},
	},
	[1] = {
		.port   = {
			.type		= PORT_PNX8XXX,
			.iotype		= UPIO_MEM,
			.membase	= (void __iomem *)PNX833X_UART1_PORTS_START,
			.mapbase	= PNX833X_UART1_PORTS_START,
			.irq		= PNX833X_PIC_UART1_INT,
			.uartclk	= 3692300,
			.fifosize	= 16,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 1,
#ifdef CONFIG_SERIAL_PNX8XXX_TTYS1
			.not_ttys	= 0,
#elif defined(CONFIG_SERIAL_PNX8XXX_UART1)
			.not_ttys	= 1,	/* serial port not used as ttyS */
#else
			.not_ttys	= 2,	/* serial port not available */
#endif
		},
	},
};

static struct platform_device pnx833x_uart_device = {
	.name = "pnx8xxx-uart",
	.id = -1,
	.dev = {
		.dma_mask = &uart_dmamask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
		.platform_data = pnx8xxx_ports,
	},
	.num_resources = ARRAY_SIZE(pnx833x_uart_resources),
	.resource = pnx833x_uart_resources,
};
#endif

#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
static u64 ehci_dmamask = DMA_BIT_MASK(32);

static struct resource pnx833x_usb_ehci_resources[] = {
	[0] = {
		.start	= PNX833X_USB_PORTS_START,
		.end	= PNX833X_USB_PORTS_END,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= PNX833X_PIC_USB_INT,
		.end	= PNX833X_PIC_USB_INT,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct platform_device pnx833x_usb_ehci_device = {
	.name = "pnx833x-ehci",
	.id = -1,
	.dev = {
		.dma_mask = &ehci_dmamask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
	},
	.num_resources = ARRAY_SIZE(pnx833x_usb_ehci_resources),
	.resource = pnx833x_usb_ehci_resources,
};
#endif

#if defined(CONFIG_I2C_PNX0105) || defined(CONFIG_I2C_PNX0105_MODULE)
static struct resource pnx833x_i2c0_resources[] = {
	{
		.start	= PNX833X_I2C0_PORTS_START,
		.end	= PNX833X_I2C0_PORTS_END,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= PNX833X_PIC_I2C0_INT,
		.end	= PNX833X_PIC_I2C0_INT,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct resource pnx833x_i2c1_resources[] = {
	{
		.start	= PNX833X_I2C1_PORTS_START,
		.end	= PNX833X_I2C1_PORTS_END,
		.flags	= IORESOURCE_MEM,
	},
	{
		.start	= PNX833X_PIC_I2C1_INT,
		.end	= PNX833X_PIC_I2C1_INT,
		.flags	= IORESOURCE_IRQ,
	},
};

static struct i2c_pnx0105_dev pnx833x_i2c_dev[] = {
	{
		.base		= PNX833X_I2C0_PORTS_START,
#if defined (CONFIG_I2C0_MODE_POLLING)
		.irq		= -1,
#elif defined (CONFIG_I2C0_MODE_IRQ)
		.irq		= PNX833X_PIC_I2C0_INT,
#else
		.irq		= -1,			/* default to polling mode */
#endif
		.clock		= I2C0_CLOCK_FREQ,	/* 100 kHz (Maximum for HDMI), 50kHz (Prefered for HDMI HDCP) */
		.bus_addr	= 0,			/* no slave support */
	},
	{
		.base		= PNX833X_I2C1_PORTS_START,
#if defined (CONFIG_I2C1_MODE_POLLING)
		.irq		= -1,
#elif defined (CONFIG_I2C1_MODE_IRQ)
		.irq		= PNX833X_PIC_I2C1_INT,
#else
		.irq		= PNX833X_PIC_I2C1_INT, /* default to IRQ mode */
#endif
		.clock		= I2C1_CLOCK_FREQ,
		.bus_addr	= 0xE4, 		/* slave mode */
	},
};

static struct platform_device pnx833x_i2c0_device = {
	.name = "i2c-pnx0105",
	.id = 0,
	.dev = {
		.platform_data = &pnx833x_i2c_dev[0],
	},
	.num_resources = ARRAY_SIZE(pnx833x_i2c0_resources),
	.resource = pnx833x_i2c0_resources,
};

static struct platform_device pnx833x_i2c1_device = {
	.name = "i2c-pnx0105",
	.id = 1,
	.dev = {
		.platform_data = &pnx833x_i2c_dev[1],
	},
	.num_resources = ARRAY_SIZE(pnx833x_i2c1_resources),
	.resource = pnx833x_i2c1_resources,
};
#endif

#if defined(CONFIG_IP3902) || defined(CONFIG_IP3902_MODULE)
static u64 ip3902_ethernet_dmamask = DMA_BIT_MASK(32);

static struct resource pnx833x_ethernet_resources[] = {
	[0] = {
		.start = PNX8335_IP3902_PORTS_START,
		.end   = PNX8335_IP3902_PORTS_END,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = PNX8335_PIC_ETHERNET_INT,
		.end   = PNX8335_PIC_ETHERNET_INT,
		.flags = IORESOURCE_IRQ,
	},
};

static struct platform_device pnx833x_ethernet_device = {
	.name = "ip3902-eth",
	.id = -1,
	.dev = {
		.dma_mask = &ip3902_ethernet_dmamask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
	},
	.num_resources = ARRAY_SIZE(pnx833x_ethernet_resources),
	.resource = pnx833x_ethernet_resources,
};
#endif

#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
static u64 smsc911x_ethernet_dmamask = DMA_BIT_MASK(32);

static struct resource smsc911x_ethernet_resources[] = {
	[0] = {
		.start = MIU_NCS3_BASE_PHYS,
		.end   = MIU_NCS3_BASE_PHYS + 0x100,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = PNX833X_GPIO_15_INT,
		.end   = PNX833X_GPIO_15_INT,
		.flags = IORESOURCE_IRQ,
	},
};

static struct platform_device smsc911x_ethernet_device = {
	.name = "smsc911x-eth",
	.id = -1,
	.dev = {
		.dma_mask = &smsc911x_ethernet_dmamask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
	},
	.num_resources = ARRAY_SIZE(smsc911x_ethernet_resources),
	.resource = smsc911x_ethernet_resources,
};
#endif

#if defined(CONFIG_SATA_PNX833X) || defined(CONFIG_SATA_PNX833X_MODULE)
static struct resource pnx833x_sata_resources[] = {
	[0] = {
		.start = PNX8335_SATA_PORTS_START,
		.end   = PNX8335_SATA_PORTS_END,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = PNX8335_PIC_SATA_INT,
		.end   = PNX8335_PIC_SATA_INT,
		.flags = IORESOURCE_IRQ,
	},
};

static struct platform_device pnx833x_sata_device = {
	.name = "pnx833x-sata",
	.id = -1,
	.num_resources = ARRAY_SIZE(pnx833x_sata_resources),
	.resource = pnx833x_sata_resources,
};
#endif

#if defined(CONFIG_NXP_PNX833X_ADMA) || defined(CONFIG_NXP_PNX833X_ADMA_MODULE)
static u64 pnx833x_adma_dmamask = DMA_BIT_MASK(32);

static struct nxp_adma_platform_data pnx833x_adma_data = {
	.pool_size = PAGE_SIZE,
};

static struct resource pnx833x_adma_resources[] = {
	[0] = {
		.start = PNX8335_SGDX_PORTS_START,
		.end   = PNX8335_SGDX_PORTS_END,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = PNX833X_PIC_SGDX_DMA_INT,
		.end   = PNX833X_PIC_SGDX_DMA_INT,
		.flags = IORESOURCE_IRQ
	},
};

static struct platform_device pnx833x_adma_device = {
	.name = "nxp-adma",
	.id = 0,
	.num_resources = ARRAY_SIZE(pnx833x_adma_resources),
	.resource = pnx833x_adma_resources,
	.dev = {
		.dma_mask = &pnx833x_adma_dmamask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
		.platform_data = (void *) &pnx833x_adma_data,
	},
};
#endif

#if defined (CONFIG_MTD_NAND_PLATFORM) || defined (CONFIG_MTD_NAND_PLATFORM_MODULE)
/*
   If both NOR and NAND are enabled, access NOR via nCS0 + nCS1 and NAND via nCS2.
   If only NAND is enabled, access it via nCS0.
*/
#if defined (CONFIG_MTD_PHYSMAP)
#define STB225_NAND_BASE	MIU_NCS2_BASE_PHYS
#else
#define STB225_NAND_BASE	MIU_NCS0_BASE_PHYS
#endif
#define STB225_NAND_CLE_MASK	0x00100000	/* offset from base of I/O location with CLE high */
#define STB225_NAND_ALE_MASK	0x00010000	/* offset from base of I/O location with ALE high */

#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "cmdlinepart", 0 };
#endif

static void pnx833x_nand_cmd_ctrl (struct mtd_info *mtd, int dat, unsigned int ctrl)
{
	struct nand_chip *this = mtd->priv;
	unsigned long nandaddr;

	if (dat == NAND_CMD_NONE)
		return;

	nandaddr = (unsigned long) this->IO_ADDR_W;
	if (ctrl & NAND_CLE)
		nandaddr += STB225_NAND_CLE_MASK;
	else
		nandaddr += STB225_NAND_ALE_MASK;

	writeb (dat, (void __iomem *) nandaddr);
}

static void pnx833x_nand_write_buf (struct mtd_info *mtd, const uint8_t *buf, int len)
{
	struct nand_chip *this = mtd->priv;

	nand_data_write ((unsigned long) this->IO_ADDR_W, buf, len);
}

static void pnx833x_nand_read_buf (struct mtd_info *mtd, uint8_t *buf, int len)
{
	struct nand_chip *this = mtd->priv;

	nand_data_read ((unsigned long) this->IO_ADDR_R, buf, len);
}

#ifdef CONFIG_NAND_FLASH_SUPPORTS_READY_SIGNAL
static int pnx833x_nand_dev_ready (struct mtd_info *mtd)
{
	/*
	 * return raw pin state, ie low == busy, high == ready
	 */
	return gpio_read (4);
}
#endif

static struct platform_nand_data pnx833x_nand_data = {
	.chip = {
		.chip_delay		= 25,
#ifdef CONFIG_MTD_PARTITIONS
		.part_probe_types	= part_probes,
#endif
	},
	.ctrl = {
#ifdef CONFIG_NAND_FLASH_SUPPORTS_READY_SIGNAL
		.dev_ready		= pnx833x_nand_dev_ready,
#endif
		.cmd_ctrl		= pnx833x_nand_cmd_ctrl,
		/*
		 * Standard nand platform driver doesn't provide support for
		 * over-riding write_buf and read_buf, ie we need a hack in the
		 * nand platform driver to do so. Maybe we'd be better off not
		 * trying to squeeze into the platform driver framework... ?
		 */
		.write_buf		= pnx833x_nand_write_buf,
		.read_buf		= pnx833x_nand_read_buf,
	}
};

static struct resource pnx833x_nand_resource = {
	.start	= STB225_NAND_BASE,
	.end	= STB225_NAND_BASE | STB225_NAND_CLE_MASK | STB225_NAND_ALE_MASK,
	.flags	= IORESOURCE_MEM,
};

static struct platform_device pnx833x_nand = {
	.name = "gen_nand",
	.id = -1,
	.num_resources = 1,
	.resource = &pnx833x_nand_resource,
	.dev = {
		.platform_data = &pnx833x_nand_data,
	},
};
#endif	/* CONFIG_MTD_NAND_PLATFORM */

static struct platform_device *pnx833x_platform_devices[] __initdata = {
#if defined(CONFIG_SERIAL_PNX8XXX) || defined(CONFIG_SERIAL_PNX8XXX_MODULE)
	&pnx833x_uart_device,
#endif
#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
	&pnx833x_usb_ehci_device,
#endif
#if defined(CONFIG_I2C_PNX0105) || defined(CONFIG_I2C_PNX0105_MODULE)
	&pnx833x_i2c0_device,
	&pnx833x_i2c1_device,
#endif
#if defined(CONFIG_SMSC911X) || defined(CONFIG_SMSC911X_MODULE)
	&smsc911x_ethernet_device,
#endif
#if defined(CONFIG_IP3902) || defined(CONFIG_IP3902_MODULE)
	&pnx833x_ethernet_device,
#endif
#if defined(CONFIG_SATA_PNX833X) || defined(CONFIG_SATA_PNX833X_MODULE)
	&pnx833x_sata_device,
#endif
#if defined(CONFIG_MTD_NAND_PLATFORM) || defined(CONFIG_MTD_NAND_PLATFORM_MODULE)
	&pnx833x_nand,
#endif
#if defined(CONFIG_NXP_PNX833X_ADMA) || defined(CONFIG_NXP_PNX833X_ADMA_MODULE)
	&pnx833x_adma_device,
#endif
};

int __init pnx833x_platform_init (void)
{
	if (ARRAY_SIZE(pnx833x_platform_devices) == 0)
		return 0;

#ifdef CONFIG_PNX8335_AUDIO_MIPS
	/* On the Audio MIPS, we disable the ttyS0/ttyS1 if
	 * we CAN NOT find that they are used in the boot cmdline,
	 * e.g. "console=ttyS1,115200"
	 */
#ifdef CONFIG_SERIAL_PNX8XXX_TTYS0
	if (! strstr (saved_command_line, "console=ttyS0"))
		pnx8xxx_ports[0].port.not_ttys = 2; // don't use it
#endif
#ifdef CONFIG_SERIAL_PNX8XXX_TTYS1
	if (! strstr (saved_command_line, "console=ttyS1"))
		pnx8xxx_ports[1].port.not_ttys = 2; // don't use it
#endif
#endif

#if defined(CONFIG_NXP_PNX833X_ADMA) || defined(CONFIG_NXP_PNX833X_ADMA_MODULE)
	dma_cap_set (DMA_MEMCPY, pnx833x_adma_data.cap_mask);
	dma_cap_set (DMA_INTERRUPT, pnx833x_adma_data.cap_mask);
#endif

	return platform_add_devices (pnx833x_platform_devices,
				     ARRAY_SIZE(pnx833x_platform_devices));
}

arch_initcall(pnx833x_platform_init);

