/*
 * Support for CompuLab EM-x270 platform
 *
 * Copyright (C) 2007 CompuLab, Ltd.
 * Author: Mike Rapoport <mike@compulab.co.il>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/irq.h>
#include <linux/platform_device.h>

#include <linux/dm9000.h>
#include <linux/rtc-v3020.h>

#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>

#include <asm/mach-types.h>

#include <asm/mach/arch.h>

#include <asm/arch/pxa-regs.h>
#include <asm/arch/pxa2xx-gpio.h>
#include <asm/arch/pxafb.h>
#include <asm/arch/ohci.h>
#include <asm/arch/mmc.h>
#include <asm/arch/bitfield.h>

#include "generic.h"

/* GPIO IRQ usage */
#define EM_X270_MMC_PD		(105)
#define EM_X270_ETHIRQ		IRQ_GPIO(41)
#define EM_X270_MMC_IRQ		IRQ_GPIO(13)

static struct resource em_x270_dm9k_resource[] = {
	[0] = {
		.start = PXA_CS2_PHYS,
		.end   = PXA_CS2_PHYS + 3,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = PXA_CS2_PHYS + 8,
		.end   = PXA_CS2_PHYS + 8 + 0x3f,
		.flags = IORESOURCE_MEM,
	},
	[2] = {
		.start = EM_X270_ETHIRQ,
		.end   = EM_X270_ETHIRQ,
		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
	}
};

/* for the moment we limit ourselves to 32bit IO until some
 * better IO routines can be written and tested
 */
static struct dm9000_plat_data em_x270_dm9k_platdata = {
	.flags		= DM9000_PLATF_32BITONLY,
};

/* Ethernet device */
static struct platform_device em_x270_dm9k = {
	.name		= "dm9000",
	.id		= 0,
	.num_resources	= ARRAY_SIZE(em_x270_dm9k_resource),
	.resource	= em_x270_dm9k_resource,
	.dev		= {
		.platform_data = &em_x270_dm9k_platdata,
	}
};

/* audio device */
static struct platform_device em_x270_audio = {
	.name		= "pxa2xx-ac97",
	.id		= -1,
};

/* WM9712 touchscreen controller. Hopefully the driver will make it to
 * the mainstream sometime */
static struct platform_device em_x270_ts = {
	.name		= "wm97xx-ts",
	.id		= -1,
};

/* RTC */
static struct resource em_x270_v3020_resource[] = {
	[0] = {
		.start = PXA_CS4_PHYS,
		.end   = PXA_CS4_PHYS + 3,
		.flags = IORESOURCE_MEM,
	},
};

static struct v3020_platform_data em_x270_v3020_platdata = {
	.leftshift = 0,
};

static struct platform_device em_x270_rtc = {
	.name		= "v3020",
	.num_resources	= ARRAY_SIZE(em_x270_v3020_resource),
	.resource	= em_x270_v3020_resource,
	.id		= -1,
	.dev		= {
		.platform_data = &em_x270_v3020_platdata,
	}
};

/* NAND flash */
#define GPIO_NAND_CS	(11)
#define GPIO_NAND_RB	(56)

static inline void nand_cs_on(void)
{
	GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
}

static void nand_cs_off(void)
{
	dsb();

	GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
}

/* hardware specific access to control-lines */
static void em_x270_nand_cmd_ctl(struct mtd_info *mtd, int dat,
				 unsigned int ctrl)
{
	struct nand_chip *this = mtd->priv;
	unsigned long nandaddr = (unsigned long)this->IO_ADDR_W;

	dsb();

	if (ctrl & NAND_CTRL_CHANGE) {
		if (ctrl & NAND_ALE)
			nandaddr |=  (1 << 3);
		else
			nandaddr &= ~(1 << 3);
		if (ctrl & NAND_CLE)
			nandaddr |=  (1 << 2);
		else
			nandaddr &= ~(1 << 2);
		if (ctrl & NAND_NCE)
			nand_cs_on();
		else
			nand_cs_off();
	}

	dsb();
	this->IO_ADDR_W = (void __iomem *)nandaddr;
	if (dat != NAND_CMD_NONE)
		writel(dat, this->IO_ADDR_W);

	dsb();
}

/* read device ready pin */
static int em_x270_nand_device_ready(struct mtd_info *mtd)
{
	dsb();

	return GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB);
}

static struct mtd_partition em_x270_partition_info[] = {
	[0] = {
		.name	= "em_x270-0",
		.offset	= 0,
		.size	= SZ_4M,
	},
	[1] = {
		.name	= "em_x270-1",
		.offset	= MTDPART_OFS_APPEND,
		.size	= MTDPART_SIZ_FULL
	},
};

static const char *em_x270_part_probes[] = { "cmdlinepart", NULL };

struct platform_nand_data em_x270_nand_platdata = {
	.chip = {
		.nr_chips = 1,
		.chip_offset = 0,
		.nr_partitions = ARRAY_SIZE(em_x270_partition_info),
		.partitions = em_x270_partition_info,
		.chip_delay = 20,
		.part_probe_types = em_x270_part_probes,
	},
	.ctrl = {
		.hwcontrol = 0,
		.dev_ready = em_x270_nand_device_ready,
		.select_chip = 0,
		.cmd_ctrl = em_x270_nand_cmd_ctl,
	},
};

static struct resource em_x270_nand_resource[] = {
	[0] = {
		.start = PXA_CS1_PHYS,
		.end   = PXA_CS1_PHYS + 12,
		.flags = IORESOURCE_MEM,
	},
};

static struct platform_device em_x270_nand = {
	.name		= "gen_nand",
	.num_resources	= ARRAY_SIZE(em_x270_nand_resource),
	.resource	= em_x270_nand_resource,
	.id		= -1,
	.dev		= {
		.platform_data = &em_x270_nand_platdata,
	}
};

/* platform devices */
static struct platform_device *platform_devices[] __initdata = {
	&em_x270_dm9k,
	&em_x270_audio,
	&em_x270_ts,
	&em_x270_rtc,
	&em_x270_nand,
};


/* PXA27x OHCI controller setup */
static int em_x270_ohci_init(struct device *dev)
{
	/* Set the Power Control Polarity Low */
	UHCHR = (UHCHR | UHCHR_PCPL) &
		~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSE);

	/* enable port 2 transiever */
	UP2OCR = UP2OCR_HXS | UP2OCR_HXOE;

	return 0;
}

static struct pxaohci_platform_data em_x270_ohci_platform_data = {
	.port_mode	= PMM_PERPORT_MODE,
	.init		= em_x270_ohci_init,
};


static int em_x270_mci_init(struct device *dev,
			    irq_handler_t em_x270_detect_int,
			    void *data)
{
	int err;

	/* setup GPIO for PXA27x MMC controller */
	pxa_gpio_mode(GPIO32_MMCCLK_MD);
	pxa_gpio_mode(GPIO112_MMCCMD_MD);
	pxa_gpio_mode(GPIO92_MMCDAT0_MD);
	pxa_gpio_mode(GPIO109_MMCDAT1_MD);
	pxa_gpio_mode(GPIO110_MMCDAT2_MD);
	pxa_gpio_mode(GPIO111_MMCDAT3_MD);

	/* EM-X270 uses GPIO13 as SD power enable */
	pxa_gpio_mode(EM_X270_MMC_PD | GPIO_OUT);

	err = request_irq(EM_X270_MMC_IRQ, em_x270_detect_int,
			  IRQF_DISABLED | IRQF_TRIGGER_FALLING,
			  "MMC card detect", data);
	if (err) {
		printk(KERN_ERR "%s: can't request MMC card detect IRQ: %d\n",
		       __func__, err);
		return err;
	}

	return 0;
}

static void em_x270_mci_setpower(struct device *dev, unsigned int vdd)
{
	/*
	   FIXME: current hardware implementation does not allow to
	   enable/disable MMC power. This will be fixed in next HW releases,
	   and we'll need to add implmentation here.
	*/
	return;
}

static void em_x270_mci_exit(struct device *dev, void *data)
{
	free_irq(EM_X270_MMC_IRQ, data);
}

static struct pxamci_platform_data em_x270_mci_platform_data = {
	.ocr_mask	= MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31,
	.init 		= em_x270_mci_init,
	.setpower 	= em_x270_mci_setpower,
	.exit		= em_x270_mci_exit,
};

/* LCD 480x640 */
static struct pxafb_mode_info em_x270_lcd_mode = {
	.pixclock	= 50000,
	.bpp		= 16,
	.xres		= 480,
	.yres		= 640,
	.hsync_len	= 8,
	.vsync_len	= 2,
	.left_margin	= 8,
	.upper_margin	= 0,
	.right_margin	= 24,
	.lower_margin	= 4,
	.cmap_greyscale	= 0,
};

static struct pxafb_mach_info em_x270_lcd = {
	.modes		= &em_x270_lcd_mode,
	.num_modes	= 1,
	.cmap_inverse	= 0,
	.cmap_static	= 0,
	.lccr0		= LCCR0_PAS,
	.lccr3		= LCCR3_PixClkDiv(0x01) | LCCR3_Acb(0xff),
};

static void __init em_x270_init(void)
{
	/* setup LCD */
	set_pxa_fb_info(&em_x270_lcd);

	/* register EM-X270 platform devices */
	platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));

	/* set MCI and OHCI platform parameters */
	pxa_set_mci_info(&em_x270_mci_platform_data);
	pxa_set_ohci_info(&em_x270_ohci_platform_data);

	/* setup STUART GPIOs */
	pxa_gpio_mode(GPIO46_STRXD_MD);
	pxa_gpio_mode(GPIO47_STTXD_MD);

	/* setup BTUART GPIOs */
	pxa_gpio_mode(GPIO42_BTRXD_MD);
	pxa_gpio_mode(GPIO43_BTTXD_MD);
	pxa_gpio_mode(GPIO44_BTCTS_MD);
	pxa_gpio_mode(GPIO45_BTRTS_MD);

	/* Setup interrupt for dm9000 */
	set_irq_type(EM_X270_ETHIRQ, IRQT_RISING);
}

MACHINE_START(EM_X270, "Compulab EM-x270")
	.boot_params	= 0xa0000100,
	.phys_io	= 0x40000000,
	.io_pg_offst	= (io_p2v(0x40000000) >> 18) & 0xfffc,
	.map_io		= pxa_map_io,
	.init_irq	= pxa27x_init_irq,
	.timer		= &pxa_timer,
	.init_machine	= em_x270_init,
MACHINE_END
