[ARM] pxa: Add multi-io support for e-series

    This patchset provides support for the TMIO based IO controller used in the
    Toshiba e-series PDAs.

    Signed-off-by: Ian Molton <spyro@f2s.com>
    Acked-by: Samuel Ortiz <sameo@openedhand.com>
    Acked-by: Eric Miao <eric.y.miao@gmail.com>
diff --git a/arch/arm/mach-pxa/e330.c b/arch/arm/mach-pxa/e330.c
index d488ede..1bd7f74 100644
--- a/arch/arm/mach-pxa/e330.c
+++ b/arch/arm/mach-pxa/e330.c
@@ -1,5 +1,5 @@
 /*
- * Hardware definitions for the Toshiba eseries PDAs
+ * Hardware definitions for the Toshiba e330 PDAs
  *
  * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
  *
@@ -12,6 +12,9 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/tc6387xb.h>
 
 #include <asm/setup.h>
 #include <asm/mach/arch.h>
@@ -19,13 +22,44 @@
 
 #include <mach/mfp-pxa25x.h>
 #include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/eseries-gpio.h>
 #include <mach/udc.h>
 
 #include "generic.h"
 #include "eseries.h"
+#include "clock.h"
+
+/* -------------------- e330 tc6387xb parameters -------------------- */
+
+static struct tc6387xb_platform_data e330_tc6387xb_info = {
+	.enable   = &eseries_tmio_enable,
+	.disable  = &eseries_tmio_disable,
+	.suspend  = &eseries_tmio_suspend,
+	.resume   = &eseries_tmio_resume,
+};
+
+static struct platform_device e330_tc6387xb_device = {
+	.name           = "tc6387xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e330_tc6387xb_info,
+	},
+	.num_resources = 2,
+	.resource      = eseries_tmio_resources,
+};
+
+/* --------------------------------------------------------------- */
+
+static struct platform_device *devices[] __initdata = {
+	&e330_tc6387xb_device,
+};
 
 static void __init e330_init(void)
 {
+	eseries_register_clks();
+	eseries_get_tmio_gpios();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
 	pxa_set_udc_info(&e7xx_udc_mach_info);
 }
 
diff --git a/arch/arm/mach-pxa/e350.c b/arch/arm/mach-pxa/e350.c
index 8ecbc54..2511293 100644
--- a/arch/arm/mach-pxa/e350.c
+++ b/arch/arm/mach-pxa/e350.c
@@ -1,5 +1,5 @@
 /*
- * Hardware definitions for the Toshiba eseries PDAs
+ * Hardware definitions for the Toshiba e350 PDAs
  *
  * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
  *
@@ -12,20 +12,54 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/t7l66xb.h>
 
 #include <asm/setup.h>
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 
 #include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
 #include <mach/hardware.h>
+#include <mach/eseries-gpio.h>
 #include <mach/udc.h>
 
 #include "generic.h"
 #include "eseries.h"
+#include "clock.h"
+
+/* -------------------- e350 t7l66xb parameters -------------------- */
+
+static struct t7l66xb_platform_data e350_t7l66xb_info = {
+	.irq_base               = IRQ_BOARD_START,
+	.enable                 = &eseries_tmio_enable,
+	.suspend                = &eseries_tmio_suspend,
+	.resume                 = &eseries_tmio_resume,
+};
+
+static struct platform_device e350_t7l66xb_device = {
+	.name           = "t7l66xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e350_t7l66xb_info,
+	},
+	.num_resources = 2,
+	.resource      = eseries_tmio_resources,
+};
+
+/* ---------------------------------------------------------- */
+
+static struct platform_device *devices[] __initdata = {
+	&e350_t7l66xb_device,
+};
 
 static void __init e350_init(void)
 {
+	eseries_register_clks();
+	eseries_get_tmio_gpios();
+	platform_add_devices(devices, ARRAY_SIZE(devices));
 	pxa_set_udc_info(&e7xx_udc_mach_info);
 }
 
diff --git a/arch/arm/mach-pxa/e400.c b/arch/arm/mach-pxa/e400.c
index 544bbaa..7716ad0 100644
--- a/arch/arm/mach-pxa/e400.c
+++ b/arch/arm/mach-pxa/e400.c
@@ -12,20 +12,26 @@
 
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/t7l66xb.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
 
 #include <asm/setup.h>
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 
-#include <mach/pxa-regs.h>
 #include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
 #include <mach/hardware.h>
-
+#include <mach/eseries-gpio.h>
 #include <mach/pxafb.h>
 #include <mach/udc.h>
 
 #include "generic.h"
 #include "eseries.h"
+#include "clock.h"
 
 /* ------------------------ E400 LCD definitions ------------------------ */
 
@@ -65,7 +71,10 @@
 	GPIO42_BTUART_RXD,
 	GPIO43_BTUART_TXD,
 	GPIO44_BTUART_CTS,
-	GPIO45_GPIO, /* Used by TMIO for #SUSPEND */
+
+	/* TMIO controller */
+	GPIO19_GPIO, /* t7l66xb #PCLR */
+	GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
 
 	/* wakeup */
 	GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
@@ -73,10 +82,60 @@
 
 /* ---------------------------------------------------------------------- */
 
+static struct mtd_partition partition_a = {
+	.name = "Internal NAND flash",
+	.offset =  0,
+	.size =  MTDPART_SIZ_FULL,
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr e400_t7l66xb_nand_bbt = {
+	.options = 0,
+	.offs = 4,
+	.len = 2,
+	.pattern = scan_ff_pattern
+};
+
+static struct tmio_nand_data e400_t7l66xb_nand_config = {
+	.num_partitions = 1,
+	.partition = &partition_a,
+	.badblock_pattern = &e400_t7l66xb_nand_bbt,
+};
+
+static struct t7l66xb_platform_data e400_t7l66xb_info = {
+	.irq_base 		= IRQ_BOARD_START,
+	.enable                 = &eseries_tmio_enable,
+	.suspend                = &eseries_tmio_suspend,
+	.resume                 = &eseries_tmio_resume,
+
+	.nand_data              = &e400_t7l66xb_nand_config,
+};
+
+static struct platform_device e400_t7l66xb_device = {
+	.name           = "t7l66xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e400_t7l66xb_info,
+	},
+	.num_resources = 2,
+	.resource      = eseries_tmio_resources,
+};
+
+/* ---------------------------------------------------------- */
+
+static struct platform_device *devices[] __initdata = {
+	&e400_t7l66xb_device,
+};
+
 static void __init e400_init(void)
 {
 	pxa2xx_mfp_config(ARRAY_AND_SIZE(e400_pin_config));
+	/* Fixme - e400 may have a switched clock */
+	eseries_register_clks();
+	eseries_get_tmio_gpios();
 	set_pxa_fb_info(&e400_pxafb_mach_info);
+	platform_add_devices(devices, ARRAY_SIZE(devices));
 	pxa_set_udc_info(&e7xx_udc_mach_info);
 }
 
diff --git a/arch/arm/mach-pxa/e740.c b/arch/arm/mach-pxa/e740.c
index a9f070b..b00d670 100644
--- a/arch/arm/mach-pxa/e740.c
+++ b/arch/arm/mach-pxa/e740.c
@@ -15,6 +15,8 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/mfd/t7l66xb.h>
 
 #include <video/w100fb.h>
 
@@ -23,13 +25,16 @@
 #include <asm/mach-types.h>
 
 #include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
 #include <mach/hardware.h>
+#include <mach/eseries-gpio.h>
 #include <mach/udc.h>
 #include <mach/irda.h>
 
 #include "generic.h"
 #include "eseries.h"
-
+#include "clock.h"
+#include "devices.h"
 
 /* ------------------------ e740 video support --------------------------- */
 
@@ -117,7 +122,10 @@
 	GPIO42_BTUART_RXD,
 	GPIO43_BTUART_TXD,
 	GPIO44_BTUART_CTS,
-	GPIO45_GPIO, /* Used by TMIO for #SUSPEND */
+
+	/* TMIO controller */
+	GPIO19_GPIO, /* t7l66xb #PCLR */
+	GPIO45_GPIO, /* t7l66xb #SUSPEND (NOT BTUART!) */
 
 	/* UDC */
 	GPIO13_GPIO,
@@ -150,15 +158,39 @@
 	GPIO0_GPIO | WAKEUP_ON_EDGE_RISE,
 };
 
+/* -------------------- e740 t7l66xb parameters -------------------- */
+
+static struct t7l66xb_platform_data e740_t7l66xb_info = {
+	.irq_base 		= IRQ_BOARD_START,
+	.enable                 = &eseries_tmio_enable,
+	.suspend                = &eseries_tmio_suspend,
+	.resume                 = &eseries_tmio_resume,
+};
+
+static struct platform_device e740_t7l66xb_device = {
+	.name           = "t7l66xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e740_t7l66xb_info,
+	},
+	.num_resources = 2,
+	.resource      = eseries_tmio_resources,
+};
+
 /* ----------------------------------------------------------------------- */
 
 static struct platform_device *devices[] __initdata = {
 	&e740_fb_device,
+	&e740_t7l66xb_device,
 };
 
 static void __init e740_init(void)
 {
 	pxa2xx_mfp_config(ARRAY_AND_SIZE(e740_pin_config));
+	eseries_register_clks();
+	clk_add_alias("CLK_CK48M", &e740_t7l66xb_device.dev,
+			"UDCCLK", &pxa25x_device_udc.dev),
+	eseries_get_tmio_gpios();
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 	pxa_set_udc_info(&e7xx_udc_mach_info);
 	e7xx_irda_init();
diff --git a/arch/arm/mach-pxa/e750.c b/arch/arm/mach-pxa/e750.c
index 1410ad7..84d7c1a 100644
--- a/arch/arm/mach-pxa/e750.c
+++ b/arch/arm/mach-pxa/e750.c
@@ -15,6 +15,7 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/fb.h>
+#include <linux/mfd/tc6393xb.h>
 
 #include <video/w100fb.h>
 
@@ -23,12 +24,15 @@
 #include <asm/mach-types.h>
 
 #include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
 #include <mach/hardware.h>
+#include <mach/eseries-gpio.h>
 #include <mach/udc.h>
 #include <mach/irda.h>
 
 #include "generic.h"
 #include "eseries.h"
+#include "clock.h"
 
 /* ---------------------- E750 LCD definitions -------------------- */
 
@@ -101,14 +105,41 @@
 	.resource       = e750_fb_resources,
 };
 
-/* ----------------------------------------------------------------------- */
+/* ----------------- e750 tc6393xb parameters ------------------ */
+
+static struct tc6393xb_platform_data e750_tc6393xb_info = {
+	.irq_base       = IRQ_BOARD_START,
+	.scr_pll2cr     = 0x0cc1,
+	.scr_gper       = 0,
+	.gpio_base      = -1,
+	.suspend        = &eseries_tmio_suspend,
+	.resume         = &eseries_tmio_resume,
+	.enable         = &eseries_tmio_enable,
+	.disable        = &eseries_tmio_disable,
+};
+
+static struct platform_device e750_tc6393xb_device = {
+	.name           = "tc6393xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e750_tc6393xb_info,
+	},
+	.num_resources = 2,
+	.resource      = eseries_tmio_resources,
+};
+
+/* ------------------------------------------------------------- */
 
 static struct platform_device *devices[] __initdata = {
 	&e750_fb_device,
+	&e750_tc6393xb_device,
 };
 
 static void __init e750_init(void)
 {
+	clk_add_alias("CLK_CK3P6MI", &e750_tc6393xb_device.dev,
+			"GPIO11_CLK", NULL),
+	eseries_get_tmio_gpios();
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 	pxa_set_udc_info(&e7xx_udc_mach_info);
 	e7xx_irda_init();
diff --git a/arch/arm/mach-pxa/e800.c b/arch/arm/mach-pxa/e800.c
index a293e09..9a86a42 100644
--- a/arch/arm/mach-pxa/e800.c
+++ b/arch/arm/mach-pxa/e800.c
@@ -15,6 +15,7 @@
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/fb.h>
+#include <linux/mfd/tc6393xb.h>
 
 #include <video/w100fb.h>
 
@@ -23,12 +24,14 @@
 #include <asm/mach-types.h>
 
 #include <mach/mfp-pxa25x.h>
+#include <mach/pxa-regs.h>
 #include <mach/hardware.h>
 #include <mach/eseries-gpio.h>
 #include <mach/udc.h>
 
 #include "generic.h"
 #include "eseries.h"
+#include "clock.h"
 
 /* ------------------------ e800 LCD definitions ------------------------- */
 
@@ -160,14 +163,41 @@
 	.gpio_pullup_inverted = 1
 };
 
+/* ----------------- e800 tc6393xb parameters ------------------ */
+
+static struct tc6393xb_platform_data e800_tc6393xb_info = {
+	.irq_base       = IRQ_BOARD_START,
+	.scr_pll2cr     = 0x0cc1,
+	.scr_gper       = 0,
+	.gpio_base      = -1,
+	.suspend        = &eseries_tmio_suspend,
+	.resume         = &eseries_tmio_resume,
+	.enable         = &eseries_tmio_enable,
+	.disable        = &eseries_tmio_disable,
+};
+
+static struct platform_device e800_tc6393xb_device = {
+	.name           = "tc6393xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e800_tc6393xb_info,
+	},
+	.num_resources = 2,
+	.resource      = eseries_tmio_resources,
+};
+
 /* ----------------------------------------------------------------------- */
 
 static struct platform_device *devices[] __initdata = {
 	&e800_fb_device,
+	&e800_tc6393xb_device,
 };
 
 static void __init e800_init(void)
 {
+	clk_add_alias("CLK_CK3P6MI", &e800_tc6393xb_device.dev,
+			"GPIO11_CLK", NULL),
+	eseries_get_tmio_gpios();
 	platform_add_devices(devices, ARRAY_SIZE(devices));
 	pxa_set_udc_info(&e800_udc_mach_info);
 }
diff --git a/arch/arm/mach-pxa/eseries.c b/arch/arm/mach-pxa/eseries.c
index ee79e33..dfce7d5 100644
--- a/arch/arm/mach-pxa/eseries.c
+++ b/arch/arm/mach-pxa/eseries.c
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/gpio.h>
+#include <linux/delay.h>
 #include <linux/platform_device.h>
 
 #include <asm/setup.h>
@@ -26,6 +27,7 @@
 #include <mach/irda.h>
 
 #include "generic.h"
+#include "clock.h"
 
 /* Only e800 has 128MB RAM */
 void __init eseries_fixup(struct machine_desc *desc,
@@ -86,3 +88,82 @@
 	.shutdown = e7xx_irda_shutdown,
 };
 
+int eseries_tmio_enable(struct platform_device *dev)
+{
+	/* Reset - bring SUSPEND high before PCLR */
+	gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 0);
+	gpio_set_value(GPIO_ESERIES_TMIO_PCLR, 0);
+	msleep(1);
+	gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 1);
+	msleep(1);
+	gpio_set_value(GPIO_ESERIES_TMIO_PCLR, 1);
+	msleep(1);
+	return 0;
+}
+
+int eseries_tmio_disable(struct platform_device *dev)
+{
+	gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 0);
+	gpio_set_value(GPIO_ESERIES_TMIO_PCLR, 0);
+	return 0;
+}
+
+int eseries_tmio_suspend(struct platform_device *dev)
+{
+	gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 0);
+	return 0;
+}
+
+int eseries_tmio_resume(struct platform_device *dev)
+{
+	gpio_set_value(GPIO_ESERIES_TMIO_SUSPEND, 1);
+	msleep(1);
+	return 0;
+}
+
+void eseries_get_tmio_gpios(void)
+{
+	gpio_request(GPIO_ESERIES_TMIO_SUSPEND, NULL);
+	gpio_request(GPIO_ESERIES_TMIO_PCLR, NULL);
+	gpio_direction_output(GPIO_ESERIES_TMIO_SUSPEND, 0);
+	gpio_direction_output(GPIO_ESERIES_TMIO_PCLR, 0);
+}
+
+/* TMIO controller uses the same resources on all e-series machines. */
+struct resource eseries_tmio_resources[] = {
+	[0] = {
+		.start  = PXA_CS4_PHYS,
+		.end    = PXA_CS4_PHYS + 0x1fffff,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+		.end    = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+/* Some e-series hardware cannot control the 32K clock */
+static void clk_32k_dummy(struct clk *clk)
+{
+}
+
+static const struct clkops clk_32k_dummy_ops = {
+	.enable         = clk_32k_dummy,
+	.disable        = clk_32k_dummy,
+};
+
+static struct clk tmio_dummy_clk = {
+	.ops	= &clk_32k_dummy_ops,
+	.rate	= 32768,
+};
+
+static struct clk_lookup eseries_clkregs[] = {
+	INIT_CLKREG(&tmio_dummy_clk, NULL, "CLK_CK32K"),
+};
+
+void eseries_register_clks(void)
+{
+	clks_register(eseries_clkregs, ARRAY_SIZE(eseries_clkregs));
+}
+
diff --git a/arch/arm/mach-pxa/eseries.h b/arch/arm/mach-pxa/eseries.h
index bcfc589..5930f5e 100644
--- a/arch/arm/mach-pxa/eseries.h
+++ b/arch/arm/mach-pxa/eseries.h
@@ -5,3 +5,12 @@
 extern struct pxaficp_platform_data e7xx_ficp_platform_data;
 extern int e7xx_irda_init(void);
 
+extern int eseries_tmio_enable(struct platform_device *dev);
+extern int eseries_tmio_disable(struct platform_device *dev);
+extern int eseries_tmio_suspend(struct platform_device *dev);
+extern int eseries_tmio_resume(struct platform_device *dev);
+extern void eseries_get_tmio_gpios(void);
+extern struct resource eseries_tmio_resources[];
+extern struct platform_device e300_tc6387xb_device;
+extern void eseries_register_clks(void);
+
diff --git a/arch/arm/mach-pxa/include/mach/eseries-gpio.h b/arch/arm/mach-pxa/include/mach/eseries-gpio.h
index 794bc33..efbd2aa 100644
--- a/arch/arm/mach-pxa/include/mach/eseries-gpio.h
+++ b/arch/arm/mach-pxa/include/mach/eseries-gpio.h
@@ -47,4 +47,6 @@
 
 /* ASIC related GPIOs */
 #define GPIO_ESERIES_TMIO_IRQ        5
+#define GPIO_ESERIES_TMIO_PCLR      19
+#define GPIO_ESERIES_TMIO_SUSPEND   45
 #define GPIO_E800_ANGELX_IRQ      8