Re: [UPDATED PATCH] Support for Toshiba TMIO multifunction devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 2007-11-22 at 00:52 +0000, Russell King - ARM Linux wrote:
> On Thu, Nov 22, 2007 at 12:34:09AM +0000, ian wrote:

> Unfortunately, this is broken as designed (in fact this whole file is.)

Fix attached below.

> (Not looked at the rest because you really really need to get this
> right first.)

Since this core handles all the resource management, this fix should
sort that for all three drivers.

Here goes then - pass three...
>From ac98fb6ac3d4c44b81347228d35bb5714f968e2e Mon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Thu, 22 Nov 2007 10:48:47 +0000
Subject: [PATCH] Reuseable MFD core code suitable for multifunction
chips with
 built in IRQ multiplexing and local RAM.

---
 drivers/mfd/mfd-core.c |  104
++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/mfd/mfd-core.h |   26 ++++++++++++
 include/linux/ioport.h |    3 +
 3 files changed, 133 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/mfd-core.c
 create mode 100644 drivers/mfd/mfd-core.h

diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
new file mode 100644
index 0000000..bff886e
--- /dev/null
+++ b/drivers/mfd/mfd-core.c
@@ -0,0 +1,104 @@
+/*
+ * drivers/mfd/mfd-core.c
+ *
+ * core MFD support
+ * Copyright (c) 2006 Ian Molton
+ *
+ * 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/ioport.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include "mfd-core.h"
+
+void mfd_free_devices(struct platform_device **devices, int nr_devs)
+{
+	int i;
+
+	for (i = 0; i < nr_devs; i++) {
+		platform_device_unregister(*devices++);
+	}
+	kfree(devices);
+}
+EXPORT_SYMBOL_GPL(mfd_free_devices);
+
+#define SIGNED_SHIFT(val, shift) ((shift) >= 0 ? ((val) << (shift)) :
((val) >> -(shift)))
+
+struct platform_device **mfd_add_devices(struct platform_device *dev,
+					struct mfd_device_data *mfd,
+                                        int nr_devs,
+					struct resource *mem,
+					int relative_addr_shift, int irq_base)
+{
+	struct platform_device **devices;
+	int i, r, base;
+
+	devices = kzalloc(nr_devs * sizeof(struct platform_device*),
+	                  GFP_KERNEL);
+	if (!devices)
+		return NULL;
+
+	for (i = 0; i < nr_devs; i++) {
+		struct platform_device *sdev;
+		struct mfd_device_data *blk = &mfd[i];
+		struct resource *res;
+
+		if (!(sdev = platform_device_alloc (blk->name, -1)))
+			goto fail;
+
+		sdev->dev.parent = &dev->dev;
+		devices[i] = sdev;
+		platform_device_add_data (sdev, &(blk->hwconfig),
+		                          sizeof(void*));
+
+		/* Allocate space for the subdevice resources temporarily so
+		   that we can modify them */
+
+                res = kzalloc(blk->num_resources * sizeof (struct
resource), GFP_KERNEL);
+                if (!res)
+                        goto fail;
+
+		for (r = 0; r < blk->num_resources; r++) {
+			res[r].name = blk->res[r].name;
+
+			/* Find out base to use */
+			base = 0;
+			if (blk->res[r].flags & IORESOURCE_MEM) {
+				base = mem->start;
+			} else if ((blk->res[r].flags & IORESOURCE_IRQ) &&
+				(blk->res[r].flags & IORESOURCE_IRQ_MFD_SUBDEVICE)) {
+				base = irq_base;
+			}
+
+			/* Adjust resource */
+			if (blk->res[r].flags & IORESOURCE_MEM) {
+				res[r].parent = mem;
+				res[r].start = base + SIGNED_SHIFT(blk->res[r].start,
relative_addr_shift);
+				res[r].end   = base + SIGNED_SHIFT(blk->res[r].end,
relative_addr_shift);
+			} else {
+				res[r].start = base + blk->res[r].start;
+				res[r].end   = base + blk->res[r].end;
+			}
+			res[r].flags = blk->res[r].flags;
+		}
+		platform_device_add_resources(sdev, res, blk->num_resources);
+		kfree(res);
+
+		if (platform_device_add(sdev)) {
+			goto fail;
+		}
+
+		printk(KERN_INFO "MFD: registering %s\n", blk->name);
+	}
+	return devices;
+
+fail:
+	mfd_free_devices(devices, i + 1);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(mfd_add_devices);
diff --git a/drivers/mfd/mfd-core.h b/drivers/mfd/mfd-core.h
new file mode 100644
index 0000000..8f9dd76
--- /dev/null
+++ b/drivers/mfd/mfd-core.h
@@ -0,0 +1,26 @@
+/*
+ * drivers/mfd/mfd-core.h
+ *
+ * core MFD support
+ * Copyright (c) 2006 Ian Molton
+ *
+ * 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.
+ *
+ */
+
+struct mfd_device_data {
+	char *name;
+	struct resource *res;
+	int num_resources;
+	void *hwconfig; /* platform_data to pass to the subdevice */
+};
+
+struct platform_device **mfd_add_devices(struct platform_device *dev,
+					struct mfd_device_data *mfd, int n_devs,
+					struct resource *mem,
+					int relative_addr_shift, int irq_base);
+
+void mfd_free_devices(struct platform_device **devices, int nr_devs);
+
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 6187a85..5e8360a 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -57,6 +57,9 @@ struct resource_list {
 #define IORESOURCE_IRQ_LOWLEVEL		(1<<3)
 #define IORESOURCE_IRQ_SHAREABLE	(1<<4)
 
+/* MFD device IRQ specific bits (IORESOURCE_BITS) */
+#define IORESOURCE_IRQ_MFD_SUBDEVICE    (1<<5)
+
 /* ISA PnP DMA specific bits (IORESOURCE_BITS) */
 #define IORESOURCE_DMA_TYPE_MASK	(3<<0)
 #define IORESOURCE_DMA_8BIT		(0<<0)
-- 
1.5.3.5.737.gdee1b

>From 0ff1dd5ecedd43794515c46f503abf606ea5ce12 Mon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Thu, 22 Nov 2007 00:17:41 +0000
Subject: [PATCH] Preliminary support for Toshibas TMIO based
multifunction chips.

        Includes support for:
         * t7l66xb
         * tc6387xb
         * tc6393xb
---
 drivers/mfd/Kconfig       |   25 +++
 drivers/mfd/Makefile      |    3 +
 drivers/mfd/t7l66xb.c     |  286 +++++++++++++++++++++++++++++++++++
 drivers/mfd/tc6387xb.c    |  143 +++++++++++++++++
 drivers/mfd/tc6393xb.c    |  369
+++++++++++++++++++++++++++++++++++++++++++++
 include/linux/t7l66xb.h   |   56 +++++++
 include/linux/tc6387xb.h  |   34 ++++
 include/linux/tc6393.h    |  136 +++++++++++++++++
 include/linux/tmio_mmc.h  |   26 +++
 include/linux/tmio_nand.h |   15 ++
 include/linux/tmio_ohci.h |   15 ++
 11 files changed, 1108 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mfd/t7l66xb.c
 create mode 100644 drivers/mfd/tc6387xb.c
 create mode 100644 drivers/mfd/tc6393xb.c
 create mode 100644 include/linux/t7l66xb.h
 create mode 100644 include/linux/tc6387xb.h
 create mode 100644 include/linux/tc6393.h
 create mode 100644 include/linux/tmio_mmc.h
 create mode 100644 include/linux/tmio_nand.h
 create mode 100644 include/linux/tmio_ohci.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 2571619..38edfdc 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -15,6 +15,31 @@ config MFD_SM501
 	  interface. The device may be connected by PCI or local bus with
 	  varying functions enabled.
 
+config MFD_T7L66XB
+	bool "Toshiba T7L66XB SoC support"
+	---help---
+	  This driver supports the T7L66XB, which incorporates SD/MMC, and
+	  USB host functionality. associated subdevices are:
+	      tmio_mmc
+	      tmio_ohci
+
+config MFD_TC6387XB
+	bool "Toshiba TC6387XB SoC support"
+	---help---
+	  This driver supports the TC6393XB, which incorporates SD/MMC, NAND,
+	  Video, and USB host functionality. associated subdevices are:
+	      tmio_mmc
+
+config MFD_TC6393XB
+	bool "Toshiba TC6393XB SoC support"
+	---help---
+	  This driver supports the TC6393XB, which incorporates SD/MMC, NAND,
+	  Video, and USB host functionality. associated subdevices are:
+	      tmio_mmc
+	      tmio_nand
+	      tmio_fb
+	      tmio_ohci
+
 endmenu
 
 menu "Multimedia Capabilities Port drivers"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5143209..5ae3877 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -3,6 +3,9 @@
 #
 
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
+obj-$(CONFIG_MFD_T7L66XB)       += t7l66xb.o  mfd-core.o
+obj-$(CONFIG_MFD_TC6387XB)      += tc6387xb.o mfd-core.o
+obj-$(CONFIG_MFD_TC6393XB)      += tc6393xb.o mfd-core.o
 
 obj-$(CONFIG_MCP)		+= mcp-core.o
 obj-$(CONFIG_MCP_SA11X0)	+= mcp-sa11x0.o
diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
new file mode 100644
index 0000000..e3057f6
--- /dev/null
+++ b/drivers/mfd/t7l66xb.c
@@ -0,0 +1,286 @@
+/*
+ * drivers/mfd/t7l66xb.c
+ *
+ * Toshiba T7L66XB support
+ * Copyright (c) 2005 Ian Molton
+ *
+ * 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.
+ *
+ * This is based on my and Dirk Opfers work on the tc6393xb SoC.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/arch/pxa-regs.h>
+
+#include <linux/t7l66xb.h>
+#include <linux/tmio_mmc.h>
+#include "mfd-core.h"
+
+#define platform_get_platdata(_dev)      ((_dev)->dev.platform_data)
+
+struct t7l66xb_data
+{
+	int irq_base, irq_nr;
+	void *mapbase;
+	struct platform_device **devices;
+	int ndevices;
+};
+
+struct tmio_ohci_hwconfig t7l66xb_ohci_hwconfig = {
+	.start = NULL,
+};
+
+static struct resource t7l66xb_mmc_resources[] = {
+	{
+		.name = "control",
+		.start = T7L66XB_MMC_CTL_BASE,
+		.end   = T7L66XB_MMC_CTL_BASE + 0x1ff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name = "config",
+		.start = T7L66XB_MMC_CNF_BASE,
+		.end   = T7L66XB_MMC_CNF_BASE + 0xff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start = T7L66XB_MMC_IRQ,
+		.end = T7L66XB_MMC_IRQ,
+		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
+	},
+};
+
+static struct mfd_device_data t7l66xb_devices[] = {
+	{
+		.name = "tmio_mmc",
+		.res = t7l66xb_mmc_resources,
+		.num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
+	},
+};
+
+/* Handle the T7L66XB interrupt mux */
+static void t7l66xb_irq_handler(unsigned int irq, struct irq_desc
*desc)
+{
+	int req, i;
+	struct t7l66xb_data *data = get_irq_data(irq);
+
+	/* Acknowledge the parent IRQ */
+	desc->chip->ack(irq);
+  
+	while ( (req = (readb(data->mapbase + T7L66XB_SYS_ISR)
+			& ~(readb(data->mapbase + T7L66XB_SYS_IMR)))) ) {
+		for (i = 0; i <= 7; i++) {
+			int dev_irq = data->irq_base + i;
+			struct irq_desc *d = NULL;
+			if ((req & (1<<i))) {
+				d = irq_desc + dev_irq;
+				d->handle_irq(dev_irq, d);
+			}
+		}
+	}
+}
+
+
+static void t7l66xb_mask_irq(unsigned int irq)
+{
+	struct t7l66xb_data *data = get_irq_chip_data(irq);
+
+	writeb(readb(data->mapbase + T7L66XB_SYS_IMR) | 1 << (irq -
data->irq_base), data->mapbase + T7L66XB_SYS_IMR);
+}
+
+static void t7l66xb_unmask_irq(unsigned int irq)
+{
+	struct t7l66xb_data *data = get_irq_chip_data(irq);
+
+	writeb(readb(data->mapbase + T7L66XB_SYS_IMR) & ~( 1 << (irq -
data->irq_base)),data->mapbase + T7L66XB_SYS_IMR);
+}
+
+static struct irq_chip t7l66xb_chip = {
+	.name   = "t7l66xb",
+	.ack	= t7l66xb_mask_irq,
+	.mask	= t7l66xb_mask_irq,
+	.unmask	= t7l66xb_unmask_irq,
+};
+
+/* Install the IRQ handler */
+static void t7l66xb_setup_irq(struct t7l66xb_data *tchip)
+{
+	int i;
+
+	for (i = 0; i < T7L66XB_NR_IRQS; i++) {
+		int irq = tchip->irq_base + i;
+		set_irq_chip (irq, &t7l66xb_chip);
+		set_irq_chip_data (irq, tchip);
+		set_irq_handler(irq, handle_level_irq);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	set_irq_data (tchip->irq_nr, tchip);
+        set_irq_chained_handler (tchip->irq_nr, t7l66xb_irq_handler);
+	set_irq_type (tchip->irq_nr, IRQT_FALLING);
+}
+
+static void t7l66xb_hwinit(struct platform_device *dev)
+{
+	struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+	if (pdata && pdata->hw_init)
+		pdata->hw_init();
+}
+
+
+#ifdef CONFIG_PM
+
+static int t7l66xb_suspend(struct platform_device *dev, pm_message_t
state)
+{
+	struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+
+	if (pdata && pdata->suspend)
+		pdata->suspend();
+
+	return 0;
+}
+
+static int t7l66xb_resume(struct platform_device *dev)
+{
+	struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+	if (pdata && pdata->resume)
+		pdata->resume();
+
+	t7l66xb_hwinit(dev);
+
+	return 0;
+}
+#endif
+
+static int t7l66xb_probe(struct platform_device *dev)
+{
+	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+	unsigned long pbase = (unsigned long)dev->resource[0].start;
+	unsigned long plen = dev->resource[0].end - dev->resource[0].start;
+	int err = -ENOMEM;
+	struct t7l66xb_data *data;
+
+	data = kmalloc (sizeof (struct t7l66xb_data), GFP_KERNEL);
+	if (!data)
+		goto out;
+
+	data->irq_base = pdata->irq_base;
+	data->irq_nr   = dev->resource[1].start;
+
+	if (!data->irq_base) {
+		printk("t7166xb: uninitialized irq_base!\n");
+		goto out_free_data;
+	}
+
+	data->mapbase = ioremap(pbase, plen);
+	if(!data->mapbase)
+		goto out_free_irqs;
+
+	platform_set_drvdata(dev, data);
+	t7l66xb_setup_irq(data);
+	t7l66xb_hwinit(dev);
+
+	/* Mask IRQs  -- should we mask/unmask on suspend/resume? */
+	writew(0xbf, data->mapbase + T7L66XB_SYS_IMR);
+
+	printk(KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n",
+	       dev->name,  readw(data->mapbase + T7L66XB_SYS_RIDR),
+	       (unsigned long)data->mapbase, data->irq_base,
+	       data->irq_base + T7L66XB_NR_IRQS - 1, data->irq_nr);
+
+	data->devices = mfd_add_devices(dev, t7l66xb_devices,
+	                                ARRAY_SIZE(t7l66xb_devices),
+	                                &dev->resource[0], 0, data->irq_base);
+
+	if(!data->devices){
+		printk(KERN_INFO "%s: Failed to allocate devices!\n",
+		       dev->name);
+		goto out_free_devices;
+	}
+
+	return 0;
+
+out_free_devices:
+	mfd_free_devices(data->devices, ARRAY_SIZE(t7l66xb_devices));
+out_free_irqs:
+out_free_data:
+	kfree(data);
+out:
+	return err;
+}
+
+static int t7l66xb_remove(struct platform_device *dev)
+{
+	struct t7l66xb_data *tchip = platform_get_drvdata(dev);
+	int i;
+
+	/* Free subdevices */
+        mfd_free_devices(tchip->devices, ARRAY_SIZE(t7l66xb_devices));
+
+	/* Take down IRQ handling */
+        for (i = 0; i < T7L66XB_NR_IRQS; i++) {
+                int irq = i + tchip->irq_base;
+                set_irq_handler (irq, NULL);
+                set_irq_chip (irq, NULL);
+                set_irq_chip_data (irq, NULL);
+        }
+	set_irq_chained_handler (tchip->irq_nr, NULL);
+	
+	/* Free core resources */
+	iounmap (tchip->mapbase);
+
+	return 0;
+}
+
+
+static struct platform_driver t7l66xb_platform_driver = {
+	.driver = {
+		.name		= "t7l66xb",
+	},
+	.probe		= t7l66xb_probe,
+	.remove		= t7l66xb_remove,
+#ifdef CONFIG_PM
+	.suspend	= t7l66xb_suspend,
+	.resume		= t7l66xb_resume,
+#endif
+};
+
+
+static int __init t7l66xb_init(void)
+{
+	int retval = 0;
+		
+	retval = platform_driver_register (&t7l66xb_platform_driver);
+	return retval;
+}
+
+static void __exit t7l66xb_exit(void)
+{
+	platform_driver_unregister(&t7l66xb_platform_driver);
+}
+
+module_init(t7l66xb_init);
+module_exit(t7l66xb_exit);
+
+MODULE_DESCRIPTION("Toshiba T7L66XB core driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Molton and Dirk Opfer");
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
new file mode 100644
index 0000000..d309b9f
--- /dev/null
+++ b/drivers/mfd/tc6387xb.c
@@ -0,0 +1,143 @@
+/*
+ * drivers/mfd/tc6387xb.c
+ *
+ * Toshiba TC6387XB support
+ * Copyright (c) 2005 Ian Molton
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+#include <linux/tmio_mmc.h>
+#include <linux/tc6387xb.h>
+#include "mfd-core.h"
+
+struct tc6387xb_data {
+	int irq;
+	struct tc6387xb_platform_data *platform;
+};
+
+static void tc6387xb_hwinit(struct platform_device *dev)
+{
+	struct tc6387xb_data *data = platform_get_drvdata(dev);
+
+	if (data && data->platform && data->platform->hw_init)
+		data->platform->hw_init();
+
+}
+
+#ifdef CONFIG_PM
+
+static int tc6387xb_suspend(struct platform_device *dev, pm_message_t
state)
+{
+	struct tc6387xb_data *data = platform_get_drvdata(dev);
+
+	if (data && data->platform && data->platform->suspend)
+		data->platform->suspend();
+
+	return 0;
+}
+
+static int tc6387xb_resume(struct platform_device *dev)
+{
+	struct tc6387xb_data *data = platform_get_drvdata(dev);
+
+	if (data && data->platform && data->platform->resume)
+		data->platform->resume();
+
+	return 0;
+}
+#endif
+
+static struct resource tc6387xb_mmc_resources[] = {
+        {
+                .name = "control",
+                .start = TC6387XB_MMC_CTL_BASE,
+                .end   = TC6387XB_MMC_CTL_BASE + 0x1ff,
+                .flags = IORESOURCE_MEM,
+        },
+        {
+                .name = "config",
+                .start = TC6387XB_MMC_CNF_BASE,
+                .end   = TC6387XB_MMC_CNF_BASE + 0xff,
+                .flags = IORESOURCE_MEM,
+        },
+        {
+                .start = TC6387XB_MMC_IRQ,
+                .end   = TC6387XB_MMC_IRQ,
+                .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
+        },
+};
+
+static struct mfd_device_data tc6387xb_devices[] = {
+        {
+                .name = "tmio_mmc",
+                .res = tc6387xb_mmc_resources,
+                .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
+        },
+};
+
+
+static int tc6387xb_probe(struct platform_device *pdev)
+{
+	struct tc6387xb_data *data;
+
+	data = kmalloc(sizeof(struct tc6387xb_data), GFP_KERNEL);
+	if(!data)
+		return -ENOMEM;
+
+	data->irq = pdev->resource[1].start;
+	data->platform = pdev->dev.platform_data;
+	platform_set_drvdata(pdev, data);
+
+	tc6387xb_hwinit(pdev);
+	
+	mfd_add_devices(pdev, tc6387xb_devices, ARRAY_SIZE(tc6387xb_devices),
&pdev->resource[0], 0, data->irq);
+
+	/* Init finished. */
+	return 0;
+}
+
+static struct platform_driver tc6387xb_platform_driver = {
+	.driver = {
+		.name		= "tc6387xb",
+	},
+	.probe		= tc6387xb_probe,
+#ifdef CONFIG_PM
+	.suspend	= tc6387xb_suspend,
+	.resume		= tc6387xb_resume,
+#endif
+};
+
+
+static int __init tc6387xb_init(void)
+{
+	return platform_driver_register (&tc6387xb_platform_driver);
+}
+
+static void __exit tc6387xb_exit(void)
+{
+	platform_driver_unregister(&tc6387xb_platform_driver);
+}
+
+module_init(tc6387xb_init);
+module_exit(tc6387xb_exit);
+
+MODULE_DESCRIPTION("Toshiba TC6387XB core driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Molton");
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
new file mode 100644
index 0000000..3d43ce3
--- /dev/null
+++ b/drivers/mfd/tc6393xb.c
@@ -0,0 +1,369 @@
+/*
+ * drivers/mfd/tc6393xb.c
+ *
+ * Toshiba TC6393 support
+ * Copyright (c) 2005 Dirk Opfer
+ * Copyright (c) 2005 Ian Molton <[email protected]>
+ *
+ *	Based on code written by Sharp/Lineo for 2.4 kernels
+ *	Based on locomo.c
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/arch/pxa-regs.h>
+
+#include <linux/tmio_mmc.h>
+#include <linux/tmio_nand.h>
+#include <linux/tmio_ohci.h>
+#include <linux/tc6393.h>
+#include "mfd-core.h"
+
+#define platform_get_platdata(_dev)      ((_dev)->dev.platform_data)
+
+struct tc6393xb_data
+{
+	int irq_base, irq_nr;
+	void *mapbase;
+	struct platform_device **devices;
+	int ndevices;
+};
+
+/* Setup the TC6393XB NAND flash controllers configuration registers */
+static void tc6393xb_nand_hwinit(struct platform_device *sdev) {
+	
+	struct tc6393xb_data *chip = platform_get_drvdata(sdev);
+	
+	/* Sequence: 
+	 * SMD Buffer ON (gpio related)
+	 * Enable the clock (SCRUNEN)
+	 * Set the ctl reg base address
+	 * Enable the ctl reg
+	 * Configure power control (control bt PCNT[1,0] 4ms startup delay)
+	 */
+	/* (89h) SMD Buffer ON By TC6393XB SystemConfig gpibfc1*/
+	writew(0xff, chip->mapbase + TC6393_SYS_GPIBCR1);
+
+}
+
+static struct tmio_nand_hwconfig tc6393xb_nand_hwconfig = {
+	.hwinit  = tc6393xb_nand_hwinit,
+};
+
+static void tc6393xb_mmc_set_clock(struct platform_device *sdev, int
state) {
+	struct tc6393xb_data *chip = platform_get_drvdata(sdev);
+	unsigned char tmp;
+
+	if(state == MMC_CLOCK_ENABLED){
+		tmp = readw(chip->mapbase + TC6393_SYS_GPIBCR1);
+		writew(tmp | CK32KEN, chip->mapbase + TC6393_SYS_GPIBCR1);
+	}
+}
+
+static struct tmio_mmc_hwconfig tc6393xb_mmc_hwconfig = {
+        .set_mmc_clock  = tc6393xb_mmc_set_clock,
+};
+
+static struct resource tc6393xb_mmc_resources[] = {
+	{
+		.name = "control",
+		.start = TC6393XB_MMC_CTL_BASE,
+		.end   = TC6393XB_MMC_CTL_BASE + 0x1ff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name = "config",
+		.start = TC6393XB_MMC_CNF_BASE,
+		.end   = TC6393XB_MMC_CNF_BASE + 0xff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.start = TC6393XB_MMC_IRQ,
+		.end   = TC6393XB_MMC_IRQ,
+		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
+	},
+};
+
+static struct resource tc6393xb_nand_resources[] = {
+	{
+		.name = "control",
+		.start = TC6393XB_NAND_CTL_BASE,
+		.end   = TC6393XB_NAND_CTL_BASE + 0x1ff,
+		.flags = IORESOURCE_MEM,
+	},
+	{
+		.name = "config",
+		.start = TC6393XB_NAND_CNF_BASE,
+		.end   = TC6393XB_NAND_CNF_BASE + 0xff,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+static struct mfd_device_data tc6393xb_devices[] = {
+	{
+		.name = "tmio_mmc",
+		.res = tc6393xb_mmc_resources,
+		.num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
+		.hwconfig = &tc6393xb_mmc_hwconfig,
+	},
+	{
+		.name = "tmio-nand",
+		.res = tc6393xb_nand_resources,
+		.num_resources = ARRAY_SIZE(tc6393xb_nand_resources),
+		.hwconfig = &tc6393xb_nand_hwconfig,
+	},
+};
+
+/** TC6393 interrupt handling stuff.
+ * NOTE: TC6393 has a 1 to many mapping on all of its IRQs.
+ * that is, there is only one real hardware interrupt
+ * we determine which interrupt it is by reading some IO memory.
+ * We have two levels of expansion, first in the handler for the
+ * hardware interrupt we generate an interrupt
+ * IRQ_TC6393_*_BASE and those handlers generate more interrupts
+ *
+ */
+static void tc6393xb_irq_handler(unsigned int irq, struct irq_desc
*desc)
+{
+	int req, i;
+	struct tc6393xb_data *data = get_irq_data(irq);
+	
+	/* Acknowledge the parent IRQ */
+	desc->chip->ack(irq);
+  
+	while ( (req = (readb(data->mapbase + TC6393_SYS_ISR)
+			& ~(readb(data->mapbase + TC6393_SYS_IMR)))) ) {
+		for (i = 0; i <= 7; i++) {
+			int dev_irq = data->irq_base + i;
+			struct irq_desc *d = NULL;
+			if ((req & (1<<i))) {
+				if(i != 1)  printk("IRQ! from %d\n", i);
+				d = irq_desc + dev_irq;
+				d->handle_irq(dev_irq, d);
+			}
+		}
+	}
+}
+
+static void tc6393xb_mask_irq(unsigned int irq)
+{
+	struct tc6393xb_data *tc6393 = get_irq_chip_data(irq);
+
+	writeb(readb(tc6393->mapbase + TC6393_SYS_IMR) | 1 << (irq -
tc6393->irq_base),tc6393->mapbase + TC6393_SYS_IMR);
+}
+
+static void tc6393xb_unmask_irq(unsigned int irq)
+{
+	struct tc6393xb_data *tc6393 = get_irq_chip_data(irq);
+
+	writeb(readb(tc6393->mapbase + TC6393_SYS_IMR) & ~( 1 << (irq -
tc6393->irq_base)),tc6393->mapbase + TC6393_SYS_IMR);
+}
+
+static struct irq_chip tc6393xb_chip = {
+	.ack	= tc6393xb_mask_irq,
+	.mask	= tc6393xb_mask_irq,
+	.unmask	= tc6393xb_unmask_irq,
+};
+
+
+static void tc6393xb_setup_irq(struct tc6393xb_data *tchip)
+{
+	int i;
+	
+	for (i = 0; i < TC6393XB_NR_IRQS; i++) {
+		int irq = tchip->irq_base + i;
+		set_irq_chip (irq, &tc6393xb_chip);
+		set_irq_chip_data (irq, tchip);
+		set_irq_handler(irq, handle_level_irq);
+		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	set_irq_data (tchip->irq_nr, tchip);
+	set_irq_chained_handler (tchip->irq_nr, tc6393xb_irq_handler);
+	set_irq_type (tchip->irq_nr, IRQT_FALLING);
+}
+
+void tc6393xb_hwinit(struct platform_device *dev)
+{
+	struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+	struct tc6393xb_data *tchip =  platform_get_drvdata(dev);
+
+	if(!pdata || !tchip){
+		BUG_ON("no driver data!\n");
+		return;
+	}
+
+	if (pdata->hw_init)
+		pdata->hw_init();
+		
+	writew(0, tchip->mapbase + TC6393_SYS_FER);
+
+	/* Clock setting */
+	writew(pdata->sys_pll2cr,   tchip->mapbase + TC6393_SYS_PLL2CR);
+	writew(pdata->sys_ccr,      tchip->mapbase + TC6393_SYS_CCR);
+	writew(pdata->sys_mcr,      tchip->mapbase + TC6393_SYS_MCR);
+
+  	/* GPIO */
+	writew(pdata->sys_gper,     tchip->mapbase + TC6393_SYS_GPER);
+	writew(pdata->sys_gpodsr1,  tchip->mapbase + TC6393_SYS_GPODSR1);
+	writew(pdata->sys_gpooecr1, tchip->mapbase + TC6393_SYS_GPOOECR1);
+}
+
+
+#ifdef CONFIG_PM
+
+static int tc6393xb_suspend(struct platform_device *dev, pm_message_t
state)
+{
+	struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+
+	if (pdata && pdata->suspend)
+		pdata->suspend();
+
+	return 0;
+}
+
+static int tc6393xb_resume(struct platform_device *dev)
+{
+	struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+
+	if (pdata && pdata->resume)
+		pdata->resume();
+
+	tc6393xb_hwinit(dev);
+
+	return 0;
+}
+
+#endif
+
+static int tc6393xb_probe(struct platform_device *dev)
+{
+	struct tc6393xb_platform_data *pdata = dev->dev.platform_data;
+	unsigned long pbase = (unsigned long)dev->resource[0].start;
+	unsigned long plen = dev->resource[0].end - dev->resource[0].start;
+	int err = -ENOMEM;
+	struct tc6393xb_data *data;
+
+	data = kmalloc (sizeof (struct tc6393xb_data), GFP_KERNEL);
+	if (!data)
+		goto out;
+
+	data->irq_base = pdata->irq_base;
+	data->irq_nr = dev->resource[1].start;
+
+	if (!data->irq_base) {
+		printk("tc6393xb: uninitialized irq_base!\n");
+		goto out_free_data;
+	}
+
+	data->mapbase = ioremap(pbase, plen);
+	if(!data->mapbase)
+		goto out_free_irqs;
+
+	platform_set_drvdata(dev, data);
+	tc6393xb_setup_irq (data);
+	tc6393xb_hwinit(dev);
+
+	/* Enable (but mask!) our IRQs */
+	writew(0,    data->mapbase + TC6393_SYS_IRR);
+	writew(0xbf, data->mapbase + TC6393_SYS_IMR);
+
+	printk(KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n",
+	       dev->name,  readw(data->mapbase + TC6393_SYS_RIDR),
+	       (unsigned long)data->mapbase, data->irq_base,
+	       data->irq_base + TC6393XB_NR_IRQS - 1, data->irq_nr);
+
+	data->devices =  mfd_add_devices(dev, tc6393xb_devices,
+	                                ARRAY_SIZE(tc6393xb_devices),
+	                                &dev->resource[0], 0, data->irq_base);
+
+	if(!data->devices) {
+		printk(KERN_INFO "%s: Failed to allocate devices!\n",
+		       dev->name);
+		goto out_free_devices;
+	}
+
+	return 0;
+
+out_free_devices:
+	mfd_free_devices(data->devices, ARRAY_SIZE(tc6393xb_devices));
+out_free_irqs:
+out_free_data:
+	kfree(data);
+out:
+	return err;
+}
+
+static int tc6393xb_remove(struct platform_device *dev)
+{
+	struct tc6393xb_data *tchip = platform_get_drvdata(dev);
+	int i;
+
+	/* Free subdevices */
+        mfd_free_devices(tchip->devices, ARRAY_SIZE(tc6393xb_devices));
+
+	/* Take down IRQ handling */
+	for (i = 0; i < TC6393XB_NR_IRQS; i++) {
+		int irq = i + tchip->irq_base;
+		set_irq_handler (irq, NULL);
+		set_irq_chip (irq, NULL);
+		set_irq_chip_data (irq, NULL);
+	}
+	
+	set_irq_chained_handler (tchip->irq_nr, NULL);
+
+	/* Free core resources */
+        iounmap (tchip->mapbase);
+
+	return 0;
+}
+
+
+static struct platform_driver tc6393xb_device_driver = {
+	.driver = {
+		.name		= "tc6393xb",
+	},
+	.probe		= tc6393xb_probe,
+	.remove		= tc6393xb_remove,
+#ifdef CONFIG_PM
+	.suspend	= tc6393xb_suspend,
+	.resume		= tc6393xb_resume,
+#endif
+};
+
+
+static int __init tc6393xb_init(void)
+{
+	int retval = 0;
+	retval = platform_driver_register (&tc6393xb_device_driver);
+	return retval;
+}
+
+static void __exit tc6393xb_exit(void)
+{
+	platform_driver_unregister(&tc6393xb_device_driver);
+}
+
+module_init(tc6393xb_init);
+module_exit(tc6393xb_exit);
+
+MODULE_DESCRIPTION("Toshiba TC6393 core driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dirk Opfer and Ian Molton");
diff --git a/include/linux/t7l66xb.h b/include/linux/t7l66xb.h
new file mode 100644
index 0000000..f2d013a
--- /dev/null
+++ b/include/linux/t7l66xb.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * This file contains the definitions for the T7L66XB
+ *
+ * (C) Copyright 2005 Ian Molton <[email protected]>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+
+/* FIXME - this needs to be a common struct to all TMIO based MFDs. */
+struct tmio_hwconfig {
+	void (*hwinit)(struct platform_device *sdev);
+	void (*suspend)(struct platform_device *sdev);
+	void (*resume)(struct platform_device *sdev);
+};
+
+struct tmio_ohci_hwconfig {
+	void (*start)(struct platform_device *dev);
+};
+
+struct t7l66xb_platform_data
+{
+	/* Standard MFD properties */
+	int irq_base;
+	struct platform_device **child_devs;
+	int num_child_devs;
+
+	void (* hw_init) (void);
+	void (* suspend) (void);
+	void (* resume)  (void);
+};
+
+
+#define T7L66XB_NAND_CNF_BASE  (0x000100)
+#define T7L66XB_NAND_CTL_BASE  (0x001000)
+
+#define T7L66XB_MMC_CNF_BASE   (0x000200)
+#define T7L66XB_MMC_CTL_BASE   (0x000800)
+#define T7L66XB_MMC_IRQ        (1)
+
+#define T7L66XB_USB_CNF_BASE   (0x000300)
+#define T7L66XB_USB_CTL_BASE   (0x002000)
+#define T7L66XB_OHCI_IRQ       (0)
+
+/* System Configuration register */
+#define T7L66XB_SYS_RIDR    0x008        // Revision ID
+#define T7L66XB_SYS_ISR     0x0e1        // Interrupt Status
+#define T7L66XB_SYS_IMR     0x042        // Interrupt Mask
+
+#define T7L66XB_NR_IRQS	8
+
diff --git a/include/linux/tc6387xb.h b/include/linux/tc6387xb.h
new file mode 100644
index 0000000..99c01a3
--- /dev/null
+++ b/include/linux/tc6387xb.h
@@ -0,0 +1,34 @@
+/*
+ *
+ * (C) Copyright 2005 Ian Molton <[email protected]>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+/* FIXME - this needs to be a common struct to all TMIO based MFDs. */
+struct tmio_hwconfig {
+	void (*hwinit)(struct platform_device *sdev);
+	void (*suspend)(struct platform_device *sdev);
+	void (*resume)(struct platform_device *sdev);
+};
+
+struct tc6387xb_platform_data
+{
+	/* Standard MFD properties */
+	int irq_base;
+	struct platform_device **child_devs;
+	int num_child_devs;
+
+	void (* hw_init) (void);
+	void (* suspend) (void);
+	void (* resume)  (void);
+};
+
+#define TC6387XB_MMC_CNF_BASE    (0x000200)
+#define TC6387XB_MMC_CTL_BASE    (0x000800)
+#define TC6387XB_MMC_IRQ         (0)
+
diff --git a/include/linux/tc6393.h b/include/linux/tc6393.h
new file mode 100644
index 0000000..99ecd42
--- /dev/null
+++ b/include/linux/tc6393.h
@@ -0,0 +1,136 @@
+/*
+ *
+ * (C) Copyright 2005 Dirk Opfer
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License.  See linux/COPYING for more information.
+ *
+ */
+
+struct tc6393xb_platform_data
+{
+	/* Standard MFD properties */
+	int irq_base;
+	struct platform_device **child_devs;
+	int num_child_devs;
+
+	u16 sys_gper;
+	u16 sys_gpodsr1;
+	u16 sys_gpooecr1;
+	u16 sys_pll2cr;
+	u16 sys_ccr;
+	u16 sys_mcr;
+	void (* hw_init) (void);
+	void (* suspend) (void);
+	void (* resume)  (void);
+};
+
+#define CK32KEN 0x1
+#define USBCLK  0x2
+
+/*
+ TC6393XB Resource Area Map (Offset)
+  System Configration Register Area         0x00000000 - 0x000000FF 
+  NAND Flash Host Controller Register Area  0x00000100 - 0x000001FF 
+  USB Host Controller Register Area         0x00000300 - 0x000003FF
+  LCD Host Controller Register Area         0x00000500 - 0x000005FF
+  NAND Flash Control Register               0x00001000 - 0x00001007
+  USB Control Register                      0x00003000 - 0x000031FF
+  LCD Control Register                      0x00005000 - 0x000051FF
+  Local Memory 0 (32KB)                     0x00010000 - 0x00017FFF
+  Local Memory 0 (32KB) alias               0x00018000 - 0x0001FFFF
+  Local Memory 1 (1MB)                      0x00100000 - 0x001FFFFF
+*/
+
+#define TC6393_SYS_BASE		0
+
+#define TC6393XB_NAND_CNF_BASE	(TC6393_SYS_BASE + 0x000100)
+#define TC6393XB_NAND_CTL_BASE  (TC6393_SYS_BASE + 0x001000)
+
+#define TC6393XB_MMC_CNF_BASE	(TC6393_SYS_BASE + 0x000200)
+#define TC6393XB_MMC_CTL_BASE	(TC6393_SYS_BASE + 0x000800)
+#define TC6393XB_MMC_IRQ          (1)
+
+#define TC6393XB_USB_CNF_BASE   (TC6393_SYS_BASE + 0x000300)
+#define TC6393XB_USB_CTL_BASE   (TC6393_SYS_BASE + 0x000a00)
+#define TC6393XB_USB_IRQ        (0)
+
+#define TC6393_SERIAL_CONF_BASE	(TC6393_SYS_BASE + 0x000400)
+#define TC6393_GC_CONF_BASE	(TC6393_SYS_BASE + 0x000500)
+#define TC6393_RAM0_BASE	(TC6393_SYS_BASE + 0x010000)
+#define TC6393_RAM0_SIZE	(32*1024)
+#define TC6393_RAM1_BASE	(TC6393_SYS_BASE + 0x100000)
+#define TC6393_RAM1_SIZE	(64 * 1024 * 16)
+
+
+/* 
+ * Internal Local Memory use purpose
+ *   RAM0 is used for USB
+ *   RAM1 is used for GC
+ */
+/* Internal register mapping */
+#define TC6393_GC_INTERNAL_REG_BASE	0x000600	/* Length 0x200 */
+
+	
+/* System Configuration register */
+#define TC6393_SYS_RIDR		0x008			// Revision ID
+#define TC6393_SYS_ISR		0x050			// Interrupt Status
+#define TC6393_SYS_IMR		0x052			// Interrupt Mask
+#define TC6393_SYS_IRR		0x054			// Interrupt Routing
+#define TC6393_SYS_GPER		0x060			// GP Enable
+#define TC6393_SYS_GPAIOEN	0x061			// GP Alternative Enable
+#define TC6393_SYS_GPISR1	0x064			// GPI Status 1
+#define TC6393_SYS_GPISR2	0x066			// GPI Status 2
+#define TC6393_SYS_GPIIMR1	0x068			// GPI INT Mask 1
+#define TC6393_SYS_GPIIMR2	0x06A			// GPI INT Mask 2
+#define TC6393_SYS_GPIEDER1	0x06C			// GPI Edge Detect Enable 1
+#define TC6393_SYS_GPIEDER2	0x06E			// GPI Edge Detect Enable 2
+#define TC6393_SYS_GPILIR1	0x070			// GPI Level Invert 1
+#define TC6393_SYS_GPILIR2	0x072			// GPI Level Invert 2
+#define TC6393_SYS_GPODSR1	0x078			// GPO Data set 1
+#define TC6393_SYS_GPODSR2      0x07A			// GPO Data set 2
+#define TC6393_SYS_GPOOECR1     0x07C			// GPO Data OE Contorol 1
+#define TC6393_SYS_GPOOECR2     0x07E			// GPO Data OE Contorol 2
+#define TC6393_SYS_GPIARCR1     0x080			// GP Internal Active Register
Contorol 1
+#define TC6393_SYS_GPIARCR2     0x082			// GP Internal Active Register
Contorol 2
+#define TC6393_SYS_GPIARLCR1    0x084			// GP Internal Active Register
Level Contorol 1
+#define TC6393_SYS_GPIARLCR2    0x086			// GP Internal Active Register
Level Contorol 2
+
+#define TC6393_SYS_GPIBCR1      0x089			// GPa Internal Activ Register
Contorol 1
+
+#define TC6393_SYS_GPIBCR2      0x08A			// GPa Internal Activ Register
Contorol 2
+#define TC6393_SYS_GPaIARCR     0x08C			
+#define TC6393_SYS_GPaIARLCR    0x090
+#define TC6393_SYS_GPaIBCR      0x094
+#define TC6393_SYS_CCR          0x098			/* Clock Control Register */
+#define TC6393_SYS_PLL2CR       0x09A			// PLL2 Control
+#define TC6393_SYS_PLL1CR1      0x09C			// PLL1 Control 1
+#define TC6393_SYS_PLL1CR2      0x09E			// PLL1 Control 2
+#define TC6393_SYS_DCR          0x0A0
+#define TC6393_SYS_FER          0x0E0			/* Function Enable Register */
+#define TC6393_SYS_MCR          0x0E4
+#define TC6393_SYS_ConfigCR     0x0FC
+
+/* GPIO bit */
+#define TC6393_GPIO19  ( 1 << 19 )
+#define TC6393_GPIO18  ( 1 << 18 )
+#define TC6393_GPIO17  ( 1 << 17 )
+#define TC6393_GPIO16  ( 1 << 16 )
+#define TC6393_GPIO15  ( 1 << 15 )
+#define TC6393_GPIO14  ( 1 << 14 )
+#define TC6393_GPIO13  ( 1 << 13 )
+#define TC6393_GPIO12  ( 1 << 12 )
+#define TC6393_GPIO11  ( 1 << 11 )
+#define TC6393_GPIO10  ( 1 << 10 )
+#define TC6393_GPIO9   ( 1 << 9 )
+#define TC6393_GPIO8   ( 1 << 8 )
+#define TC6393_GPIO7   ( 1 << 7 )
+#define TC6393_GPIO6   ( 1 << 6 )
+#define TC6393_GPIO5   ( 1 << 5 )
+#define TC6393_GPIO4   ( 1 << 4 )
+#define TC6393_GPIO3   ( 1 << 3 )
+#define TC6393_GPIO2   ( 1 << 2 )
+#define TC6393_GPIO1   ( 1 << 1 )
+#define TC6393_GPIO0   ( 1 << 0 )
+
+#define TC6393XB_NR_IRQS	8
diff --git a/include/linux/tmio_mmc.h b/include/linux/tmio_mmc.h
new file mode 100644
index 0000000..00358bf
--- /dev/null
+++ b/include/linux/tmio_mmc.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2005 Ian Molton
+ *
+ * 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/platform_device.h>
+
+#define MMC_CLOCK_DISABLED 0
+#define MMC_CLOCK_ENABLED  1
+
+#define TMIO_WP_ALWAYS_RW ((void*)-1)
+
+struct tmio_mmc_hwconfig {
+	void (*hwinit)(struct platform_device *sdev);
+	void (*set_mmc_clock)(struct platform_device *sdev, int state);
+
+	/* NULL - use ASIC3 signal, 
+	   TMIO_WP_ALWAYS_RW - assume always R/W (e.g. miniSD) 
+	   otherwise - machine-specific handler */
+	int (*mmc_get_ro)(struct platform_device *pdev);
+	short address_shift;
+};
diff --git a/include/linux/tmio_nand.h b/include/linux/tmio_nand.h
new file mode 100644
index 0000000..e1b38eb
--- /dev/null
+++ b/include/linux/tmio_nand.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2005 Ian Molton
+ *
+ * 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.
+ *
+ */
+
+struct tmio_nand_hwconfig {
+        void (*hwinit)(struct platform_device *sdev);
+        void (*suspend)(struct platform_device *sdev);
+        void (*resume)(struct platform_device *sdev);
+};
+
diff --git a/include/linux/tmio_ohci.h b/include/linux/tmio_ohci.h
new file mode 100644
index 0000000..5d07083
--- /dev/null
+++ b/include/linux/tmio_ohci.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2005 Ian Molton
+ *
+ * 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.
+ *
+ */
+
+struct tmio_ohci_hwconfig {
+        void (*hwinit)(struct platform_device *sdev);
+        void (*suspend)(struct platform_device *sdev);
+        void (*resume)(struct platform_device *sdev);
+};
+
-- 
1.5.3.5.737.gdee1b

>From 9d9be4115972ae4c75d27c1a6b7aa1e3a840a98f Mon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Thu, 22 Nov 2007 00:00:20 +0000
Subject: [PATCH] Add eseries platform support for the relevant TMIO
multifunction devices used.

---
 arch/arm/mach-pxa/Makefile        |    5 ++
 arch/arm/mach-pxa/e330_tc6387xb.c |   98
+++++++++++++++++++++++++++++++++
 arch/arm/mach-pxa/e400_t7l66xb.c  |  107
++++++++++++++++++++++++++++++++++++
 arch/arm/mach-pxa/e740_t7l66xb.c  |  103
+++++++++++++++++++++++++++++++++++
 arch/arm/mach-pxa/e750_tc6393xb.c |  109
+++++++++++++++++++++++++++++++++++++
 5 files changed, 422 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-pxa/e330_tc6387xb.c
 create mode 100644 arch/arm/mach-pxa/e400_t7l66xb.c
 create mode 100644 arch/arm/mach-pxa/e740_t7l66xb.c
 create mode 100644 arch/arm/mach-pxa/e750_tc6393xb.c

diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 2c50e9d..938cb2c 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -30,6 +30,11 @@ ifeq ($(CONFIG_MACH_ZYLONITE),y)
 endif
 
 obj-$(CONFIG_ARCH_PXA_ESERIES)	+= eseries.o
+obj-$(CONFIG_MACH_E330)		+= e330_tc6387xb.o
+obj-$(CONFIG_MACH_E740)		+= e740_t7l66xb.o
+obj-$(CONFIG_MACH_E750)		+= e750_tc6393xb.o
+obj-$(CONFIG_MACH_E400)		+= e400_t7l66xb.o
+obj-$(CONFIG_MACH_E800)		+= e750_tc6393xb.o
 
 obj-$(CONFIG_MACH_ARMCORE)      += cm-x270.o
 
diff --git a/arch/arm/mach-pxa/e330_tc6387xb.c
b/arch/arm/mach-pxa/e330_tc6387xb.c
new file mode 100644
index 0000000..9859de2
--- /dev/null
+++ b/arch/arm/mach-pxa/e330_tc6387xb.c
@@ -0,0 +1,98 @@
+/*
+ *
+ *  Copyright (C) Ian Molton.
+ *
+ *  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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/tc6387xb.h>
+
+static struct resource e330_tc6387xb_resources[] = {
+	[0] = {
+		.start  = PXA_CS4_PHYS,
+		.end    = PXA_CS4_PHYS + 0x1fffff,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = IRQ_GPIO(5),
+		.end    = IRQ_GPIO(5),
+		.flags  = IORESOURCE_IRQ,
+	},
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or
kernel ? */
+static void e330_tc6387xb_hwinit(void) {
+	GPCR(45) = GPIO_bit(45);                        /* #SUSPEND low */
+	GPCR(19) = GPIO_bit(19);                        /* #PCLR low (reset)
*/
+	/* 33MHz is supplied from an Xtal on the e330 (I think) */
+	pxa_gpio_mode(GPIO12_32KHz_MD);
+	mdelay(10);
+	GPSR(45) = GPIO_bit(45);                        /* #SUSPEND high */
+	mdelay(10);
+	GPSR(19) = GPIO_bit(19);                        /* #PCLR high */
+	mdelay(10);
+}
+
+static void e330_tc6387xb_suspend(void) {
+	GPCR(45) = GPIO_bit(45); /* #SUSPEND low */
+	mdelay(10);
+#if 0
+	GPCR(19) = GPIO_bit(19); /* #PCLR low */
+#endif
+}
+
+static void e330_tc6387xb_resume(void) {
+	GPSR(45) = GPIO_bit(45); /* #SUSPEND high */
+        mdelay(10);
+}
+
+static struct tc6387xb_platform_data e330_tc6387xb_info = {
+	.hw_init                = &e330_tc6387xb_hwinit,
+	.suspend                = &e330_tc6387xb_suspend,
+	.resume                 = &e330_tc6387xb_resume,
+};
+
+static struct platform_device e330_tc6387xb_device = {
+	.name           = "tc6387xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e330_tc6387xb_info, 
+	},
+	.num_resources = ARRAY_SIZE(e330_tc6387xb_resources),
+	.resource      = e330_tc6387xb_resources,
+}; 
+
+static int __init e330_tc6387xb_init(void)
+{
+	if(!machine_is_e330())
+		return -ENODEV;
+
+	platform_device_register(&e330_tc6387xb_device);
+	return 0;
+}
+
+module_init(e330_tc6387xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>");
+MODULE_DESCRIPTION("e330 tc6387xb device support");
+MODULE_LICENSE("GPLv2");
+
+
diff --git a/arch/arm/mach-pxa/e400_t7l66xb.c
b/arch/arm/mach-pxa/e400_t7l66xb.c
new file mode 100644
index 0000000..5c77388
--- /dev/null
+++ b/arch/arm/mach-pxa/e400_t7l66xb.c
@@ -0,0 +1,107 @@
+/* 
+ *
+ *  Copyright (C) Ian Molton.
+ *
+ *  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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/t7l66xb.h>
+
+/* FIXME - set properly when known */
+#define GPIO_E400_TMIO_IRQ GPIO_ESERIES_TMIO_IRQ
+
+static struct resource e400_t7l66xb_resources[] = {
+	[0] = {
+		.start  = PXA_CS4_PHYS,
+		.end    = PXA_CS4_PHYS + 0x1fffff,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.start  = IRQ_GPIO(GPIO_E400_TMIO_IRQ),
+		.end    = IRQ_GPIO(GPIO_E400_TMIO_IRQ),
+		.flags  = IORESOURCE_IRQ,
+	},
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or
kernel ? */
+static void e400_t7l66xb_hwinit(void) {
+	return;
+	GPCR(19) = GPIO_bit(19); /* #SUSPEND low */
+	GPCR(7) = GPIO_bit(7);   /* #PCLR low (reset) */
+	pxa_gpio_mode(GPIO7_48MHz_MD);
+	pxa_gpio_mode(GPIO12_32KHz_MD);
+	mdelay(10);
+	GPSR(19) = GPIO_bit(19); /* #SUSPEND high */
+	mdelay(10);
+	GPSR(7) = GPIO_bit(7);   /* #PCLR high */
+	mdelay(10);
+}
+
+static void e400_t7l66xb_suspend(void) {
+	return;
+	GPCR(19) = GPIO_bit(19); /* #SUSPEND low */
+	mdelay(10);
+	GPCR(7) = GPIO_bit(7);   /* #PCLR low */
+
+#if 0
+	/* kill clock */
+	pxa_gpio_mode(GPIO7_48MHz_MD|GPIO_OUT);
+	GPSR0 = GPIO_bit(GPIO7_48MHz);
+#endif
+}
+
+static void e400_t7l66xb_resume(void) {
+	e400_t7l66xb_hwinit();
+}
+
+static struct t7l66xb_platform_data e400_t7l66xb_info = {
+	.irq_base 		= IRQ_BOARD_START,
+	.hw_init                = &e400_t7l66xb_hwinit,
+	.suspend                = &e400_t7l66xb_suspend,
+	.resume                 = &e400_t7l66xb_resume,
+};
+
+static struct platform_device e400_t7l66xb_device = {
+	.name           = "t7l66xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e400_t7l66xb_info, 
+	},
+	.num_resources = ARRAY_SIZE(e400_t7l66xb_resources),
+	.resource      = e400_t7l66xb_resources,
+}; 
+
+static int __init e400_t7l66xb_init(void)
+{
+	if(!machine_is_e400())
+		return -ENODEV;
+
+	platform_device_register(&e400_t7l66xb_device);
+	return 0;
+}
+
+module_init(e400_t7l66xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>");
+MODULE_DESCRIPTION("e400 t7l66xb device support");
+MODULE_LICENSE("GPLv2");
+
diff --git a/arch/arm/mach-pxa/e740_t7l66xb.c
b/arch/arm/mach-pxa/e740_t7l66xb.c
new file mode 100644
index 0000000..26d1252
--- /dev/null
+++ b/arch/arm/mach-pxa/e740_t7l66xb.c
@@ -0,0 +1,103 @@
+/* 
+ *
+ *  Copyright (C) Ian Molton.
+ *
+ *  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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/t7l66xb.h>
+
+static struct resource e740_t7l66xb_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,
+	},
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or
kernel ? */
+static void e740_t7l66xb_hwinit(void) {
+
+	GPCR(19) = GPIO_bit(19);            /* #SUSPEND low */
+	GPCR(7) = GPIO_bit(7);              /* #PCLR low (reset) */
+	pxa_gpio_mode(GPIO7_48MHz_MD);
+	pxa_gpio_mode(GPIO12_32KHz_MD);
+	mdelay(10);
+	GPSR(19) = GPIO_bit(19);            /* #SUSPEND high */
+	mdelay(10);
+	GPSR(7) = GPIO_bit(7);              /* #PCLR high */
+	mdelay(10);
+}
+
+static void e740_t7l66xb_suspend(void) {
+	GPCR(19) = GPIO_bit(19); /* #SUSPEND low */
+	mdelay(10);
+	GPCR(7) = GPIO_bit(7);   /* #PCLR low */
+
+#if 0
+	/* kill clock */
+	pxa_gpio_mode(GPIO7_48MHz_MD|GPIO_OUT);
+	GPSR0 = GPIO_bit(GPIO7_48MHz);
+#endif
+}
+
+static void e740_t7l66xb_resume(void) {
+	e740_t7l66xb_hwinit();
+}
+
+static struct t7l66xb_platform_data e740_t7l66xb_info = {
+	.irq_base 		= IRQ_BOARD_START,
+	.hw_init                = &e740_t7l66xb_hwinit,
+	.suspend                = &e740_t7l66xb_suspend,
+	.resume                 = &e740_t7l66xb_resume,
+};
+
+static struct platform_device e740_t7l66xb_device = {
+	.name           = "t7l66xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e740_t7l66xb_info, 
+	},
+	.num_resources = ARRAY_SIZE(e740_t7l66xb_resources),
+	.resource      = e740_t7l66xb_resources,
+}; 
+
+static int __init e740_t7l66xb_init(void)
+{
+	if(!machine_is_e740())
+		return -ENODEV;
+
+	platform_device_register(&e740_t7l66xb_device);
+	return 0;
+}
+
+module_init(e740_t7l66xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>");
+MODULE_DESCRIPTION("e740 t7l66xb device support");
+MODULE_LICENSE("GPLv2");
+
+
diff --git a/arch/arm/mach-pxa/e750_tc6393xb.c
b/arch/arm/mach-pxa/e750_tc6393xb.c
new file mode 100644
index 0000000..c204e2f
--- /dev/null
+++ b/arch/arm/mach-pxa/e750_tc6393xb.c
@@ -0,0 +1,109 @@
+/* 
+ *
+ *  Copyright (C) Ian Molton.
+ *
+ *  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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/tc6393.h>
+
+static struct resource e750_tc6393xb_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,
+	},
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or
kernel ? */
+static void e750_tc6393xb_hwinit(void) {
+	pxa_gpio_mode(GPIO11_3_6MHz_MD);
+
+	GPCR(45) = GPIO_bit(45); /* #SUSPEND low      */
+	GPCR(19) = GPIO_bit(19); /* #PCLR low (reset) */
+	mdelay(1);
+	GPSR(45) = GPIO_bit(45); /* #SUSPEND high     */
+	mdelay(10);              /* FIXME - probably 1000x too long */
+	GPSR(19) = GPIO_bit(19); /* #PCLR high        */
+}
+
+static void e750_tc6393xb_suspend(void) {
+	GPSR(45) = GPIO_bit(45); /* #SUSPEND high */
+	mdelay(10);
+	GPSR(19) = GPIO_bit(19); /* #PCLR high    */
+	
+	pxa_gpio_mode(GPIO11_3_6MHz_MD|GPIO_OUT);
+	GPSR0 = GPIO_bit(GPIO11_3_6MHz);
+}
+
+/*
+MCR : 80aa
+CCR: 1310
+PLL2CR: 0c01
+PLL1CR1: f743
+PLL1CR2: 00f2
+SYS_DCR: 1033
+*/
+
+static struct tc6393xb_platform_data e750_tc6393xb_info = {
+	.irq_base 		= IRQ_BOARD_START,
+	.sys_pll2cr             = 0x0cc1,
+	.sys_ccr                = 0x1310,
+	.sys_mcr                = 0x80aa,
+	.sys_gper		= 0,
+	.sys_gpodsr1            = 0,
+	.sys_gpooecr1           = 0,
+	.hw_init                = &e750_tc6393xb_hwinit,
+	.suspend                = &e750_tc6393xb_suspend,
+};
+
+static struct platform_device e750_tc6393xb_device = {
+	.name           = "tc6393xb",
+	.id             = -1,
+	.dev            = {
+		.platform_data = &e750_tc6393xb_info, 
+	},
+	.num_resources = ARRAY_SIZE(e750_tc6393xb_resources),
+	.resource      = e750_tc6393xb_resources,
+}; 
+
+static int __init e750_tc6393xb_init(void)
+{
+	if(!(machine_is_e750() || machine_is_e800()))
+		return -ENODEV;
+
+	platform_device_register(&e750_tc6393xb_device);
+	return 0;
+}
+
+module_init(e750_tc6393xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>");
+MODULE_DESCRIPTION("e740 tc6393 device support");
+MODULE_LICENSE("GPLv2");
+
+
-- 
1.5.3.5.737.gdee1b



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux